目录
Less23-注释符被过滤的情况下注入
首先我们进入23关:![](https://img-blog.csdnimg.cn/2908292f2a5b46c491d662bcefc70ab7.png)
可以发现是一个非常普通的 GET请求 注入,我们输入单引号测试一下注入点:![](https://img-blog.csdnimg.cn/64f00497ebe348ed8865222bab56e29a.png)
页面回显了错误,那么注入点可能就是单引号,然后我们去测试一下列数。
输入语句:
?id=1' order by 4 --+
页面回显:
![](https://img-blog.csdnimg.cn/d17a1dc62c934f69952657e613ee8fa5.png)
可以发现页面报了语法错误,那可能是注入点问题,我们测试一下双引号做注入点:
![](https://img-blog.csdnimg.cn/b6a5b3894ede45f29626c2fe52e2b900.png)
输入双引号后页面都不报错了,那注入点肯定不是双引号,但是单引号做注入点输入的 Order By 语句又无效。我这里准备直接用 Union 语句去测试列数,因为只要列数不匹配依旧会报错,也可以判断列数。注入点必定不是双引号,这里我依旧拿单引号做测试。
输入语句:
?id=1' union select 1,2,3 --+
页面回显:
可以看到报了SQL语句错误,我这里分析有以下几个原因:
1、注入点错误。
2、列数错误。
3、注释符被过滤导致插入语句后发生语法错误。
经过我不断尝试,最终结果是注释符被过滤导致的报错,你们可以自行尝试。注释符被过滤也就导致我们无法过滤其原本的单引号从而导致单引号不匹配报错。
我们进行代码审计看一下:
![](https://img-blog.csdnimg.cn/488b1d2a56ae4065bfe3271ec64cde6e.png)
preg_replace() 函数的作用是进行一个正则表达式的搜索和替换。那么上述语句的含义就是将我们输入的 '#' 和 '--' 注释符都做了替换,也就是过滤了。
![](https://img-blog.csdnimg.cn/9e8e9f0605334aed9b7edccdae32e7d3.png)
替换之后我们就无法过滤后面的单引号,所以会报错。其实知道了它的原理之后就很好处理了,我们首先要理解我们每次输入注释符的意义何在。
假设这里注释符没有被过滤,我们输入一条查询列数的语句,当其被带入到后台时所执行的语句是这样的:
$sql="SELECT * FROM users WHERE id='1' order by 3 --' LIMIT 0,1";
当注释符没有被过滤的时候位于注释符后的语句就都无效了,实际执行的语句是:
$sql="SELECT * FROM users WHERE id='1' order by 3;
但是当注释符被过滤时实际执行的语句就变成了:
$sql="SELECT * FROM users WHERE id='1' order by 3' LIMIT 0,1";
可以看到单引号是不匹配的,此时也就会报错了。从这里也能看出我们使用注释符的作用其实也就是闭合单引号,让每一个单引号都有与之对应的单引号,这样就不会报错了。
我们可以将注释符替换为语句:
and '1'='1
我们将其加到原本输入注释符的地方,这样我们测试列数的语句在后台执行的真正语句是:
$sql="SELECT * FROM users WHERE id='1' order by 3 and '1'='1' LIMIT 0,1";
这样单引号就完全闭合了,这是绕过单引号闭合的一种方法,其余方法我将在后续关卡中一一介绍。所以我们只要把注释符替换为语句 and '1'='1 就可以了。另外 $sql="SELECT * FROM users WHERE id='1' order by 3 and '1'='1' LIMIT 0,1"; 这条语句是错误的,上述我只是为了展现效果写了这条语句。在 Mysql 语法中如果存在 where 子句,那么 order by 语句必须放到语句的最后执行,否则将报错。所以这里测试列数不能使用 order by 语句,只能使用 Union 语句进行测试。
我们输入语句测试列数:
?id=0' union select 1,2,3 and '1'='1
页面回显:
可以看到列数正好是三,而且显示位也回显出来了。这里为什么也能使用 Union 语句进行列数的判断呢?因为在使用 Union 语句的时候要求查询的列数必须和前面的查询语句相同而且数据类型要一致。所以也可以以用 Union 语句去判断列数。
爆破数据库名
输入语句:
?id=0' union select 1,database(),3 and '1'='1
页面回显:
![](https://img-blog.csdnimg.cn/780aab12509a4ca3b6049fba48cc0369.png)
可以看到成功爆破了数据库名字。
爆破表名
输入语句:
?id=0' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' and '1'='1
页面回显: ![](https://img-blog.csdnimg.cn/2c98f29a008f4aac9a7639b0d2841566.png)
可以看到成功爆破了表名。
爆破列名
输入语句:
?id=0' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='emails' and '1'='1
页面回显: ![](https://img-blog.csdnimg.cn/90c4a941a4764660860f28e6294f4889.png)
可以看到成功爆破了表 emails 的列名。
爆破字段值
输入语句:
?id=0' union select 1,group_concat(id,email_id),3 from emails where 1=1 and '1'='1
这里爆破表名的时候要注意保证 and 前后作用的对象要一致,如果不加 where 1=1 的话,and 前的对象是表,而后面是数值,这样就会报错。所以我们要加一个 where 1=1 去保证 and 前后作用的对象都是数值。前面的语句不用加的原因是前面的语句本身带有 where 条件筛选。
或者也可以使用以下语句:
?id=0' union select 1,(select group_concat(id,email_id) from emails),3'
这条语句直接将整个查询语句放到了显示位上,后面用一个单引号闭合,也可以。
也可以使用 or 关键字进行闭合:
?id=0' union select 1,(select group_concat(id,email_id) from emails),3 or '1'='1
这里就是把单引号换成了语句 or '1'='1 。
页面回显:![](https://img-blog.csdnimg.cn/049504eacd4b44098cd422bf902c9040.png)
23关小结
这一关主要讲了如何绕过注释符被过滤,主要理解为什么要加注释符就好了(其实就是为了闭合符号)。另外这里要注意一下 and 前后对象一致的问题。 当然绕过的方法很多,可以使用 and '1'='1 绕过、or '1'='1 绕过、使用 URL编码 替换绕过等等。
Less24-二次注入
首先我们进入24关: ![](https://img-blog.csdnimg.cn/5d60bc63139d440eadde95998cc3ef21.png)
可以看到让我们输入用户名和密码才能有下一步操作,但是我们并不知道用户名和密码。刚好下边有新建用户,我们点进去看一下:![](https://img-blog.csdnimg.cn/ac3a8f64d341408996d1d6866288c455.png)
可以看到是一个新用户创建的页面,我们尝试随便输入一些数据看一下是否能够创建用户:![](https://img-blog.csdnimg.cn/209afea361b54289b95c7b6398aa47c6.png)
这里我把用户名以及密码都设置为1,进行创建。创建完成后登录看一下页面回显:
![](https://img-blog.csdnimg.cn/93e8bdd4f1d947e19516e01ab9b6cdf8.png)
可以看到成功登录后的页面是一个密码修改页面。那么目前我们知道的页面有:用户登录界面、用户创建界面、忘记密码界面以及成功登录界面。我们思考一下,每一个系统都应该有普通用户和管理员用户,管理员用户可以管理普通用户。我们现在是创建了一个普通用户,是没有权限查看其他用户的。但是管理员账户是可以查看其他用户的,如果我们能够得到管理员账户,那不就可以随意查看其他用户了吗?这就是这一关的目标,想办法弄到管理员账户。
一般管理员账户的用户名就是 admin 这里我就假设管理员用户名是这个。因为刚刚插入数据成功了,说明我们在新建用户的时候肯定是和后台数据库有交互的。但是我们是无法在输入框直接进行注入操作的,因为后台都做了过滤处理,大家可以尝试一下,这里我就不去尝试了。直接使用二次注入了,在这之前我讲一下原理。
二次注入理解及原理
使用场景:当后台使用反斜杠 \ 或者其他手段对我们的注入语句进行了过滤,从而导致我们无法使用一般的注入语句。但是我们可以成功向后台数据库插入或者构建一个新用户,这个时候就可以试试二次注入了,注入的时候我们首先判断注入点是什么,然后我们还需要知道管理员的用户名,这是前提。
注入原理:SQL二次注入,指的是在某些特定应用场景下,我们先把SQL注入的脏代码写入到目标站点数据库中。然后在某些实际应用中通过后台数据库将该数据取出使用(例如查询,修改密码等等),使得我们写入的脏代码执行。也可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。
缺陷原理:防御者虽然在前台对我们输入的语句进行了过滤导致我们无法注入,但是却没有对后台输入作出处理,也就是过分信任后台数据,从而导致读取后台数据的时候发生注入。
注入流程:
第一步要求我们插入的恶意数据能够成功写入到目标站点的数据库中,站点可以对我们写入的恶意数据进行转义,但是数据本身不会被修改,也不会因为是恶意的数据而拒绝我们的写入;
第二步要求我们的恶意数据能够在某种应用场景下取出,并且不会对其站点内部的数据进行检验(充分信任内部数据)。
原理示意图:
![](https://img-blog.csdnimg.cn/e81dbac6e1cb471a89c2ce6d3fc2f59c.png)
修改管理员账户密码
我们首先创造一个新用户 admin'# ,密码就设置为1,创建完成后登录新用户:![](https://img-blog.csdnimg.cn/670af449c2d841b999ccdd2cabde8afd.png)
从页面看我们的用户确实添加成功了,之后我们修改其密码为2: ![](https://img-blog.csdnimg.cn/df1ee3ca6eed4a60b94cafc6da8386ea.png)
可以看到已经修改成功了。我们要知道真正的管理员用户名是 admin 。我们要做的是将原本 admin 的密码修改为我们输入的密码,也就是2。 其实现在已经修改成功了,我们尝试登录管理员账户 admin(密码为2):![](https://img-blog.csdnimg.cn/6effde38160a4c58af23857dad2e743f.png)
![](https://img-blog.csdnimg.cn/21a5456c27924215a4fe6f8138f171a7.png)
可以看到我成功登录了管理员账户。二次注入也就完成了。接下来我给大家详细解析一下后台运行的情况。
首先当我们插入了数据:用户名:admin'# 密码:1的时候我们看一下此时的数据库:
可以看到我们已经成功插入了数据,我们进行代码审计看一下具体的代码,先进入login_create.php 文件查看:
这部分代码的含义是当我们输入一个新用户的时候首先检查用户名有没有重名。![](https://img-blog.csdnimg.cn/f7f6982d84f54e7da313e4eaaf8cb990.png)
这条语句就是插入语句。虽然对我们输入的注入语句进行过滤,但也只是让特殊符号无效化,并没有给我们直接删除,这也造成了二次注入的漏洞。
接下来我们分析一下 login.php 中的源码:
![](https://img-blog.csdnimg.cn/e68df99ca45f4d6ead7f73f344bf12b5.png)
这个页面就是我们的报错页面,当你输入注入点的时候就会出现这个页面,从这段代码可以看到,当我们输入一些数据的时候都会调用这个文件,而这个文件会对我们输入的数据进行过滤,使用 mysql_real_escape_string() 函数进行过滤,该函数的作用是转义 SQL 语句中使用的字符串中的特殊字符:
\x00
\n
\r
\
'
"
\x1a
接下来我们分析 pass_change.php 文件,也就是修改密码的后台文件:![](https://img-blog.csdnimg.cn/4c4c322ed5974a3da62a2e7913ac7737.png)
从这段代码可以看到,在修改密码的时候对密码的输入框使用函数mysql_real_escape_string() 进行了过滤,但是对用户名却没有做任何过滤处理,直接就带入到后面的 Update 更新语句进行了更新,因为我们输入的用户名是 admin'# 当被带入到更新语句后实际执行的语句是:
$sql = "UPDATE users SET PASSWORD='1' where username='admin'#' and password='$curr_pass' ";
# 后面的语句都被注释了,所以后台真正执行的语句是:
$sql = "UPDATE users SET PASSWORD='1' where username='admin';
这条语句的意思是更新用户 admin 的密码为1。这样我们就成功修改了管理员用户的密码。此时我们看一下数据库中的情况:
![](https://img-blog.csdnimg.cn/eefe1196f76b47d1b0682fef891c8f26.png)
可以看到成功修改了管理员 admin 用户的密码为1,注入成功。
二次注入到此讲解完成。
总结
这里我重点讲了二次注入,以及绕过注释符过滤的一种方法,后续我将讲解多种绕过方法,包括 and 和 or 被注释的绕过、Union 和 Select 被注释的绕过。