美文网首页
20220101.1059: 慢慢说透sql注入(一)

20220101.1059: 慢慢说透sql注入(一)

作者: 我的职业生涯 | 来源:发表于2022-01-01 18:26 被阅读0次

#每日三件事,第1059天#

SQL注入是一种将SQL代码插入或添加到应用(用户)的输入参数中的攻击,之后再将这些参数传递给后台的SQL服务器加以解析并执行。凡是构造SQL语句的步骤均存在被潜在攻击的风险,因为SQL的多样性和构造时使用的方法均提供了丰富的编码手段。

SQL注入的主要方式是直接将代码插入到参数中,这些参数会被置入SQL命令中加以执行。不太直接的攻击方式是将恶意代码插入到字符串中,之后再将这些字符串保存到数据库的数据表中或将其当作元数据。当将存储的字符串置入动态SQL命令中时,恶意代码就被执行。

SQL注入就是通过系统的漏洞构造新的SQL语句并执行,得到我们想要的结果。

SQL注入分为报错注入和盲注,盲注又有布尔盲注和时间盲注两种。按注入方式来讲,可以分为GET型注入和POST注入。

1. SQL注入点的判断

首先就是找到注入点,也就是可以接收我们构造的SQL语句的地方。下面以SQLi-Lab为例进行说明。

在Less-1的环境中,按照题目提示,输入:

http://localhost/Less-1/?id=1

可以得到Your Login name:Dumb,Your Password:Dumb的提示信息。

接下来通过各种特殊符号来测试此处是否有注入点,特殊符号为单引号,双引号,反斜杠等“

http://localhost/Less-1/?id=1'
http://localhost/Less-1/?id=1"
http://localhost/Less-1/?id=1\

只有单引号和反斜杠的时候有错误提示:

 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1 
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1\' LIMIT 0,1' at line 1

从上面的错误提示中,我们可以得到如下信息:

  1. WEB应用后台的数据库系统是MySQL;
  2. 在【'1\' LIMIT 0,1】地方出现了语法错误;

由此我们可以推断,WEB应用的查询语句大致是下面这个样子:

select * from table where id='1'

我们输入的参数是1‘,带入到上面的SQL语句中就变成了:

select * from table where id ='1''

这条SQL语句多了一个单引号,或者多了一个反斜杠,自然要出错。

如果把输入变成1' or ',1' and ' 或者1' -- ,或者1' #,就会构造出新的语句:

select * from table where id =' 1' or ''

此时构造的SQL的条件为 id=1 or ‘ ’;结果为true,页面会显示id=1的信息;

select * from table where id =' 1' and ''

此时构造的SQL的条件为id=1 and ‘ ’;结果为False,页面不显示任何信息;

select * from table where id =' 1'#   '

在MySQL中,#为注释符,但在url地址栏,#首先会被浏览器处理,并不会发送到后台交给数据库来处理。因此,#注释符在GET方式的注入中不能使用。

select * from table where id =' 1'-- '

MySQL的另外一种注释符号是“-- ”,注意,后面有个空格。同样,这个空格在url地址栏中输入时,同样会被浏览器首先处理,并不会发送到后台。因此,要使用url编码方式输入空格,即“--%20”或者“--+”,都可以被浏览器转译为MySQL的注释符。

现在来看看Less-4,当输入"\"时,提示的信息为:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"1\") LIMIT 0,1' at line 1

可以看到提示信息中为【 “1\") 】,也就是说,sql查询语句大致是这样:

select * from table where id="1"

通过对比Less-1和Less-4对输入"\"的错误提示信息,可以得知Less-1需要处理单引号出的变量,Less-4需要处理双引号处的变量,并且要将查询语句最后的那个单引号或者双引号进行闭合或者注释。

同样对比Less-2,不管输入单引号、双引号还是反斜杠,提示信息中均没有其他字符:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' LIMIT 0,1' at line 1
 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '" LIMIT 0,1' at line 1 
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\ LIMIT 0,1' at line 1

因此,对于Less-2我们可以判定这是数字型注入,而非Less-1和Less-4的字符型注入。Less-2不用考虑引号的闭合问题,因为数字型注入点没有引号。

2. 构造SQL语句

2.1 判断payload能否被执行

当我们输入【1' --+】的时候,系统还是按照原来的方式执行并给出结果。其实在在单引号后面就可以添加我们的攻击载荷了,俗称payload【1‘ payload --+】,并保证paylaod被数据库执行。

通常的做法是使用and 1=1 以及and 1=2,or 1=1 以及or 1=2等观察结果是否有所变化。由于数据库执行的时候,会把我们的payload作为true 或者false,并结合原来的语句执行后给出结果。因此,使用以or开始的paylaod并不能得的有用的信息。使用以and为首的paylaod更好一些。

当payload为and 1=1的时候,sql语句被执行后,返回的结果就是id=1的查询结果;

当payload为and 1=2的事实,sql语句被执行后,由于条件为False,不返回任何结果。由以上两点可以判断,我们的payload被传进了数据库并且执行。

比较简单判断payload是否执行,可以使用and sleep(3),并观察浏览器的结果返回时间。也可以通过F12点击网络,查看系统的响应时间。如果大于3秒,则说明payload被执行,否则就没有执行。

2.2 使用union联合查询库名

如果payload能够使用select之类的查询语句,那就就能查询到数据库的库名、表名、列名以及表的内容。脱库也是顺手牵羊的事情;如果能够将文件写入操作系统中的话,上传木马,控制系统也就不是什么难事儿了。

union联合查询时,select查询的列的数量必须得和主查询select的列保持一致。使用下面的语句探测主select查询的列的数量:

http://localhost/Less-1/?id=1' union select 1,2,3,4 --+

可以先从select 1开始,然后是select 1,2,select 1,2,3……当两个查询语句的列数不一致时,提示信息为:

The used SELECT statements have a different number of columns

列数一致时会显示查询到的信息,union 查询的结果会附在主查询结果的后面。

http://localhost/Less-1/?id=1' union select 1,2,3 --+

执行上面的结果后,没有错误信息,显示的内容还是id=1的内容,union查询的结果并没有显示出来,至少可以说明主查询的列数为3。通过 order by 也可以判断主查询的列数,payload为:order by column_name/index :

http://localhost/Less-1/?id=1' order by 5 --+

通过错误信息【Unknown column '5' in 'order clause'】可以逐步判断,当order by 3的时候,系统不再报错。同样可以说明主查询的列数为3,和union select 1,2,3 查询的结果一致。

union select会把结果和主查询的结果一起显示出来,而order by只是对某一列进行排序,并不会显示相关的结果。最重要的是,我们希望查询的结果可以在页面上显示出来

其实回过头看看输入单引号时的错误提示信息,我们看到还有一个“LIMIT 0,1"。系统通过limit来限制输出的结果,同样我们可以在payload中加入limit来控制输出的结果:

http://localhost/Less-1/?id=1' union select 1,2,3 limit 0,1 --+

Limit [offset,] rows ,是limit的语法。由于Less-1的结果只显示一行,因此,rows设置为1,只要从0开始递增offset,就可以查看所有的查询结果。

当offset为1时,查询到的结果中会显示Your Login name:2,Your Password:3。这里的2和3就是我们union select中的2和3。在2和3位置的查询结果可以通过页面显示出来,比如用database()替换2的位置,用version()替换3的位置:

http://localhost:55001/Less-1/?id=1'  union select 1,database(),version() limit 1,1 --+

我们会得到:Your Login name:security,Your Password:5.5.44-0ubuntu0.14.04.1。“security”就是database()函数执行后的结果,“5.5.44-0ubuntu0.14.04.1”就是version()函数执行后的结果。


当然,输入一个错误的函数,只要系统给错误信息,就能看到数据库的名称:

http://localhost:55001/Less-1/?id=1' and sldkjf() --+

错误信息是这样的:FUNCTION security.sldkjf does not exist。是不是就可以看到当前应用连接的数据库是security了?

相关文章

网友评论

      本文标题:20220101.1059: 慢慢说透sql注入(一)

      本文链接:https://www.haomeiwen.com/subject/cvxaqrtx.html