PHP 代码审计

1. 工具

1.1. 静态代码审计

2. 基础函数

  • get_defined_vars(void)

此函数返回一个包含当前可用的变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量.在函数中使用此函数可以调试函数中的变量,而不会返回其他的变量

  • get_defined_functions(void)

获取所有已经定义的函数,包含内部函数和用户定义的函数。

# 输出用户定义的函数方法为:
$funcs = get_defined_functions();
var_dump($funcs['user']);
  • get_defined_constants(void)

返回所有可用的常量,包含系统常量和用户定义的常量

  • get_declared_classes(void)

返回所有可用的类,包含系统类和用户定义的类。

  • get_included_files()

返回所有的包含的文件路径的数组,included 和 required 的包含文件

3. 弱类型比较

3.1. 类型转换

  • int vs string
>>> 0 == "abc"
=> true
>>> 0 == "1abc"
=> false
>>> 0 == "0abc"
=> true
  • int vs float
>>> 1.000000000000001 == 1
=> false
>>> 1.0000000000000001 == 1
=> true

3.2. 字符串比较

  • \d+0e\d+

比较运算时, 遇到 \d+e\d+ 类型字符串会将其解析为科学计数法

>>> "0e1234" == "0e7654321"
=> true
>>> "0e1234" == "0e1234abc"
=> false
>>> "0e1234" == "0"
=> true
>>> "0e1234" == "0e"
=> false
>>> "0e1234" == "0e1"
=> true

# md5
>>> md5('240610708')
=> "0e462097431906509019562988736854"
>>> md5('QNKCDZO')
=> "0e830400451993494058024219903391"
>>> md5('240610708') == md5('QNKCDZO')
=> true

>>> "1e2" == "100"
=> true
  • 0x

比较运算时, 字符串以 0x 开头, php会尝试将字符串转换为十进制然后再比较

>>> "0xf" == "15"
=> true
>>> "0xf" == 0xf
=> true
>>> "0xf" == 15
=> true

4. 类型转换

4.1. intval

intval()转换的时候,会将从字符串的开始处进行转换,直到遇到一个非数字的字符。即使出现无法转换的字符串,intval()不会报错而是返回0

>>> intval("3")
=> 3
>>> intval("3abc")
=> 3
>>> intval("abc")
=> 0

5. 内置函数参数的松散性

5.1. md5

string md5 ( string $str [, bool $raw_output = false ] )

md5 需要是一个string类型的参数, 你传递一个array时,md5()不会报错,只是会无法正确地求出 array 的md5值, 导致任意2个array的md5值都会相等。

5.2. strcmp

strcmp函数比较字符串的本质是将两个变量转换为ascii,然后进行减法运算,然后根据运算结果来决定返回值。

5.3. in_array

bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

如果strict参数没有提供,那么in_array就会使用松散比较来判断$needle是否在$haystack中。当strince的值为true时,in_array()会比较needls的类型haystack中的类型是否相同。

>>> $arr = [0, 1, 2, '3']
=> [
     0,
     1,
     2,
     "3",
   ]
>>> in_array('abc', $arr)       # 'abc' 转换为 0
=> true
>>> in_array('1abc', $arr)      # '1abc' 转换为 1
=> true

seem to array_search()

6. 序列化

6.1. unserialize

unserialize 函数反序列化不成功会返回false, 函数会从第一个字符开始解析,解析完完整的反序列化字符串就会返回,而不再解析之后的可能存在的字符串,如下所示:

$tmp = 's:3:"123";';
var_dump(unserialize($tmp));
=>string '123' (length=3)

$tmp = 's:3:"123";s:2:"34";';
=>string '123' (length=3)

$tmp = 's:3:"123";xxx';
=>string '123' (length=3)

$tmp = ' s:3:"123";';
=>boolean false

7. 变量覆盖

很多应用会有这个逻辑,就是在全局覆盖变量前,检查$_REQUEST变量,如果$_REQUEST中含有一些不允许覆盖的变量时就报错;如果不含有非法变量,再依次将$_GET $_POST $_COOKIE三者覆盖到全局变量。

这在php5.3以前都是比较安全的,但php5.3以后 $_REQUEST变量中不再包含$_COOKIE ,所以第一步检查的时候也就不会检查$_COOKIE中的变量,但第二步又是包含了$_COOKIE的。这种情景不光在变量覆盖中存在,比如一些WAF也可能会因为php5.3而漏掉检查$_COOKIE

php 5.3以后$_REQUEST变量中不再包含$_COOKIE, 这是php5.3.0以后$_REQUEST是受php.ini中的request_order影响的,关于 request_order 这个配置选项,是php的5.3.0版本中新增加的, request_order默认值为GP,也就是说默认配置下$_REQUEST只包含$_GET$_POST而不包括$_COOKIE。通过 COOKIE就可以提交GLOBALS变量。

从而绕过了大多开源程序中的全局变量防御。因此要将次选项更改为 request_order = "CGP"

results matching ""

    No results matching ""