记一次由于整型参数错误导致的任意文件上传

当时误打误撞发现的,觉得挺奇葩的,记录下

一个正常的图片上传的点,文件类型白名单

在这里插入图片描述

但是比较巧的是当时刚对上面的id进行过注入测试,有一些遗留的测试 payload 没删,然后在测试上传的时候就发现.php的后缀可以上传了,直接从白名单变成任意文件上传了。

id的正常格式是一串数字,但是如果将id出改成字符串,然后再修改上传的文件名为.php,就可以成功上传了,很奇怪的逻辑处理。当时的猜测是id处判断字符串错误进入了一个没有验证上传文件类型的代码段,导致直接任意文件上传了。

在这里插入图片描述

后面出于好奇,想验证下和我的猜测是否一致,去源码里面看了一眼,果然跟我的猜测一致,关键代码如下:

<?php 
    public function upload()
    {
    	.......
    	$id = intval($this->post('id'));
    	.......

    	$real_file_type = [];       // 限制文件类型
        $real_file_size = 0;        // 限制文件大小
        if ($id) {
            $where = "id = {$id}";
            if ($menu_id) {
                $where .= " and menu_id = '{$menu_id}'";
            }
            $config = publics::getOne(ApplyExtraField::TABLE, 'type, options', $where);
            if (empty($config)) {
                return $this->returnJson(400, $this->language['parameter_error']);
            }

            if (!in_array($config['type'], [8, 11, 12])) {
                return $this->returnJson(400, $this->language['error_field']);
            }

            $options = json_decode($config['options'], true);
            $condition = $options['condition'] ?? [];
            $real_file_type = $condition['file_type'] ?? [];
            $real_file_size = isset($condition['file_size']) && $condition['file_size'] ? $condition['file_size'] * 1024 : 0;
        }

        .......

        // 上传文件只会上传一个
        foreach ($this->request->getUploadedFiles() as $file) {
            $file_size = $file->getSize();
            $file_name = $file->getName();
            $file_type = strtolower($file->getExtension());

            // 判断文件类型
            if ($real_file_type && !in_array($file_type, $real_file_type)) {
                // 删除 Redis 列表中值
                $this->redis->lRem($uploadFileKey, $uploadFileVal, 0);
                $this->redis->sRem($executionListKey, $fileJsonStr);

                return $this->returnJson(400, $this->language['file_format_error']);
            }
            $fileExt = $fileExt ?: $file_type;

            .......
        }
		.......
		return $this->returnJson(200, $this->language['upload_success'], [
                'name' => $file_name,
                'url' => $file['url'],
                'file' => $file
            ]);
    }
 ?>

首先这里的正常功能是接收到的id是一串数字,只要不是数字0if ($id)就是True的,然后会根据menu_id去数据库中查询相关信息返回给,经过几层处理$real_file_type = $condition['file_type'],会得到数据库中查询出的文件白名单类型。

然后if ($real_file_type && !in_array($file_type, $real_file_type))会判断$file_type是否在白名单$real_file_type中,不在的话,就从Redis列表和集合中删除相应的值,并返回一个JSON格式的错误响应。

而当$id是字符串时,if ($id) 就是False

在这里插入图片描述

这样的话$real_file_type数组就没有接收到数据库查询出来的白名单,还是为空。而$real_file_type = []的话,if ($real_file_type && !in_array($file_type, $real_file_type)) == False,直接直接跳过判断文件类型,数组为空也没法做白名单校验。直接导致了任意文件上传。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

末 初

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值