渗透测试tips:两处有趣的文件上传到getshell

2020-01-01 约 122 字 预计阅读 1 分钟

声明:本文 【渗透测试tips:两处有趣的文件上传到getshell】 由作者 raul 于 2020-01-01 09:14:14 首发 先知社区 曾经 浏览数 223 次

感谢 raul 的辛苦付出!

渗透测试tips:两处有趣的文件上传到getshell

0x00 写在前面

前阵子某安全厂商对我司的某系统进行了渗透测试,报告中有两处由于文件上传导致的getshell。在复现和研究这两处的漏洞过程中,觉得漏洞挺有意思,几个漏洞组合在一起getshell,大佬的思路也挺牛逼的,故做简单的一个记录。

两处漏洞产生的功能点都为:个人资料编辑处的上传头像功能(两处不同的上传头像接口)。该两处上传头像的接口类似,上传流程如下:先将头像图片上传到阿里云的OSS,随后有个头像裁剪功能,裁剪头像将头像下载到本地,保存到根目录。

0x01 漏洞接口1-漏洞详情

(1)漏洞1:任意文件读取

该接口(/invite/uploadavatar)的正常上传流程如下:

先将图片上传到阿里云OSS,接口如下:

图片上传成功后,界面会弹出裁剪图片:

点击确定,裁剪的接口数据包如下:

该接口四个参数,url参数是阿里云OSS返回的图片url地址。

该url参数未进行校验地址是否合法,故url参数我们可以随意构造。所以很显然,该url参数也存在ssrf。

除了ssrf,url参数我们也可使用file协议构造(原理是源码中使用了copy()函数,后面贴源码我会进行介绍)。

将url参数构造出file:///etc/passwd,user_id用base64编码一下。响应包中,会直接返回裁剪后(保存在本地)的url地址,如下图所示:

然后直接访问响应中的url地址:http:///www.xxx.com/avatar_MQ==,该文件内容就是etc/passwd的内容:

(2)代码审计

由于系统中某些功能报错,页面直接返回了代码的绝对路径,所以我们直接可以读取该上传接口控制器的源代码:

可以看到请求包中的参数(extension、url、user_id)都没有进行校验。可以看到代码中使用了copy()函数,php 的 copy 函数可访问 file:///等协议,然后在某个地点回显出文件内容。

例如copy(source,destination),就是将文件从 source 拷贝到 destination。如果成功则返回 TRUE,否则返回 FALSE。

而传入copy()中的两个参数都没有进行校验。copy结束后返回url地址。

copy函数的第二个参数,是由'$dir''$new_name'拼接的,而$newname参数是由`'avatar''$userId''$extension'`拼接而成的。

所以我们可以自己写一个控制器,里面包含shell的代码,然后copy到控制器根目录,这样就可以getshell了。

(3)getshell

首先,根据我们下载的控制器代码,自己写一个控制器(目的是执行test函数,打印"avatar"):

# 代码结构跟前面file协议读取的控制器代码一样,就自己写了个test()方法
<?php
namespace app\controller;
use My;
use my\api\Controller;

class MyevalController extends BaseController
{

    private $inviteService = false;
    private $commonHelper = false;

    public function __construct()
    {
        parent::__construct();

    }
    public function test()
    {
        exit("avatar");
    }
}

将编写的控制器脚本上传到我们自己的服务器上,随后构造extension参数:

前文源码中我们已经知道copy函数的第二个参数($upload_file),结构为:

$dir + 'avatar_' + $userId + $extension

源码中$dir的值为'/tmp/',若请求包中的user_id参数为345,extension参数为789,则$upload_file参数的值为:

/tmp/avatar_345789

那我们构造user_id参数为345,extension参数为:/../../data/www/xx/controller/MyTestController.php,如下图所示(当然,user_id参数也没有校验,我们也可以把构造的内容放在user_id后面):

$upload_file参数就是这样的:

/tmp/avatar_345/../../data/www/xx/controller/MyTestController.php

这个upload_file参数的路径就有意思了,copy函数执行的时候,先将我们的MyTestController.php拷贝到/tmp/avatar_345/目录,然后执行/../../,返回两个上级目录,又回到了当前目录,然后再进入到/data/www/xx/controller/目录下。所以最终我们的控制器就直接拷贝在了/data/www/xx/controller/下。

有点类似linux里面的这种(两个/../执行,又回到当前目录):

所以我们直接浏览器访问我们的控制器:

http://www.xxxx.com/MyTestController/test

执行test方法,页面上打印"avatar"。

故如何getshell,也就很简单了。

0x02 漏洞接口2-漏洞详情

第二个接口跟第一个接口原理一样,不同的是,接口2的代码中先对url进行了校验,然后直接调用了接口1的uploadAvatar代码:

判断了url的host是否为www.xxxx.com,如果不是的就直接返回“地址有误”。

绕过该限制的方法是:我们直接在上传头像到阿里云OSS处,就把我们的控制器放在图片内容里(该上传接口未对图片内容进行校验):

访问阿里云OSS返回的图片地址,可以看到图片内容是我们控制器的代码:

其他的就跟第一个接口一样,将url参数构造为阿里云OSS返回的包含我们控制器代码地址,extension参数也是一样,给定控制器的目录。

故拿到shell也是轻而易举了(很显然,这个方法同样可以在漏洞接口1中利用)。

0x03 漏洞修复

在复现和分析完这两个漏洞后,让开发严格限制了:

  • user_id只能为数字,不能包含字母和特殊字符;
  • extension参数严格过滤点号、斜线等可能造成跨目录访问的字符;
  • url参数严格过滤只能http协议、校验url的host白名单、校验图片内容;

关键词:[‘渗透测试’, ‘渗透测试’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now