栈溢出实验

栈溢出实验

FindFuner author

前言

子函数在运行过程中,由于存在危险函数,在数据移动过程中,突破了函数栈空间的边界,破坏了母函数栈空间中的内容,导致程序运行状态异常

分析环境和工具

Windows XP Professional、VC 6.0、IDA、OllyICE

StackOverflow程序结构

1

Main函数

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
.text:00401090
.text:00401090 ; =============== S U B R O U T I N E =======================================
.text:00401090
.text:00401090 ; Attributes: bp-based frame
.text:00401090
.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401090 _main proc near ; CODE XREF: _main_0j
.text:00401090
.text:00401090 var_444 = byte ptr -444h
.text:00401090 Str1 = byte ptr -404h
.text:00401090 var_4 = dword ptr -4
.text:00401090 argc = dword ptr 8
.text:00401090 argv = dword ptr 0Ch
.text:00401090 envp = dword ptr 10h
.text:00401090
.text:00401090 push ebp
.text:00401091 mov ebp, esp
.text:00401093 sub esp, 444h
.text:00401099 push ebx
.text:0040109A push esi
.text:0040109B push edi
.text:0040109C lea edi, [ebp+var_444]
.text:004010A2 mov ecx, 111h
.text:004010A7 mov eax, 0CCCCCCCCh
.text:004010AC rep stosd
.text:004010AE mov [ebp+var_4], 0
.text:004010B5
.text:004010B5 loc_4010B5: ; CODE XREF: _main:loc_401115j
.text:004010B5 mov eax, 1
.text:004010BA test eax, eax
.text:004010BC jz short loc_401117
.text:004010BE push offset Format ; "please input password: "
.text:004010C3 call _printf
.text:004010C8 add esp, 4
.text:004010CB lea ecx, [ebp+Str1]
.text:004010D1 push ecx
.text:004010D2 push offset aS ; "%s"
.text:004010D7 call _scanf
.text:004010DC add esp, 8
.text:004010DF lea edx, [ebp+Str1]
.text:004010E5 push edx ; Str1
.text:004010E6 call j_?verify_password@@YAHPAD@Z ; verify_password(char *)
.text:004010EB add esp, 4
.text:004010EE mov [ebp+var_4], eax
.text:004010F1 cmp [ebp+var_4], 0
.text:004010F5 jz short loc_401106
.text:004010F7 push offset aIncorrectPassw ; "incorrect password!\n\n"
.text:004010FC call _printf
.text:00401101 add esp, 4
.text:00401104 jmp short loc_401115
.text:00401106 ; ---------------------------------------------------------------------------
.text:00401106
.text:00401106 loc_401106: ; CODE XREF: _main+65j
.text:00401106 push offset aCongratulation ; "congratulations! you have passed the ve"...
.text:0040110B call _printf
.text:00401110 add esp, 4
.text:00401113 jmp short loc_401117
.text:00401115 ; ---------------------------------------------------------------------------
.text:00401115
.text:00401115 loc_401115: ; CODE XREF: _main+74j
.text:00401115 jmp short loc_4010B5
.text:00401117 ; ---------------------------------------------------------------------------
.text:00401117
.text:00401117 loc_401117: ; CODE XREF: _main+2Cj
.text:00401117 ; _main+83j
.text:00401117 push offset Command ; "pause"
.text:0040111C call _system
.text:00401121 add esp, 4
.text:00401124 pop edi
.text:00401125 pop esi
.text:00401126 pop ebx
.text:00401127 add esp, 444h
.text:0040112D cmp ebp, esp
.text:0040112F call __chkesp
.text:00401134 mov esp, ebp
.text:00401136 pop ebp
.text:00401137 retn
.text:00401137 _main endp
.text:00401137
.text:00401137 ; ---------------------------------------------------------------------------

2

上图所示,为main函数创建函数栈空间。

1
mov     dword ptr [ebp-4], 0

创建局部变量,并置为零

3

上图部分,开始进入循环

1
2
3
mov     eax, 1
test eax, eax
jz short loc_401117

eax为1,始终无法跳出循环,可判断是while(1)循环

1
2
3
push   00427090
call printf
add esp, 4

调用printf,并输出数据段存储的数据 “please input password: ”

1
2
3
4
5
6
7
8
lea     ecx, dword ptr [ebp-404]
push ecx
push offset aS ; "%s"
call _scanf
add esp, 8
lea edx, dword ptr [ebp-404]
push edx
call 00401005

调用scanf,接收输入的数据,将接收的数据存入 esp+8 的位置,接着将 ebp-404 中所存数据存入 edx 中,并传给接下来调用的子函数 verify_password ,ebp-404 中存的是地址,所以传给子函数的也是地址

接下来就进入子函数verify_password

从子函数会到主函数,如下:

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
add     esp, 4
mov dword ptr [ebp-4], eax
cmp dword ptr [ebp-4], 0
je short 00401106
push 00427070 ; /format = "incorrect password!",LF,LF,""
call printf ; \printf
add esp, 4
jmp short 00401115
push 00427030 ; /format = "congratulations! you have passed the verification",LF,LF,""
call printf ; \printf
add esp, 4
jmp short 00401117
jmp short 004010B5
push 00427028 ; /command = "pause"
call system ; \system
add esp, 4
pop edi
pop esi
pop ebx
add esp, 444
cmp ebp, esp
call _chkesp
mov esp, ebp
pop ebp
retn

esp加4

将从子函数返回的值——eax中存的数据,存到ebp-4中,再将ebp-4中的值与0作比较。

若结果等于零,跳转至00401106的位置,对应“push 00427030”,从而调用printf函数,输出”congratulations! you have passed the verification”,esp加4,接着跳转至0040117,对应“push 00427028”,接着调用system函数,执行“pause”指令,终结循环,接着销毁函数栈,退出main函数。

若结果不等于零,继续,调用函数printf,输出“incorrect password!”,esp加4,跳转至00401115,对应“jmp short 004010B5”,又回到循环开始,继续循环。

verify_password函数

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
.text:00401020
.text:00401020 ; =============== S U B R O U T I N E =======================================
.text:00401020
.text:00401020 ; Attributes: bp-based frame
.text:00401020
.text:00401020 ; int __cdecl verify_password(char *Str1)
.text:00401020 ?verify_password@@YAHPAD@Z proc near ; CODE XREF: verify_password(char *)j
.text:00401020
.text:00401020 var_4C = byte ptr -4Ch
.text:00401020 Dest = byte ptr -0Ch
.text:00401020 var_4 = dword ptr -4
.text:00401020 Str1 = dword ptr 8
.text:00401020
.text:00401020 push ebp
.text:00401021 mov ebp, esp
.text:00401023 sub esp, 4Ch
.text:00401026 push ebx
.text:00401027 push esi
.text:00401028 push edi
.text:00401029 lea edi, [ebp+var_4C]
.text:0040102C mov ecx, 13h
.text:00401031 mov eax, 0CCCCCCCCh
.text:00401036 rep stosd
.text:00401038 push offset Str2 ; "1234567"
.text:0040103D mov eax, [ebp+Str1]
.text:00401040 push eax ; Str1
.text:00401041 call _strcmp
.text:00401046 add esp, 8
.text:00401049 mov [ebp+var_4], eax
.text:0040104C mov ecx, [ebp+Str1]
.text:0040104F push ecx ; Source
.text:00401050 lea edx, [ebp+Dest]
.text:00401053 push edx ; Dest
.text:00401054 call _strcpy
.text:00401059 add esp, 8
.text:0040105C mov eax, [ebp+var_4]
.text:0040105F pop edi
.text:00401060 pop esi
.text:00401061 pop ebx
.text:00401062 add esp, 4Ch
.text:00401065 cmp ebp, esp
.text:00401067 call __chkesp
.text:0040106C mov esp, ebp
.text:0040106E pop ebp
.text:0040106F retn
.text:0040106F ?verify_password@@YAHPAD@Z endp
.text:0040106F
.text:0040106F ; ---------------------------------------------------------------------------

4

创建子函数的函数栈空间

1
2
3
4
5
6
push    0042701C                         ; /s2 = "1234567"
mov eax, dword ptr [ebp+8] ; |
push eax ; |s1
call strcmp ; \strcmp
add esp, 8
mov dword ptr [ebp-4], eax

将0042701C 中存的数据压入,再将ebp+8的位置存的数据通过eax压入,再调用strcmp函数对两者进行比较,将比较结果存入eax中,由于我输入的数据是123456,在与1234567比较后,eax中所存的值为FFFFFFFF,若我输入的值为1254,则eax中所存的值为00000001,若我输入的数据为1234567,则eax中所存数据为00000000。

将esp加8

将比较结果从eax中取出,存入ebp-4的位置,也就是子函数verify_password的栈底

1
2
3
4
5
6
mov     ecx, dword ptr [ebp+8]
push ecx ; /src
lea edx, dword ptr [ebp-C] ; |
push edx ; |dest
call strcpy ; \strcpy
add esp, 8

将ebp+8中存的数据存入ecx,再将ebp-C(ebp-8,C->Char)中的值存入edx,然后调用strcpy函数,将拷贝的值存入esp+8的位置

1
2
3
4
5
6
7
8
9
10
mov     eax, dword ptr [ebp-4]
pop edi
pop esi
pop ebx
add esp, 4C
cmp ebp, esp
call _chkesp
mov esp, ebp
pop ebp
retn

将ebp-4中所存结果存入eax中(这是用来保存子函数需要返回给主函数的值),然后进行一系列操作销毁函数栈,并执行retn,返回到主函数

栈溢出分析与利用

该程序产生栈溢出的主要原因在于,子函数中采用了strcmp函数,在执行子函数时,会向函数栈的栈底先后存入整型变量authentication的值和char型数组buffer的值,这两个数据存放位置相邻,若通过strcpy函数存入buffer的字符串长度恰好等于buffer数组的长度,由于字符串最后一位还有截断字符,那么,阶段字符会将authentication的值覆盖,这将导致程序功能发生错误,比如原本程序输入“1234567”时才会显示成功,而现在我们只需要在保证authentication的值为00000001的情况下输入任意长度为8的一串数字就能得到显示成功的结果,效果如下图所示:

5

6

我们继续研究,就这个程序来说,我们有没有可能,利用栈溢出漏洞,做点其他事,比如弹出个cmd啥的,这一点由于个人能力有限,便向能力强的同学请教,得到以下利用方式:

其中“^\”是ctrl+\,“^Q”是ctrl+Q,最后一个符号是@

7

在OllyICE中分析过程,可以看到,在执行完strcmp函数后,子函数verify_password栈底附近的变化,如下图所示

8

9

可以看到,上述一串字符,覆盖至0012FB28的位置,导致该位置存的地址发生改变,改变之前,该地址指向主函数中执行完子函数后的位置,改变后的地址指向主函数中执行system的位置,也就是0040111C,从而执行了系统命令,达到弹出cmd的效果。

  • 标题: 栈溢出实验
  • 作者: FindFuner
  • 创建于 : 2023-03-19 19:46:05
  • 更新于 : 2023-03-19 19:56:14
  • 链接: https://nnna48.github.io/2023/03/19/栈溢出漏洞逆向分析/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。