PHP 漏洞
PHP 相关
基本思路
两种思路:由危险函数回溯寻找可利用的点;或者整体理解代码,寻找其中的可能存在问题的部分审计。
危险函数
in_array 类型转换
1 | bool in_array ( mixed $needle, array $haystack [, bool $strict = FALSE ]) |
该函数检测数组中是否存在某个值,默认情况下会进行类型转换,将$needle
转换为$haystack
中元素的类型。
1 | in_array('1shell', range(0,5)); |
preg_replace 的代码执行
preg_replace 函数在第一个参数包含 /e
修饰符时存在代码执行问题,会将第二个参数当作 php 代码执行。
1 | function complex($regex, $value) { |
命令执行时,相当于eval('strtolower("\\1");')
,\\1
就是\1
,\1
在正则表达式中有自己的含义,指的是第一个子匹配项。
对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ‘\n’ 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
payload: /?.*={${phpinfo()}}
,对应 preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});
直接写这个 preg_replace 语句可以执行 phpinfo,但是通过 get 传参时,.
会被转换成_
,php 会将一些非法的 get 数组参数名转化成下划线,非法字符为首字符时,只有.
会被替换成下划线,在其他位置时,ascii码为 32, 43, 46, 91, 95 的也会被替换成下划线。
可以换一个 payload: \$*=${phpinfo()}
需要匹配到{${phpinfo()}}
或者${phpinfo()}
才能执行phpinfo
函数。因为php存在可变变量,当phpinfo执行之后会返回 true,相当于${1}
。
1 | var_dump(phpinfo()); // 结果:布尔 true |
phar 反序列化
phar文件结构
- stub
格式为 .*<?php.*__HALT_COMPILER\(\);?>
,即以__HALT_COMPILER()?>
结尾的可以被识别为phar文件。
- manifest
phar文件本质上是一种压缩文件,每个被压缩文件的权限、属性信息存放在这部分,同时用户自定义的meta-data也会以序列化方式存储在这里(核心内容)
size(bytes) | description |
---|---|
4 | Length of manifest in bytes(1 MB limit) |
4 | Number of files in th Phar |
2 | API version of the Phar manifest (currently 1.0.0) |
4 | Global Phar bitmapped flags |
4 | Length of Phar alias |
? | Phar alias (length based on previous) |
4 | Length of Phar metadata (0 for none) |
? | Serialized Phar Meta-data, stored in serialize() format |
at least 24*number of entries bytes | entries for each file |
- contents
文件内容
- signature(optional)
签名,可以添加来验证文件。
demo
根据以上信息构建一个phar文件,php内置phar类处理相关操作
要将php.ini
中的phar.readonly
设置为Off
,否则无法生存phar文件。
Ubuntu 中的位置 /usr/local/php/etc/php.ini
phar_gen.php:
1 |
|
生成的phar的meta-data部分是以序列化方式存储的。
php的许多文件系统函数在通过 phar://
协议解析phar文件时会将meta-data反序列化
受影响的函数 | |||
---|---|---|---|
fileatime | filectime | file_exists | file_get_contents |
file_put_contents | file | filegroup | fopen |
fileinode | filemtime | fileowner | fileperms |
is_dir | is_executable | is_file | is_link |
is_readable | is_writable | is_writeable | parse_in_file |
copy | unlink | stat | readfile |
demo
1 |
|
当文件系统函数参数可控时,可以再不调用unserialize()的情况下反序列化。
测试
phar_test1.php
1 |
|
运行一下,得到Destruct called
。
再试一下file_exist()
,phar_test2.php
1 |
|
也可以得到Destruct called
将phar伪造成其他格式文件
php识别phar是通过文件头的stub,对其他内容没有要求,因此可以通过添加任意文件头和修改后缀名的方式将phar文件伪造成其他格式的文件。
1 |
|
可以用于绕过上传检测。
利用条件
- phar文件能够上传到服务器
- 有可用的魔术方法作为跳板
- 文件操作函数参数可控,且
:
/
phar
等没有被过滤
防御
- 文件系统函数参数可控时,严格检查参数
- 检查上传文件内容,而不是只检查文件头
- 可能的话禁用可执行系统命令、代码的危险函数
MD5
0e00275209979