利用Lief在AWD中Patch-ELF

September 14, 2019 PWN 访问: 96 次

为了准备第五空间的比赛,google了一番怎么在线下中修复pwn,之前对这块的知识完全不懂,上次在国赛中其实是吃了大亏的。

Lief

这是跨平台库,可以利用它来对ELF、PE、macho格式的文件进行解析和修改

安装

如果是用PIP直接安装的话我也不知道为什么它会出错,我是直接在python官网上下载的zip包,然后手动安装,安装的时候需要下载一个egg后缀的东西,下载后放到/root/下即可

使用方法

import lief
file = lief.parse("filename")#首先是载入文件
print file#输出文件的信息,我感觉和 readelf -a filename 的效果差不多
puts_symbols = filter(lambda e:e.name=="****",file.imported_symbols)[0] #(python2写法) 获取到某一个函数的symbols的地址
****_symbols = filter(lambda e:e.name=="puts",file.imported_symbols).__next__()
'''
python2 返回list
python3 返回filter
'''
****_symbols.name='system'
file.write("patch_filename")#将修改过后的文件写成新的ELF文件

经过验证发现函数确实被改过来的,但是修改过后的文件大小和原来的大小相差得有点大

 一筐萝卜➜ patch  du -h ./shell && du -h patch_shell
12K ./shell
36K patch_shell

.eh_frame段上插入代码

这个方法是个很不错得方法
首先,.eh_frame一般都在0x400000 0x401000,而这段地址上是具有可执行权限的

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
          0x400000           0x401000 r-xp     1000 0      /root/patch/shell
          0x600000           0x601000 r--p     1000 0      /root/patch/shell
          0x601000           0x602000 rw-p     1000 1000   /root/patch/shell
    0x7ffff7a0d000     0x7ffff7bcd000 r-xp   1c0000 0      /lib/x86_64-linux-gnu/libc-2.23.so
    0x7ffff7bcd000     0x7ffff7dcd000 ---p   200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
    0x7ffff7dcd000     0x7ffff7dd1000 r--p     4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
    0x7ffff7dd1000     0x7ffff7dd3000 rw-p     2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
    0x7ffff7dd3000     0x7ffff7dd7000 rw-p     4000 0
    0x7ffff7dd7000     0x7ffff7dfd000 r-xp    26000 0      /lib/x86_64-linux-gnu/ld-2.23.so
    0x7ffff7fd9000     0x7ffff7fdc000 rw-p     3000 0
    0x7ffff7ff7000     0x7ffff7ffa000 r--p     3000 0      [vvar]
    0x7ffff7ffa000     0x7ffff7ffc000 r-xp     2000 0      [vdso]
    0x7ffff7ffc000     0x7ffff7ffd000 r--p     1000 25000  /lib/x86_64-linux-gnu/ld-2.23.so
    0x7ffff7ffd000     0x7ffff7ffe000 rw-p     1000 26000  /lib/x86_64-linux-gnu/ld-2.23.so
    0x7ffff7ffe000     0x7ffff7fff000 rw-p     1000 0
    0x7ffffffde000     0x7ffffffff000 rw-p    21000 0      [stack]
0xffffffffff600000 0xffffffffff601000 r-xp     1000 0      [vsyscall]

一般这种方法都是把源代码是call *******,然后我们可以在.eh_frame上写一个函数,然后把代码改成call new_addr
形如这样的,

.eh_frame:0000000000400630
.eh_frame:0000000000400630 ; =============== S U B R O U T I N E =======================================
.eh_frame:0000000000400630
.eh_frame:0000000000400630
.eh_frame:0000000000400630 ; __int64 __fastcall sub_400630(const char *buf)
.eh_frame:0000000000400630 sub_400630      proc near               ; CODE XREF: main+9↑p
.eh_frame:0000000000400630                 mov     rsi, rdi        ; buf
.eh_frame:0000000000400633                 mov     rdi, 0          ; fd
.eh_frame:000000000040063A                 mov     rdx, 14h        ; count
.eh_frame:0000000000400641                 mov     rax, 1
.eh_frame:0000000000400648                 syscall                 ; LINUX - sys_write
.eh_frame:000000000040064A                 retn
.eh_frame:000000000040064A sub_400630      endp
.eh_frame:000000000040064A
.eh_frame:000000000040064A ; ---------------------------------------------------------------------------

如果说是要直接调用函数的话,那么改动的东西会很多,总所周知,ELF大部分都是动态链接,所以直接改函数的话不是太容易;再者来说,利用系统调用来调用函数也是很方便的
系统调用标号和函数对应表

直接修改动态链接库中的函数表

一般pwn都是采用的是动态链接,所以程序中用到的系统函数都是在动态链接库中的,所以我们可以尝试着直接改动态链接库

import lief
file = lief.parse("/lib/x86_64-linux-gnu/libc.so.6")
# print file
puts_symbols = filter(lambda e:e.name=="puts",file.dynamic_symbols).__next__()
system_symbols = filter(lambda e:e.name=="system",file.dynamic_symbols).__next__()
print(puts_symbols)
print(system_symbols)
puts_symbols.name="system"
system_symbols.name="puts"
file.write("libc.so.6")

可以看到成功的执行了system函数

 一筐萝卜➜ patch  LD_LIBRARY_PATH=. ./shell_old
binsh.c  libc.so.6  mypatch.py  patchelf-0.8  patchelf-0.8.tar.bz2  shell_old
 一筐萝卜➜ patch  ./shell_old
ls
 一筐萝卜➜ patch

添加新评论