This post documents the complete walkthrough of Pit, a retired vulnerable VM created by polarbearer and GibParadox, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.

On this post


Pit is a retired vulnerable VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

masscan -e tun0 -p1-65535,U:1-65535 --rate=500
Starting masscan 1.3.2 ( at 2021-05-16 15:29:47 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 9090/tcp on
Discovered open port 161/udp on
Discovered open port 22/tcp on
Discovered open port 80/tcp on

Looks pretty normal to me, except for 161/udp. SNMP is available? Let’s do one better with nmap scanning the discovered ports to establish their services.

nmap -n -v -Pn -p22,80,9090 -A --reason -oN nmap.txt
22/tcp   open  ssh             syn-ack ttl 63 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            syn-ack ttl 63 nginx 1.14.1
| http-methods:
|_  Supported Methods: GET HEAD
|_http-server-header: nginx/1.14.1
|_http-title: Test Page for the Nginx HTTP Server on Red Hat Enterprise Linux
9090/tcp open  ssl/zeus-admin? syn-ack ttl 63
| fingerprint-strings:
|   GetRequest, HTTPOptions:
|     HTTP/1.1 400 Bad request
|     Content-Type: text/html; charset=utf8
|     Transfer-Encoding: chunked
|     X-DNS-Prefetch-Control: off
|     Referrer-Policy: no-referrer
|     X-Content-Type-Options: nosniff
|     Cross-Origin-Resource-Policy: same-origin
|     <!DOCTYPE html>
|     <html>
|     <head>
|     <title>
|     request
|     </title>
|     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <style>
|     body {
|     margin: 0;
|     font-family: "RedHatDisplay", "Open Sans", Helvetica, Arial, sans-serif;
|     font-size: 12px;
|     line-height: 1.66666667;
|     color: #333333;
|     background-color: #f5f5f5;
|     border: 0;
|     vertical-align: middle;
|     font-weight: 300;
|_    margin: 0 0 10p
| ssl-cert: Subject: commonName=dms-pit.htb/organizationName=4cd9329523184b0ea52ba0d20a1a6f92/countryName=US
| Subject Alternative Name: DNS:dms-pit.htb, DNS:localhost, IP Address:
| Issuer: commonName=dms-pit.htb/organizationName=4cd9329523184b0ea52ba0d20a1a6f92/countryName=US
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-04-16T23:29:12
| Not valid after:  2030-06-04T16:09:12
| MD5:   0146 4fba 4de8 5bef 0331 e57e 41b4 a8ae
|_SHA-1: 29f2 edc3 7ae9 0c25 2a9d 3feb 3d90 bde6 dfd3 eee5
|_ssl-date: TLS randomness does not represent time

I’d better map dms-pit.htb and pit.htb to in /etc/hosts.

SNMP Enumeration

I’d better use snmpwalk to enumerate SNMP. It does a better job than nmap at gathering and interpreting the OIDs, coupled with the fact that I’ve downloaded MIB files in advance. I’d show some of the more “interesting” strings.

snmpwalk -v2c -c public .
SNMPv2-MIB::sysDescr.0 = STRING: Linux pit.htb 4.18.0-240.22.1.el8_3.x86_64 #1 SMP Thu Apr 8 19:01:30 UTC 2021 x86_64
SNMPv2-MIB::sysObjectID.0 = OID: NET-SNMP-MIB::netSnmpAgentOIDs.10
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (7481110) 20:46:51.10
SNMPv2-MIB::sysContact.0 = STRING: Root <[email protected]> (configure /etc/snmp/snmp.local.conf)
SNMPv2-MIB::sysName.0 = STRING: pit.htb
UCD-SNMP-MIB::dskPath.1 = STRING: /
UCD-SNMP-MIB::dskPath.2 = STRING: /var/www/html/seeddms51x/seeddms
UCD-SNMP-MIB::dskDevice.1 = STRING: /dev/mapper/cl-root
UCD-SNMP-MIB::dskDevice.2 = STRING: /dev/mapper/cl-seeddms
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 1
NET-SNMP-EXTEND-MIB::nsExtendCommand."monitoring" = STRING: /usr/bin/monitor
NET-SNMP-EXTEND-MIB::nsExtendArgs."monitoring" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendInput."monitoring" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendCacheTime."monitoring" = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendExecType."monitoring" = INTEGER: exec(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType."monitoring" = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendStorage."monitoring" = INTEGER: permanent(4)
NET-SNMP-EXTEND-MIB::nsExtendStatus."monitoring" = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."monitoring" = STRING: Memory usage
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."monitoring" = STRING: Memory usage
              total        used        free      shared  buff/cache   available
Mem:          3.8Gi       413Mi       2.8Gi       147Mi       625Mi       3.0Gi
Swap:         1.9Gi          0B       1.9Gi
Database status
OK - Connection to database successful.
System release info
CentOS Linux release 8.3.2011
SELinux Settings

                Labeling   MLS/       MLS/
SELinux User    Prefix     MCS Level  MCS Range                      SELinux Roles

guest_u         user       s0         s0                             guest_r
root            user       s0         s0-s0:c0.c1023                 staff_r sysadm_r system_r unconfined_r
staff_u         user       s0         s0-s0:c0.c1023                 staff_r sysadm_r unconfined_r
sysadm_u        user       s0         s0-s0:c0.c1023                 sysadm_r
system_u        user       s0         s0-s0:c0.c1023                 system_r unconfined_r
unconfined_u    user       s0         s0-s0:c0.c1023                 system_r unconfined_r
user_u          user       s0         s0                             user_r
xguest_u        user       s0         s0                             xguest_r

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
michelle             user_u               s0                   *
root                 unconfined_u         s0-s0:c0.c1023       *
System uptime
 11:47:10 up 20:47,  0 users,  load average: 2.44, 1.32, 1.10

Looks like we have SeedDMS installed and a user michelle.


Interestingly, Cockpit is behind 9090/tcp. Cockpit is a web-based graphical interface to manage servers. It’s available at https://dms-pit.htb:9090/.

Let’s leave it at this for a while…


What is SeedDMS?

SeedDMS is a free document management system with an easy to use web based user interface for small and medium sized enterprises. It is based on PHP and MySQL or sqlite3 and runs on Linux, MacOS and Windows. Many years of development has made it a mature, powerful and enterprise ready platform for sharing and storing documents.

Recall in the SNMP enumeration we had this path /var/www/html/seeddms51x/seeddms? Put two and two together, and we have SeedDMS at http://dms-pit.htb/seeddms51x/seeddms/.

Remember michelle? I’m surprised I was able to log in with (michelle:michelle) :laughing:.

SeedDMS versions < 5.1.11 - Remote Command Execution

Although a note was left by the Administrator that SeedDMS was upgraded to 5.1.15 from 5.1.10, I’m not entirely convinced that’s the case.

According to EDB-ID 47022, as long as you have the document identifier, you can access the uploaded file, e.g. a PHP backdoor, through hardcoded path like so.

Upload a document

View a document

The document identifier can be seen in the URL.

Armed with the document identifier, we can load the PHP file by going to the following URL.


Now that we have remote command execution via the PHP backdoor, let’s start enumerating /var/www/html, shall we? Very quickly, this is what I found.

<database dbDriver="mysql" dbHostname="localhost" dbDatabase="seeddms" dbUser="seeddms" dbPass="ied^ieY6xoquu" doNotCheckVersion="false"></database>

Cockpit Redux

Remember this? Let’s see if we can log in to Cockpit with this credential (michelle:ied^ieY6xoquu).

And what do we have here?

This is pretty awesome. The file user.txt is at michelle’s home directory.

Privilege Escalation

During enumeration of michelle’s account, I notice that /usr/bin/monitor from the SNMP enumeration is a shell script.

Moving on to enumerate /usr/local/monitoring, this is what I saw.

Notice the small plus (+) symbol on monitoring? This tells me that there are extra ACL attributes on the directory. We can use getfacl on the directory.

So, michelle can write to this directory. :thinking:

SNMP nsExtendObjects

How about this?

We write a shell script to /usr/local/monitoring/ as michelle. We can then trigger /usr/bin/monitor to run by simply snmpwalking nsExtendObjects.

Let’s test who’s running /usr/bin/monitor.

Check this out.

I guess the end is near. We’ll just write a SSH public key we control to /root/.ssh/authorized_keys.

Getting root.txt with a root shell is trivial.