代码审计典型语法结构

代码执行

eval

php中可以执行代码的方式还是很多的,最普通的就是eval()这个字符串,
需要注意一下
(1)一般情况下双引号中的内容可以被替换和解释,单引号的内容是不能变化的
(2)在eval中申请的变量,所有PHP代码都可访问到
<?php
	$foobar = "xxx";
	eval("echo $foobar;");
	eval('echo $foobar;');
?>
类似的函数还有
eval() assert() system() exec() shell_exec() passthru() escapeshellcmd() pcntl_exec()

include

文件包含漏洞,如果开启了远程包含,很容易就会转变为代码执行漏洞。

<?php
$to_include = $_GET['file'];
require_once($to_include . '.html');
?>
之前做过整理
http://blog.csdn.net/wangyi_lin/article/details/9837257#t19
类似的函数还有
nclude() include_once() require() require_once() spl_autoload()

preg_replace

比较常用的正则表达式,也可以引发代码执行,源于PCRE (Perl Compatible Regular Expressions) 中的e(PREG_REPLACE_EVAL)选项,
这个选项会吧replacement中的内容当做PHP代码执行,并取返回结果的值,其中后项引用可以$1 也可以 \\1
<?php
$var = '<tag>phpinfo()</tag>';
preg_replace("/<tag>(.*?)<\/tag>/e", '$1', $var);
?>
但是一般的利用情况是不会有E选项的,我们可以通过%00截断等方式来添加e这个选项
<?php
$regexp = $_GET['re'];
$var = '<tag>phpinfo()</tag>';
preg_replace("/<tag>(.*?)$regexp<\/tag>/", '\\1', $var);
?>

重音符

这个php会把重音符中的字符串当做代码来执行,这里是执行系统命令而不是php代码
<?php
echo `dir`;
?>

大括号


php中会吧大括号中的值当做一个变量,这里我们可以使用${`dir`}这种形式来执行变量,但是不会有回显

<?php
$year = "10";
echo "That was 20${year}-th year.";

$foobar = 'phpinfo';
${foobar}();
?>

这里也可以使用大括号来执行php语句
<?php
$k = "{${phpinfo()}}";
?>

一般情况下,花括号里面的第一个字符如果是某些特殊字符,后面字符串就可以被执行
http://www.yunsec.net/plus/view.php?aid=11894


ob_start

这个函数可以设定一个回调函数,来进行代码执行,不过貌似只能返回命令执行的最后一行
<?php
$foobar = 'system';
ob_start($foobar);
echo 'dir';
ob_end_flush();
?>


array_map

这个函数也是可以设定一个回调函数,对一个数组中的元素做处理,并返回处理后的数组
<?php
$evil_callback = $_GET['callback'];
$some_array = array(0, 1, 2, 3);
$new_array = array_map($evil_callback, $some_array);
?>

unserialize / serialize

__destruct

值个函数执行的时候,会触发原类中的__destruct函数,用户构造一个比较蛋疼的提交的话,就会导致代码执行,但是这种情况也是比较苛刻的
当然也有很多前面带下划线的函数会执行,destruct只是其中一个,__wakeup也会被调用
<?php
class Example {
	var $var = '';
	function __destruct() {
		eval($this->var);
	}
}
unserialize($_GET['saved_code']);
?>
像这样构造提交
http://127.0.0.1/test.php?saved_code=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}

__toString

这个函数,会在serialize调用这个函数,这个函数会返回一个字符串决定serialize的值,重载这个函数会影响这个值
比如下面这个代码就会产生文件包含
class just4fun {
        public $filename;
 
        function __toString() {
            return @file_get_contents($this->filename);
        }
    }

绕过方法

有一些程序中会判断用户输入是不是一个序列化的数据,常见的代码如下
$token = $data[0];
switch ( $token ) {
    case 's' :
        if ( '"' !== $data[$length-2] )
            return false;
    case 'a' :
    case 'O' :
        return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
    case 'b' :
    case 'i' :
    case 'd' :
        return (bool) preg_match( "/^{$token}:[0-9.E-]+;\$/", $data );
这个可以用+来绕过,这也属于PHP的一种特性
O:+4:"test":1:{s:1:"a";s:3:"aaa";} 
当然还有很多类似的绕过方法
$str1 = 's:8:"ryatsyne";';
$str2 = 's:8:"ryatsyne"t';
$str3 = 'S:8:"\72\79\61\74\73\79\6e\65"';



其他容易导致问题的函数

array_map()
usort(), uasort(), uksort()
array_filter()
array_reduce()
array_diff_uassoc(), array_diff_ukey()
array_udiff(), array_udiff_assoc(), array_udiff_uassoc()
array_intersect_assoc(), array_intersect_uassoc()
array_uintersect(), array_uintersect_assoc(), array_uintersect_uassoc()
array_walk(), array_walk_recursive()
xml_set_character_data_handler()
xml_set_default_handler()
xml_set_element_handler()
xml_set_end_namespace_decl_handler()
xml_set_external_entity_ref_handler()
xml_set_notation_decl_handler()
xml_set_processing_instruction_handler()
xml_set_start_namespace_decl_handler()
xml_set_unparsed_entity_decl_handler()
stream_filter_register()
set_error_handler()
register_shutdown_function()
register_tick_function()


文件包含

http://blog.csdn.net/wangyi_lin/article/details/9837257#t19 



变量覆盖

变量覆盖漏洞产生的原因有两种
第一种是register_globals为on的情况,PHP4默认开启,PHP5以后默认关闭。
第二种是人为注册成为全局变量

变量注册

<?php
       foreach(Array('_GET','_POST','_COOKIE') as $_request) 
       { 
             foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v); 
       }
?>
这里如果我们构造这样的提交,便可以修改GLOBALS中的变量
http://127.0.0.1/test.php?_POST[GLOBALS][XXX]=wyl
程序会通过$_GET注册了$_POST,通过$_POST注册了$GLOBALS! 

变量销毁

有的时候为了防止全局变量覆盖,会出现这样的代码
//if register globals = on, undo var overwrites
foreach(array('_GET','_POST','_REQUEST','_COOKIE') as $method){
     foreach($$method as $key=>$value){
          unset($$key);
     }
}
这样很容易导致一些关键变量被销毁

$_REQUEST

这个数组保存了Get  Post传递的变量,不过在变量名相同的情况下post中的变量会覆盖get中的变量
比如如下的代码,用来检测变量中是否有数组存在
foreach($_REQUEST as $request) {
    if(is_array($request)) {
        die("Can not use Array in request!");
    }
}
当提交  GET:LanLan[xxxxx]=xxxxx   POST:LanLan=1  便可绕过这个检测 

其他导致变量覆盖的函数

还有一些函数的使用可能导致变量覆盖
parse_str   mb_parse_str  import_request_variables

随机函数

PHP中一般使用随机函数来生成session

Rand()

此函数生曾的最大随机数是32767,很容易被暴力破解。



字符串

offset

这是php字符串的一个特性,下面的两条echo都会输出L,PHP对字符串大括号中的变量,进行类型转换'hi'转换成整形之后的值为0
$xigr = 'LanLan';
echo $xigr[0];
echo $xigr['hi'];
这种特性会引发一些漏洞,比如这样的情况,如果admin被变量覆盖成了字符串型,那么就可以绕过下面的check判断
$admin['check'] = "0";
$admin['pass']  = "angel";
 ......
if($admin['check'] == "1") {
....
}
总的来说就是把一个原本是数组型的变量,被覆盖成字符串型,但是根据PHP的特性,程序依然可以继续执行~但是会给各种不法分子提供做坏事的机会。
这种特性在GPC开启的情况下,可以用来引入反斜杠,不过条件比较苛刻。























































你可能感兴趣的:(代码审计典型语法结构)