PHP 弱类型与黑魔法
0x0 前言
继续整理上古技术。
0x1 整数和字符串发生的化学反应
首先是众所周知的弱类型转换:
TRUE: "0000" == int(0)
TRUE: "1abc" == int(1)
TRUE: "0abc" == int(0)
TRUE: "abc" == int(0)
同时,PHP 会将正确格式的字符串转换为数字:科学记数法 和 16进制。
TRUE: "2e1" == int(20)
TRUE: "0x1" == int(1)
那如果遇到等号两方都是字符串呢?对于 PHP 这个世界上最好的 语言来说,在某些情况下会对两边的字符串进行转换:
TRUE: "0x1" == "1"
TRUE: "1e2" == "100"
所以在一些特殊的环境下,会遇到一些意想不到的效果:
TRUE: md5('240610708') == md5('QNKCDZO')
// '0e462097431906509019562988736854' == '0e830400451993494058024219903391'
例如:
<?php
function noother_says_correct($number)
{
$one = ord('1');
$nine = ord('9');
for ($i = 0; $i < strlen($number); $i++)
{
$digit = ord($number{$i});
if ( ($digit >= $one) && ($digit <= $nine) )
{
return false;
}
}
return $number == '54975581388';
}
$flag='*';
if(noother_says_correct($_GET['key']))
echo $flag;
else
echo 'access denied';
?>
限制了数字字符(但是没有过滤字符 0),但是由于函数最后同数字对比,所以提交 54975581388 的 16 进制值 0xCCCCCCCCC 即可绕过。
0x2 PHP 文件包含的各种协议
PHP 支持多种 URL 作为输入流,主要包含以下协议,其中 ssh2、ogg 和 except 默认是不支持的,需要安装特定运行库。
- file:// — 访问本地文件系统
- http:// — 访问 HTTP(s) 网址
- ftp:// — 访问 FTP(s) URLs
- php:// — 访问各个输入/输出流(I/O streams)
- zlib:// — 压缩流
- rar:// — RAR
- data:// — 数据(RFC 2397)
- phar:// — PHP 归档
- glob:// — 查找匹配的文件路径模式
- ssh2:// — Secure Shell 2
- ogg:// — 音频流
- expect:// — 处理交互式的流
在 php.ini 配置文件中,有两个重要的参数:
allow_url_fopen = On/Off
allow_url_include = On/Off
前者控制是否支持 URL 模式的输入流,后者用于控制是否应用于 include、require 等函数,所以 allow_url_include 的前提是启用 allow_url_fopen。
0x3 当 allow_url_fopen 和 all_url_include 均为 Off
是否在两个配置均为关闭的状态下,就无法利用了呢?No。
Cheat | file_get_contents | include |
---|---|---|
file:// | Yes | Yes |
zlib:// | Yes | Yes |
php:// | Yes | No |
zip 协议以 zip://file.zip#dir/file
的格式进行访问,使用绝对路径:
0x4 PHP I/O 流
PHP 中实现了 I/O 流,重点放在 php://input 和 php://filter 两个流操作。php://input 用于读取 POST 内容,但不支持 enctype="multipart/form-data” 方式的流:
php://filter 用于读取数据流,通过可选的中间函数处理后进行输出。例如下面的 URL 从指定服务器读取页面内容,并转换为大写字符后以 rot13 编码输出:
php://filter/read=string.toupper|string.rot13/resource=http://www.example.com
通常用这种方式在本地读取文件源码:
更对 PHP 流操作可参考:http://cn2.php.net/manual/zh/wrappers.php.php
0x5 data 协议
data 协议在 Web 开发和 XSS 中比较常见,同样可作为输入流并被 PHP 解析。data 比较灵活,支持多种数据格式,同时支持自定义字符编码,其基本 URI 格式定义如下:
data:[<media type>][;charset=<character set>][;base64],<data>
基于 data 协议的 Base64 编码数据:
支持的更多格式可参考 Wikipedia 文档:https://en.wikipedia.org/wiki/Data_URI_scheme
0x6 函数特性
intval 对字符串进行转换时,遇到非 [0-9] 的字符会停止解析,并且会忽略字符开头的 ' ‘, ‘\t’, ‘\n’, ‘\r’, ‘\v’, ‘\f’ 等字符:
图片来自 phith0n 菊苣的文章:http://drops.wooyun.org/tips/10564
intval("1.0246") == int(1)
intval(" \r \n \t 1") == int(1)
strcmp 和 md5 等函数,在传入数组参数的时候,会抛出警告并返回 NULL,由于 PHP 的弱类型转换原因,又可能引发一些安全问题。当两个字符相等时,strcmp 会返回 0,即为 NULL:
if (! strcmp($_POST['password'], 'guessMe')) {
// login success
}
ereg 可通过终止符进行截断,绕过正则匹配:
$input = $_GET['input']; // 233%00whitealbum
if (ereg('/[0-9]+$/', $input)) {
// rock you
}