There’s a lot you can do within an interpreter even without direct access to source.
dir()will list out all the available names inside the current space. You can also use
dir()on a function to see what attributes and properties the function has.
func_name.func_codehas a ton of useful properties to get information about the function. Among the most useful:
func_name.func_code.co_code- Gives you the bytecode representing the function.
func_name.func_code.co_consts- Constant numbers and strings present inside the function.
func_name.func_code.co_varnames- Names of variables inside the function.
I first came across this problem during TJCTF 2018, “Mirror Mirror”.
We were dropped into an REPL with the knowledge that there was a get_flag function.
Here is what the code looked like:
super_secret_string = 'this_is_the_super_secret_string'
if input.isalnum() or '_' in input:
return "Character not allowed"
if(eval(input) == super_secret_string):
print eval(input) + ' is not a valid character\n'
print "nice, here's your flag" + flag
print "You didn't guess the value of my super_secret_string\n"
Of course, most special functions were blocked so I couldn’t view the flag directly.
With big credit to http://wapiflapi.github.io/2013/04/22/plaidctf-pyjail-story-of-pythons-escape/, we were able to create a string that would evaluate to another string, bypassing the input check. I stole the following from https://ctftime.org/writeup/10677.
You can do tons of interesting things in a shell using shell expansions to call binaries even if you are restricted by the letters you can input. For example:
/???/???/?????32 expands to /usr/bin/linux32, which will give you a simple dash shell. You could also run python using