I love hacking. Recently, YesWeHack released a video on YouTube of Brumens’s talk about advanced SSTI techniques from the 2024 Ekoparty conference. This has inspired me to circle back on this topic, redo the Portswigger labs and document. I know SSTI is there, but, to be honest, I usually don’t really check for it unless I am doing a CTF and there’s a hint that might be the path.
YouTube Video: https://www.youtube.com/watch?v=FVm6wYc1S6A
Let’s get rolling with a series of blog posts solving all of the Portswigger Web Academy labs on the topic.
- >>SSTI – Basic server-side template injection<<
- SSTI – Basic server-side template injection (code context)
- SSTI – Server-side template injection using documentation
- SSTI – Server-side template injection in an unknown language with a documented exploit
- SSTI – Server-side template injection with information disclosure via user-supplied objects
- SSTI – Server-side template injection in a sandboxed environment
- SSTI – Server-side template injection with a custom exploit
NOTE – Links will light up as posts become available.
Fuzzing resources:
- PayloadsAllTheThings: https://github.com/swisskyrepo/PayloadsAllTheThings
- SecLists: https://github.com/danielmiessler/SecLists
- AssetNote: https://wordlists.assetnote.io/
What is SSTI
SSTI is server side template injection. There are a ton of posts that go deeper into exactly what SSTI is (or ask your favorite LLM). Essentially, Server-Side Template Injection (SSTI) occurs when user input is unsafely processed by a web application’s template engine, allowing attackers to execute arbitrary code on the server.
If a vulnerable web app processes input like {{7*7}}
, it evaluates the expression instead of treating it as text, returning 49
. If further exploited, attackers can execute system commands, access sensitive data, or take control of the server.
More great resources are available here: SSTI Resources
What’s really cool about SSTI is that we’re not just focused on business logic vulnerabilities. Sure, it’s great to find APIs you can abuse for the purposes of breaking the business logic and accomplishing things like BOLAs (Broken Object Level Authorizations), however, to me, it seems a lot cooler to just take over the entire server. With SSTI, you’re really looking for an injection vulnerability that leads to RCE (remote code execution) on the server which can lead to things like throwing a remote shell. Get your Netcat ready!
Note – for bug bounty, throwing a remote shell typically isn’t necessary. A simple whoami
demonstrates code execution and is plenty scary.
Lab: Basic server-side template injection
Recently, I have been using Caido as my primary web pen-testing tool. Burp is definitely still in my arsenal, however, I do like the slick and bit more simplistic interface Caido presents. The devs have been working hard on bringing great tooling into Caido and it does seem lighter weight and a tad quicker (totally just my feel – data to back that up).
Get Caido here: https://caido.io/
Step 1: Recon
Let’s see if we can figure out where the SSTI vulnerability exists within this lab. Loading the lab, we get the standard shop interface:

Clicking View Details button on the first item and I get the following:

However, I can see the URL has changed:

So, do we have template injection where the template literally reflects what’s passed through the message query parameter?
Step 2: Fuzz
One of the things I do like about Caido is that it seems easier to manage scope. As soon as I launch Chromium via Caido:

All of the Google noise starts raining in and filling the HTTP History log:

I haven’t even plugged the URL for the lab into the browser and there’s just so much noise heading back to the mothership. With that, it’s super easy to create out of scope items and apply them to our work here so that we can get rid of the noise:

And then tune the scope:

And, now there’s no noise in the history. This is pretty nice:

Ok, now let’s plug the lab URL into our Chromium browser and see what we see.

Make sure these are checked to remove a images and styling from the history for this lab. You will not always want to have these checked, but for this lab we probably won’t be dealing with CSS or images.

Let’s fuzz this request so send to Automate (equivalent to Burp Intruder)
First, let’s set the spot we want to fuzz. Highlighter and click the plus to add:

Then we need to set the payload. You can either import a payload as a file and then utilize or you can paste in a simple list. I am going to use this list (copy and paste in as simple): https://raw.githubusercontent.com/swisskyrepo/PayloadsAllTheThings/refs/heads/master/Server%20Side%20Template%20Injection/Intruder/ssti.fuzz
Now, the problem with running a list like this is that we really don’t know what we’re looking for within the response. So, let’s prepend a flag that will be easy to watch for in the response. That way we can easily search for that special string within the responses. Now, hit run

We can see here that the string reflected is the string that was passed in from the payload list. Arrowing through the responses, I find one that looks promising very early:

What was in payload position #4?
<%= 7 * 7 %>
There are different types of template engines. Different payloads are pointed at triggering code execution within those different engines. This specific payload most likely triggered code execution from Embedded Ruby (ERB).
We have RCE!
Step 3: Explore
Let’s see if we can get additional information about the system. Right mouse click on the request that triggered the RCE and hit send to Replay (equivalent to Burp Repeater).
Quick note on Caido – there are Plugins that extend the functionality of Caido just like Burp has its extensions. One of the Plugins I load immediately for Caido is Convert Tools.

This tool adds in different options and boxes within the UI that enable different types of encoding conversions on the fly. Within Replay, we get this nifty box in the lower left that is handy for changing the string within the payload without having to decode and then recode:

This is pretty sweet. The little editor window let me paste in a payload from PayloadsAllTheThings which truly proves we have RCE by retrieving the /etc/passwd file.
Step 4: Solve the lab
Lab instructions: This lab is vulnerable to server-side template injection due to the unsafe construction of an ERB template.
To solve the lab, review the ERB documentation to find out how to execute arbitrary code, then delete the morale.txt
file from Carlos’s home directory.
I guess if we had read the instructions we would have seen that the underlying implementation was utilizing ERB and we could have skipped the fuzzing step. All good. Regardless, we now need a payload that is going to delete the morale.txt file from Carlos’s home directory. Since we retrieved /etc/passwd, the path is probably going to be /home/carlos/morale.txt. However, let’s walk it a step at a time.

returns:

So,

returns:

So,

returns:

So,


and

Done!
Summary
Server-Side Template Injection (SSTI) is a powerful attack vector that allows execution of arbitrary code by injecting payloads into vulnerable template engines. Above, we walked through the first Portswigger lab exploiting SSTI specifically with the ERB template engine, demonstrating how seemingly harmless user input can lead to full system compromise. Good stuff!
Key Takeaways:
- SSTI = Code Execution – If a template engine is injectable, it’s a very powerful vuln that can lead to a high payout.
- Know Your Payloads – Different engines have different syntax—identify the target and adapt. Use the payloads from the community and then craft your own over time.
- Post-Exploitation Matters – Once inside, leverage access for deeper attacks. Show true RCE and then turn it in!
SSTI isn’t just about injecting expressions – often its about owning the entire box.
Happy Hacking!