我了解利用经典的基于堆栈的缓冲区溢出的原理,现在我想练习一下。因此,我编写了以下测试应用程序:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void public(char *args) {
char buff[12];
memset(buff, 'B', sizeof(buff));
strcpy(buff, args);
printf("\nbuff: [%s] (%p)(%d)\n\n", &buff, buff, sizeof(buff));
}
void secret(void) {
printf("SECRET\n");
exit(0);
}
int main(int argc, char *argv[]) {
int uid;
uid = getuid();
// Only when the user is root
if (uid == 0)
secret();
if (argc > 1) {
public(argv[1]);
}
else
printf("Kein Argument!\n");
}
当启动程序的用户为root 时,secret()正在调用该方法,否则,public(...)正在调用该方法。我使用的是 debian-gnome x64,所以我必须专门将它编译为 x86 才能获得 x86-assembly(我比 x64 更了解它)。我用gcc编译了程序:gcc ret.c -o ret -m32 -g -fno-stack-protector
目标:
我想在secret()不是root-user 的情况下调用该方法。{要做到这一点我必须覆盖RE打开Instruction Pointer( RIP)与函数的地址secret()}
漏洞:
该方法public(...)将带有 unsafestrcpy()方法的 program-args 复制到char-array buff 中。因此,当用户以arg > 11启动程序时,可以覆盖堆栈上的数据,其中arg应该是字符串 arg 的长度。
需要的信息:
- 函数的地址
secret()。 - 第一个缓冲区的第一个元素的地址。由于
ASCII-Encoding 我知道每个char都有一个大小1 byte,所以缓冲区的最后一个元素12 bytes在第一个元素之前。 - 的地址
RIP,因为我必须覆盖它secret()的地址。 - 可选:它也有助于知道
SafedFramePointer (SFP)的地址。
有条不紊的方法:
- 将程序加载到
gdb: 中gdb -q ret。 - 要获得该方法的完整堆栈框架的概述,
public(...)我必须在那里设置一个断点,从那里function-epilogue开始。这是}在 line的封闭大括号处11。 - 现在,我必须与一个有效的ARG运行程序:
run A。 在断点处,我现在想查看堆栈帧。
(gdb) info frame 0 Stack frame at 0xffffd2f0: eip = 0x804852d in public (ret.c:11); saved eip = 0x804858c called by frame at 0xffffd330 source language c. Arglist at 0xffffd2e8, args: args=0xffffd575 "A" Locals at 0xffffd2e8, Previous frame's sp is 0xffffd2f0 Saved registers: ebp at 0xffffd2e8, eip at 0xffffd2ec因为从中我可以收集到以下信息:
- 在
RIP位于0xffffd2ec与包含地址0x804858c包含的指令0x804858c <main+61>: add $0x10,%esp。 - 在
SFP位于0xffffd2e8。 现在我需要
secret()- 函数开始的地址:(gdb) 打印机密 $2 = {void (void)} 0x804852f
- 在
最后,但并非最不重要的是,我得到了缓冲区的地址:
(gdb) print/x &buff $4 = 0xffffd2d4把它们加起来:
RIP在0xffffd2ec。SFP在0xffffd2e8。buff在0xffffd2d4。
这意味着我必须使用0xffffd2ec- 0xffffd2d4+ 0x04= 28 bytes(= chars)运行程序。
因此,要利用它,我必须使用一个很28 bytes长的 arg 运行程序,而最后一个4 bytes包含函数的地址secret()(并注意小端排序):
(gdb) run `perl -e '{print "A"x24; print "\xec\d2\ff\ff"; }'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/patrick/Projekte/C/I. Stack_Overflow/ret `perl -e '{print "A"x24; print "\xec\d2\ff\ff"; }'`
buff: [AAAAAAAAAAAAAAAAAAAAAAAA�d2
f
f] (0xffffd2b4)(12)
Program received signal SIGSEGV, Segmentation fault.
0x0c3264ec in ?? ()
两个问题浮出水面:
为什么它不起作用。这个例子基本上来自我正在阅读的一本旧书。但理论上它应该有效,所以我认为......
为什么
buff和之间SFP有8-byte差距?这个内存区域包含什么?
编辑:这是二进制文件的下载链接。