gitbook/Web漏洞挖掘实战/docs/484539.md
2022-09-03 22:05:03 +08:00

242 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 19 | 失效的输入检测(上):攻击者有哪些绕过方案?
你好,我是王昊天。今天我们来学习失效的输入检测,看看攻击者有哪些绕过方案。
在现实生活中,我们在乘坐一些交通工具时,需要经过安检,以防止有人携带危险物品,避免一些危害公众安全的行为。但是这种安全检查也不是万能的,比如进地铁站的时候不会检查我们衣服口袋里的物品。
对于一个交互的系统来说,同样也需要对输入进行安全检查,也同样很难做到万无一失。
交互系统的输入,可能来自用户的输入或者其他系统的传递。在系统获得预期内的输入信息之后,就会将它们当作参数,运行相应的命令来实现自己想要的功能。那么问题来了,如果忽略了对输入的验证或者验证得不够充分,在攻击者的恶意操作下,系统就会接收到预期之外的数据,进而随着命令的运行就让攻击者实现了自己想要的目标,进而产生难以想象的后果。
这,其实就是失效的输入检测。
**根据检测技术的不同失效的输入检测可以分为6种它们分别是不安全的输入检查、中间件的输入输出、不安全的映射、编码及转义、编码及混淆、WAF及绕过。**
在接下来的2讲内容中我会带你学习这6种常见的失效输入检测是如何产生的以及应该如何应对。
## 不安全的输入检查
不安全的输入检查产生的原因,其实很好理解,就是当一个产品需要接收数据的输入时,却没有正确地对这些输入进行验证。
为了抵御这个问题,解决方案也比较简单,开发者只需要对一些输入数据进行安全性验证就可以。但其中的难点在于,如果安全性验证不够充分,攻击者就可以将输入构造成安全人员意料之外的形式,导致系统接收到意料之外的恶意输入。
这是非常危险的,因为攻击者甚至可以借此实现任意命令的执行。
我们来看一个关于消费行为的例子:
```c++
public static final double price = 20.00;
# 用户可以自由指定购买商品的数量
int quantity = currentUser.getAttribute(“quantity”);
# 计算总价
double total = price * quantity;
chargeUser(total);
```
在这段代码中,商品单价的值用户是无法修改的,但没有对购买数量的值进行限制。这时候,如果攻击者提供一个负值,那么他就不用进行消费,反而还能获得相应的收入。
接下来,我们开始学习失效的输入检测的第二种情况:中间件的输入输出。
## 中间件的输入输出
通常情况下,一个系统会由多个组件构成。上游组件在接收到外部输入后,会将它传给中间件来构建部分命令、数据结构或记录,然后就将它们发送给下游组件。
需要注意的是,**中间件并不能正确地处理这些输入中的特殊元素。这种失效的输入检测问题,就是中间件的输入输出问题。**
我们直接看个例子。
```c++
int main(int argc, char** argv) {
char cmd[CMD_MAX] = "/usr/bin/cat";
strcat(cmd, argv[1]);
system(cmd);
}
```
如果这个程序是以`root`权限运行的,那么对`system()`的调用也会以`root`权限执行。如果用户传入的参数是标准的文件名,那么调用会按预期工作。
但是,如果攻击者传递了一个恶意输入,比如一个`; rm -rf /`形式的字符串,那么对`system()`的调用也会因为缺少参数而无法执行`cat`命令,从而运行恶意命令,递归删除根分区的内容。
这个示例就是中间件没有对用户传入的恶意输入进行处理导致的。首先,在输入检查就存在问题,导致该输入没有被拦截。其次,中间件没有对这部分信息进行过滤处理,直接将接收到的恶意参数当成了命令来执行,造成了严重的后果。
到这里,我们已经学习了两种最直接的失效的输入检测风险类型,接下来我们再来学习一种更加隐蔽的风险种类,也就是不安全的映射。
## 不安全的映射
不安全的映射发生的场景是:当应用程序需要使用带有映射的外部输入来选择要执行的代码时,却没有充分验证这些外部输入是否合法,这时候攻击者就可以将恶意文件上传到应用会执行的位置。
这对于应用来说是毁灭性的漏洞,非常危险。我们再通过一个例子,来理解下这种漏洞是怎么产生的吧。
下面这个例子,显示了一个不使用映射的命令调度程序,它的代码书写方式看起来并不十分优雅:
```java
String ctl = request.getParameter(ctl);
Worker ao = null;
// 判断是否ctl参数中是Add字符串
if (ctl.equals("Add"))
{
ao = new AddCommand();
}
// 判断是否ctl参数中是Modify字符串
else if (ctl.equals("Modify"))
{
ao = new ModifyCommand();
}
else {
throw new UnknownActionError();
}
ao.doAction(request);
```
我们品味一番,可以发现上述代码写得属实不够优雅,而优秀的开发人员可能会使用映射的方式来进行代码重构,如下所示:
```java
String ctl = request.getParameter("ctl");
Class cmdClass = Class.forName(ctl + "Command");
Worker ao = (Worker)cmdClass.newInstance();
ao.doAction(request);
```
重构后的这段代码确实提供了许多优势代码更加简洁了if/else块也消失了在不修改命令调度程序的情况下也可以添加新的命令类型。
但是重构后的代码有个漏洞。攻击者可以先利用Worker接口创建一个类然后使用它们。这里创建的类是没有限制的它是由攻击者控制的参数ctl所决定。攻击者可以利用创建的这个类去执行恶意命令。
## 编码及转义
软件为了与另一个组件通信,会准备要发送的消息。它的结构需要符合通信协议的要求,如果数据的编码或转义过程中发生丢失或者执行错误,就可能会导致消息的结构发生变化。
不正确的编码或转义,可能允许攻击者将发送的正常命令更改为恶意命令。大多数软件都会遵循双方规定的协议进行通信。通信消息可以为带有控制信息的原始数据。
这么说有些抽象,我们看一个具体的示例:
```plain
“GET/index.html HTTP/1.1”是一个结构化消息其中包含一个命令“GET”和一个参数“/index.html”和有关正在使用的协议版本“HTTP/1.1”)。
```
如果应用程序使用攻击者提供的输入来构建结构化消息,而没有正确编码或转义,那么攻击者就可以在这条消息中插入特殊字符,导致数据被解释为控制信息。因此,接收输出的组件,就将会执行错误的操作。
我们再通过一个示例,来看看涉及编码及转义的攻击方式是如何发生的。
现在有这么一个聊天应用程序它的前端Web应用程序与后端服务器之间要进行通信。因为后端是不执行身份验证或授权的遗留代码所以我们必须在前端必须实现这个功能。聊天协议规定只支持两个命令SAY和BAN而且BAN命令只有管理员才可以使用。每个参数必须由一个空格分隔原始输入经过URL编码消息协议允许在一行中执行多个以``分隔的命令。
我们先看后端的代码:
```php
$inputString = readLineFromFileHandle($serverFH);
# generate an array of strings separated by the "|" character.
@commands = split(/\|/, $inputString);
foreach $cmd (@commands) {
# separate the operator from its arguments based on a single whitespace
($operator, $args) = split(/ /, $cmd, 2);
$args = UrlDecode($args);
if ($operator eq "BAN") {
ExecuteBan($args);
}
else if ($operator eq "SAY") {
ExecuteSay($args);
}
}
```
前端Web应用程序接收命令后对其进行编码然后发送到权限查看服务器执行授权的检查然后再将命令发送给后端。
```php
$inputString = GetUntrustedArgument("command");
($cmd, $argstr) = split(/\s+/, $inputString, 2);
/# removes extra whitespace and also changes CRLF's to spaces/
$argstr =~ s/\s+/ /gs;
$argstr = UrlEncode($argstr);
if (($cmd eq "BAN") && (! IsAdministrator($username))) {
die "Error: you are not the admin.\n";
}
/# communicate with file server using a file handle/
$fh = GetServerFileHandle("myserver");
print $fh "$cmd $argstr\n";
```
我们可以发现一个很明显的问题,虽然协议和后端都允许在一个请求中发送多个命令,但前端只打算发送一个命令。可是`UrlEncode`函数可能会留下``字符。
也就是说,如果攻击者提供`SAY hello world|BAN user12`前端会看到这是一个SAY命令`$argstr` 就为`hello world | BAN user12`。由于命令是SAY对BAN命令的检查会失败。前端会向后端发送URL编码的命令
```plain
SAY hello%20world|BAN%20user12
```
后端就会把这个解析为如下两条命令来运行:
```plain
SAY hello world
BAN user12
```
但是请注意,如果前端使用正确的编码将`` 编码为`%7C` ,那么后端将只处理一个命令。
这就是一个典型的编码错误导致的输入验证失效的例子。
## 编码及混淆
除了编码及转义,**攻击者还可以通过编码混淆攻击来逃避输入的检查,为攻击区注入有害负载**。这种攻击方式,就叫做编码及混淆。
客户端和服务器会使用各种不同的编码在系统之间传递数据,而当它们想要使用数据时就需要首先对其进行解码。
在构建攻击时,我们需要考虑有害负载的注入位置。如果可以根据关联环境推断出输入是如何被解码的,那么我们就可以知道,要用什么方式对有害负载进行编码。
在URL中有一系列具有特殊含义的保留字符。例如`&`用作分隔符它可以分隔查询字符串中的参数。基于URL的输入可能包含这些字符比如用户搜索`Fish & Chips`之类的内容会发生什么呢?
浏览器会自动对任何可能导致解析器歧义的字符进行URL编码。这意味着用%字符和它们的二位十六进制代码替换它们,成为这样`[…]/?search=Fish+%26+Chips`,来确保`&` 不会被误认为是分隔符。
任何基于URL的输入在分配给相关变量之前都会在服务器端自动进行URL解码。这意味着就大多数服务器而言查询参数中的`%22`、`%3D`和`%3E`等序列分别与`“`、`<`和`>`字符同义。也就是说我们可以通过URL注入URL编码的数据它通常仍会被后端应用程序正确解释。
有时我们可能会发现WAF等在检查你的输入时无法正确地对你的输入进行 URL解码。在这种情况下我们只需对列入黑名单的任何字符或单词进行编码就可以将有害负载绕过检测发送给后端应用程序实现攻击行为。
在XSS注入中我们经常会需要输入<script>进行攻击,但是往往输入检测会将它拦截,使得我们无法成功攻击,这时我们就可以对它进行编码混淆,将它改为:
```
[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]])()(([]+[])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+[]]+(![]+[])[!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(!![]+[])[+[]]+([]+[])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[!+[]+!+[]])
```
利用这个方法,我们可以绕过很多的输入检测。
## 总结
好了,今天的主要内容就到这里,我们一起小结下。
今天这节课我们一起学习了不安全的输入检查、中间件的输入输出、不安全的映射、编码及转义、编码及混淆这5种失效的输入检测是如何产生的。
不安全的输入检查,主要就是应用没有正确地过滤用户的输入,使得攻击者使用精心设计的恶意输入,成功实现对系统的攻击。
中间件的输入输出问题,是因为应用的中间组件在接收到其他组件的输入数据时,没有正确地对这个输入进行过滤所导致的问题。它的危害性极大,攻击者可以凭此实现任意命令的执行。
不安全的映射,发生在应用程序需要执行外部文件,而这个外部文件可以由攻击者上传的情况下。这主要是因为,应用程序没有对这个外部文件进行足够的限制所导致的。
对于编码及转义、编码及混淆这两个问题,它们都是编码相关的问题,区别在于:编码及转义问题,主要是因为系统没有对特殊字符做转义处理,使得攻击者可以借助这些特殊字符实现攻击行为;而编码及混淆问题,主要是系统只对一些危险的字符串进行了限制,却没有对这些字符对应的混淆编码进行拦截,导致攻击者可以凭借将恶意输入进行混淆,从而实现恶意输入的上传。
我把这些重点信息也提炼到一张思维导图中,你可以保存下来,方便复习:
![](https://static001.geekbang.org/resource/image/28/23/281e5d9c00c26d59a9d79f93119dd523.jpg?wh=2250x1250)
在下一讲中我会和你一起学习失效的输入检测中最复杂的一部分WAF检测以及如何让自己的应用避免失效的输入检测问题的发生。
## 思考
学完这一讲,请你思考下,失效的输入检测问题的核心到底是什么呢?你能想到什么好办法解决这一类问题吗?
欢迎在评论区留下你的思考。如果你觉得今天的内容对你有所帮助的话,欢迎你把课程分享给其他同事或朋友,我们共同学习进步!