任意文件读取是属于文件操作漏洞的一种,一般任意文件读取漏洞可以读取的配置信息甚至系统重要文件。
严重的话,就可能导致SSRF,进而漫游至内网。
步骤1:代码审计,分析原理
我们已经知道,用户的头像显示,是使用file_get_contents
函数来读取的,而它读取的内容是从session
里面获取的。那么session
的值又是从哪里获取的?
使用全局搜索法对其进行搜索:$_SESSION['avatar']
,可以搜索到下面几个页面:
logcheck.php,登录页面 regcheck.php,注册的页面 updateAvatar.php 上传页面
我们着重查看updateAvatar.php
里面的内容,代码如下:
if (isset($_POST['submit']) && isset($_FILES['upfile'])) {
if(is_pic($_FILES['upfile']['name'])){
$avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name'];
if (move_uploaded_file($_FILES['upfile']['tmp_name'], $avatar)) {
//更新用户信息
$query = "UPDATE users SET user_avatar = '$avatar' WHERE user_id = '{$_SESSION['user_id']}'";
mysql_query($query, $conn) or die('update error!');
mysql_close($conn);
//刷新缓存
$_SESSION['avatar'] = $avatar;
header('Location: edit.php');
}
在第11行,files
变量拼接后,直接带入Sql语句查询,也没有sqlwaf的过滤。而且FILES变量也没有转义。这时候,只需要bypass掉单引号就可以注入了,因此可以通过注入去把它的路径改为我们想要的。
我们的思路是这样的:图像显示读取的内容是session
,而session
,可以从登陆的地方获取到数据库信息,而上传的地方可以更改数据库的信息。那么如何修改,变成想要的路径,去读取任意文件呢?
这时候就可以用到update
的特性,它的特性是:可以进行多重设置,但只取最后一个值。例如:
UPDATE users SET user_avatar = '1',user_avatar = '2' WHERE user_id = '1'
因此,我们就可以不管前面的1,并使用单引号去闭合它,并设置2的内容,改为我们想要的路径。
但是再看代码的第7行:它会判断文件名是否为图片后缀,因此还需要对其进行截断,继续构造语句:
UPDATE users SET user_avatar = '1',user_avatar = '2' WHERE user_name = 'test'#.png
因为我们并不知道参数的ID是多少,所以改为user_name。name即是我们的账户名,后面再修改为png格式。这样就可以成功的绕过。
最终的payload为:
',user_avatar = '2' WHERE user_name = 'test'#.png
只要将上传页面的数据包name值替换为我们构造好的payload,就可以成功的对数据库进行注入,插入我们构造的语句,从而达到任意文件读取的目的。 修复演示代码
if (isset($_POST['submit']) && isset($_FILES['upfile'])) {
if(is_pic($_FILES['upfile']['name'])){
$avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name'];
$avatar = sec($avatar);
if (move_uploaded_file($_FILES['upfile']['tmp_name'], $avatar)) {
//更新用户信息
$query = "UPDATE users SET user_avatar = '$avatar' WHERE user_id = '{$_SESSION['user_id']}'";
// UPDATE users SET user_avatar = '1',user_avatar = '2' WHERE user_name = 'test'#.png
// ',user_avatar = '2' WHERE user_name = 'test'#.png
mysql_query($query, $conn) or die(mysql_error());
mysql_close($conn);
//刷新缓存
$_SESSION['avatar'] = $avatar;
header('Location: edit.php');
对avatar变量进行了过滤,将单引号或其他敏感字符转换为斜杠,这样就修复了漏洞