技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> PHP --> PHP apache_lookup_uri函数bug分析

PHP apache_lookup_uri函数bug分析

浏览:1318次  出处信息
很久没写东西了,水一个……
真的 这函数我觉得除了我用到了很难看见普遍的应用了- -
-----------------------------------------------------------------------------
首先看一下PHP内部定义的数据类型:
l - 长整形
d - 双精度浮点类型
s - 字符串 (也可能是空字节)和其长度
b - 布尔型 ? r - 资源, 保存在 zval*
a - 数组, 保存在zval*
o - (任何类的)对象, 保存在 zval *
O - (由class entry 指定的类的)对象, 保存在 zval *
z - 实际的 zval*

apache_lookup_uri的这个bug和参数类型有关,函数原型如下:
object apache_lookup_uri ( string $filename ),函数最终会返回一个URI信息的object
这个函数可以很方便的处理URL里面的相关资源,返回结果里的the_request字段不能出现http://之类的资源符,也不能出现<>之类的标签服,否则会报错:
测试代码:
---------------------------------------
<?php
print_r(apache_lookup_uri($_GET[a]));
?>
---------------------------------------
http://localhost/server.php?a=<hr>
Warning: apache_lookup_uri() [function.apache-lookup-uri]: Unable to include '<hr>' - error finding URI in ...
但是如果我们将变量作为数组提交,看看会返回什么?
http://localhost/server.php?a[]=<script>alert(1)</script>
可以看到<script>中的代码被执行了,返回结果如下:
stdClass Object
(
[status] => 200
[the_request] => GET /server.php?a[]=<script>alert(1)</script> HTTP/1.1
[method] => GET
[mtime] => 0
[clength] => 0
[chunked] => 0
[no_cache] => 0
[no_local_copy] => 1
[unparsed_uri] => /Array
[uri] => /Array
[filename] => D:/wwwroot/Array
[path_info] =>
[allowed] => 0
[sent_bodyct] => 0
[bytes_sent] => 0
[request_time] => 1261465695
)
由于apache_lookup_uri函数本身不会产生输出,所以危险性比较小,如果后续处理代码没有检查apache_lookup_uri函数的返回信息就直接使用的话可能会造成一些安全问题,接下来看apache_lookup_uri函数的具体实现:
PHP_FUNCTION(apache_lookup_uri)
{
request_rec *rr;
zval **filename;

if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &filename) == FAILURE) {
WRONG_PARAM_COUNT;
}
函数开始在定义参数的时候就错误的把filename定义为zval类型,这也就允许了可以将变量作为数组提交

....
convert_to_string_ex(filename);

if (!(rr = php_apache_lookup_uri(Z_STRVAL_PP(filename) TSRMLS_CC))) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - URI lookup failed", Z_STRVAL_PP(filename));
RETURN_FALSE;
}
通过convert_to_string_ex强制转换字符串,数组将会被转换成Array,apache_lookup_uri函数返回信息里可以很明显的看到这一点
[unparsed_uri] => /Array
[uri] => /Array
[filename] => D:/wwwroot/Array
这时候的参数类型已经是一个字符串,所在可以顺利通过php_apache_lookup_uri的验证
....
if (rr->status == HTTP_OK) {
object_init(return_value);

ADD_LONG(status);
ADD_STRING(the_request);
ADD_STRING(status_line);
ADD_STRING(method);
ADD_TIME(mtime);
ADD_LONG(clength);
前面通过了所有的检查过程后调用object_init来处理返回结果,object_init在/ZEND/zend_API.c中实现
ZEND_API int _object_init(zval *arg ZEND_FILE_LINE_DC TSRMLS_DC)
{
return _object_init_ex(arg, zend_standard_class_def ZEND_FILE_LINE_RELAY_CC TSRMLS_CC);
}
这里接受的参数类型也是zval,所以之前提交的数组类型变量值被保存在返回结果中,造成了之后添加到[the_request]的字段内容可以存放XSS代码,由于apache_lookup_uri函数的功能只是处理传入的参数解析后保存在一个object里,本身压根就没准备做内容检查,所以这也算不上一个漏洞。

在PHP官方的ChangeLog里没有搜到关于这个函数的Bug,本文里贴出的PHP源代码是5.2.11/12,之后我看了下5.3.1的代码,发现这个小bug已经被改掉了,处理如下:
php-5.3.1\sapi\apache2handler\php_functions.c
-----line:118->126------
PHP_FUNCTION(apache_lookup_uri)
{
request_rec *rr;
char *filename;
int filename_len;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
return;
}
......
可以看到已经明确的定义了参数类型为字符串,并通过zend_parse_parameters函数指定"s"来获取参数。
zend_parse_parameters()在解析参数的同时会尽可能地转换参数类型,这样就可以确保我们总是能得到所期望的类型的变量。任何一种标量类型都可以转换为另外一种标量类型,但是不能在标量类型与复杂类型之间进行转换。这种情况下如果再将变量作为数组提交将会返回一个warning级的错误:
Warning:  expects parameter 1 to be string, array given in....
处理流程大概是:
zend_parse_parameters->zend_parse_va_args->
if (!quiet) {
zend_function *active_function = EG(function_state_ptr)->function;
char *class_name = active_function->common.scope ? active_function->common.scope->name : "";
zend_error(E_WARNING, "%s%s%s() expects %s %d parameter%s, %d given",
class_name,
class_name[0] ? "::" : "",
get_active_function_name(TSRMLS_C),
min_num_args == max_num_args ? "exactly" : num_args < min_num_args ? "at least" : "at most",
num_args < min_num_args ? min_num_args : max_num_args,
(num_args < min_num_args ? min_num_args : max_num_args) == 1 ? "" : "s",
num_args);
}
return FAILURE;

最后发现在PHP5.3.1里直接连错误提示都改了:
zend_error(E_WARNING, "%s%s%s(): bad type specifier while parsing parameters"....
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1