DEV Community

Cover image for Server-Side Template Injection Vulnerability?
I Am A Hacker
I Am A Hacker

Posted on

Server-Side Template Injection Vulnerability?

Hackers have a sneaky trick called "server-side template injection," and it can turn your secure haven into a data-breached disaster zone. But fear not, internet warriors! We're here to unveil this hidden threat and equip you with the tools to shut it down for good. No more sleepless nights worrying about stolen passwords or hijacked accounts. Let's build an impenetrable shield against server-side template injection, one line of code at a time!

Meet Integrity—an exciting online platform that serves as a global hub for crowdsourced security. This dynamic space not only facilitates bug bounty programs but also empowers bug bounty hunters to showcase their skills. I've been avidly tracking their Twitter account for quite some time now. Occasionally, they throw out intriguing challenges—be it a snippet of code or a puzzle—and invite people to take on the quest. It's a thrilling journey of problem-solving and community engagement!

Integrity just dropped another enigma on Twitter, and my detective senses are tingling! Let's unravel the latest challenge before the clock runs out.

Image description

so the challenge is basically about Server-Side Template Injection Vulnerability but in a very nice way so they posted Python code based on the Flask framework.

from flask import Flask, request
from jinja2 import Environment

app = Flask(__name__)
Jinja2 = Environment()

@app.route("/email-settings/opt-out")
def email_opt_out():
    user_email = request.values.get("user_email")
    user_email = user_email.sub( "(\{|\})", "", user_email, 2 )

    output = Jinja2.from_string(
        '<h1> Are you sure you want to opt out ' + user_email
        + ' from receiving any future promotional emails ? </h1>'
        + '<a href="/">cancel</a>'
        + '<button style="margin-left:1rem;" onClick="submit()"> Unsubscribe</button>'
    ).render()

    return output

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000)

Enter fullscreen mode Exit fullscreen mode

when I tried and run the code there was a slight problem that was the code was missing re library and also instead of using user_email to sub-function you need to use re so the proper working code looks like below.

from flask import Flask, request, render_template_string
from jinja2 import Environment
import re

app = Flask(__name__)
Jinja2 = Environment()

@app.route("/email-settings/opt-out")
def email_opt_out():
    user_email = request.values.get("user_email")
    user_email = re.sub( "(\{|\})", "", user_email, 2 )

    output = Jinja2.from_string(
        '<h1> Are you sure you want to opt out ' + user_email
        + ' from receiving any future promotional emails ? </h1>'
        + '<a href="/">cancel</a>'
        + '<button style="margin-left:1rem;" onClick="submit()"> Unsubscribe</button>'
    ).render()

    return output

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000)

Enter fullscreen mode Exit fullscreen mode

Now that the code works let's understand what was the solution implemented by the developer and where does it fails.

Once you start the server you can access the function by the following URL...

http://localhost:5000/email-settings/opt-out?user_email={{7*'7'}}
Enter fullscreen mode Exit fullscreen mode

it would work fine for removing just two brackets but if you enter 3 the check will be bypassed.

http://localhost:5000/email-settings/opt-out?user_email={{{7*'7'}}}
Enter fullscreen mode Exit fullscreen mode

Now if you are a bug bounty hunter and working with a client and you have come this far your job is done you can now report this to the company and they will approve but if you are a developer and you need to find a solution for this problem read more because a solution to the problem is coming...

In order to resolve the issue in the code you can use proper sanitization...

for instance, you can use the flask escape function

from flask import escape

# ...

user_email = escape(request.values.get("user_email"))
Enter fullscreen mode Exit fullscreen mode

Or you can use jinja2 Markup function like below...

from jinja2 import Markup

# ...

output = Jinja2.from_string(
    Markup('<h1> Are you sure you want to opt out ' + user_email
    + ' from receiving any future promotional emails ? </h1>'
    + '<a href="/">cancel</a>'
    + '<button style="margin-left:1rem;" onClick="submit()"> Unsubscribe</button>')
).render()
Enter fullscreen mode Exit fullscreen mode

hopefully, the blog is an interesting read if so do like share, and follow because some awesome content is coming...

Top comments (0)