【HUST】网络攻防实践|5_二进制文件补丁技术|实验二 getshell
文章目录
实验要求
将printf
函数改成新的newprintf
函数,要getshell
,并执行以下指令:
whoami
echo "学号" > num.txt
cat num.txt
exit
其中/bin/sh
字符串就是getshell
中printf
的参数,因此这个可以直接使用。
实验过程
我采取的实验原理:将补丁程序的.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/sh
。execve
在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就直接抄指导书脚本。
脚本的思路与指导书上的是一致的:读入并解析getshell
、hook
文件,将hook
的.text
节复制到getshell
的.eh_frame
节,并将call _printf
语句修改成call .eh_frame的起始地址
。最后,将修改后的可执行文件写入getshell.patched
中。
只是由于python
版本问题,有3个需要注意修改的地方:
- python3的
print
输出是需要加括号的; - python3中区分
bytes
和string
这两个数据类型,而在python2中混用,因此在python3中字节型字符串前需要添加b
,才能与p32转换结果进行拼接。 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
节并跳转执行,会报出如下段错误:
就行。
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
成功,又没有改变文件大小。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix