在缓冲区溢出 return 地址被不正确的地址覆盖,但它仍然有效
In buffer overflow return address is overwritten with incorrect address but it still works
我正在尝试使缓冲区溢出,这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password) {
char password_buffer[16];
int auth_flag = 0;
strcpy(password_buffer, password);
if(strcmp(password_buffer, "brillig") == 0)
auth_flag = 1;
if(strcmp(password_buffer, "outgrabe") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("Usage: %s <password>\n", argv[0]);
exit(0);
}
if(check_authentication(argv[1])) {
printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf(" Access Granted.\n");
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
}
else {
printf("\nAccess Denied.\n");
}
}
从命令行提供的密码将与 "brilling" 和 "outgrabe" 进行比较,如果用户输入匹配其中任何一个,将授予访问权限,否则将被拒绝。据我所知,如果提供的密码大于 16,return 地址将被覆盖,但是当我输入 "A" 17 时,它不会被覆盖。而不是 auth_flag 被覆盖并且是 65(十六进制中的 0x41,即 A)。我不明白为什么变量被覆盖而不是 return 地址。我正在用这个编译
gcc -fno-stack-protector -z execstack -g -o test test.c
希望大家帮帮忙。谢谢
一旦输入的 (password
) 字符串包含超过 15
(不是 16
,需要终止 null)字符,使用 strcpy()
将调用 undefined behaviour.
- 在此之前(除非发生这种情况),您的程序没有问题。
- 在那之后,你无法预测任何事情。
因此,在您的程序显示 UB 之后,您不能指望它以期望的方式运行。它可以做任何预期或意外的事情。是否覆盖auth_flag
,无法确定。
也许值得一提的是,变量的内存分配不一定按任何特定顺序(出现)发生。您的编译器可以按照它认为合适的任何顺序自由分配内存。
正如 Sourav Ghosh 所说,您的程序调用了未定义的行为。
实际上,这意味着您需要深入研究生成的代码以查看堆栈的使用方式。
由于这在很大程度上取决于您的环境(机器、OS、编译器版本等),因此我们几乎无法为您调试它。
在 my 环境中,authenticated
flag 存储在 ebp-12,而 buffer 存储在 ebp-28。 ebp-28 被传递给 strcmp,这意味着溢出将导致 authenticated
被覆盖。
更长的字符串最终会破坏 return 地址:“1234567890123456000000000000” 在我这里触发段错误。
推荐的起点可以是编译为汇编:
gcc -fno-stack-protector -z execstack -g -S test.c
生成 test.s,这将向您展示堆栈的外观。
您还可以使用反汇编器查看二进制文件(想到 objdump
和 ida pro
),如果从源代码编译不是您负担得起的设施;-)。
编辑: 从你的 pastebin,这是我收集的:
显然存在某种形式的堆栈保护:
movl %gs:20, %eax
[...]
movl -12(%ebp), %edx
xorl %gs:20, %edx
je .L5
call __stack_chk_fail
如果 -12(%ebp) 的位置不再是 %gs:20(例如,您的堆栈已损坏),则调用 __stack_chk_fail
。
附带说明一下,如果想要绕过身份验证的真正攻击者可以控制您的 "authenticated" 标志的值,他们甚至不会费心尝试删除 return 地址它的美丽。
我正在尝试使缓冲区溢出,这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password) {
char password_buffer[16];
int auth_flag = 0;
strcpy(password_buffer, password);
if(strcmp(password_buffer, "brillig") == 0)
auth_flag = 1;
if(strcmp(password_buffer, "outgrabe") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("Usage: %s <password>\n", argv[0]);
exit(0);
}
if(check_authentication(argv[1])) {
printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf(" Access Granted.\n");
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
}
else {
printf("\nAccess Denied.\n");
}
}
从命令行提供的密码将与 "brilling" 和 "outgrabe" 进行比较,如果用户输入匹配其中任何一个,将授予访问权限,否则将被拒绝。据我所知,如果提供的密码大于 16,return 地址将被覆盖,但是当我输入 "A" 17 时,它不会被覆盖。而不是 auth_flag 被覆盖并且是 65(十六进制中的 0x41,即 A)。我不明白为什么变量被覆盖而不是 return 地址。我正在用这个编译
gcc -fno-stack-protector -z execstack -g -o test test.c
希望大家帮帮忙。谢谢
一旦输入的 (password
) 字符串包含超过 15
(不是 16
,需要终止 null)字符,使用 strcpy()
将调用 undefined behaviour.
- 在此之前(除非发生这种情况),您的程序没有问题。
- 在那之后,你无法预测任何事情。
因此,在您的程序显示 UB 之后,您不能指望它以期望的方式运行。它可以做任何预期或意外的事情。是否覆盖auth_flag
,无法确定。
也许值得一提的是,变量的内存分配不一定按任何特定顺序(出现)发生。您的编译器可以按照它认为合适的任何顺序自由分配内存。
正如 Sourav Ghosh 所说,您的程序调用了未定义的行为。
实际上,这意味着您需要深入研究生成的代码以查看堆栈的使用方式。
由于这在很大程度上取决于您的环境(机器、OS、编译器版本等),因此我们几乎无法为您调试它。
在 my 环境中,authenticated
flag 存储在 ebp-12,而 buffer 存储在 ebp-28。 ebp-28 被传递给 strcmp,这意味着溢出将导致 authenticated
被覆盖。
更长的字符串最终会破坏 return 地址:“1234567890123456000000000000” 在我这里触发段错误。
推荐的起点可以是编译为汇编:
gcc -fno-stack-protector -z execstack -g -S test.c
生成 test.s,这将向您展示堆栈的外观。
您还可以使用反汇编器查看二进制文件(想到 objdump
和 ida pro
),如果从源代码编译不是您负担得起的设施;-)。
编辑: 从你的 pastebin,这是我收集的:
显然存在某种形式的堆栈保护:
movl %gs:20, %eax
[...]
movl -12(%ebp), %edx
xorl %gs:20, %edx
je .L5
call __stack_chk_fail
如果 -12(%ebp) 的位置不再是 %gs:20(例如,您的堆栈已损坏),则调用 __stack_chk_fail
。
附带说明一下,如果想要绕过身份验证的真正攻击者可以控制您的 "authenticated" 标志的值,他们甚至不会费心尝试删除 return 地址它的美丽。