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 的格式进行访问,使用绝对路径:

zip_stream

0x4 PHP I/O 流

PHP 中实现了 I/O 流,重点放在 php://input 和 php://filter 两个流操作。php://input 用于读取 POST 内容,但不支持 enctype="multipart/form-data” 方式的流:

php_input_stream

php://filter 用于读取数据流,通过可选的中间函数处理后进行输出。例如下面的 URL 从指定服务器读取页面内容,并转换为大写字符后以 rot13 编码输出:

php://filter/read=string.toupper|string.rot13/resource=http://www.example.com

通常用这种方式在本地读取文件源码:

php_filter_base64.png

更对 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 编码数据:

data_uri_schema

支持的更多格式可参考 Wikipedia 文档:https://en.wikipedia.org/wiki/Data_URI_scheme

0x6 函数特性

intval 对字符串进行转换时,遇到非 [0-9] 的字符会停止解析,并且会忽略字符开头的 ' ‘, ‘\t’, ‘\n’, ‘\r’, ‘\v’, ‘\f’ 等字符:

intval_skip_and_intercept

图片来自 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
}

untagged

336 Words

2016-05-23 23:13 +0800