【技术分享】梨子带你刷burpsuite靶场系列之高级漏洞篇
优采云 发布时间: 2022-06-20 17:27【技术分享】梨子带你刷burpsuite靶场系列之高级漏洞篇
高级漏洞篇介绍
相对于服务器端漏洞篇和客户端漏洞篇,高级漏洞篇需要更深入的知识以及更复杂的利用手段,该篇也是梨子的全程学习记录,力求把漏洞原理及利用等讲的通俗易懂。
Web缓存投毒专题
什么是Web缓存投毒?
首先了解一下什么是Web缓存,Web缓存就是服务器会先将之前没见过的请求对应的响应缓存下来,然后当有认为是相同请求的时候直接将缓存发给用户,这样可以减轻服务器的负荷。但是服务器端在识别的时候是根据特征来的,如果两个请求的特征相同即会认为是相同的请求,此时如果攻击者首先触发服务器缓存附有恶意payload的响应,当其他用户发送相同请求时即会接收到这个恶意的响应。从影响范围来看,一旦成功缓存被投毒的响应,会影响到大量的用户,比起以往某些只能针对某个用户发起的攻击,危害大很多很多。
Web缓存的流程是什么样的?
其实前面梨子已经讲了差不多了,这一小节主要是补充讲解。其实这个Web缓存不是永久的,它只保存固定的一段时间,过了这个时间以后就会重新生成缓存,所以这类攻击还是有一定难度的。那么服务器端怎么识别等效的请求呢,我们接下来介绍一下缓存键的概念。
缓存键
缓存键就是服务器端用来识别等效请求的一系列特征的统称。一般缓存键包括请求行和Host头。服务器端只识别设置为缓存键的特征是否相同,这也就导致了Web缓存投毒漏洞的产生。
如何构造Web缓存投毒攻击?
一般构造Web缓存投毒攻击需要以下几个步骤
识别并确认不会被缓存的输入
我们想要构造Web缓存投毒就需要我们的输入能够反馈在响应中,但是如果我们的输入被设置为缓存键,那就不可能有用户发出等效请求,所以我们需要不断调试直到找到我们的输入既不会是缓存键又可以被反馈在被缓存的响应中。这样才能保证被投毒的响应缓存被投放到受害者那里,burp推荐了一款插件Param Miner来辅助我们寻找这样的不会被缓存的字段。
Param Miner
这款插件会自动去检测不会被缓存的字段使用方法很简单,只需要右键选择Guess headers即可。并且为了不给真实用户造成困扰,可以开启cache buster(缓存粉碎机)。
从服务器诱发被投毒的响应
我们确认了不会被缓存的输入以后,我们就要看服务端是如何处理这个输入的,如果可以动态反馈到响应中,就是我们能够发动Web缓存投毒的关键。
得到被缓存的响应
我们的输入可以被反馈到响应中还不够,还得能够生成缓存,这样才可以真正地将恶意payload落地。所以我们为此还是要不断调试才能成功找到生成投毒缓存的操作。
基于缓存设计缺陷的Web缓存投毒攻击
使用Web缓存投毒发动XSS攻击
因为XSS攻击也是有一部分是输入被反馈在响应中,所以Web缓存投毒当然也可以用来发动XSS攻击。我们关注这样一对请求和响应
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: innocent-website.co.uk<br />HTTP/1.1 200 OKCache-Control: public
我们观察到X-Forwarded-Host指定的URL会代替Host的值被反馈在响应中,并且X-Forwarded-Host是不会被缓存的字段,但是Host和请求行是缓存键。所以所有Host为的用户请求/en?region=uk都会接收到被投毒的响应,像这样
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: a.">alert(1)"<br />HTTP/1.1 200 OKCache-Control: publicalert(1)"/cms/social.png" />
当然了,alert只是弹窗验证而已,攻击者可以构造更复杂的XSS payload获取更多的东西。
使用Web缓存投毒发动不安全的资源导入
有的应用程序会导入Host指定服务器的某个资源,比如JS资源,但是如果我们像上面一样通过X-Forwarded-Host代替Host进行导入,则可能导入同名但是内容为恶意payload的JS资源,例如
GET / HTTP/1.1Host: innocent-website.comX-Forwarded-Host: evil-user.netUser-Agent: Mozilla/5.0 Firefox/57.0<br />HTTP/1.1 200 OK
配套靶场:使用不被缓存头的Web缓存投毒
首先我们随便设置一个查询参数,然后随便设置一个值,只是为了测试Web缓存
从上图来看,现在是没有产生Web缓存的,然后我们插入到X-Forwarded-Host字段会替换掉本应是Host字段的值,这样就相当于resources/js/tracking.js这个文件是可以伪造的,然后我们再go一下看一下缓存之后是什么样子的
好,我们现在看到Web缓存已经生成了,这样,所有被识别为发出等效请求的用户都会接收到这个响应,就会访问到”中毒”后的网页,模拟结束,现在我们到Exploit Server把payload写入到这个同名文件下
然后随便找一个靶场里面的帖子进去,抓包改包,把Exploit Server的域名插到X-Forwarded-Host字段下,然后重放几次让Web缓存生成
然后在浏览器访问这个页面,就能实现弹窗了,如果没生效就再缓存一次就行
使用不被缓存Cookie的Web缓存投毒
像上面一样,我们观察这样的请求
GET /blog/post.php?mobile=1 HTTP/1.1Host: innocent-website.comUser-Agent: Mozilla/5.0 Firefox/57.0Cookie: language=pl;Connection: close
我们看到应用程序通过Cookie中的language的值来调整网站的语言,当该请求生成响应后,等效请求的用户收到的就是波兰语(pl)的页面了。当然了,这种攻击方式比较少,因为很容易因为影响到正常用户被发现。
配套靶场:使用不被缓存Cookie的Web缓存投毒
首先我们观察一下请求与响应的关系
我们可以看到Cookie中的fehost字段被自动拼接到响应中的script节点下,那么我们可以修改这个字段的值实现XSS攻击
我们可以看到现在Web缓存已经生成了,并且我们修改后的字段值也是直接拼接到响应里,为了防止直接插XSS语句不生效,我们在前后多加了一般的script标签分别把前面后面的script标签闭合掉,从而成功发动XSS
使用多重头发动Web缓存投毒攻击
如果我们想要发动更高级的攻击可以操纵多个请求头来实现,现在我们考虑这样的一个情况,就是应用程序默认是使用HTTPS协议传输的,但是如果我们使用HTTP协议访问会自动触发一个指向Host的重定向,但是我们可以通过X-Forwarded-Host代替Host的值重定向到恶意域,像这样
GET /random HTTP/1.1Host: innocent-site.comX-Forwarded-Proto: http<br />HTTP/1.1 301 moved permanentlyLocation: https://innocent-site.com/random
配套靶场:使用多重头的Web缓存投毒
首先我们还是把HTTP History中的首页的那个包发到Repeater里面,随便加一个X-Forwarded-Host,Go一下,发现并没有什么变化,而且/resources/js/tracking.js变成了相对路径,那么我们再观察一下是相对于谁呢?那么我们就想办法让页面重定向,这就需要X-Forwarded-Scheme字段了,这个字段作用和X-Forwarded-Proto的效果是一样的,如果请求方法是这个方法指定的则会重定向到用HTTPS请求主机名
我们发现当同时有X-Forwarded-Host和X-Forwarded-Scheme两个字段的时候,就会有组合效果,因为请求方法为HTTP,则会重定向HTTPS流去请求主机名,然后这时候我们已经将主机名转为我们指定的主机名,这样它的相对路径就可以生效了,我们先去Exploit Server构造Payload
然后我们在Repeater里面抓包改包,让服务器产生Web缓存
我们可以看到重定向成功了,会跳转到Exploit Server下的/resources/js/tracking.js
利用暴露大量信息的响应
有时,网站会泄露大量有关自身及其行为的信息,从而使自己更容易遭受Web缓存投毒攻击。### Cache-control指令 有的时候响应会暴露缓存有效期等敏感信息,例如
HTTP/1.1 200 OKVia: 1.1 varnish-v4Age: 174Cache-Control: public, max-age=1800
有效期可以帮助我们去计算时间从而达到某种恶意目的。### Vary头 Vary头指定了一些可以被视为缓存键的字段列表,常见的如User-Agent头,应用程序通过其可以仅向指定用户群投放响应,也可以利用这个特点向特定用户群发动Web缓存投毒攻击。
配套靶场:使用未知请求头的定向Web缓存投毒
找到隐藏字段X-Host,然后像之前那样生成Web缓存,直接放生成缓存的结果
然后我们发现与之前的不同是User-Agent也是缓存键,所以我们还要去留言板钓他们的User-Agent
进到Exploit Server查收钓到的User-Agent,然后再结合刚才的那个生成新的Web缓存
使用Web缓存投毒发动基于DOM的漏洞攻击
不仅可以通过Web缓存投毒导入恶意JS文件,还可以导入恶意的JSON字符串,例如
{"someProperty" : ""}
如果这条payload被传递到恶意的sink就可能触发基于DOM的漏洞,如果想要让网站加载恶意JSON就需要CORS授权允许跨站,例如
HTTP/1.1 200 OKContent-Type: application/jsonAccess-Control-Allow-Origin: *<br />{ "malicious json" : "malicious json"}
配套靶场:在严格可缓存性标准下使用Web缓存投毒发动基于DOM的漏洞攻击
首先我们还是,默认使用Param Miner扫到可伪造字段X-Forwarded-Host。然后我们把首页放到Repeater里看一下有没有什么新东西
这些应该都是json的东西,大概的行为就是以data.host下的/resources/json/geolocate.json为参数执行initGeoLocate函数,然后我们再追踪一下这个geolocate.js和geolocate.json
从图上来看,geolocate.js会把j.country变量直接拼接进来,所以我们看一下这个country变量在哪,应该是在geolocate.json文件里面
那么我们就需要在Exploit Server中伪造属于我们的geolocate.json文件,只是country的值换成xss语句
好的,然后我们就可以生成Web缓存了
刷新首页成功加载我们自己的geolocate.json
注:扫到字段以后要把Param Miner的cachebuster都关了,不然永远也不会生成缓存
Web缓存投毒链
如果将我们学过的几种漏洞与Web缓存投毒结合起来,可以发动更高级的攻击。下面我们直接通过一道靶场来深入理解。
Web缓存投毒链
我们识别到两个可用来投毒的字段:X-Forwarded-Host、X-Original-URL,然后分析一下/resources/js/translations.js这个文件,看一下initTranslations()这个函数是怎么运作的
首先我们来看一下提取现在网页的语言的函数
大概是这么一个流程,然后是翻译函数,因为只翻译首页,所以所谓的翻译就是*敏*感*词*的替换,然后我们留意一下红框部分,就是说除了英语其他的都会执行翻译,也就是说除了英语以外的我们都能用来插入DOM-XSS语句,考虑到乱码问题,我们选择en-gb这个,虽然也是英语,但是因为代码里严格匹配en,所以这个也是可以用来插入的,而且也不用考虑乱码的问题,现在我们去Exploit Server写入DOM-XSS语句进去
然后我们需要抓取/?localized=1这个包,因为你选择翻译的时候会请求这个页面,然后修改X-Forwarded-Host字段为Exploit Server的,并产生Web缓存
但是这是翻译页面,我们要怎样才能让用户首先进到的就是我们的翻译页面呢,那就是在首页加入一个X-Original-URL字段指向翻译页面,这样访问首页的人就都会被重定向到这个页面了
因为需要生成两个Web缓存并且需要在靶场访问的时候同时在生效中,所以需要多试几次
基于缓存实现缺陷的Web缓存投毒攻击
缓存键缺陷
传统的攻击经常通过将payload注入查询字符串实现,但是请求行是缓存键,这就导致不可能会有用户发出等效请求,也就接收不到投毒响应缓存,但是如果有CDN的话,他们会将缓存键内容进行某些处理之后再存入缓存,例如
尤其是前两条,即使我们注入payload到查询字符串或参数中,用户也可能收到被投毒的响应缓存。
识别合适的缓存断言
首先我们要识别合适的缓存断言以测试缓存的过程。我们需要知道我们接收到的响应是来自缓存还是服务器,比如从以下地方可以得到反馈
有的时候应用程序会使用第三方的缓存组件,此时可以通过查阅相关文档的方式得知缓存的过程。例如,基于Akamai的网站可能支持Pragma:akamai-x-get-cache-key,它可以在响应标头中显示缓存键。
GET /?param=1 HTTP/1.1Host: innocent-website.comPragma: akamai-x-get-cache-key<br />HTTP/1.1 200 OKX-Cache-Key: innocent-website.com/?param=1
探测缓存键的处理过程
我们应该还要观察缓存是否对缓存键有其他的处理。比如剔除Host中的端口号等。下面我们来关注这样一对请求和响应。它会动态将Host值拼接到Location中
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com/enCache-Status: miss
然后我们在Host中随便加入一个端口,观察一下响应
GET / HTTP/1.1Host: vulnerable-website.com:1337<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com:1337/enCache-Status: miss
我们再去掉端口号重新发送请求
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com:1337/enCache-Status: hit
发现已经生成缓存,但是缓存是我们加了端口号发出时的响应版本。这就说明端口号是不会加入缓存键中的。
识别可利用的漏洞
讲了这么多,其实Web缓存投毒的危害程度完全取决于其能够用来发动的攻击,常见的有像反射型XSS、开放重定向等客户端漏洞。并且Web缓存投毒不需要用户点击任何链接,可能在发出正常的请求时也会接收到被投毒的响应缓存。
缓存键缺陷的利用
不被缓存的端口号
前面我们介绍过,缓存键有时候可能不会只会缓存域名或主机名而不缓存端口号。所以我们可以利用这个特点发动如DDOS(向任意端口号发出大量请求)、XSS(在端口号位置插入payload)等攻击。
不被缓存的查询字符串
探测不被缓存的查询字符串
有的应用程序并不会在响应中告知是否产生缓存。而且如果查询字符串不是缓存键的话,即使不同的查询字符串也会得到相同的响应缓存。那么我们可以在其他请求头中下手,例如加在Accept-Encoding字段中
Accept-Encoding: gzip, deflate, cachebusterAccept: */*, text/cachebusterCookie: cachebuster=1Origin: https://cachebuster.vulnerable-website.com
其实也可以在Param Miner开启动态的缓存粉碎机(cachebuster)。还有一种办法就是修改不同的路径,但是仍然可以的到相同的响应缓存。例如
Apache: GET //Nginx: GET /%2FPHP: GET /index.php/xyz.NET GET /(A(xyz)/
利用不被缓存的查询字符串
查询字符串不会被缓存可能会扩大XSS攻击面,因为附有XSS payload的查询字符串的请求在缓存看来与普通请求无异。但是普通用户可能就会接收到被投毒的响应缓存。
配套靶场:通过不被缓存的查询字符串的Web缓存响应
这一关考察就是常规的web缓存投毒,这一关并没有对查询字符串进行绑定,所以同一个路径下即识别为等效页面即投放缓存,那么我们直接在URL里面投毒
响应中的Age字段可以告诉我们它的缓存已经生效多长时间了,这一关设定的是35秒后重新缓存,可以作为我们投毒的一个信号标,于是我们看一下这时候访问根目录的响应是怎样的
我们得到的就是投毒后的缓存,这时访问首页的人都会接收到投毒的缓存
不被缓存的查询参数
有的时候缓存仅将某几个查询参数排除在缓存键中,例如utm_content 等utm参数。但是这种攻击方式会因为参数不太会被专门处理而没有那么大的危害。但是如果有功能点可以处理整个URL则可能会有转机。
配套靶场:通过不被缓存的查询参数的Web缓存响应
这一关考点是它只有某些查询参数不会作为缓存键,我们扫到utm_content这个查询参数不是缓存键,所以我们可以用这个参数实现Web缓存投毒
等到重新生成Web缓存的时候,成功通关
缓存参数隐藏
有的时候因为解析差异,可以将某些本来是缓存键的查询参数隐藏到非缓存键中。例如查询字符串中通过与(&)区分各个参数,通过问号(?)识别查询参数的开始。但是如果有多个问号,就会以第一个问号作为查询参数的开始,例如
GET /?example=123?excluded_param=bad-stuff-here
这时,excluded_param就不会被作为缓存键处理。
利用参数解析差异
有的应用程序有一些特殊的查询参数解析规则,比如Ruby on Rails框架将与(&)和分号 (;)作为参数分隔符。但是如果缓存不支持这样解析参数,就会造成差异。例如
GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here
如上所示,keyed_param就会被视为缓存键,而excluded_param不会,而且只会被缓存解析成两个参数
keyed_param=abcexcluded_param=123;keyed_param=bad-stuff-here
而被Ruby on Rails解析成三个参数
keyed_param=abcexcluded_param=123keyed_param=bad-stuff-here
此时keyed_param的值就会被覆盖为bad-stuff-here,也会得到由这个值生成的响应,但是在缓存看来只要该参数值为abc的都会被视为该响应的等效请求。利用这种特点可以发动JSONP攻击,它会将回调函数名附在查询参数中,例如
GET /jsonp?callback=innocentFunction
我们可以利用上面的特点替换成我们指定的函数名。
配套靶场:参数差异
这一关主要是考察缓存与后台对URL参数的处理方式不同导致的Web缓存投毒,通过扫描得知utm_content是可伪装的缓存键,后台将(&)和(;)识别为分界符,这样就可以利用这种差异覆盖回调参数的值为我们指定的函数
这样访问首页的人就也会接收到我们的投毒缓存了
利用支持fat GET请求
有的时候请求方法并不是缓存键,这时候如果支持fat GET请求方法就也可以用来进行Web缓存投毒。fat GET请求很特殊,拥有URL查询参数和正文参数,而只有请求行是缓存键,而且应用程序会从正文参数获取参数值,例如我们构造这样的请求
GET /?param=innocent HTTP/1.1…param=bad-stuff-here