HTB: Templated

This is a pretty quick challenge which stresses the importance of sanitizing user input – especially with server-side rendering. To start this, select the challenge on HTB and you’ll be provided with a link to a docker container that you can access outside the VPN.

Initially the link just shows a little “under construction” page and a note that it’s powered by Flask/Jinja2. Having built a few sites with Flask and Jinja templating there are a few easy checks to see if an input is vulnerable to injection, but since we don’t have any inputs here we need to enumerate the site a bit and see if there is somewhere else we can probe around.

Running dirbuster leads to some strange results – every single directory is returning with a 200 code. Running over to /test we can confirm this behavior:

Interestingly the path is displayed in the error message – maybe this is something we can attack! We can try the standard {{7*7}} check to see if we can run expressions in the URL:

Navigating to this URL shows us some success:

With ’49’ appearing instead of ‘7*7’ we have proven that we can run expressions by placing them in the URL. Flask holds a lot of configuration info in the config variable, so lets put that in and see what happens:

This gives us a ton of info, but not really anything useful for the challenge. Since the flag is not located in one of these config keys we’ll need to dig a bit further.

RCE in Flask/Jinja2

After doing some research in exploiting Flask/Jinja2 applications we find that we can pull in other classes by taking advantage of how Python handles inheritance. For example, we can create an empty string and by using the __class__ and __base__ dunders end up with every subclass in the currently loaded interpreter. To see this, navigate to:

/{{''.__class__.__base__.__subclasses__()}}

This mess of a “URL” will also return a mess – a giant array of classes:

There are some interesting classes in here, so slice around until you find one that you like – in my case subprocess.Popen was available at the 414th index. Since we can run system commands with Popen we should be able to execute code. To test this out, run the following in the url:

/{{''.__class__.__base__.__subclasses__()[414]('ls', shell=True, stdout=-1).communicate()}}

This runs the ls command, which actually shows us the flag location!

Before trying to spawn a reverse shell we can attempt to read the flag file by replacing ls with cat flag.txt.

All done!

Leave a comment