September 9, 2026
Forge v4.2 โ From SSRF to Full Server Compromise
Introduction

By Maryam Salahli
6 min read
Introduction
In this lab, I worked on Forge v4.2, a deliberately vulnerable application designed to demonstrate how multiple server-side vulnerabilities can be chained together.
The interesting part of this challenge wasn't just finding individual vulnerabilities. The real objective was understanding how seemingly separate weaknesses could be combined to move from limited application-level access to server-side code execution and eventually full server compromise.
During the assessment, I identified and exploited several vulnerabilities, including:
- Server-Side Request Forgery (SSRF)
- Server-Side Template Injection (SSTI)
- Path Traversal
- Insecure File Upload
- Insecure Deserialization
- Remote Code Execution (RCE)
The assessment was performed against the local Forge v4.2 environment.
Reconnaissance & Endpoint Discovery
I started by looking at the application's available functionality and identifying interesting server-side endpoints.
The main endpoints in scope were:
/api/fetch
/api/preview
/api/template
/api/upload
/api/state/import
/api/render
/api/deploy/api/fetch
/api/preview
/api/template
/api/upload
/api/state/import
/api/render
/api/deployRather than immediately trying random payloads, I tested how each endpoint handled user-controlled input.
This quickly revealed that several endpoints were performing security-sensitive operations directly on data supplied by the user.
SSRF โ Reaching Internal Resources
The first interesting endpoint was:
The endpoint accepted a URL and made the request from the server side.
That immediately made SSRF a potential attack vector.
The application attempted to block local addresses, but the protection relied on a blacklist. This meant that although obvious values such as 127.0.0.1 and localhost were blocked, the protection could be bypassed.
By abusing the server-side request functionality, I was able to reach the internal metadata service.
This exposed sensitive information including credentials and a secret flag:
The finding was classified as High severity, CWE-918.
SSTI โ When User Input Becomes Code
Next, I looked at:
The endpoint appeared to process user-controlled content through a template engine.
I started with a harmless mathematical expression:
{{7*7}}{{7*7}}Instead of returning the expression as text, the server returned:
4949That was a strong indication that the input was being interpreted by the template engine.
This confirmed Server-Side Template Injection (SSTI).
The corresponding flag was:
The important lesson here is that template engines should treat user input as data, not as template source code.
Path Traversal โ Escaping the Sandbox
The next target was:
The endpoint accepted a filename and was supposed to restrict access to a specific directory.
The application blocked the classic:
../../pattern.
That suggested a blacklist-based protection.
Instead of stopping there, I considered how URL encoding affects request processing.
The application's validation happened before decoding, while the path was later normalized/decoded by the server.
This allowed the traversal protection to be bypassed.
As a result, I was able to read:
/etc/passwd/etc/passwdand eventually access the hidden flag outside the intended directory:
The key takeaway:
Blocking ../ is not path traversal protection.
Proper path normalization and validation against an allowed base directory is required.
Insecure File Upload
The next endpoint was:
Initially, I sent a request without a file to understand what the endpoint expected.
The response indicated that the application required a multipart parameter named:
filefileI then tested how the upload validation worked.
The important discovery was that the application relied heavily on the uploaded file's extension and request metadata.
A specially prepared upload was accepted and processed by the server.
The response contained:
stored: true
processed: truestored: true
processed: trueThe stage resulted in:
This demonstrated why file uploads should never rely solely on:
filename
extension
Content-Typefilename
extension
Content-Typefor security decisions.
Insecure Deserialization
Things became much more interesting at:
The endpoint was designed to import serialized build-state data.
The problem?
The server deserialized attacker-controlled data.
I created a specially crafted serialized object, encoded it appropriately, and supplied it through the state parameter.
The server successfully processed the object during deserialization.
This resulted in:
This is a classic example of why untrusted serialized data should never be deserialized directly.
SSTI โ RCE
The most interesting stage was:
Once again, I started with:
The server returned:
4949So the template engine was evaluating expressions.
But this endpoint was different.
The template environment was not properly sandboxed.
By investigating objects available to the template context, I was able to reach operating-system-level functionality.
Eventually, command execution was confirmed.
The server returned:
uid=0(root) gid=0(root) groups=0(root)uid=0(root) gid=0(root) groups=0(root)That meant the template injection had escalated to Remote Code Execution as root.
The flag:
At this point, the vulnerability was no longer simply SSTI.
It had become:
SSTI
โ
Python object access
โ
OS interaction
โ
Command execution
โ
RootSSTI
โ
Python object access
โ
OS interaction
โ
Command execution
โ
RootFull Compromise
The final stage demonstrated why vulnerability chaining matters.
The vulnerable functionality was spread across different endpoints, but the weaknesses could be combined.
The /api/deploy endpoint accepted either:
manifestmanifestor:
manifest_urlmanifest_urlUsing the server-side retrieval functionality, I was able to make the server retrieve a manifest from an internal source.
The deployment functionality then processed that manifest server-side.
The final result was:
The Complete Attack Chain
This was the most valuable part of the entire challenge for me.
The vulnerabilities weren't isolated findings.
They formed an attack chain:
Forge v4.2
โ
โผ
SSRF
โ
โผ
Internal Resource Access
โ
โผ
SSTI
โ
โผ
Path Traversal
โ
โผ
Insecure File Upload
โ
โผ
Insecure Deserialization
โ
โผ
Template RCE
โ
โผ
Root Execution
โ
โผ
Full CompromiseForge v4.2
โ
โผ
SSRF
โ
โผ
Internal Resource Access
โ
โผ
SSTI
โ
โผ
Path Traversal
โ
โผ
Insecure File Upload
โ
โผ
Insecure Deserialization
โ
โผ
Template RCE
โ
โผ
Root Execution
โ
โผ
Full CompromiseThe final stage specifically demonstrated how server-side fetching and unsafe rendering could be combined to access an internal manifest and achieve server-side execution.
Business Impact
If vulnerabilities like these existed in a real application, the consequences could be severe.
An attacker could potentially:
- Access internal credentials and secrets
- Read sensitive server files
- Access information belonging to other users
- Execute commands on the server
- Compromise the entire application
- Potentially move laterally into other internal systems
The most serious issue wasn't necessarily one individual vulnerability.
It was the combination of vulnerabilities.
Key Takeaways
The main lessons I took from this challenge were:
1. Don't rely on blacklists
Blocking strings such as ../, localhost, or 127.0.0.1 isn't enough. Use proper validation and allowlists.
2. Never treat user input as template source
User input should be passed as data rather than dynamically evaluated as a template.
3. Don't deserialize untrusted data
Unsafe serialization mechanisms can turn seemingly simple input handling into code execution.
4. File uploads need real validation
Checking only the filename extension or Content-Type is not sufficient.
5. Think in attack chains
This was probably the biggest lesson from Forge. A vulnerability that looks "medium" or "high" in isolation can become critical when combined with another weakness.
Conclusion
Forge v4.2 was a great example of how modern server-side vulnerabilities can interact with each other.
What started with seemingly separate issues โ SSRF, SSTI, Path Traversal, insecure file upload and insecure deserialization โ ultimately demonstrated a path toward root-level command execution and full server compromise.
For me, the most valuable part of the challenge wasn't simply getting the flags.
It was understanding why each vulnerability worked, how the application's trust boundaries were broken, and how individual weaknesses could be chained together into a much more serious attack path.
That is also what makes vulnerability research and penetration testing interesting: sometimes the real vulnerability isn't just one bug โ it's what happens when several bugs meet. ๐