国赛半决赛的时候需要fix,因为没有做太多准备,第一天直接用的ida,第二天用的keypatch插件,但是效果都不是太好,感觉很水,所以单独学习一下
Tools
keypatch
说到patch容易看到的一款ida的插件了,用法倒是很简单,
https://github.com/keystone-engine/keypatch 下载完毕后放到ida的插件目录下重启ida就ok了
lief
刚发现的一款神器,可以对elf,pe等多种格式的文件进行修改
python 使用直接pip安装即可
Method
这里主要使用lief
直接修改库的函数
如果程序使用了system来getshell,如果是已经动态链接的话,实际上是可以将它给改为其他函数的
测试1
2
3
4
5
6
7#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
system("/bin/sh");
return EXIT_SUCCESS;
}
如果是正常的程序,运行就可以直接拿到shell
我们编写一份patch.py,就是简单的将system换为puts
1 | import lief |
效果展示:
1 | sirius@ubuntu:~/tikool/test/test2$ ./main |
修改导入库的函数
这里就跟着lief的官方文档来进行实验
主程序,会调用exp函数求e的n次幂1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//gcc main.c -o main -lm
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char **argv) {
if (argc != 2) {
printf("Usage: %s <a> \n", argv[0]);
exit(-1);
}
double a = atoi(argv[1]);
printf("exp(%lf) = %lf\n", a, exp(a));
return 0;
}
因为该函数是在libm.so.6中,我们将其复制到当前目录,编写hook,生成库
这里hook函数用来计算平方值。
1 | //gcc -Os -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook |
编写patch.py,这里是在libm中新添加了一个段来添加后面新加的exp函数,最后将其写入libm.so.6
1 | import lief |
查看效果,因为默认使用的是系统库,所以正常,优先使用当前目录的库时,原来的exp变为平方
1 | sirius@ubuntu:~/tikool/test/test3$ ldd main |
特定地址函数patch
例如UAF漏洞经常会结合其他漏洞一块存在,如果可以对free进行修补则可以在一定程度上对其进行维护
这里使用别的函数进行演示
主程序,就是两次输出1
2
3
4
5
6
7
8#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int a=2;
printf("%d\n",a);
printf("%d\n",a);
return EXIT_SUCCESS;
}
目标是将第一个printf修改成我们自己写的比如说write函数,将其使用汇编进行编写为hook
1 | //gcc -Os -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook |
反汇编查看第一次printf的调用地址1
2
3
4
5
6
7
8
940053f: 89 c6 mov esi,eax
400541: bf f4 05 40 00 mov edi,0x4005f4
400546: b8 00 00 00 00 mov eax,0x0
40054b: e8 b0 fe ff ff call 400400 <printf@plt>
400550: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
400553: 89 c6 mov esi,eax
400555: bf f4 05 40 00 mov edi,0x4005f4
40055a: b8 00 00 00 00 mov eax,0x0
40055f: e8 9c fe ff ff call 400400 <printf@plt>
编写patch.py 这里是先将myprintf添加到main程序中,再修改函数调用部分为我们的myprintf_addr
1 | from pwn import * |
测试1
2
3
4
5
6
7
8
9sirius@ubuntu:~/tikool/test/test1$ ./main
2
2
sirius@ubuntu:~/tikool/test/test1$ python patch.py
0x802000
0: e8 8a 1d 40 00 call 0x401d8f
sirius@ubuntu:~/tikool/test/test1$ ./main_patch
%d
;4�����8���P2
效果很明显,但是看不出来第二个printf是否被影响了,查看反汇编确定修改成功
1 | 40053f: 89 c6 mov esi,eax |
直接修改got表
这里我还不太确定其内部实际上与第一种是否相同
仍然使用第三次的程序,直接上patch.py
1 | from pwn import * |
这里就是对printf的调用修改成了自定义的myprintf
测试1
2
3
4sirius@ubuntu:~/tikool/test/test1$ ./main_patch
%d
;4�����8���P%d
;4�����8���P
查看反汇编发现与修改前一致,应该是直接对got表进行了修改
Notice
这个lief比较好用,就写了个小工具来方便patch,现在只有一个简单点指定地址长度的nop与针对UAF漏洞的free函数的patch2
如果需要的话看这里https://github.com/siriuswhiter/ELF-Patcher
需要提醒的是UAF的patch2是不能直接将其patch完成的,因为函数调用的姿势经常不尽相同,需要根据call free之前参数的传递方式来进行修改判断是否需要迁移代码。
如果运气比较好像这样1
2
3
4
5
6.text:0000000000000E64 lea rdx, ds:0[rax*8]
.text:0000000000000E6C lea rax, unk_202060
.text:0000000000000E73 mov rax, [rdx+rax]
.text:0000000000000E77 mov rax, [rax]
.text:0000000000000E7A mov rdi, rax ; ptr
.text:0000000000000E7D call _free
就完全不需要迁移,将0xE77 nop 掉就可以将指向ptr的指针传参进去了
而像下面这种,就需要进行部分迁移了1
2
3
4
5
6.text:000000000040094A push rbp
.text:000000000040094B mov rbp, rsp
.text:000000000040094E mov rax, cs:buf
.text:0000000000400955 mov rdi, rax ; ptr
.text:0000000000400958 call _free
.text:000000000040095D lea rdi, aDone ; "Done!"
在使用时选择migration,指定起始地址为0x400955,结束地址为0x40095d,就可以将这两行进行迁移,
之后在IDA中将 mov rax, cs:buf 改为 lea rax, cs:buf 取地址即可
ps: 然后最后在比赛的时候完全没用着,14k的文件添加一个free的patch后会到达25k,然后因为过大而判定失败……,所以像国赛这样的patch可能就与LIEF无缘了