74CMS漏洞打包(從老博客轉)

引子

這套CMS是上個月中作的審計,總共找到幾個後臺漏洞,可後臺getshell,一個邏輯漏洞可任意發短信,還有一個前臺注入漏洞。不過發到了某平臺上以後,審覈又要求我提交利用的poc,因此懶得發去了,就這裏發來,剛纔看了下最新版,已經修復了。版本是74cms_v3.6_20150902。php

詳情

邏輯漏洞

第一個邏輯漏洞是短信發送處的問題,在ajax_user.php文件中,原代碼片斷以下:ajax

$mobile=trim($_POST['mobile']); 
$sms_type=$_POST['sms_type']?$_POST['sms_type']:"reg"; 
if (empty($mobile) || !preg_match("/^(13|15|14|17|18)\d{9}$/",$mobile)) 
{ 
	exit("手機號錯誤"); 
} 
$rand=mt_rand(100000, 999999);        
switch ($sms_type) { 
	case 'reg': $sms_str="您正在註冊{$_CFG['site_name']}的會員,手機驗證碼爲:{$rand},此驗證碼有效期爲10分鐘"; break; 
	case 'getpass': $sms_str="您正在找回{$_CFG['site_name']}的會員密碼,手機驗證碼爲:{$rand},此驗證碼有效期爲10分鐘"; break; 
} 
if($_SESSION['verify_mobile']==$mobile && time()<$_SESSION['send_time']+180) 
{ 
	exit("180秒內僅能獲取一次短信驗證碼,請稍後重試"); 
} 
else { 
	$r=send_sms($mobile,$sms_str); 
} 
if ($r=="success"){ 
$_SESSION['mobile_rand']=substr(md5($rand), 8,16); 	$_SESSION['send_time']=time(); 	$_SESSION['verify_mobile']=$mobile; 	exit("success"); 
} 
else { exit("SMS配置出錯,請聯繫網站管理員"); }

能夠看出,這裏每次會把此次輸入的手機號和session中記錄的手機號作驗證,若是相同而且時間間隔不夠3分鐘,那麼就會報錯,可是這裏有一個問題,就是每次輸入了手機號後若是成功發送了短信,那麼就會覆蓋掉session中的手機號,因此咱們只須要輪詢兩個正常的手機號便可越過限制。目前版本已經修復,修復方法也很簡單,就是先作一個時間驗證便可,無論輸入的手機號是多少,發短信的時間間隔不可少於60s,很聰明的fix。
後臺還有一個任意文件刪除的問題,文件是admin/admin_article.php,這裏會刪除掉$_GET來的img指向的文件,代碼片斷以下:sql

$id=intval($_GET['id']); 
$img=$_GET['img']; 
$img=str_replace("../","***",$img); 
$sql="update ".table('article')." set Small_img='' where id=".$id." LIMIT 1"; 
$db->query($sql); 
@unlink($upfiles_dir.$img); 
@unlink($thumb_dir.$img);

看到這裏過濾了../,然而在windows下使用..\也是能夠的,因此能夠任意刪除文件。shell

後臺GetShell

後臺GetShell的方法很多,這裏算是其中一個,並且估計不會在短時間內修補,由於算是一個正常的系統功能。
這個GetShell的方法很簡單,就是先經過普通會員的頭像文件上傳,傳上來有要執行的代碼的圖片,後臺在包含進來便可。
思路就是這樣,那麼前臺頭像上傳必需要沒有驗證上傳的文件內容,看代碼:windows

$savePath = "../../data/avatar/100/";  //圖片存儲路徑
   $savePathThumb = "../../data/avatar/48/";  //圖片存儲路徑
   $savePicName = time();//圖片存儲名稱
   $file_src = $savePath.$savePicName."_src.jpg";
   $filename150 = $savePath.$savePicName.".jpg"; 
   $filename50 = $savePathThumb.$savePicName.".jpg"; 
   $src=base64_decode($_POST['pic']);
   $pic1=base64_decode($_POST['pic1']);   
   $pic2=base64_decode($_POST['pic2']);
   if($src) {
            file_put_contents($file_src,$src);
   }
   file_put_contents($filename150,$pic1);

這裏直接fileput_contents了,不過惋惜沒辦法控制文件名,通常來講這裏就沒啥用了,不事後臺的一個任意文件包含就能夠用上了。
代碼以下:session

if (!empty($crons))
    {
            if (!file_exists(QISHI_ROOT_PATH."include/crons/".$crons['filename']))
            {
            adminmsg("任務文件 {$crons['filename']} 不存在!",0);
            }
    require_once(QISHI_ROOT_PATH."include/crons/".$crons['filename']);
    adminmsg("執行成功!",2);
    }

這個文件是在後臺的計劃任務管理那裏,具體名字我也忘了……可是這個部分的目的是爲了幫助管理員執行一些簡單的任務,好比統計站點訪問的狀況或者其餘的自定義代碼,而這裏的問題就是爲了方便的作到統計的目的,這套cms在common.inc.php(具體名字忘了)裏include了執行計劃的代碼,也就是說,只要後臺添加了一個計劃任務,前臺便可執行。驗證方法也很簡單,這裏你們能夠作一個phpinfo的文件添加到計劃任務,再訪問下主頁便可看見主頁上用戶登陸處出現了phpinfo的結果。
因爲這個計劃任務的功能是系統功能的一部分,估計不會作啥修復,不過前臺頭像上傳可能作一些修改,可是也不怕,這裏其實還有一個問題。就是CSRF,雖然他有一個防CSRF的功能,可是他在後臺居然有個能夠關閉CSRF的開關,那麼出於方便或者啥的緣由,若是管理員關閉了CSRF,那麼只要管理員看見了一個精心構造的圖片便可getshell(不過這可能性很低)。函數

SQL注入漏洞

這裏只找到了一個前臺注入漏洞,不過當時經過了官方DEMO站的驗證。
文件位置: ajax_user.php,當act爲get_pass_check時,出現了注入,代碼以下:測試

require_once(QISHI_ROOT_PATH.'include/fun_user.php'); 
$username=$_POST['username']?trim($_POST['username']):exit("false"); 
if (preg_match("/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/",$username)) 
{ 
	$usinfo=get_user_inemail($username); 
} 
elseif (preg_match("/^(13|14|15|18|17)\d{9}$/",$username)) { 
$usinfo=get_user_inmobile($username); 
} 
else 
{ 
	$usinfo=get_user_inusername($username); 
}

這裏沒有對$username作過濾,不過全局有addslash,可是這裏能夠寬字節注入,那麼就能夠注入了。DEMO站示意圖:
正常請求:
正常請求
這裏反回了false,由於不存在這個用戶,那麼換個payload:網站

sunrain%df%27%20or%20%df%271%df%27=%df%271

這句話就是簡單地or 1=1,若是能夠注入,那麼應該是返回true的:
注入
固然,這裏也是能夠出數據的,當時本機測試了,可是沒截圖。
後臺注入仍是很多,這裏列出兩個:
第一處: admin_category.php文件,當act是edit_color_save時,直接執行了以下語句:ui

$info=get_color_one($_POST['id']);

跟入函數:

function get_color_one($id) { 
global $db; 
$sql = "select * from ".table('color')." WHERE id=".$id.""; 
return $db->getone($sql); 
}

發現這裏沒有引號,因此能夠注入:
效果
第二處: 在admin_baiduxml.php文件中,當act是setsave時,執行了以下語句:

foreach($_POST as $k => $v) { 
	!$db->query("UPDATE ".table('baiduxml')." SET value='{$v}' WHERE name='{$k}'")?adminmsg('保存失敗', 1):""; 
}

因此注入就很明顯了:
效果

相關文章
相關標籤/搜索