PHP之序列化与反序列化(字符逃逸篇)

前言

前面做题目就遇到过字符逃逸且记录了,但是就只是写了那个题的解题思路和方法,今天把这个知识点剖析开来,细细讲道。

什么是字符逃逸,字符逃逸有哪些,如何利用字符逃逸。这就是我接下来想要讲的东西。

字符逃逸

长替短吞字符

先给个测试

PHP之序列化与反序列化(字符逃逸篇)_第1张图片

序列化后的字符串的大意我在反序列化基础篇中讲了就不赘述了,可以看到如果用了字符串的替换函数会使得原本的字符串变短,但由于本身长度数字在字符串里一直没有变,这就使得一串字符没有结束,把双引号当做字符会向后一直吞并直至长度合适。

也就是说上面的情况会出现

 但这样的话后面就乱了,那如果我们想办法吞并到第二个字符串的引号呢?像下图

PHP之序列化与反序列化(字符逃逸篇)_第2张图片

 那岂不是就闭合了?然后键值都对换了,但是这不重要,重要的是达到目的了,只要对象的属性有三个就没有问题。

吞并的危害很大,比如如果我想要一个验证函数或者匹配函数检测安全,但是你把那个函数名吞并为一串字符,那个函数也就失去了效果,最后也就可以达到我们绕过的目的。回到上面,我们该怎么做才能达到这种目的,这就是我后面题目会将的。

短替长丢字符

上面的逃逸是长字符串被替换成了短字符串,而如果是短字符串替换成了长字符串又该怎么逃逸呢?再看一个测试

PHP之序列化与反序列化(字符逃逸篇)_第3张图片

由于字符串长度大于本应该的长度5,所以当匹配到第五个字符时就不会再匹配了,那后面的怎么办?不闭合就乱了呗,这串字符就有问题,那咋闭合呢?这里说说方法,前面讲序列化的时候我提到过,序列化后的字符是以遇到的第一个分号加大括号';}'结尾的,也就是说如果我们能想办法让‘;}’出现在前面,那么后面的字符串就会被当作垃圾丢弃,也就是丢字符。

PHP之序列化与反序列化(字符逃逸篇)_第4张图片

 这就是为什么短替为长字符需要丢弃一部分字符串的原因。

而这个丢字符的危害我想应该是比上一个还要大的,长替短只是让一些函数或者属性失效了,但是短替换长的丢弃了后面的数据不说,自己还加了一部分数据,这个时候可以自己修改键值达到目的。

[安洵杯 2019]easy_serialize_php

又是这个题目,哈哈。虽然已将写过一次了,还可以再复习一遍。想复现可以去BUUCTF或者NSSCTF都是可以滴

source_code';
}

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
#echo serialize($_SESSION);//这里我建议在本地测试然后加上这段做题会更直观
$serialize_info = filter(serialize($_SESSION));

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']));
}

有个phpinfo就先看看,然后看到个好东西

 

代码一路看下去,有个extract()重定向函数,何为重定向,简单来说就是把一个数组中的键定义为变量名如果这个前面出现过这个变量就会被覆盖,如图:

PHP之序列化与反序列化(字符逃逸篇)_第5张图片

了解一下,感觉这题用处不大,主要是里面的$_POST有巨用,另外就是上面的 unset(),他会把数据销毁也就是置空。

PHP之序列化与反序列化(字符逃逸篇)_第6张图片

再测试一下

PHP之序列化与反序列化(字符逃逸篇)_第7张图片 

按理来说,unset()以后就只有post传上去的1数据了,这里出现的img是因为还有后面没有分析的部分。

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

不赋值就base64加密,赋值了就base64+sha1加密,所以只能说没用,可以不管。

可以利用的就是filter()函数

function filter($img){
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$img);
}

它有个preg_replace()就是字符逃逸的利用点,也就是长被替换为短,可以吃后面的东西。但是这里的利用不仅仅是吃东西,因为最后两行的代码为:

    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));

读取文件名是img键对应的值,也就是说我们一定要用到这个img键来赋值,而在这里我们能利用的除了键还有值,想办法用键吃后面的值然后闭合。

PHP之序列化与反序列化(字符逃逸篇)_第8张图片

 

把开始phpinfo康到的好东西加密放在img键的内容中

PHP之序列化与反序列化(字符逃逸篇)_第9张图片

 

所以payload为:

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

PHP之序列化与反序列化(字符逃逸篇)_第10张图片

再把这个根目录的加密一下就行了

 PHP之序列化与反序列化(字符逃逸篇)_第11张图片

 

总结

过来人表示字符逃逸一开始确实挺难的(也可能是我天赋差),想搞懂字符逃逸,最重要的就是去想,一定要去想,然后反复看代码何payload。编程也好安全也好,很多东西都是刚开始比较难接受的,也很难想,但是如果想通了就会豁然开朗。

你可能感兴趣的:(PHP反序列化,php,安全,web安全)