Closed splitline closed 2 years ago
Oo, nice catch. The library already includes support for allowing single classes to be used (this is normally handled by the passed FakeClassFactory
instance), but the safe_modules argument exists as a bypass for modules that supposedly contain only data classes, like the _ast
module. I thought collections was also like that, since its documentation purely specifies it containing data classes. Unfortunately I seem to have forgotten about python's total lack of privacy for internal functions so all module imports are accessible through it as well, which causes this issue.
I would not like to get rid of the safe_modules argument as it does have a place, but the way it works right now is indeed almost impossible to use safely on untrusted code. I'm thinking of checking against module.__all__
, which should normally only contain module public items.
Pretty cool pickle compiler btw, reminds me of the pickleast
module I never really finished, but which is useful enough to embed the decompiler into pickles itself.
Should be fixed now, feel free to try breaking it again!
Overview
We have a
SafeUnpickler
to provide a safe way to deserialize. But the implementation seems not really safe and basically bypassable.How to Bypass (PoC)
In the
find_class
method, it just checks whether themodule
is safe, and doesn't check thename
at all, which can make the bypass more easily.https://github.com/CensoredUsername/unrpyc/blob/e1da6d51e5d2ccc6f98dc71c68b440a8caea07c7/decompiler/magic.py#L498-L506
Now let's check about what
safe_modules
we used. With a simple search (https://github.com/CensoredUsername/unrpyc/search?q=safe_loads), we could found that it basically allow two modules:{"_ast", "collections"}
. And I do found a cool gadget incollections
module.First, you can get
builtins
in dict type fromcollections.__builtins__
(actually all the modules have a__builtins__
attribute). Then you can usecollections._itemgetter
to get arbitrary item fromcollections.__builtins__
. Now you have aexec
oreval
, you are able to execute any python code.I use my toy compiler to generate the pickle bytecode. Exploits should execute code:
__import__('os').system('id')
.PoC:
Bytecode is generated by:
The Proper Way?
A better way to restrict globals is restricting both
module
andname
infind_class
at the same time, just like what the document do.