Write-up: Basic server-side template injection @ PortSwigger Academy

Frank Leitner
InfoSec Write-ups
Published in
4 min readNov 21, 2022

--

This write-up for the lab Basic server-side template injection is part of my walk-through series for PortSwigger’s Web Security Academy.

Learning path: Advanced topics → Server-side template injection

Python script: script.py

Lab description

Steps

Analysis

As usual, the first step is to analyze the functionality of the lab application. In this lab, it is a shop website.

I’m not provided with any credentials nor is there any login functionality. Therefore the vulnerability is on the public page. I browse a bit to check the application’s usage of URL parameters.

For the product details, the productID is passed via the URL which is used by the application to load the proper information.

For one product, I receive an out-of-stock notification. The link on the page contains the normal productID argument. It appears that the application checks availability server-side. If a product is out of stock, the server redirects to the main page with the corresponding message as a URL parameter. The browser then requests this page:

The response contains the message embedded within a div tag.

Finding the vulnerability

I don’t know if any sanitization is done on the message text. But the very existence of the full message text as URL parameter merits a closer look.

To perform various tests I send the /?message= request to Burp Repeater.

As a first check, I test a message containing various urlencoded special characters that are often used in templates ${{<%[%'"}}%\:

The result shows that the characters <% are not contained in the response. It may be due to some sanitization or because it was evaluated by some logic (e.g. a template engine) on the server.

The lab description references ERB templates, so I go check the documentation for Ruby templates.

The documentation states that <% and %> are used as the start and end of template expressions respectively. This corresponds to my finding above.

An expression can be evaluated with the syntax <%= Ruby expression %>, so I try to execute a simple math operation:

The response contains the mathematical result instead of the message text. This indicates that I can execute arbitrary ERB expressions on the server.

The malicious payload

According to the ERB documentation, arbitrary ruby code can be executed by <% Ruby code -- inline with output %>.

I continue searching the documentation until I find the proper method to remove a file.

Now I have all the information required to craft my malicious payload. The fileutils module is not available directly, so I need to require it first: <% require 'fileutils'; FileUtils.rm '/home/carlos/morale.txt' %>

The request does not result in any error. The div is empty but this is expected as FileUtils.rm does not return anything. Refreshing the lab application in the browser shows that the lab is solved:

How to avoid this flaw

Most of these issues could be avoided by not including the message text in the URL but using an ID together with a server-side lookup table containing the messages (it is much easier to sanitize a known ID format than an arbitrary text message).

From Infosec Writeups: A lot is coming up in the Infosec every day that it’s hard to keep up with. Join our weekly newsletter to get all the latest Infosec trends in the form of 5 articles, 4 Threads, 3 videos, 2 GitHub Repos and tools, and 1 job alert for FREE!

--

--

Tech nerd, doing security stuff for fun and some as a job | CISSP-ISSAP, OSCP