开始
作为测试的一部分,客户端提供了一个文档根目录列表,并且在启动对Intruder的全面扫描之前,我正在通过一些选择查看Burp中继器中的页面。一个看起来非常有趣的页面是secret.html。我提出了要求并获得了秘密内容:
这似乎很容易,但我不抱怨,所以我转向浏览器,输入网址,并被告知“不,你看不到数据”。
这不是我所期望的,也许是与会话有关,但在Burp的代理中查看请求看起来非常相似。Firefox中引入了一些额外的头文件,但这是可以预料的。
如果不是Firefox,我可以使用curl看到内容吗?看到我打开Repeater选项卡时,我从那里抓取URL并将其放到命令行上:
$ curl -i http://192.168.0.93secret.html
好的,这很奇怪,URL被破坏了,但我可以修复它:
$ curl -i http://192.168.0.93/secret.html HTTP/1.1 200 OK Content-Type: text/html Accept-Ranges: bytes ETag: "4177750045" Last-Modified: Wed, 18 Apr 2018 22:36:19 GMT Content-Length: 40 Date: Fri, 20 Apr 2018 19:28:13 GMT Server: lighttpd/1.4.45 You are not allowed to see that content
我并不期待那样,所以我让Burp给我一个curl命令,然后运行:
$ curl -i -s -k -X $'GET' \ -H $'Host: 192.168.0.93' -H $'Connection: close' \ $'http://192.168.0.93secret.html'
什么都没有回来,有点头疼,我注意到URL又被打破了。在这一点上,我应该已经意识到有什么不对,但我没有,我只是修正了URL并重试:
$ curl -i -s -k -X $'GET' \ -H $'Host: 192.168.0.93' -H $'Connection: close' \ $'http://192.168.0.93secret.html' HTTP/1.1 200 OK Content-Type: text/html Accept-Ranges: bytes ETag: "4177750045" Last-Modified: Wed, 18 Apr 2018 22:36:19 GMT Content-Length: 40 Date: Fri, 20 Apr 2018 19:29:32 GMT Server: lighttpd/1.4.45 You are not allowed to see that content
好吧,非常奇怪的是,Burp可以获得秘密的东西,但Firefox和curl不能,让我们试试netcat。我建立连接,从Repeater复制请求并...
$ nc 192.168.0.93 80 GET secret.html HTTP/1.1 Host: 192.168.0.93 Connection: close
没有回来,连接保持打开状态,但服务器没有响应。也许我有一个错字,所以我把请求放到一个文件中,然后尝试着:
$ cat get_secret_request | nc 192.168.0.93 80
另一个挂起的连接,没有回应。在这一点上,我真的很困惑,这是我得到的:
Burp可以获得秘密内容
Curl无法使用Burp提供的网址获得秘密
Burp提供的curl命令无法获得该秘密
任何与netcat的尝试只是挂起
请求之间一定有区别,但我看不到它们,所以让我们走低一点,看看Wireshark。
首先,Burp请求:
接下来由Burp创建的curl命令:
有一些差异,这个请求有curl用户代理和一个额外的accept头。WAF和其他简单的保护系统通常会在用户代理检查时回复,所以也许这很简单,让我们再次尝试去除这些额外的头文件:
$ curl -i -s -k -X $'GET' \ -H $'Host: 192.168.0.93' -H $'Connection: close' \ -H $'User-Agent:' -H $'Accept:' \ $'http://192.168.0.93/secret.html'
盯着这些请求,他们看起来都是一样的,但是我发现它们有所不同,Burp正在请求secret.html,curl正在请求/secret.html。额外的领先/必定是差异,这也解释了为什么URL Burp给我和它创建的curl命令都缺少/。Burp能够在请求中指定页面名称时发出请求,但curl要求页面成为URL的一部分。
鉴于我无法用curl重现请求,让我们回到netcat,看看我们是否可以找出失败的原因。让我们来看看Wireshark中的netcat连接:
这两个请求看起来与请求转到secret.html而不是/secret.html的请求看起来完全相同,但是它们之间必须有一些差别,但是很小。十六进制视图是什么样的?
经过一番凝视,我终于发现了差异,Burp请求使用DOS行尾(\ r \ n),netcat使用的是Unix(\ n)。看到我在文件中获得了请求,使用vim很容易改变行结束符,只需打开文件,输入:
:set ff=dos
然后保存它。如果您不是vim用户,则dos2unix软件包中的unix2dos应用程序也是一个选项。
转换后,让我们再试一次:
$ cat get_secret_request_dos | nc 192.168.0.93 80 HTTP/1.1 200 OK Content-Type: text/html Accept-Ranges: bytes ETag: "4169396764" Last-Modified: Wed, 18 Apr 2018 22:42:18 GMT Content-Length: 43 Connection: close Date: Fri, 20 Apr 2018 21:35:12 GMT Server: lighttpd/1.4.45 This is top secret stuff you shouldn't see
大奖!现在我可以通过netcat重现请求,让我们检查它是否是领先的/是否有所作为。我把/放在了地方,然后再试一次:
$ cat get_secret_request_dos | nc 192.168.0.93 80 HTTP/1.1 200 OK Content-Type: text/html Accept-Ranges: bytes ETag: "4177750045" Last-Modified: Wed, 18 Apr 2018 22:36:19 GMT Content-Length: 40 Connection: close Date: Fri, 20 Apr 2018 21:39:01 GMT Server: lighttpd/1.4.45 You are not allowed to see that content
并且我们拥有它,请求secret.html提供访问权限,请求/secret.html被拒绝。这种类型的请求不能通过浏览器或其他任何以完整URL作为参数的工具来完成,只能通过理解页面和主机是两个独立实体的方式来进行请求。
所以现在我可以重现这个问题,但我不知道为什么第一个地方存在差异。这很烦人,但我想我可以在某个时候与开发人员讨论它,看看他们是否有任何想法。
稍后...
除了进行应用程序测试之外,客户还要求对服务器配置进行审查,并且作为他们提供lighttpd配置的一部分,当我发现此行时,我正在努力解决这个问题:
url.rewrite-once = ( "^/secret.html" => "/not_permitted.html" )
看起来相当简单,对以/secret.html开头的页面名称的任何请求都将在内部重定向到/not_permitted.html。但是因为我要求的是secret.html,而不是/secret.html,所以这条规则不适用于我,我不会重定向,因此可以查看秘密内容。
知道这一点,我想看看我是否可以用curl或浏览器查看内容。我尝试的第一个URL是:
http://192.168.0.93/./secret.html
但是,curl以及我尝试过的所有浏览器,在提出请求之前简化了此操作,因此我仍然以/secret.html结束。我尝试过各种各样的./和../组合,都失败了,直到我终于取得成功:
http://192.168.0.93/.././..////secret.html
反转回到它停止工作,我发现虽然点得到简化,但额外的斜线不能,所以下面的URL是有效的,并且获取秘密数据,因为请求的文件是//secret.html,它不匹配正则表达式。
http://192.168.0.93//secret.html
在确认此URL可用于各种浏览器之后,我可以在报告中加入一些内容,作为开发的示例。花了一段时间才完成,但这比给Repeater截图并且说“看,我得到了你的数据但我不知道如何”要好得多。
我希望你已经发现了我对这个漏洞的调试方法。它有助于表明计算机是决定性的,并且背后有一个原因,有时只需要一些工作就可以找出规则是什么。一旦你了解规则,玩游戏和赢得胜利就容易多了。
其他Web服务器
我决定对Apache,NGINX和IIS进行尝试,所有三个人都拒绝了“400错误请求”响应请求。我也证实了所有三个人都对DOS或Unix系列结局感到满意。
阿帕奇
$ cat get_secret_request | nc 192.168.0.93 80 HTTP/1.1 400 Bad Request Date: Fri, 20 Apr 2018 20:26:09 GMT Server: Apache/2.4.25 (Debian) Content-Length: 304 Connection: close Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>400 Bad Request</title> </head><body> <h1>Bad Request</h1> <p>Your browser sent a request that this server could not understand.<br /> </p> <hr> <address>Apache/2.4.25 (Debian) Server at 192.168.0.93 Port 80</address> </body></html>
错误日志文件中还有以下条目:[Fri Apr 20 20:26:09.244512 2018] [core:error] [pid 31042] [client 192.168.0.3:47832] AH00126: Invalid URI in request GET secret.html HTTP/1.1NGINX
$ cat get_secret_request | nc 192.168.0.93 80 HTTP/1.1 400 Bad Request Server: nginx/1.10.3 Date: Fri, 20 Apr 2018 19:22:39 GMT Content-Type: text/html Content-Length: 173 Connection: close <html> <head><title>400 Bad Request</title></head> <body bgcolor="white"> <center><h1>400 Bad Request</h1></center> <hr><center>nginx/1.10.3</center> </body> </html>NGINX日志文件中没有条目
iis
$ cat get_secret_request_dos | nc microsoft.com 80 HTTP/1.1 400 Bad Request Content-Type: text/html; charset=us-ascii Server: Microsoft-HTTPAPI/2.0 Date: Sun, 22 Apr 2018 18:00:10 GMT Connection: close Content-Length: 324 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"> <HTML><HEAD><TITLE>Bad Request</TITLE> <META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD> <BODY><h2>Bad Request - Invalid URL</h2> <hr><p>HTTP Error 400. The request URL is invalid.</p> </BODY></HTML>我没有访问IIS日志来检查条目。
防御
最简单的防御措施是不要在文档根目录中存储任何不想浏览的内容。如果secret.html存储在文档根目录之外,它仍然可以用于任何需要的页面,但是无法在URL中引用它。从一些实验看来,mod_access函数看起来像是通过了一个清理好的页面名称,如果需要添加了前导斜杠,并删除了所有额外的内容,那么即使我们格式错误的请求,下面的拒绝规则也会给出“403 Forbidden”:
$HTTP["url"] =~ "^/(secret.html)$" { url.access-deny = ("") }
然后可以 将server.errorfile-prefix选项设置为提供自定义403页面。如果您想用重写规则修复它,最简单的方法是从正则表达式中删除前导斜杠:
"secret.html" => "/not_permitted.html"这将阻止访问其名称中包含secret.html的任何页面。如果这是这个网站上唯一的这样的网页,那么这个解决方案就会起作用,如果其他人拥有了页面view_secret.html,那么您只是为他们创建了一大堆问题,试图找出他们为什么不能再看到他们的页面。
这是一个更好的规则,它说,从页面名称开始,任何数量的斜线后跟secret.html。这可以防止我们的原始旁路作为零斜杠被允许,以及后来使用两个或更多斜杠的旁路。
"^[/]*secret.html" => "/not_permitted.html"我仍然认为这不是一个完美的解决方案,因为可能有其他字符可以插入到页面名称中,从而绕过规则。
最后一个解决方案将取决于secret.html的目的。如果它是由其他页面引入的模板文件,则可以在其中构建一些逻辑,以使其不会泄露其内容,除非以正确的方式访问,例如,通过执行相同的身份验证和授权检查引用它的页面
本文作者为Mr.Bai,转载请注明。