1.1.2 SQL注入进阶

1.二次注入

二次注入是一种用来绕过输入点防御的方法。通常开发人员会在用户的输入点进行关键字过滤和特殊字符转义,给我们利用漏洞带来了很大的困难。我们输入的数据插入数据库时会被还原并存储在数据库中,而当Web程序再次调用存储在数据库中的数据时,由于没有将提取出来的数据进行转义和过滤,就会执行我们插入的恶意SQL语句。下面介绍具体方法和步骤。

第一步,插入恶意数据。在向数据库中插入数据时(如创建新用户、添加评论、修改用户信息等),Web程序对插入的数据进行转义和过滤,在写入数据库时又将数据还原。

第二步,引用恶意数据。当Web程序将数据从数据库中取出并调用时,恶意SQL语句被代入原始语句中,造成SQL二次注入,如图1-27所示。

图1-27 二次注入原理示意图

以2019-CISCN华北赛区Day1 Web5-CyberPunk题目为例,通过文件包含漏洞方法读出源码,这里不做详述,如图1-28~图1-30所示。

图1-28 index.php源码

图1-29 change.php源码

图1-30 search.php源码

可以看到,address虽然经过了过滤,但是在修改地址的时候从数据库中取出的是没有过滤直接带入的SQL语句,从而导致SQL注入,如图1-31所示。

图1-31 修改地址处没有过滤

由于这里是update语句,因此我们使用报错注入,代码如下。

结果如图1-32~图1-34所示。

图1-32 插入恶意语句

图1-33 再次调用

图1-34 注入成功

2.无列名注入

通常在注入时我们可以利用information_schema库获取所有库的库名、表名、列名,但是这个库经常被WAF(Web Application Firewall,网站应用级入侵防御系统)过滤。无列名注入适用于已经获取数据表,但无法查询列的情况,在大多数CTF题目中,information_schema库被过滤,使用这种方法可以获取列名。

无列名注入的原理很简单,类似于将我们不知道的列名进行取别名操作,在取别名的同时进行数据查询。

先创建一个数据库demo,再创建一个testuser表,结构如图1-35所示。

图1-35 创建数据库和数据表

往testuser表里插入一些数据,代码如下。

正常查询,结果如图1-36所示。

图1-36 正常查询结果

这时再使用一个union查询,如图1-37所示。

图1-37 union查询结果

利用数字3代替未知的列名,需要加上反引号。代码后面加了一个a,是为了表示这个表(select 1,2,3 union select*from testuser)的别名,如图1-38所示。

图1-38 代替未知列名

当反引号不能使用时,用别名来代替,如图1-39所示。

图1-39 列名被替换

以2019-SWPU-Web1题目为例,本题主要考查无列名注入和空格绕过。我们在试出列数后直接注入即可,如图1-40所示。

图1-40 注入成功

3.堆叠注入

顾名思义,堆叠注入就是一堆SQL语句一起执行。在MySQL语句中,每条语句的结尾都有一个“;”,代表一条语句结束。我们将多个SQL语句用“;”连接起来,就可以达到多条语句一起执行的效果,从而造成SQL注入。堆叠注入和union联合查询本质上都是将两条语句连接在一起执行,区别在于union查询只能连接两条查询语句,而堆叠注入可以连接两条任意的语句。当WAF没有过滤show、rename、alert等关键词时,我们就可以考虑使用堆叠注入。

执行下列语句达到堆叠的效果,运行结果如图1-41所示。

图1-41 列名被替换

以2019-强网杯-随便注题目为例,利用堆叠注入查看表信息,如图1-42所示。

图1-42 查看表名

查看1919810931114514表中的字段(注意,要将表名用反引号引出),如图1-43所示。

图1-43 获取字段名

发现flag字段后,查看words表中的字段,如图1-44所示。

图1-44 获取words表的字段名

发现id和data两个字段,因为没有过滤rename和alert,考虑将1919810931114514表改名为words, flag字段改名为data。

流程是将words表名改为其他名字,然后将1919810931114514表改名为words,最后将flag字段更名为data,代码如下。

查询1即可获得flag,如图1-45所示。

图1-45 获取flag

4.SQL注入与其他漏洞结合

有些时候,SQL注入漏洞并不能直接获取flag,而是为了配合其他漏洞的使用,如SSTI(Server-Side Template Injection,服务端模板注入)、SSRF(Server Side Request Forgery,服务端请求伪造)等,其原理是控制某个漏洞处引用的值,从而达到文件读取或RCE(Remote Command/Code Execute,远程命令/代码执行)的目的。

以2018-科来杯-Web3题目为例,本题考查SQL注入与SSTI利用。注入点并不难发现,使用常规的union联合查询就可以轻松发现注入点,代码如下,运行结果如图1-46所示。

图1-46 发现注入点

这个时候利用SQL注入去拖库会发现库里什么都没有。利用SSTI漏洞的原理就是在注入点处,Web程序将查询到的内容再次引入具有SSTI漏洞的代码。下面我们进行SSTI的测试(这里可以使用十六进制编码),如图1-47所示。

图1-47 打印出配置信息

接下来就是常规的SSTI获取服务器权限,代码如下,效果如图1-48所示。

图1-48 获取flag

上述代码等同于如下代码。

以2018-网鼎杯-Fakebook题目为例,本题的原理和上一题基本相同,都是先将可控点的值设为恶意代码,然后由Web程序带入另一段有漏洞的代码中实现利用。可以理解为SQL注入起到的只是控制变量的作用。该题目首先具有源码泄露,如图1-49所示。

图1-49 备份文件中得到源码

在注册界面输入的blog字段经过了isValidBlog()函数的过滤。由于get()函数存在SSRF漏洞,因此直接在注册界面输入file://var/www/html/flag.php就能拿到flag。

这时我们注册一个账号进入,如图1-50所示。

图1-50 注册用户登录

使用admin账号登录后发现URL里面有参数no,尝试注入、爆库,具体操作不再赘述,直接给出最终获取数据的Payload。

获取数据如图1-51所示。

图1-51 获取数据

data数据段是一个序列化串,程序正常应该是从data字段中取出序列化串,然后进行反序列化,最终把信息展示给我们。我们在data字段中将blog的值修改为file:///var/www/html/flag.php,即可触发SSRF漏洞并获取flag,代码如下。

运行结果如图1-52所示。

图1-52 获取flag

最后进行Base64解码即可。

本题还有一种非预期解法,因为出题人忘记对load_file函数进行过滤,导致我们可以直接任意读取文件,如图1-53所示,代码如下。

图1-53 用非预期解法获取flag