无效HTTP请求和绕过Lightttpd中的重写规则

Mr.Bai 1,018 浏览 0

开始

作为测试的一部分,客户端提供了一个文档根目录列表,并且在启动对Intruder的全面扫描之前,我正在通过一些选择查看Burp中继器中的页面。一个看起来非常有趣的页面是secret.html。我提出了要求并获得了秘密内容: burp_request_response.png

 

这似乎很容易,但我不抱怨,所以我转向浏览器,输入网址,并被告知“不,你看不到数据”。

firefox_denied.png 

这不是我所期望的,也许是与会话有关,但在Burp的代理中查看请求看起来非常相似。Firefox中引入了一些额外的头文件,但这是可以预料的。

burp_proxy.png 

如果不是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_ascii.png

接下来由Burp创建的curl命令:

curl_ascii.png

有一些差异,这个请求有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'

curl_no_headers_ascii.png

盯着这些请求,他们看起来都是一样的,但是我发现它们有所不同,Burp正在请求secret.html,curl正在请求/secret.html。额外的领先/必定是差异,这也解释了为什么URL Burp给我和它创建的curl命令都缺少/。Burp能够在请求中指定页面名称时发出请求,但curl要求页面成为URL的一部分。

鉴于我无法用curl重现请求,让我们回到netcat,看看我们是否可以找出失败的原因。让我们来看看Wireshark中的netcat连接:

manual_netcat_ascii.png

这两个请求看起来与请求转到secret.html而不是/secret.html的请求看起来完全相同,但是它们之间必须有一些差别,但是很小。十六进制视图是什么样的?

burp_hex.png

manual_netcat_hex.png

经过一番凝视,我终于发现了差异,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.1

NGINX


$ 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的目的。如果它是由其他页面引入的模板文件,则可以在其中构建一些逻辑,以使其不会泄露其内容,除非以正确的方式访问,例如,通过执行相同的身份验证和授权检查引用它的页面

发表评论 取消回复
表情 图片 链接 代码

分享
请选择语言