【HUST】网络攻防实践|5_二进制文件补丁技术|实验二 getshell

实验要求

printf函数改成新的newprintf函数,要getshell,并执行以下指令:

whoami
echo "学号" > num.txt
cat num.txt
exit

其中/bin/sh字符串就是getshellprintf的参数,因此这个可以直接使用。

实验过程

我采取的实验原理:将补丁程序的.text复制到目标程序的.eh_frame,然后修改目标程序的函数跳转地址,使漏洞程序跳转到.eh_frame来执行补丁代码。

1. 编写新的printf函数

我用的内联汇编。内联汇编使用的语法是AT&T。与平时写的Intel汇编不同。对于内联汇编的编写,我参考了这篇简书:GCC内联汇编基础 - 简书 (jianshu.com)
再次感谢博主详细细致的讲解!

编写的hook.c代码如下:

void myprintf(char *a, int b) {
    
    
  int tmp;
  asm(
    "mov %1,%%rdi\n"
    :"=r"(tmp)
    :"r"(a)
    :"%rdi"
  );
  asm(
    "mov $0,%rsi\n"
    "mov $0,%rdx\n"
    "mov $59,%rax\n"
    "syscall\n"
  );
}

主要含义是,构造execve(a,0,0),其中a是原来的printf的参数/bin/shexecve在64位系统中对应系统调用号59

将上述代码编译成静态链接库:

gcc -c hook.c -o hook

2. 编写使用LIEF的Python脚本

首先安装依赖:

pip install lief -y
pip install pwn -y

注:-y参数是弹出“是否安装”时一律默认yes,pip版本较低不支持这个选项,就别加。我个人习惯加-y参数。

下载python2的lief时,总是报错。上lief官网上看了一下,最新版lief对python2不支持了。所以使用了python3环境。

如果用python2就直接抄指导书脚本。

脚本的思路与指导书上的是一致的:读入并解析getshellhook文件,将hook.text节复制到getshell.eh_frame节,并将call _printf语句修改成call .eh_frame的起始地址。最后,将修改后的可执行文件写入getshell.patched中。

只是由于python版本问题,有3个需要注意修改的地方:

  1. python3的print输出是需要加括号的;
  2. python3中区分bytesstring这两个数据类型,而在python2中混用,因此在python3中字节型字符串前需要添加b,才能与p32转换结果进行拼接。
  3. ord()这个函数接受的类型是一个长度为1的字符串,当for...in...遍历bytes类型时,得到的已经是int类型的数据,在python2中有必要调用ord,而在python3中则可以不调用它了。

具体脚本lief_test.py如下:

import lief
from pwn import *

def patch_call(file,srcaddr, dstaddr , arch = "amd64"):
  print(hex(dstaddr))
  length = p32((dstaddr - (srcaddr + 5 ))& 0xffffffff)
  order = b'\xe8' +length
  print(disasm(order , arch=arch))
  file.patch_address(srcaddr, [i for i in order])

binary = lief.parse("./getshell")
hook = lief.parse('./hook')

# write hook 's .text content to binary's .eh_frame content
sec_ehrame = binary.get_section( '.eh_frame')
print(sec_ehrame.content)
sec_text = hook.get_section('.text')
print(sec_text.content)
sec_ehrame.content = sec_text.content
print(binary.get_section('.eh_frame').content)

# hook target call
dstaddr = sec_ehrame.virtual_address
srcaddr = 0x401149

patch_call(binary,srcaddr,dstaddr)

binary.write('getshell.patched')

3. 将getshell.eh_frame节设置为可执行

直接修改.eh_frame节并跳转执行,会报出如下段错误:

改权限:段错误|修改ELF不可执行段权限(010编辑器)

就行。

4. 打补丁并运行打补丁后的程序

编译补丁程序、打补丁、运行打了补丁的程序:

gcc -c hook.c -o hook
python3 lief_test.py
./getshell.patched

最后,验证是否成功获取shell

输入:

whoami
echo "学号" > num.txt
cat num.txt
exit

可以看到能够成功地获取shell。

5. 保证修改前后程序大小不变

与同学讨论后,发现程序大小发生非常大的变化,主要是由于LIEF这个库的局限性。这个补丁真的非常简单,所以我决定直接用IDA Pro来打补丁,保证修改前后程序大小不变。(不知道行不行,但我确实不知道别的做法

和前一个作业overflow一样的原理,具体看这篇HUST网络攻防实践|5_二进制文件补丁技术|实验一 overflow

我写得比较草率,有学长写的更好的,搜一下就搜到了:
[网安实践III] 实验5.补丁

call _printf改成jmp 0x402058,也就是跳到.eh_frame的开始位置。再向.eh_frame写入补丁代码,补丁代码最后跳回原来的执行流:

mov rsi,0
mov rdx,0
mov rax,59
syscall
jmp 0x40114E

再将原程序的.eh_frame所在段改成可执行即可。
运行的结果如下,可以看到,既getshell成功,又没有改变文件大小。

猜你喜欢

转载自blog.csdn.net/qq_46106285/article/details/125098087