COMMAND
nslookup
SYSTEMS AFFECTED
Many systems
PROBLEM
Peter van Dijk found following.
[peter@koek] ~$ nslookup `perl -e 'print "A" x 100;'`
Server: zopie.attic.vuurwerk.nl
Address: 10.10.13.1
*** zopie.attic.vuurwerk.nl can't find AAA.....AAA: Unspecified error
[peter@koek] ~$ nslookup `perl -e 'print "A" x 300;'`
Server: zopie.attic.vuurwerk.nl
Address: 10.10.13.1
*** zopie.attic.vuurwerk.nl can't find AA....AAA: Unspecified error
Segmentation fault (core dumped)
[peter@koek] ~$ nslookup `perl -e 'print "A" x 1000;'`
Server: zopie.attic.vuurwerk.nl
Address: 10.10.13.1
Segmentation fault (core dumped)
At first, this does not seem a problem: nslookup is not suid root
or anything. But several sites have cgi-scripts that call
nslookup... tests show that these will coredump when passed
enough characters. Looks exploitable. The offending line is line
684 in main.c (BIND 8.1.2 or line 681 in 4.9.7-REL):
sscanf(string, " %s", host); /* removes white space */
Under some OS's it may require a larger string to overflow the
buffer. Same goes for dig.c! Willy Tarreau successfully got a
shell using old generic exploit, with 260 bytes followed by a
pointer to esp-400.
SOLUTION
Theo de Raadt fixed 'a bucketload of sscanf's'. One patch for
dig is available at:
http://www.nrw.net/uwe/dig-8.1.2.patch
And here's Theos's patch. This patch should get people started at
repairing their distributions. This patch doesn't apply to bind
8.1.2. Revisited patch follows it after:
Index: dig/dig.c
===================================================================
RCS file: /cvs/src/usr.sbin/named/dig/dig.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- dig.c 1997/07/21 02:10:56 1.4
+++ dig.c 1998/08/30 03:39:18 1.5
@@ -765,9 +765,11 @@
char option[NAME_LEN];
char type[NAME_LEN];
char *ptr;
+ char get[80];
int i;
- i = sscanf(string, " %s", option);
+ snprintf(get, sizeof get, " %%%ds", sizeof option-1);
+ i = sscanf(string, get, option);
if (i != 1) {
fprintf(stderr, ";*** Invalid option: %s\n", option);
return(ERROR);
@@ -800,7 +802,8 @@
} else if (strncmp(option, "do", 2) == 0) { /* domain */
ptr = strchr(option, '=');
if (ptr != NULL) {
- sscanf(++ptr, "%s", _res.defdname);
+ snprintf(get, sizeof get, "%%%ds", sizeof _res.defdname-1);
+ sscanf(++ptr, get, _res.defdname);
}
} else if (strncmp(option, "ti", 2) == 0) { /* timeout */
ptr = strchr(option, '=');
Index: nslookup/list.c
===================================================================
RCS file: /cvs/src/usr.sbin/named/nslookup/list.c,v
retrieving revision 1.2
diff -u -r1.2 list.c
--- list.c 1997/03/12 10:42:47 1.2
+++ list.c 1998/08/31 17:39:27
@@ -156,13 +156,16 @@
char *namePtr;
char name[NAME_LEN];
char option[NAME_LEN];
+ char get[80];
/*
* Parse the command line. It maybe of the form "ls -t domain"
* or "ls -t type domain".
*/
- i = sscanf(string, " ls -t %s %s", option, name);
+ snprintf(get, sizeof get, " ls -t %%%ds %%%ds", sizeof option-1,
+ sizeof name-1);
+ i = sscanf(string, get, option, name);
if (putToFile && i == 2 && name[0] == '>') {
i--;
}
@@ -193,12 +196,15 @@
char *namePtr;
char name[NAME_LEN];
char option[NAME_LEN];
+ char get[80];
/*
* Parse the command line. It maybe of the form "ls domain",
* "ls -X domain".
*/
- i = sscanf(string, " ls %s %s", option, name);
+ snprintf(get, sizeof get, " ls -t %%%ds %%%ds", sizeof option-1,
+ sizeof name-1);
+ i = sscanf(string, get, option, name);
if (putToFile && i == 2 && name[0] == '>') {
i--;
}
@@ -521,6 +527,8 @@
}
+#define NAME_SLOP 80
+
PrintListInfo(file, msg, eom, qtype, domain)
FILE *file;
u_char *msg, *eom;
@@ -533,7 +541,7 @@
u_int32_t ttl;
int n, pref, count;
struct in_addr inaddr;
- char name[NAME_LEN];
+ char name[NAME_LEN + NAME_SLOP];
char name2[NAME_LEN];
Boolean stripped;
@@ -562,7 +570,7 @@
cp += nameLen + QFIXEDSZ;
}
for (count = ntohs(headerPtr->ancount); count > 0; count--) {
- nameLen = dn_expand(msg, eom, cp, name, sizeof name);
+ nameLen = dn_expand(msg, eom, cp, name, NAME_LEN);
if (nameLen < 0)
return (ERROR);
cp += nameLen;
@@ -592,7 +600,7 @@
}
}
if (!stripped && nameLen < sizeof(name)-1) {
- strcat(name, ".");
+ strncat(name, ".", sizeof(name) - strlen(name) - 1);
}
fprintf(file, NAME_FORMAT, name);
@@ -901,8 +909,10 @@
{
char file[PATH_MAX];
char command[PATH_MAX];
+ char get[80];
- sscanf(string, " view %s", file);
+ snprintf(get, sizeof get, " view %%%ds", sizeof file-1);
+ sscanf(string, get, file);
(void)sprintf(command, "grep \"^ \" %s | sort | %s", file, pager);
system(command);
}
@@ -936,6 +946,7 @@
register int lastc;
char name[NAME_LEN];
char file[NAME_LEN];
+ char get[80];
/*
* We need a valid current host info to get an inet address.
@@ -945,7 +956,8 @@
return (ERROR);
}
- if (sscanf(string, " finger %s", name) == 1) {
+ snprintf(get, sizeof get, " finger %%%ds", sizeof name-1);
+ if (sscanf(string, get, name) == 1) {
if (putToFile && (name[0] == '>')) {
name[0] = '\0';
}
Index: nslookup/main.c
===================================================================
RCS file: /cvs/src/usr.sbin/named/nslookup/main.c,v
retrieving revision 1.3
diff -u -r1.3 main.c
--- main.c 1997/03/12 10:42:48 1.3
+++ main.c 1998/08/31 17:43:19
@@ -465,6 +465,7 @@
struct in_addr *servAddrPtr;
struct in_addr addr;
char newServer[NAME_LEN];
+ char get[40];
int result;
int i;
@@ -474,12 +475,15 @@
*/
if (local) {
- i = sscanf(string, " lserver %s", newServer);
+ snprintf(get, sizeof get, "lserver %%%ds", sizeof newServer-1);
+ i = sscanf(string, get, newServer);
} else {
- i = sscanf(string, " server %s", newServer);
+ snprintf(get, sizeof get, "server %%%ds", sizeof newServer-1);
+ i = sscanf(string, get, newServer);
}
if (i != 1) {
- i = sscanf(string, " %s", newServer);
+ snprintf(get, sizeof get, " %%%ds", sizeof newServer-1);
+ i = sscanf(string, get, newServer);
if (i != 1) {
fprintf(stderr,"SetDefaultServer: invalid name: %s\n", string);
return(ERROR);
@@ -669,6 +673,7 @@
{
char host[NAME_LEN];
char file[PATH_MAX];
+ char get[20];
int result;
/*
@@ -684,7 +689,8 @@
*
*/
- sscanf(string, " %s", host); /* removes white space */
+ snprintf(get, sizeof get, " %%%ds", sizeof host-1);
+ sscanf(string, get, host); /* removes white space */
if (!putToFile) {
filePtr = stdout;
} else {
@@ -740,12 +746,15 @@
char file[PATH_MAX];
char host[NAME_LEN];
char server[NAME_LEN];
+ char get[80];
int result;
+
static HostInfo serverInfo;
curHostValid = FALSE;
- sscanf(string, " %s %s", host, server);
+ snprintf(get, sizeof get, " %%%ds %%%ds", sizeof host-1, sizeof server-1);
+ sscanf(string, get, host, server);
if (!putToFile) {
filePtr = stdout;
} else {
@@ -824,6 +833,7 @@
register char *option;
{
char type[NAME_LEN];
+ char get[40];
char *ptr;
int tmp;
@@ -854,7 +864,8 @@
} else if (strncmp(option, "do", 2) == 0) { /* domain */
ptr = strchr(option, '=');
if (ptr != NULL) {
- sscanf(++ptr, "%s", _res.defdname);
+ snprintf(get, sizeof get, "%%%ds", sizeof _res.defdname-1);
+ sscanf(++ptr, get, _res.defdname);
res_re_init();
}
} else if (strncmp(option, "deb", 1) == 0) { /* debug */
@@ -880,13 +891,15 @@
strncmp(option, "ty", 2) == 0) { /* type */
ptr = strchr(option, '=');
if (ptr != NULL) {
- sscanf(++ptr, "%s", type);
+ snprintf(get, sizeof get, "%%%ds", sizeof type-1);
+ sscanf(++ptr, get, type);
queryType = StringToType(type, queryType, stderr);
}
} else if (strncmp(option, "cl", 2) == 0) { /* query class */
ptr = strchr(option, '=');
if (ptr != NULL) {
- sscanf(++ptr, "%s", type);
+ snprintf(get, sizeof get, "%%%ds", sizeof type-1);
+ sscanf(++ptr, get, type);
queryClass = StringToClass(type, queryClass, stderr);
}
} else if (strncmp(option, "rec", 3) == 0) { /* recurse */
@@ -904,7 +917,8 @@
} else if (strncmp(option, "ro", 2) == 0) { /* root */
ptr = strchr(option, '=');
if (ptr != NULL) {
- sscanf(++ptr, "%s", rootServerName);
+ snprintf(get, sizeof get, "%%%ds", sizeof rootServerName-1);
+ sscanf(++ptr, get, rootServerName);
}
} else if (strncmp(option, "sea", 3) == 0) { /* search list */
_res.options |= RES_DNSRCH;
@@ -967,6 +981,7 @@
int n;
(void)strncpy(_res.defdname, cp, sizeof(_res.defdname) - 1);
+ _res.defdname[sizeof(_res.defdname) - 1] = '\0';
if ((cp = strchr(_res.defdname, '\n')) != NULL)
*cp = '\0';
/*
@@ -1111,8 +1126,9 @@
if ((cp = getenv("HOME")) != NULL &&
(strlen(cp) + strlen(_PATH_NSLOOKUPRC)) < sizeof(buf)) {
- (void) strcpy(buf, cp);
- (void) strcat(buf, _PATH_NSLOOKUPRC);
+ (void) strncpy(buf, cp, sizeof buf - 1);
+ buf[sizeof buf - 1] = '\0';
+ (void) strncat(buf, _PATH_NSLOOKUPRC, sizeof buf - strlen(buf) - 1);
if ((fp = fopen(buf, "r")) != NULL) {
while (fgets(buf, sizeof(buf), fp) != NULL) {
Index: nslookup/subr.c
===================================================================
RCS file: /cvs/src/usr.sbin/named/nslookup/subr.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- subr.c 1997/03/12 10:42:51 1.2
+++ subr.c 1998/08/30 03:39:21 1.3
@@ -343,6 +343,7 @@
{
char *redirect;
FILE *tmpPtr;
+ char get[80];
/*
* Open an output file if we see '>' or >>'.
@@ -354,10 +355,12 @@
return(NULL);
}
if (redirect[1] == '>') {
- sscanf(redirect, ">> %s", file);
+ snprintf(get, sizeof get, ">> %%%ds", sizeof file-1);
+ sscanf(redirect, get, file);
tmpPtr = fopen(file, "a+");
} else {
- sscanf(redirect, "> %s", file);
+ snprintf(get, sizeof get, "> %%%ds", sizeof file-1);
+ sscanf(redirect, get, file);
tmpPtr = fopen(file, "w");
}
bind 8.1.2 version:
diff -ru org/bind-8.1.2/bin/dig/dig.c bind-8.1.2/bin/dig/dig.c
--- org/bind-8.1.2/bin/dig/dig.c Thu Mar 19 20:30:18 1998
+++ bind-8.1.2/bin/dig/dig.c Tue Sep 1 17:03:21 1998
@@ -745,9 +745,11 @@
static int
SetOption(const char *string) {
char option[NAME_LEN], type[NAME_LEN], *ptr;
+ char get[80];
int i;
- i = sscanf(string, " %s", option);
+ snprintf(get, sizeof(get), " %%%ds", sizeof(option-1));
+ i = sscanf(string, get, option);
if (i != 1) {
fprintf(stderr, ";*** Invalid option: %s\n", option);
return (ERROR);
@@ -779,8 +781,10 @@
_res.options &= ~RES_DNSRCH;
} else if (strncmp(option, "do", 2) == 0) { /* domain */
ptr = strchr(option, '=');
- if (ptr != NULL)
- sscanf(++ptr, "%s", _res.defdname);
+ if (ptr != NULL) {
+ snprintf(get, sizeof(get),"%%%ds", sizeof(_res.defdname));
+ sscanf(++ptr, get, _res.defdname);
+ }
} else if (strncmp(option, "ti", 2) == 0) { /* timeout */
ptr = strchr(option, '=');
if (ptr != NULL)
diff -ru org/bind-8.1.2/bin/nslookup/list.c bind-8.1.2/bin/nslookup/list.c
--- org/bind-8.1.2/bin/nslookup/list.c Tue Nov 18 01:32:33 1997
+++ bind-8.1.2/bin/nslookup/list.c Tue Sep 1 17:09:09 1998
@@ -152,13 +152,16 @@
ListHostsByType(char *string, int putToFile) {
char *namePtr, name[NAME_LEN], option[NAME_LEN];
int i, qtype, result;
+ char get[80];
/*
* Parse the command line. It maybe of the form "ls -t domain"
* or "ls -t type domain".
*/
- i = sscanf(string, " ls -t %s %s", option, name);
+ snprintf(get, sizeof get, " ls -t %%%ds %%%ds", sizeof option-1,
+ sizeof name-1);
+ i = sscanf(string, get, option, name);
if (putToFile && i == 2 && name[0] == '>')
i--;
if (i == 2) {
@@ -183,12 +186,15 @@
ListHosts(char *string, int putToFile) {
char *namePtr, name[NAME_LEN], option[NAME_LEN];
int i, qtype, result;
+ char get[80];
/*
* Parse the command line. It maybe of the form "ls domain",
* "ls -X domain".
*/
- i = sscanf(string, " ls %s %s", option, name);
+ snprintf(get, sizeof get, " ls -t %%%ds %%%ds", sizeof option-1,
+ sizeof name-1);
+ i = sscanf(string, get, option, name);
if (putToFile && i == 2 && name[0] == '>')
i--;
if (i == 2) {
@@ -481,8 +487,10 @@
{
char file[PATH_MAX];
char command[PATH_MAX];
+ char get[80];
- sscanf(string, " view %s", file);
+ snprintf(get, sizeof get, " view %%%ds", sizeof file-1);
+ sscanf(string, get, file);
(void)sprintf(command, "grep \"^ \" %s | sort | %s", file, pager);
system(command);
}
@@ -516,6 +524,7 @@
int lastc;
char name[NAME_LEN];
char file[NAME_LEN];
+ char get[80];
/*
* We need a valid current host info to get an inet address.
@@ -525,7 +534,8 @@
return (ERROR);
}
- if (sscanf(string, " finger %s", name) == 1) {
+ snprintf(get, sizeof get, " finger %%%ds", sizeof name-1);
+ if (sscanf(string, get, name) == 1) {
if (putToFile && (name[0] == '>')) {
name[0] = '\0';
}
diff -ru org/bind-8.1.2/bin/nslookup/main.c bind-8.1.2/bin/nslookup/main.c
--- org/bind-8.1.2/bin/nslookup/main.c Fri Apr 25 02:27:18 1997
+++ bind-8.1.2/bin/nslookup/main.c Tue Sep 1 21:15:25 1998
@@ -464,6 +464,7 @@
char newServer[NAME_LEN];
int result;
int i;
+ char get[40];
/*
* Parse the command line. It maybe of the form "server name",
@@ -471,12 +472,15 @@
*/
if (local) {
- i = sscanf(string, " lserver %s", newServer);
+ snprintf(get, sizeof get, "lserver %%%ds", sizeof newServer-1);
+ i = sscanf(string, get, newServer);
} else {
- i = sscanf(string, " server %s", newServer);
+ snprintf(get, sizeof get, "server %%%ds", sizeof newServer-1);
+ i = sscanf(string, get, newServer);
}
if (i != 1) {
- i = sscanf(string, " %s", newServer);
+ snprintf(get, sizeof get, " %%%ds", sizeof newServer-1);
+ i = sscanf(string, get, newServer);
if (i != 1) {
fprintf(stderr,"SetDefaultServer: invalid name: %s\n", string);
return(ERROR);
@@ -667,6 +671,7 @@
char host[NAME_LEN];
char file[PATH_MAX];
int result;
+ char get[80];
/*
* Invalidate the current host information to prevent Finger=20
@@ -681,7 +686,8 @@
*
*/
- sscanf(string, " %s", host); /* removes white space */
+ snprintf(get, sizeof get, " %%%ds", sizeof host-1);
+ sscanf(string, get, host); /* removes white space */
if (!putToFile) {
filePtr = stdout;
} else {
@@ -738,11 +744,13 @@
char host[NAME_LEN];
char server[NAME_LEN];
int result;
+ char get[80];
static HostInfo serverInfo;
curHostValid = FALSE;
- sscanf(string, " %s %s", host, server);
+ snprintf(get, sizeof get, " %%%ds %%%ds", sizeof host-1, sizeof server=
-1);
+ sscanf(string, get, host, server);
if (!putToFile) {
filePtr = stdout;
} else {
@@ -823,6 +831,7 @@
char type[NAME_LEN];
char *ptr;
int tmp;
+ char get[40];
while (isspace(*option))
++option;
@@ -851,7 +860,8 @@
} else if (strncmp(option, "do", 2) == 0) { /* domain */
ptr = strchr(option, '=');
if (ptr != NULL) {
- sscanf(++ptr, "%s", _res.defdname);
+ snprintf(get, sizeof get, "%%%ds", sizeof _res.defdname-1);
+ sscanf(++ptr, get, _res.defdname);
res_re_init();
}
} else if (strncmp(option, "deb", 1) == 0) { /* debug */
@@ -877,13 +887,15 @@
strncmp(option, "ty", 2) == 0) { /* type */
ptr = strchr(option, '=');
if (ptr != NULL) {
- sscanf(++ptr, "%s", type);
+ snprintf(get, sizeof get, "%%%ds", sizeof type-1);
+ sscanf(++ptr, get, type);
queryType = StringToType(type, queryType, stderr);
}
} else if (strncmp(option, "cl", 2) == 0) { /* query class */
ptr = strchr(option, '=');
if (ptr != NULL) {
- sscanf(++ptr, "%s", type);
+ snprintf(get, sizeof get, "%%%ds", sizeof type-1);
+ sscanf(++ptr, get, type);
queryClass = StringToClass(type, queryClass, stderr);
}
} else if (strncmp(option, "rec", 3) == 0) { /* recurse */
@@ -901,7 +913,8 @@
} else if (strncmp(option, "ro", 2) == 0) { /* root */
ptr = strchr(option, '=');
if (ptr != NULL) {
- sscanf(++ptr, "%s", rootServerName);
+ snprintf(get, sizeof get, "%%%ds", sizeof rootServerName-1);
+ sscanf(++ptr, get, rootServerName);
}
} else if (strncmp(option, "sea", 3) == 0) { /* search list */
_res.options |= RES_DNSRCH;
@@ -964,6 +977,7 @@
int n;
(void)strncpy(_res.defdname, cp, sizeof(_res.defdname) - 1);
+ _res.defdname[sizeof(_res.defdname) - 1] = '\0';
if ((cp = strchr(_res.defdname, '\n')) != NULL)
*cp = '\0';
/*
@@ -1109,8 +1123,9 @@
if ((cp = getenv("HOME")) != NULL &&
(strlen(cp) + strlen(_PATH_NSLOOKUPRC)) < sizeof(buf)) {
- (void) strcpy(buf, cp);
- (void) strcat(buf, _PATH_NSLOOKUPRC);
+ (void) strncpy(buf, cp, sizeof buf - 1);
+ buf[sizeof buf - 1] = '\0';
+ (void) strncat(buf, _PATH_NSLOOKUPRC, sizeof buf - strlen(buf) - 1);
if ((fp = fopen(buf, "r")) != NULL) {
while (fgets(buf, sizeof(buf), fp) != NULL) {
diff -ru org/bind-8.1.2/bin/nslookup/subr.c bind-8.1.2/bin/nslookup/subr.c
--- org/bind-8.1.2/bin/nslookup/subr.c Fri Apr 25 02:27:19 1997
+++ bind-8.1.2/bin/nslookup/subr.c Tue Sep 1 21:14:39 1998
@@ -341,6 +341,7 @@
{
char *redirect;
FILE *tmpPtr;
+ char get[80];
/*
* Open an output file if we see '>' or >>'.
@@ -352,10 +353,12 @@
return(NULL);
}
if (redirect[1] == '>') {
- sscanf(redirect, ">> %s", file);
+ snprintf(get, sizeof get, ">> %%%ds", sizeof file-1);
+ sscanf(redirect, get, file);
tmpPtr = fopen(file, "a+");
} else {
- sscanf(redirect, "> %s", file);
+ snprintf(get, sizeof get, "> %%%ds", sizeof file-1);
+ sscanf(redirect, get, file);
tmpPtr = fopen(file, "w");
}