1. 程式人生 > >taint源碼分析與使用介紹

taint源碼分析與使用介紹

control 讀取 屬性 fail head list ont 問題: printf

一、介紹

動態汙點分析(Dynamic Taint Analysis,DTA)是一種動態信息流分析方法,其跟蹤程序運行時對數據的處理,並記錄處理過程中數據的傳播,汙點分析的目的是找出目的數據結果與源數據之間的依賴關系。汙點分析可以分為三個方面:汙點標記、汙點傳播和汙點檢查。汙點標記是指來自網絡等不可信渠道的數據都會被標記為“汙點”。在汙點標記後,汙點數據進行各種運算所得的結果也是不可信的,因此也被標記上了“被汙染的”的屬性,這個過程就是汙點傳播。

簡介:Taint是一個PHP插件,主要的功能有檢測XSS、SQL註入、命令註入、代碼註入等漏洞。

原理:檢查某些關鍵函數(是否直接使用(沒有經過過濾或轉義處理)了來自$_GET,$_POST,$_COOKIE,的數據,如使用則給出提示

二、安裝使用

wget http://pecl.php.net/get/taint-1.2.2.tgz (下載最新的taint)
tar zxvf taint-1.2.2.tgz
cd taint-1.2.2
Phpize
./configure
make
make install
配置:php/lib/php.ini

技術分享圖片

成功安裝後,會在nginx的error.log處生成warning日誌:

技術分享圖片

三、漏洞測試:

XSS
漏洞代碼:
$uri = Yii::app()->request->getParam(‘uri‘);echo $uri;
Error.log內容:
2018/10/09 11:39:29 [error] 14763#0: *2333 FastCGI sent in stderr: "PHP message: PHP Warning: actionIndex() [echo]: Attempt to echo a string that might be tainted in /home/dly/www/main/controller/TemplateController.php on line 18" while reading response header from upstream, client: 10.0.27.12, server: localhost, request: "GET /Template/index?uri=dlytestxssecho HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "localhost"


SQL
漏洞代碼:
public function getLikeStableLink($url){ $strSql="select id from ".$this->table." where state= 2 AND stable_link like ‘%".$url."%‘ order by create_time desc"; $result=$this->db()->query($strSql); return $result; }
Error.log內容:
2018/10/09 14:28:03 [error] 14762#0: *2483 FastCGI sent in stderr: "PHP message: PHP Warning: query() [mysqli::query]: SQL statement contains data that might be tainted in /home/dly/www/common/shared/db/DB.php on line 531 PHP message: PHP Warning: query() [mysqli::query]: SQL statement contains data that might be tainted in /home/dly/www/common/shared/db/DB.php on line 531" while reading response header from upstream, client: 10.0.27.12, server: localhost, request: "GET /getinnerlink/index?logid=634395945&url=https%3A%2f%2flocalhost%2fcredit%2f12312&category_id=8 HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "localhost"

任意文件讀取
漏洞代碼:
Echo $uri;
$this->renderFile($uri );
Error.log內容:
2018/10/09 15:36:19 [error] 14770#0: *2623 FastCGI sent in stderr: "PHP message: PHP Warning: actionIndex() [echo]: Attempt to echo a string that might be tainted in /home/dly/www/main/controller/TemplateController.php on line 18 PHP message: PHP Warning: renderInternal() [require]: File path contains data that might be tainted in /home/dly/www/framework/web/CBaseController.php on line 123" while reading response header from upstream, client: 10.0.27.12, server: localhost, request: "GET /Template/index?uri=/etc/passwd HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "localhost"


命令執行
漏洞代碼:
System($_GET[‘a‘]);
Error.log內容:2018/10/09 15:57:09 [error] 14769#0: *2651 FastCGI sent in stderr: "PHP message: PHP Warning: actionIndex() [system]: CMD statement contains data that might be tainted in /home/dly/www/main/controller/TemplateController.php on line 19" while reading response header from upstream, client: 10.0.27.12, server: localhost, request: "GET /Template/index?uri=ifconfig HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "localhost"


代碼執行
漏洞代碼:
$fun = $_GET[‘fun‘];$par = $_GET[‘par‘];$fun($par);
Error.log內容:
2018/10/09 16:00:38 [error] 14769#0: *2653 FastCGI sent in stderr: "PHP message: PHP Warning: actionIndex() [fcall]: Attempt to call a function which name might be tainted in /home/dly/www/main/controller/TemplateController.php on line 21" while reading response header from upstream, client: 10.0.27.12, server: localhost, request: "GET /Template/index?fun=phpinfo HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "localhost"

四、代碼分析:

1、

#define IS_STR_TAINT_POSSIBLE    (1<<7)
//定義mark規則
#define TAINT_MARK(str)        (GC_FLAGS((str)) |= IS_STR_TAINT_POSSIBLE)
#define TAINT_POSSIBLE(str) (GC_FLAGS((str)) & IS_STR_TAINT_POSSIBLE)
#define TAINT_CLEAN(str)      (GC_FLAGS((str)) &= ~IS_STR_TAINT_POSSIBLE)

2、對用戶傳入的數據進行mark

PHP_RINIT_FUNCTION(taint)
{
    if (SG(sapi_started) || !TAINT_G(enable)) {
        return SUCCESS;
    }

//汙染post數據
    if (Z_TYPE(PG(http_globals)[TRACK_VARS_POST]) == IS_ARRAY) {
        php_taint_mark_strings(Z_ARRVAL(PG(http_globals)[TRACK_VARS_POST]));
    }
//汙染GET變量數據
    if (Z_TYPE(PG(http_globals)[TRACK_VARS_GET]) == IS_ARRAY) {
        php_taint_mark_strings(Z_ARRVAL(PG(http_globals)[TRACK_VARS_GET]));
    }
//汙染COOKIE數據
    if (Z_TYPE(PG(http_globals)[TRACK_VARS_COOKIE]) == IS_ARRAY) {
        php_taint_mark_strings(Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]));
    }

    return SUCCESS;
}
//標記所有的GET、COOKIE、POST、SERVER這些array中的每個key->value初始標記為taint
static void php_taint_mark_strings(zend_array *symbol_table) /* {{{ */ {
zval *val;
ZEND_HASH_FOREACH_VAL(symbol_table, val) {
ZVAL_DEREF(val);
if (Z_TYPE_P(val) == IS_ARRAY) {
php_taint_mark_strings(Z_ARRVAL_P(val));
} else if (IS_STRING == Z_TYPE_P(val) && Z_STRLEN_P(val)) {
TAINT_MARK(Z_STR_P(val));
}
} ZEND_HASH_FOREACH_END();
} /* }}} */
 

3、遇到非變量賦值函數時,去掉MARK

賦值函數list:

static void php_taint_override_functions() /* {{{ */ {
    const char *f_join         = "join";
    const char *f_trim         = "trim";
    const char *f_split        = "split";
    const char *f_rtrim        = "rtrim";
    const char *f_ltrim        = "ltrim";
    const char *f_strval       = "strval";
    const char *f_strstr       = "strstr";
    const char *f_substr       = "substr";
    const char *f_sprintf      = "sprintf";
    const char *f_explode      = "explode";
    const char *f_implode      = "implode";
    const char *f_str_pad      = "str_pad";
    const char *f_vsprintf     = "vsprintf";
    const char *f_str_replace  = "str_replace";
    const char *f_str_ireplace = "str_ireplace";
    const char *f_strtolower   = "strtolower";
    const char *f_strtoupper   = "strtoupper";
    const char *f_dirname      = "dirname";
    const char *f_basename     = "basename";
    const char *f_pathinfo     = "pathinfo";

    php_taint_override_func(f_strval, PHP_FN(taint_strval), &TAINT_O_FUNC(strval));
    php_taint_override_func(f_sprintf, PHP_FN(taint_sprintf), &TAINT_O_FUNC(sprintf));
    php_taint_override_func(f_vsprintf, PHP_FN(taint_vsprintf), &TAINT_O_FUNC(vsprintf));
    php_taint_override_func(f_explode, PHP_FN(taint_explode), &TAINT_O_FUNC(explode));
    php_taint_override_func(f_split, PHP_FN(taint_explode), NULL);
    php_taint_override_func(f_implode, PHP_FN(taint_implode), &TAINT_O_FUNC(implode));
    php_taint_override_func(f_join, PHP_FN(taint_implode), NULL);
    php_taint_override_func(f_trim, PHP_FN(taint_trim), &TAINT_O_FUNC(trim));
    php_taint_override_func(f_rtrim, PHP_FN(taint_rtrim), &TAINT_O_FUNC(rtrim));
    php_taint_override_func(f_ltrim, PHP_FN(taint_ltrim), &TAINT_O_FUNC(ltrim));
    php_taint_override_func(f_str_replace, PHP_FN(taint_str_replace), &TAINT_O_FUNC(str_replace));
    php_taint_override_func(f_str_ireplace, PHP_FN(taint_str_ireplace), &TAINT_O_FUNC(str_ireplace));
    php_taint_override_func(f_str_pad, PHP_FN(taint_str_pad), &TAINT_O_FUNC(str_pad));
    php_taint_override_func(f_strstr, PHP_FN(taint_strstr), &TAINT_O_FUNC(strstr));
    php_taint_override_func(f_strtolower, PHP_FN(taint_strtolower), &TAINT_O_FUNC(strtolower));
    php_taint_override_func(f_strtoupper, PHP_FN(taint_strtoupper), &TAINT_O_FUNC(strtoupper));
    php_taint_override_func(f_substr, PHP_FN(taint_substr), &TAINT_O_FUNC(substr));
    php_taint_override_func(f_dirname, PHP_FN(taint_dirname), &TAINT_O_FUNC(dirname));
    php_taint_override_func(f_basename, PHP_FN(taint_basename), &TAINT_O_FUNC(basename));
    php_taint_override_func(f_pathinfo, PHP_FN(taint_pathinfo), &TAINT_O_FUNC(pathinfo));

} /* }}} */

去掉taint標記

PHP_FUNCTION(untaint)
{
    zval *args;
    int argc;
    int i;

    if (!TAINT_G(enable)) {
        RETURN_TRUE;
    }

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
    //zend_parse_parameters獲取函數傳遞的參數
    //ZEND_NUM_ARGS函數中傳遞參數的個數
        return;
    }

    for (i = 0; i < argc; i++) {
        zval *el = &args[i];
        ZVAL_DEREF(el);
//判斷變量是string型,並且被標記過
        if (IS_STRING == Z_TYPE_P(el) && TAINT_POSSIBLE(Z_STR_P(el))) {
            TAINT_CLEAN(Z_STR_P(el));
        }
    }

    RETURN_TRUE;
}

4、漏洞判斷邏輯(XSS舉例):

static int php_taint_echo_handler(zend_execute_data *execute_data) /* {{{ */ {
    const zend_op *opline = execute_data->opline;//指向當前執行的opcode,初始時指向zend_op_array起始位置
    //* opcode指令:即PHP代碼具體對應的處理動作,與二進制程序中的代碼段對應
    taint_free_op free_op1;
    zval *op1;

    op1 = php_taint_get_zval_ptr(execute_data, opline->op1_type, opline->op1, &free_op1, BP_VAR_R, 0);
    //獲取傳入函數的參數

    if (op1 && IS_STRING == Z_TYPE_P(op1) && TAINT_POSSIBLE(Z_STR_P(op1))) {
    //Z_STRVAL_P(op1)獲取zval中的string類型的值。XSS漏洞只有在string類型的時候才會觸發
        if (opline->extended_value) {//opline->extended_value據百度大佬說,這個取出來的函數參數,因為echo使用時,不存在參數傳遞,但是這個結構卻是uint32_t類型感覺跟函數名有關
            php_taint_error("print", "Attempt to print a string that might be tainted");
        } else {
            php_taint_error("echo", "Attempt to echo a string that might be tainted");
        }
    }

    return ZEND_USER_OPCODE_DISPATCH;
} /* }}} */

五、taint存在的問題:

1、無法檢測二次漏洞,二次註入,二次命令註入等。
2、使用白名單進行過濾後,依舊會報出來

3、最重要的是,taint中對不在上面所說的賦值函數list中的函數,如base64_decode都當成過濾函數看待,會導致大量漏洞

如:

$a = base64_decode($_GET[‘a‘]);
echo $a;

這種明顯是一段XSS漏洞,但是taint無法報出來。

所以修復建議:去掉untaint,自己手寫取消mark邏輯。我也是在思考中,有編碼進展會直接更新到這篇博客。

taint源碼分析與使用介紹