极限黑客攻防:CTF赛题揭秘
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

题目3 二次注入

题目概况

二次注入是指已存储在数据库内的数据被再次调用时导致的注入攻击。

在SSCTF比赛中,Web类分值达500分的题目考察的就是二次注入。下面分析一下二次注入题目的解题思路。

解题思路

打开题目提供的地址页面,了解页面的内容和功能。在CTF比赛中,题目设置大都是有目的的,页面内容大都会包含指引信息。所以,了解页面的内容和功能,可以帮助我们判断题目的考察点。

如图1-7所示,这是一个注册页面,也就是说,通过这个页面可以向数据库写数据。还有一个查询页面,用于查询邮箱和返回用户名称。注册一个用户名为“root'”、密码为“root”、邮箱为“root@admin.com”的用户,测试一下是否可以将单引号写入数据库。

单击“ok”按钮,返回“yes”,看来注册信息已经写入数据库了。接下来,尝试一下是否可以注入。查询注册的邮箱,如图1-8所示。

图1-7

图1-8

如图1-9所示,页面报错,看来在调用用户名“root'”时已经把单引号执行了。从错误信息中可以看出,目标数据库是MySQL数据库。从提示语句中也可以知道,查询语句已经把单引号引入查询语句了,说明这里存在注入漏洞。

图1-9

通过分析可知,这是一个二次注入漏洞。下面我们需要读取有用的信息。利用报错信息查看数据库表名。报错查询方法有很多种,通过floor()、extractvalue()、updatexml()、NAME_CONST等函数均可查询,如表1-1所示。

表1-1

这里使用的是floor()函数,注册的账号为“1123'”,邮箱为“test1223@123.com”,代码如下,如图1-10所示。

图1-10

如图1-11所示,在查询页面输入注册的邮箱地址,在返回的错误信息中可以看到,得到了一个表名“flag”。

图1-11

既然有了表名,就修改语句,以便查看flag表的内容。语句如下,如图1-12所示。

图1-12

从报错信息中可以得到flag。

相关知识点

以上介绍的是一个模拟的题目环境。在历年的CTF比赛中,类似的题目经常出现。

报错注入是如何产生的?程序为何会报错?主要由rand和group by的冲突所致。在MySQL的文档中有这样的描述:“RAND()in a WHERE clause is re-evaluated every time the WHERE is executed. You cannot use a column with RAND()values in an ORDER BY clause,because ORDER BY would evaluate the column multiple times.”大意是:那些不可以作为order by的条件字段,也不可以作为group by的条件字段。

为什么rand和order by不可以一起使用呢?一起使用为什么会报错呢?

新建一条记录,执行命令“select count()from test group by floor(rand(0)2);”,发现无论执行多少遍,都不会报错。添加一条记录,情况是一样的。然而,当有3条记录时,执行此命令,将开始报错。由此可知,3条及更多的记录才能引发报错。

我们把随机因子去掉,再试试看。在只有1条记录时,无论程序执行多少次,都不会报错;在有2条记录时,会随机报错;在有3条记录时,也会随机报错。

MySQL官方给过提示:在进行查询时,如果使用rand()函数,则该值会被多次计算。“被多次计算”是什么意思?就是在使用group by时,floor(rand(0)_2)会执行一次;如果虚表中没有记录,那么在插入虚表时,floor(rand(0)_2)会再执行一次。分析floor(rand(0)_2)报错的过程就能明白,在一次多记录查询中,floor(rand(0)_2)的值是定性的,为0、1、1、0、1、1……(这个顺序很重要)报错实际上就是floor(rand(0)_2)被多次执行导致的。

下面分析一下“select count(_)from TSafe group by floor(rand(0)_2);”的查询过程。

①在进行查询前,默认建立空虚拟表。

②取第1条记录,执行floor(rand(0)2),发现结果为0(第1次计算);查询虚拟表,发现0的键值不存在,floor(rand(0)2)会再执行一次,结果为1(第2次计算);插入虚表,这时第1条记录查询完毕。

③查询第2条记录,再次执行floor(rand(0)2),发现结果为1(第3次计算);查询虚表,发现1这个键值存在,所以floor(rand(0)2)不会执行第2次;直接使用count()函数进行加1操作,第2条记录查询完毕。

④查询第3条记录,再次执行floor(rand(0)2),发现结果为0(第4次计算);查询虚表,发现键值中没有0,数据库尝试插入一条新的数据。在插入数据时,floor(rand(0)2)再次执行,作为虚表的主键,其值为1(第5次计算)。因为1这个主键已存在于虚拟表中,计算得到的值也为1(主键的键值必须是唯一的),所以插入时会直接报错。

在整个查询过程中,floor(rand(0)2)执行了5次,查询原数据表的操作进行了3次。这就是数据表中需要有至少3条数据且同时使用rand和order by才会报错的原因。

我们同样可以对不添加随机因子的情况进行推理。由于没有添加随机因子,所以,floor(rand()2)是不可测的:在有2条数据时,只要出现第3次计算的值与第2次和第4次计算的值不一样的情况,就会报错。最重要的是:前几条记录被查询后,虚表中不可能存在键值0和1;如果存在,那么无论有多少条记录,都不会报错(floor(rand()2)不会再执行并作为虚表的键值),这也是如果不添加随机因子,则有时会报错,有时不会报错的原因。

在前面的记录让虚表变成现在这样之后,由于不管查询多少条记录,都能在虚表中找到floor(rand()2)的值,即不会再次计算,只是简单地增加count()的值,所以,程序不会报错。例如,floor(rand(1)*2)表示在前2条记录被查询后,虚表中已经存在键值0和1了,所以后面就不会报错了。

其他函数的报错,请读者自行查找资料,找出原因——自己找到的答案更珍贵,这也是CTF比赛的乐趣所在。

小结

在二次注入的基础上增加加密算法,是近几年CTF比赛的一个命题趋势,即不会只考察单一的知识点,选手需要将两个或两个以上的知识点组合使用,才能得到flag。在一些综合性CTF比赛中,单纯的Web类赛题已经很难见到了,大部分Web类赛题都需要选手运用逆向、密码学、Web方面的知识点来解答。