IDA修补简单漏洞
AWDPlus是一种网络攻防竞赛规则,它源自AWD比赛并加以改进。AWDPlus比赛由多个队伍组成,每个队伍有自己的攻击和防御能力,比赛中将进行攻防双方的对抗。与传统CTF不同的是,AWDPlus有防御环节:赛队识别出靶机环境中存在的漏洞时,需要对其进行修补,使得靶机环境无法被平台方攻破。
一、栈溢出修补
1.1 工具准备
Keypatch 为IDA Pro的一个插件,可以用来修改ARM指令,这使得它非常适合用于二进制文件的修改。我们可以利用KeyPatch来修改函数的行为,比如改变跳转指令、修改寄存器值等。下载链接:https://github.com/keystone-engine/keypatch ,下载好脚本后放到ida目录中的/plugins目录即可。(keypatch对IDA有版本要求,最好7.0以上)
然后还需要安装一些依赖:
1 | pip3 install keystone-engine |


可以看IDA下面的交互界面,如果提示缺了哪些包,安装即可:

下载之后如果IDA依旧提示缺少依赖,可以将包复制到IDA的python里面:


1.2 字节未限制导致的简单漏洞
比如最常见的read函数的溢出漏洞:read读取的字节超过栈字节,就造成了溢出。修补也就是对其读取字节进行限制。
如下文件,文件链接:https://wutongblogs.oss-cn-beijing.aliyuncs.com/test/fix/pwn
1.2.1 攻击
反汇编发现溢出漏洞:

栈大小只有0x50字节,read函数却有0x100字节,还有后门存在:

攻击脚本:
1 | from pwn import * |
此时可以攻击成功:

1.2.2 修补
这类漏洞修补就很简单,只需要把read读取的字节修改为 0x50 即可。
找到汇编代码部分,按 Ctrl+alt+K ,弹出KeyPatch:

将 mov edx, 100h 修改为 mov edx, 50:

修改后的汇编:

按 F5 刷新伪代码,已经成功修改了:

在 Edit-->Patch program-->Apply patches to input file 保存文件,再次执行攻击脚本攻击就不成功了:

二、整数溢出修补
修补方法:
- 把变量从有符号类型改成无符号类型(例如int改成unsigned int)
- 把跳转指令从有符号跳转指令改成无符号跳转指令(例如jle指令改成jbe指令)
- jnz:不相等时跳转 jne:如果不等于,就跳转到目标地址
- jz:相等时跳转 je:如果等于,就跳转到目标地址
- jle:小于时跳转(有符号)jl:如果小于,就跳转到目标地址
- jbe:小于时跳转(无符号)
- jge:大于时跳转(有符号) jg:如果大于,就跳转到目标地址
- jnb:大于时跳转(无符号)
如下文件:https://wutongblogs.oss-cn-beijing.aliyuncs.com/test/fix/ret2text_x64s
2.1 攻击
反汇编:

if判断的时候是整型,但是 read 读取的时候,后面字节限制的无符号型,因此可以通过输入负数造成溢出:
攻击脚本:
1 | from pwn import * |
成功获取shell:

2.2 修补
将有符号跳转指令 jle修改为无符号跳转指令 jbe,按 Ctrl+alt+K ,弹出KeyPatch:I

修改:

修改完之后刷新,成功将其修改:

保存之后,使用攻击脚本,显示输入字符过长:

三、栈溢出修补
除了1.2节介绍的简单栈溢出外,还有其它栈溢出,比如Scanf未限制字节长度,此时如何修补呢?如果新加一条判断长度的语句,在编写代码过程中非常简单,但在汇编指令中,是很复杂的,因此我们选择将无字节限制的Scanf函数修改为有字节限制的read函数。
文件链接:https://wutongblogs.oss-cn-beijing.aliyuncs.com/test/fix/ROP_x64
3.1 攻击
反汇编:

Scanf未限制输入,导致溢出
攻击脚本:
1 | from pwn import * |
成功获取shell:

3.2 防御
3.2.1 原理
将无字节限制的Scanf函数修改为有字节限制的read函数:

read函数有三个参数,分别在rdi、rsi、rdx中,要想达到输入字符且限制长度的目的,三个参数应该分别为0,v1_addr,0x20。参数我们可以通过mov指令来实现,那么如何调用read函数呢?如果直接给出read函数地址,我们就可以直接使用了,但是没有的话要怎么办呢?
这就要用到我们的系统调用功能了,我们知道64位里,系统调用是syscall,系统调用号储存在rax寄存器中,而read函数的系统调用号为0,因此我们需要构造如下汇编指令:
1 | rax=0 |
但是这么多汇编指令的,添加到原有的文件里,就会修改它们的布局,造成程序无法运行,因此我们需要一个额外的数据段,来添加汇编指令。一般.eh_frame这个数据段是有可执行权限的,因此我们在这个数据段找一段空白的地方来修改汇编指令。

在程序执行时,我们使其跳转到这里,执行完毕后,再跳转回去,就不会影响原来的指令了。
3.2.2 修补
原先Scanf的汇编指令:

1、在call指令之前都是在调用参数,可以看到rdi寄存器里放了参数%s,因此我们需要将其修改为0:

2、后面的空间不够我们使用,因此我们使用在数据段编写指令、并跳转数据段的方法,将当前地址命名(按n命名)如下:

在数据段命名:

3、 在数据段编写汇编指令如下:
1 | mov edx,20 //read的第3个参数 |

然后加入ret指令,使它跳转到这里后再跳转回原理流程:jmp jmp_ret

4、回到原来的流程段,修改原先命令使它可以跳转到这里,将多余指令nop掉,将call指令改为jmp jmp_to:、

改完如下:

5、修改完毕后,原先的scanf会变成sys_read:

保存之后,使用攻击脚本,出现错误:

四、格式化字符串漏洞修补
格式化字符串漏洞由于缺少了参数,造成字符漏洞,因此我们需要补上参数。
文件链接:https://wutongblogs.oss-cn-beijing.aliyuncs.com/test/fix/
4.1 攻击
反汇编:

存在格式化字符串漏洞。
攻击脚本:
1 | from pwn import * |
成功获取shell:

4.2 修补
1、Printf没有参数%s造成溢出,因此首先在数据段写入%s字符

2、原先的流程内,不够布置两个参数,因此同样需要跳转流程:

修改汇编指令如下:
1 | lea eax, [as] //将%s放入eax寄存器 |

3、完成修改:

刷新一下,成功修改:

保存之后,使用攻击脚本,出现错误:

IDA使用快捷键:
n:更改变量的名称g:直接跳转到某个地址ctrl+s:选择某个数据段,直接进行跳转/:在反编译后伪代码的界面中写下注释;:在反汇编后的界面中写下注释\:在反编译后伪代码的界面中隐藏/显示变量和函数的类型描述,有时候变量特别多的时候隐藏掉类型描述看起来会轻松很多c: 将数据段转化成代码x:对着某个函数、变量按该快捷键,可以查看它的交叉引用¹²y:更改变量的类型a:将数据转换为字符串H:在数字上按下H键或者右键进行选择,可以将数字转化为十进制B:按下 B键 转换为二进制也是同理u:取消定义函数、代码、数据的定义
- Title: IDA修补简单漏洞
- Author: wutong
- Created at: 2023-07-25 19:45:14
- Updated at: 2023-08-20 19:40:44
- Link: https://wutong01304.github.io/2023/07/25/fix/
- License: This work is licensed under CC BY-NC-SA 4.0.