sql注入总结
本文最后更新于 2024年7月24日 下午
前言:系统的再复习总结一次sql注入,最近比赛比较少见了,但还是要学会的。
一、sql注入的概念
SQL注入(SQL Injection)是一种网络攻击技术,攻击者通过在输入字段中插入恶意的SQL代码,利用应用程序对用户输入处理不当的漏洞,诱使其执行未经授权的SQL查询。这种攻击可以导致数据库中的敏感数据泄露、数据篡改或删除,甚至让攻击者获得对整个数据库系统的控制。SQL注入常见于不安全的Web应用程序中,是一种严重的安全威胁,通常需要通过参数化查询、预处理语句和输入验证等方法进行防御。是ctf比赛中常考的题目。
二、寻找sql注入点
通常sql注入的题目都有明显的特征,比如填表格,登陆账号这些。
1.在参数后面添加单引号或双引号,查看返回包,如果报错或者长度变化,可能存在Sql注入
判断:id=1’(常见)id=1” id=1’) id=1’)) id=1”) id=1”))
2.通常通过get,post,cookie等请求再到相应的http头信息查找敏感信息
3.构造不同的语句检测异常
三、sql注入类型
常见的主要还是MySQL注入,主要以这个为切入点。
建议可以搭建一个sql-labs的靶场,网上都有教程,自己检索。
联合注入
类型判断
数字型:select * from table where id =$id
字符型:select * from table where id='$id'
判断的时候通常通过永真式和永假式进行判断
1 |
|
1 |
|
字段个数
通常用order by
查询字段的个数
挨个查通常就能查到临界值
用sql-labs第一题来测试一下,尝试
如图可以判断出3是临界值
查找显示位
使用union select
查找显示位,需要判断具体个数在前端显示,通常将前面的改成0或-1,这里的目的是使第一个查询不存在,显示第二个查询结果,通过显示得出显示位
爆库名
使用database(),返回当前的库名
爆表名
基于已知的库名进行爆表,主要有以下函数
group_concat()
:使数据能在一行输出
information_schema.tables
:存储了数据表的元数据信息使用table_name
和table_schema
字段
爆列名
基于表名的基础上,进一步爆列名
与上文类似,用information_schema.columns和column_name来
爆信息
基于已知的列名爆出信息
1 |
|
想获得所有列对应的信息的可以使用concat_ws
报错注入
本质使用函数报错,通过报错获得想要的数据,前提是后端没有屏蔽信息。
Xpath导致的报错
归类为 XPath 格式不正确或缺失导致报错
updatexml()
是改变 XML 文档中符合条件的值,其语法如下
1 |
|
直接使用有缺陷会进行报错,XPATH syntax error: '~'
可以结合concat配合使用,如
1 |
|
接着按上面的流程来爆表
值得注意的是该函数报错长度存在字符长度限制,所以需要limit限制读行如图所示
除了limit限制之外,也可以使用substr(xxxxxx,1,30)这样的形式获得
extractvalue()
用于从 XML 格式的数据中提取指定节点的值。用法
1 |
|
语法基本和updatexml差不多,只是少一位,如图显示
注意这里的报错限制和updatexml也是一致的
主键重复导致的报错
主键报错注入是由于rand()
,count()
,floor()
三个函数和一个group by
语句联合使用造成的,缺一不可
rand()
函数的基础语法是这样的,它的参数被叫做 seed(种子),当种子为空的时候,rand()
函数会返回一个[0,1)
范围内的随机数,当种子为一个数值时,则会返回一个可复现的随机数序列
floor()
函数的作用就是返回小于等于括号内该值的最大整数,也就是取整,它这里的取整不是进行四舍五入,而是直接留下整数位,去掉小数位,如果是负数则整数位需要加一,也就是去一法
count()
得到行数
group by
列名
盲注
布尔盲注
在页面没有错误回显时完成的注入攻击。此时我们输入的语句让页面呈现出两种状态,相当于true和false,根据这两种状态可以判断我们输入的语句是否查询成功。
以sqli-labs举例当输入正确的时候只会回显一句话,输入错误的时候就没有回显。
判断数据库类型
使用exists()函数,通过语句判断是哪种类型
1 |
|
判断数据库名
先通过length()函数
和二分法判断出数据库的长度
通过调整判断得到database的长度,然后进行尝试字母
可以直接使用判断
1 |
|
也可以结合ascii()来判断
1 |
|
判断表字段
exists(select id from emails)–+可以直接懵看看
判断表的个数
1 |
|
判断表的长度
1 |
|
判断表的内容
这个是直接判断
1 |
|
用ascii来判断
1 |
|
判断字段数据
先判断长度
1 |
|
再判断内容
1 |
|
通常布尔还是sqlmap来吧手注太累了
时间盲注
通过观察页面,既没有回显数据库内容,又没有报错信息也没有布尔类型状态,那么我们可以考虑用延时注入。延时注入就是将页面的时间线作为判断依据,一点一点注入出数据库的信息。
判断库名
1 |
|
if(expr1,expr2,expr3) 如果expr1的值为true,则返回expr2的值,如果expr1的值为false,则返回expr3的值。
基本流程与布尔盲注类似
HTTP注入
流程基本相似,主要再UA头,cookie,Referer和xff这几种情况来看。
利用报错注入得到答案
宽字节注入
国内最常使用的 GBK 编码,这种方式主要是绕过 addslashes
等对特殊字符进行转移的绕过。反斜杠 \
的十六进制为 %5c
,在你输入 %bf%27
时,函数遇到单引号自动转移加入 \
,此时变为 %bf%5c%27
,%bf%5c
在 GBK 中变为一个宽字符「縗」。%bf
那个位置可以是 %81-%fe
中间的任何字符。不止在 SQL 注入中,宽字符注入在很多地方都可以应用。
堆叠注入
分号(;)是用来表示一条sql语句的结束。在 ; 结束一个sql语句后继续构造下一条语句,会另外执行。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。
图中继续执行了更新id为1 的用户的密码信息
缺点:并不是每一个环境都适合堆叠注入,且在堆叠前还需要知道一些信息才能正常注入。
无列名注入
参考:文章
简单的讲以下核心的内容
正常在order by 之后知道字段个数后
比如使用查询,这里的默认是table
1 |
|
接下来这个语句
1 |
|
这样的查询,可以得到一个派生的表,这里的a是派生的表的别称
这里前面的2是引用的2的列,成了一个新的表
如果过滤了反引号
可以继续用别名代替,比如
1 |
|
这里就是调用表里面的4的列。
再次基础上可以进行多表查询
1 |
|
这边的0x2d是-
,这里的派生表2,3的表生成新的表。
这里利用join函数可以进行无column的查询
1 |
|
因为是同一个表,构成的新表就会得到所有的列的信息,从而进行绕过。
Quine注入
参考文章,也是刷题偶观,记录一下
quine注入即查询的结果是查询的语句,举个简单的例子
1 |
|
匹配字符串”.“中ascii码为46的字符并替换为”.“,也就是将”.“转换为”.”并返回
在实际替换中,单引号会变成双引号,可以引入char(34)和char(39)进行替换,比如
1 |
|
这里面是连续两个双引号用斜杠进行正常的输入,通过匹配会输出’’
所以替换的就变成了
1 |
|
这一步就是替换单双引号
1 |
|
然后替换为相关字符
1 |
|
最后直接来个脚本吧,没太深入的明白,看看先能写题就行
1 |
|
四、刷题笔记
过滤
过滤 or
使用||代替
过滤 =
使用like代替
过滤空格
使用/**/绕过