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


引子

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

詳情

邏輯漏洞

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

$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指向的文件,代碼片段如下:

$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下使用..\也是可以的,所以可以任意刪除文件。

后台GetShell

后台GetShell的方法不少,這里算是其中一個,而且估計不會在短期內修補,因為算是一個正常的系統功能。
這個GetShell的方法很簡單,就是先通過普通會員的頭像文件上傳,傳上來有要執行的代碼的圖片,后台在包含進來即可。
思路就是這樣,那么前台頭像上傳必須要沒有驗證上傳的文件內容,看代碼:

   $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了,不過可惜沒辦法控制文件名,一般來說這里就沒啥用了,不過后台的一個任意文件包含就可以用上了。
代碼如下:

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時,直接執行了如下語句:

$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):""; 
} 

所以注入就很明顯了:
效果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM