畢業設計之php RASP(二) 威脅判斷


先感謝0kee的大哥指導 ~

這里分為三個部分
1、獲取函數的參數值
2、對http傳入值的獲取,($_GET$_POST$_COOKIE)
3、如何判斷存在威脅(詞法分析、污染標記)

函數參數獲取

Zend中有接口可以獲取到參數:zend_parse_parameterszend_get_parameters_ex

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/", &cmd, &cmd_len, &ret_code) == FAILURE) {
 return;
}

或者
zend_get_parameters_ex(argc, &command) == SUCCESS

可以跟進看一下

static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, int flags TSRMLS_DC) 
arg = (zval **) (zend_vm_stack_top(TSRMLS_C) - 1 - (arg_count-i));

或者

ZEND_API int zend_get_parameters_ex(int param_count, ...) /* {{{ */
{
    void **p;
    int arg_count;
    va_list ptr;
    zval ***param;
    TSRMLS_FETCH();

    p = zend_vm_stack_top(TSRMLS_C) - 1;
    arg_count = (int)(zend_uintptr_t) *p;

    if (param_count>arg_count) {
        return FAILURE;
    }

    va_start(ptr, param_count);
    while (param_count-->0) {
        param = va_arg(ptr, zval ***);
        *param = (zval **) p-(arg_count--);
    }
    va_end(ptr);

    return SUCCESS;
}

可以看到它其實就是從Zend虛擬機的vm棧頂上面獲取參數。

當然也可以通過php運行時的一些結構,比如EG(argument_stack)

void **p = EG(argument_stack)->top ;

函數的參數個數保存在int arg_count = opline->extended_value;中,拿到參數個數之后,我們就可以移動指針了。

比如獲取該函數的各個參數,也就是函數的調用約定,表達式為:

zval *arg1 = *((zval**)(p - arg_count));   //參數1
zval *arg2 = *((zval**)(p - (arg_count - 1)));  //參數2
zval *arg3 = *((zval**)(p - (arg_count - 2)));  //參數3

例如:

root@ubuntu:/var/www/html/bishe# cat 1.php 
<?php
echo str_replace("world","Shanghai","Hello world!");

HTTP數據獲取

從php5.4開始后就自帶了一個小型的server,所以可以對它進行調試

gdb /opt/php_debug/bin/php
set args -S 0.0.0.0:1234
run
按ctrl + c
b lemon_php_http_request
run

對於如何從php擴展中獲取$_GET$_POST等值,鳥哥一篇博文已經有了分析。

http的信息存在在http_globals,文件位置:php-src-php-5.5.9/main/php_globals.h

#define TRACK_VARS_POST		0
#define TRACK_VARS_GET		1
#define TRACK_VARS_COOKIE	2
#define TRACK_VARS_SERVER	3
#define TRACK_VARS_ENV		4
#define TRACK_VARS_FILES	5
#define TRACK_VARS_REQUEST	6

比如訪問:

http://love.lemon:1234/1.php?i=iam&i1=lemon

PG(http_globals)[TRACK_VARS_GET]中獲取GET請求的數據,它也是一個hash table

ulong ikey;
char *skey;
zval **data;
HashTable *h;
zval *arr;

arr = PG(http_globals)[TRACK_VARS_GET];
h = HASH_OF(arr);

for (zend_hash_internal_pointer_reset(h);
     zend_hash_has_more_elements(h) == SUCCESS;
     zend_hash_move_forward(h))
{
  zend_hash_get_current_data(h, (void**)&data);
  zend_hash_get_current_key(h, &skey, &ikey, 0);
} 

威脅判斷

這部分屬於最難做的地方,如果不夠精確的話,很影響最后的結果。如果要做bypass的話,可以重點研究此部分。

污染標記

可以在PHP_RINIT_FUNCTION進行設置,也就是有請求的時候將所有的數據都標記為污染數據,然后再進入流程后可根據一些函數操作來消除標記,最后進入危險函數的時候看污染標記是否存在來確認威脅。

b lemon_php_mark_strings

這里想要加上一個標記就需要了解一下php變量的結構體

typedef struct _zval_struct zval;
typedef union _zvalue_value {
	long lval;					/* long value */
	double dval;				/* double value */
	struct {
		char *val;
		int len;
	} str;
	HashTable *ht;				/* hash table value */
	zend_object_value obj;
} zvalue_value;

struct _zval_struct {
	/* Variable information */
	zvalue_value value;		/* value */
	zend_uint refcount__gc;
	zend_uchar type;	/* active type */
	zend_uchar is_ref__gc;
};

變量的值是存在_zvalue_value中,這里使用聯合體也是為了空間利用考慮,因為一個變量同時只能屬於一種類型,至於_zval_struct里面的就是變量的一些說明,比如變量類型,是否被引用等。很蛋疼的事情就是在這里面每個字段都有明確的意義,沒有一個預留的字段。

鳥哥的taint這里的做法則是把字符串的長度擴充一個int, 然后用magic number做標記寫到后面去

Z_STRVAL_PP(ppzval) = erealloc(Z_STRVAL_PP(ppzval), Z_STRLEN_PP(ppzval) + 1 + PHP_TAINT_MAGIC_LENGTH);
PHP_TAINT_MARK(*ppzval, PHP_TAINT_MAGIC_POSSIBLE);

但是要追蹤污染標記是很復雜的,因為經過比如字符串拼接,各種處理的函數,編碼之類的后就得重新打上標記。這塊也可以看上一篇如何去hook,再進行字符串處理的時候再進行標記一次或者比如進入了intval就取消標記。

但是畢設我就偷懶一點吧,簡單的判斷一下Http輸入值能不能完整進入函數參數。

<?php
$a = @$_GET['i'];
$b = "sys"."tem";
$b("echo ".$a." iaml3m0n ");


免責聲明!

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



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