To solve this machines, we begin by enumerating open ports – finding ports
80 open. On the webserver, we register an account to gain access to the notes feature. Once we have an account created, we are issued a JWS. Due to a vulnerability within the JWS, we are able to forge an administrative token – gaining access to the admin section of the site. With admin access, we are able to obtain a reverse shell – as the
www-data user. As
www-data, we are able to read a backup archive, and gain the SSH private key for the
noah user – allowing us to read
noah, we are able to execute a docker container as
root without a password. Lastly, we are able to exploit a component of docker to execute a reverse shell as
root – gaining access to
Like all machines, we begin by enumerating open ports using
nmap. From our scan, we find ports
$ sudo nmap -Pn -p- -sV -A -oA enum/nmap/tcp-all-scripts 10.129.115.24 # Nmap 7.91 scan initiated Sun Mar 7 14:32:58 2021 as: nmap -Pn -p- -sV -A -oA enum/nmap/tcp-all-scripts 10.129.115.24 Nmap scan report for 10.129.115.24 Host is up (0.045s latency). PORT STATE SERVICE VERSION 22/tcp open ssh 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) | 256 e7:81:d6:6c:df:ce:b7:30:03:91:5c:b5:13:42:06:44 (ECDSA) |_ 256 c6:06:34:c7:fc:00:c4:62:06:c2:36:0e:ee:5e:bf:6b (ED25519) 80/tcp open http nginx 1.14.0 (Ubuntu) |_http-server-header: nginx/1.14.0 (Ubuntu) |_http-title: The Notebook - Your Note Keeper 10010/tcp filtered rxapi Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port Aggressive OS guesses: Linux 5.0 - 5.3 (95%), Linux 4.15 - 5.6 (95%), Linux 3.1 (95%), Linux 3.2 (95%), Linux 5.3 - 5.4 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), Linux 2.6.32 (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Linux 3.2 - 4.9 (92%) No exact OS matches for host (test conditions non-ideal). Network Distance: 2 hops Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel TRACEROUTE (using port 22/tcp) HOP RTT ADDRESS 1 44.81 ms 10.10.14.1 2 44.97 ms 10.129.115.24 OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Sun Mar 7 14:33:23 2021 -- 1 IP address (1 host up) scanned in 24.58 seconds
Next, we use
gobuster to bruteforce files and directories. From this, we learn of a couple interesting endpoints.
# Interesting endpoints found via gobuster /admin (Status: 403) /logout (Status: 302) /login (Status: 200) /register (Status: 200)
Since the webserver is the only avenue we see forward, we head over to the website. We are presented with a page that tells us it’s a note taking app, and to get started, we should register an account.
After logging in, we are issued 2 cookies, and redirected to our home page. One of the cookies is a UUID, and the other appears to be a JWT. After decoding the JWT, we learn it may potentially fetch its key file from a remote location – noted by
http://localhost:7070/privKey.key in the screenshot.
Going to the notes page, we are allowed to add notes, and attempt to test for injection vulnerabilities. Our checks fail, so we decide to focus on the JWT.
To test the key fetch for a vulnerability, we first decode the JWT, and edit the server and port of the “kid” field to point to our machine on port
80. Next, we re-encode it. Once re-encoded, we replace the JWT in our browser’s cookie jar (via devtools). Now to test the key fetch, we start a webserver on our machine, and refresh the page. After doing so, we do get a request for
/privKey.key from the webserver.
Since we are able to control the private key of the JWT, we should be able to forge a JWT for any user we want. Since we saw the
/admin directory, we can assume
admin is a valid user, and will have access to that directory. To forge the JWT, we copy it from our request history or browser cookie jar, and head over to
https://jwt.io. Once there, we change the algorithm to “RS256” and the site will generate a sample JWT with public and private keys. Next, we post our JWT into the “Encoded” field, which decodes the JWT for us (on the right), and allows us to make edits. For our edits, we set “username” to “admin”, and change “admin_cap” to “1”. Next, we set the “kid” server and port values to point to our machine. Lastly, we copy the private key to our local machine and name it “privkey.key”.
Before we attempt to access the
/admin directory, we replace the JWT in our cookie jar with the one we created on
https://jwt.io. So the remote machine can access our key, we use
python to serve a HTTP server, so the remote machine may be able to access the private key. Then in our browser, we attempt to access the
/admin directory. In our terminal, we see a connection to our HTTP server, and in the browser, we see the admin interface.
admin has a “Notes” link as well, we think it is a good idea to check it out for possible sensitive information. There are a few notes, notably, we read that php files are able to be executed, and another note that backups are scheduled.
Since there is also an “Upload” link, we head over to this to see if we are able to upload a php reverse shell. We are presented with an upload form, and supply a php reverse shell script. *For this, check out
laudanum* Once the script uploads, it is listed under our files – with the name replaced by a hash. Assuming the “View” option will execute the reverse shell, we start our socket listener with
netcat. Once we click “View”, we do indeed get a reverse shell as
Now that we have access to the machine, we decide to look into backups, since one of
admin‘s notes mentioned them. Going to
/var/backups/, we see that
home.tar.gz is globally readable.
By the name, we assume this is a backup of
/home, so we copy it to
/tmp and extract it. After we extract the archive, we are given a backup of
noah‘s SSH key among other files.
We copy the SSH private key to our local machine, set the correct permissions, and log into the machine as
noah. We now have access to read
Now that we have a shell as
noah, we begin our initial enumeration. The first thing we check is if we can run any commands as another user using
sudo -l. From this, we learn we are able to run
sudo /usr/bin/docker exec -it webapp-dev01* as
root. To verify this, we try to run
bash on the
webapp-dev01 container. Doing so, we do indeed get a shell as
Since the sudo configuration is pretty much hard coded that we can only run a command on the container, our usual container escapes are not likely to work here. Before we attempt to dive deeper into privilege escalation via docker, we run
linpeas to automate the rest of our enumeration. Doing so, we see a note that
runc is installed and that we may be able to use it to escalate our privileges.
The rest of our
linpeas output did not point out anything else immediately interesting, so we decide to explore
runc more. First, we check its version, then we check the version for vulnerabilities via
searchsploit, we learn of 2 possible container breakouts, but
docker has to be less than version “18.09.2”. We check
docker‘s version, and see that it is version
18.06.0, which means these exploits should work.
Using this exploit as our guide, we download the archive to our local machine, extract the files, make the instructed changes, and upload the files to the
/tmp directory of the container. Next, we start our reverse shell listener on our local machine. Lastly, to execute the reverse shell, we run
pwn.sh as instructed, then run
sudo /usr/bin/docker exec -it webapp-dev01 /bin/sh from the container’s host (as
noah). Once we execute the docker shell, we get a reverse shell on our local machine as
root. We now are able to read
Thank you for taking the time to read my write-up. I am interested in other ways this machine has been solved. Feel free to reach out to me and we can discuss it. Thanks!