[英]Overflow buffer in C on x86_64 to call function
Hello i have such code 你好我有这样的代码
#include <stdio.h>
#define SECRET "1234567890AZXCVBNFRT"
int checksecret(){
char buf[32];
gets(buf);
if(strcmp(SECRET,buf)==0) return 1;
else return 0;
}
void outsecret(){
printf("%s\n",SECRET);
}
int main(int argc, char** argv){
if (checksecret()){
outsecret();
};
}
disass of outsecret 绝望
(gdb) disassemble outsecret
Dump of assembler code for function outsecret:
0x00000000004005f4 <+0>: push %rbp
0x00000000004005f5 <+1>: mov %rsp,%rbp
0x00000000004005f8 <+4>: mov $0x4006b4,%edi
0x00000000004005fd <+9>: callq 0x400480 <puts@plt>
0x0000000000400602 <+14>: pop %rbp
0x0000000000400603 <+15>: retq
I have an assumption that i don't know SECRET
, so i try to run my program with such string python -c 'print "A" * 32 + "\\x40\\x05\\xf4"[::-1]'
. 我假设我不知道
SECRET
,所以我尝试使用这样的字符串python -c 'print "A" * 32 + "\\x40\\x05\\xf4"[::-1]'
。 But it fails with segmentation fault. 但是由于分割错误而失败。 What i am doing wrong?
我做错了什么? Thank you for any help.
感谢您的任何帮助。
I want to call function outsecret
by overwriting return code in checksecret
我想通过覆盖
checksecret
返回代码来调用函数outsecret
You have to remember that all strings have an extra character that terminates the string, so if you input 32 characters then gets
will write 33 characters to the buffer. 您必须记住,所有字符串都有一个终止字符串的额外字符,因此,如果输入32个字符,则
gets
将向缓冲区写入33个字符。 Writing beyond the limits of an array leads to undefined behavior which often leads to crashes. 超出数组限制的写入操作会导致未定义的行为 ,这通常会导致崩溃。
The gets
function have no bounds-checking, and is very dangerous to use. gets
函数没有边界检查,使用起来非常危险。 It has been deprecated since long, and in the latest C11 standard it has even been removed. 很久以来它已不推荐使用,在最新的C11标准中甚至已删除。
$ python -c 'print "A" * 32 + "\x40\x05\xf4"[::1]'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@
$ perl -le 'print length("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@")'
33
Your input string is too long for buffer size of 32 characters (extra one is needed for '\\0'
terminating null character ). 您的输入字符串对于32个字符的缓冲区大小来说太长(对于
'\\0'
终止为null字符,需要加一个)。 You are victim to buffer or array overflow (sometimes also called as array overrun). 您是缓冲区或数组溢出的受害者(有时也称为数组溢出)。
Note that gets()
is deprecated in C99 and eventually it has been dropped in C11 Standard for security reasons. 请注意,由于安全原因,
gets()
在C99中已弃用,最终在C11 Standard中已将其删除 。
I want to call function outsecret by overwriting return code in checksecret
我想通过覆盖checksecret中的返回代码来调用函数outsecret
Beware, you are about to leave
relatively
safe regions of C Standard. 当心,您将离开
相对
安全的C标准区域。 This means that behaviour is relative to compiler, compiler's versions, optimization settings, ABI and so on (maybe inclucing current phase of moon). 这意味着行为与编译器,编译器的版本,优化设置,ABI等有关(可能不包括当前的月相)。
As of x86 calling conventions integer return value is stored directly in %eax
register (that's assuming that you have x86
or x86-64
CPU). 从x86调用约定开始,整数返回值直接存储在
%eax
寄存器中(假定您具有x86
或x86-64
CPU)。 Stack-likely-located array buf
is handled by %rbp
offsets within current stack frame . 通过当前堆栈frame内的
%rbp
偏移量来处理可能位于堆栈中的数组buf
。 Let's consult with gdb
disassemble command: 让我们咨询一下
gdb
disassemble命令:
$ gcc -O0 test.c
$ gdb -q a.out
(gdb) b checksecret
(gdb) r
Breakpoint 1, 0x0000000000400631 in checksecret ()
(gdb) disas
Dump of assembler code for function checksecret:
0x000000000040062d <+0>: push %rbp
0x000000000040062e <+1>: mov %rsp,%rbp
=> 0x0000000000400631 <+4>: sub $0x30,%rsp
0x0000000000400635 <+8>: mov %fs:0x28,%rax
0x000000000040063e <+17>: mov %rax,-0x8(%rbp)
0x0000000000400642 <+21>: xor %eax,%eax
0x0000000000400644 <+23>: lea -0x30(%rbp),%rax
0x0000000000400648 <+27>: mov %rax,%rdi
0x000000000040064b <+30>: callq 0x400530 <gets@plt>
0x0000000000400650 <+35>: lea -0x30(%rbp),%rax
0x0000000000400654 <+39>: mov %rax,%rsi
0x0000000000400657 <+42>: mov $0x400744,%edi
0x000000000040065c <+47>: callq 0x400510 <strcmp@plt>
0x0000000000400661 <+52>: test %eax,%eax
0x0000000000400663 <+54>: jne 0x40066c <checksecret+63>
0x0000000000400665 <+56>: mov $0x1,%eax
0x000000000040066a <+61>: jmp 0x400671 <checksecret+68>
0x000000000040066c <+63>: mov $0x0,%eax
0x0000000000400671 <+68>: mov -0x8(%rbp),%rdx
0x0000000000400675 <+72>: xor %fs:0x28,%rdx
0x000000000040067e <+81>: je 0x400685 <checksecret+88>
0x0000000000400680 <+83>: callq 0x4004f0 <__stack_chk_fail@plt>
0x0000000000400685 <+88>: leaveq
0x0000000000400686 <+89>: retq
There is no way overwrite %eax
directly from C code, but what you could do is to overwrite selective fragment of code section. 无法直接从C代码覆盖
%eax
,但是您可以做的是覆盖代码段的选择性片段。 In your case what you want is to replace: 在您的情况下,您要替换的是:
0x000000000040066c <+63>: mov $0x0,%eax
with 与
0x000000000040066c <+63>: mov $0x1,%eax
It's easy to accomplish by gdb
itself: 通过
gdb
本身很容易完成:
(gdb) x/2bx 0x40066c
0x40066c <checksecret+63>: 0xb8 0x00
set {unsigned char}0x40066d = 1
Now let's confirm it: 现在让我们确认一下:
(gdb) x/i 0x40066c
0x40066c <checksecret+63>: mov $0x1,%eax
From that point checksecret()
is returning 1
even if SECRET
does not match. 从那时起,即使
SECRET
不匹配, checksecret()
也会返回1
。 However It wouldn't be so easy to do it by buf
itself, as you need to know (guess somehow?) correct offset of particular code section instruction. 但是,通过
buf
本身来完成它并不是那么容易,因为您需要知道(以某种方式猜测)特定代码段指令的正确偏移量。
Above answers are pretty clear and corret way to exploit buffer overflow vulnerability. 以上答案是利用缓冲区溢出漏洞的非常清晰和正确的方法。 But there is a different way to do same thing without exploit vulnerability.
但是,可以利用另一种方法来利用漏洞而做同样的事情。
mince@rootlab tmp $ gcc test.c -o test
mince@rootlab tmp $ strings test
/lib64/ld-linux-x86-64.so.2
libc.so.6
gets
puts
__stack_chk_fail
strcmp
__libc_start_main
__gmon_start__
GLIBC_2.4
GLIBC_2.2.5
UH-X
UH-X
[]A\A]A^A_
1234567890AZXCVBNFRT
;*3$
Please look at last 2 row. 请看最后2行。 You will see your secret key in there.
您将在其中看到您的秘密密钥。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.