preg_match(_all)的变量初始化问题
author: 80vul-B
team:http://www.80vul.com
date:2009-04-27
一 描叙
php手册里:
—————————————————————————————–
int preg_match ( string pattern, string subject [, array matches [, int flags]] )
在 subject 字符串中搜索与 pattern 给出的正则表达式相匹配的内容。
如果提供了 matches,则其会被搜索的结果所填充。$matches[0] 将包含与整个模式匹配的文本,$matches[1] 将包含与第一个捕获的括号中的子模式所匹配的文本,以此类推。
—————————————————————————————–
这个是一个应用比较多的函数,很多应用程序忘记对preg_match(_all)的变量进行初始化,导致安全漏洞.
二 分析
patch_match的部分源码:
PHP_FUNCTION(preg_match)
{
php_do_pcre_match(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
...
// php_pcre.c
static void php_do_pcre_match(INTERNAL_FUNCTION_PARAMETERS, int global) /* {{{ */
{
...
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, ((global) ? "ssz|ll" : "ss|zll"), ®ex, ®ex_len,
&subject, &subject_len, &subpats, &flags, &start_offset) == FAILURE) {
RETURN_FALSE;
}
// 调用zend_parse_parameters检查传入的参数,参数类型及格式限定为了ss|zll
// zend_API.c
ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...)
{
...
retval = zend_parse_va_args(num_args, type_spec, &va, 0 TSRMLS_CC);
...
static int zend_parse_va_args(int num_args, char *type_spec, va_list *va, int flags TSRMLS_DC)
{
...
while (num_args-- > 0) {
arg = (zval **) p - (arg_count-i);
if (*type_spec == '|') {
type_spec++;
}
if (zend_parse_arg(i+1, arg, va, &type_spec, quiet TSRMLS_CC) == FAILURE) {
// 调用zend_parse_arg检查每个参数的类型
return FAILURE;
}
...
static int zend_parse_arg(int arg_num, zval **arg, va_list *va, char **spec, int quiet TSRMLS_DC)
{
...
expected_type = zend_parse_arg_impl(arg_num, arg, va, spec TSRMLS_CC);
if (expected_type) {
if (!quiet && *expected_type) {
char *space;
char *class_name = get_active_class_name(&space TSRMLS_CC);
zend_error(E_WARNING, "%s%s%s() expects parameter %d to be %s, %s given",
class_name, space, get_active_function_name(TSRMLS_C), arg_num, expected_type,
zend_zval_type_name(*arg));
// 如果参数的类型非法,报错并导致zend_parse_parameters返回FAILURE
}
...
static char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, char **spec TSRMLS_DC)
{
char *spec_walk = *spec;
char c = *spec_walk++;
...
switch (c) {
...
case 's':
{
char **p = va_arg(*va, char **);
int *pl = va_arg(*va, int *);
switch (Z_TYPE_PP(arg)) {
case IS_NULL:
if (return_null) {
*p = NULL;
*pl = 0;
break;
}
/* break omitted intentionally */
case IS_STRING:
case IS_LONG:
case IS_DOUBLE:
case IS_BOOL:
convert_to_string_ex(arg);
*p = Z_STRVAL_PP(arg);
*pl = Z_STRLEN_PP(arg);
break;
case IS_OBJECT:
if (parse_arg_object_to_string(arg, p, pl, IS_STRING TSRMLS_CC) == SUCCESS) {
break;
}
case IS_ARRAY:
case IS_RESOURCE:
default:
return "string";
}
}
break;
...
// 由上面的代码可以看到如果传入preg_match的第一个或者第二个参数是一个数组或者资源类型的话,将会报错并返回false,这将导致如果使用第三个参数的话,该变量将不会被赋值
三 测试代码
<?php
//preg_match.php?ipmatches[]=1111
$ip[]='1';
preg_match("/[\d\.]{7,15}/", $ip, $ipmatches);
var_dump($ipmatches);
?>
四 实际应用
[SODB-2009-01]:Discuz!<5.50 preg_match()的变量$onlineipmatches未初始化漏洞
[http://www.80vul.com/dzvul/]