November '25
AquaCommerce
This was a fun challenge containing elements I would have found on my own.
First thing I looked for was an API that would send the stock to the frontend. Unfortunately this was a dead end.
When registering and logging in, I found the profile page with a role:user, so that’s when I figured I could mass assign myself a role of admin. Unfortunately, this did not work. What did work was changing the role in the JWT, signing it with none , take that token, put it inside the token cookie and refresh the page. This gave me access to an admin panel.
Inside that admin panel, there wasn’t much to do, except for a profile page. I noticed that I could change my username, so I first tried an HTML injection with <h1>Testing</h1>but that did not work. Then I was able to inject a XSS with <script>alert()</script> but the objective was to get RCE, and with a script tag, I was not going to do that.
Then I got a hint that suggested SSTI. I never realized it, but SSTI is, in fact, a sort of RCE. With the payload {{7*7}} I saw that the username was 49. So the server performed an action.
The tricky part was to get a payload for this. I used ChatGPT to help me out.
With the command {{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }} I was able to get the ID. With the command {{ self.__init__.__globals__.__builtins__.__import__('os').popen('ls -la /app').read() }} I was able to see some contents.
Navigating to /app/.aquacommerce and doing an lsreavaled the file I was looking for.
With the command {{ self.**init**.**globals**.**builtins**.**import**('os').popen('cat /app/.aquacommerce/019a82cf.txt').read() }} could read the flag and submit my solution.
Last updated