Abusing HTTP hop-by-hop request headers 这个技术在 2019年入选了 Portswigger 安全社区评选的 top 10 Web hacking techiniques of 2019 的候选名单,尽管最终没有入选 top 10,但个人觉得还是挺有意思的,后来也出现了与之相关的真实世界的漏洞案例,因此值得学习。
什么是hop-by-hop Headers
根据 RFC 2616#13.5.1章节的描述,在 HTTP/1.1 中,以下header都是 hop-by-hop 头:
- Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- TE
- Trailers
- Transfer-Encoding
- Upgrade
hop-by-hop header 简单理解就是会被代理服务器消费掉(或者说是移除掉),不会最终转发到后端服务器去的请求头。
而与之对应的就是 end-to-end header,end-to-end header 就是会一直转发到后端服务器去的请求头,除 hop-by-hop header外的请求头都是 end-to-end header。
根据 RFC 2616#14.10章节的描述,如果想添加其他的非标准的hop-by-hop header为 hop-by-hop header,可将 该请求头添加到 Connection
请求头中,如 Connection: close,X-Auth-Token, X-Forwarded-For
。
Abusing hop-by-hop Headers的攻击原理
借用原文的图:
第一个正常的请求中,第一个代理服务器Proxy 1,会添加一个请求头X-Important-Header
,最终该请求头会到后端服务器中;
而在第二个请求中,加入了hop-by-hop header(将 X-Important-Header
添加到Connection
头中),到了第一个代理服务器Proxy 1,Proxy 1往请求中添加 X-Important-Header
请求头,然后再转发到第二个代理服务器 Proxy 2,Proxy 2看到 X-Important-Header
是一个hop-by-hop header,则会将请求中的 X-Important-Header
请求头移除,最后再把请求转发到后端服务器。
应用场景
适合用于尝试攻击那种基于 IP 作为访问控制的,且具有多级反向代理(nginx/alb/apache等)的Web服务.
1、比如隐藏源IP地址,在多级反向代理的链路中,一般第一级代理Proxy 1会将用户的访问IP添加到 X-Forwarded-For
头(也可能是X-Real-IP
或别的什么)中,然后再往下一级代理转发,最后转发到后端服务器。如果用户在原始请求中将 X-Forwarded-For
头设置为hop-by-hop头,那么在第二级代理 Proxy 2接收到请求时,发现X-Forwarded-For
为hop-by-hop 头,则移除掉,然后继续往后转发。因此用户的访问IP就不会被后端服务知道了。
2、绕过基于IP的访问控制机制。作者给的一个关于 CloudFoundry gorouter的例子。假设后端服务器会根据访问IP是否来自内部(比如 10.1.2.0/24),来决定可以访问 /admin
。首先用户在原始请求中将X-Forwarded-For
作为hop-by-hop头,然后请求到达 Proxy 1,Proxy 1将用户访问IP添加到X-Forwarded-For
,然后将请求转发到Proxy 2,Proxy 2发现X-Forwarded-For
为hop-by-hop头,则移除X-Forwarded-For
,然后转发到gorouter,gorouter看到X-Forwarded-For
头缺失,则会添加X-Forwarded-For
且设置为 上一级代理服务器的IP(假设为 10.1.2.3),从而导致最终访问到了 /admin
.
3、结合缓存投毒实现DoS。这个作者并没有现实世界的案例,只是搭了个环境作了演示。其实很简单,就是利用hop-by-hop,使某些请求头在代理链过程中被移除了,导致接口访问异常,同时接口访问可以命中缓存,所以导致别人访问该接口也会异常,实现了DoS.
由于hop-by-hop 是利用的header,所以实际利用的时候可以通过 https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/BurpSuite-ParamMiner/lowercase-headers 这些header进行暴破.
5、另外一些比如服务指纹识别、SSRF、WAF绕过,作者在文章中也有提到,虽然不是很详细,也没有实际例子作支撑。但是给人启发,足矣。
另外提几个实际的漏洞案例:
1、Go ReverseProxy CVE-2021-33197
假设web服务的代理链如下:
基于前面的场景,再添加一个proxy,如:client -> proxy 1 -> proxy 2 -> proxy 3 -> backend,则hop-by-hop headers及 Connection 在到达 proxy 2 处理后就不会转发给proxy3,对于 proxy3 接收到的请求并不存在 hop-by-hop。
该漏洞在于,可以传入两个Connection,从而让 Connection转发到下一个proxy,让hop-by-hop在下一个proxy中生效。
问题代码在:https://github.com/golang/go/blob/2ebe77a2fda1ee9ff6fd9a3e08933ad1ebaea039/src/net/http/httputil/reverseproxy.go#L250-L265
获取到第一个 Connection:头为空时,直接 continue。
因此在该漏洞中,如果 client -> proxy 1 -> Go ReverseProxy -> proxy 3 -> backend,
client发起如下请求:
GET /api/test
Connection:
Connection: X-Real-IP
proxy 1添加 X-Real-IP,并添加用户的访问IP,再转发到 Go ReverseProxy,如下:
GET /api/test
X-Real-IP:10.0.0.1
Connection:
Connection: X-Real-IP
Go ReverseProxy获取到一个 Connection 为空,直接continue,然后转发到 proxy 3,proxy 3移除 hop-by-hop,到backend的请求如下:
GET /api/test
Reference
https://nathandavison.com/blog/abusing-http-hop-by-hop-request-headers
https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1
https://tools.ietf.org/html/rfc2616#section-14.10
https://github.com/golang/go/issues/46313
https://articles.zsxq.com/id_rfsu4pm43qno.html
https://twitter.com/jinonehk/status/1420413477521301507
https://twitter.com/albinowax/status/1523667032646242306