Usually, in every medium/high size company Network, there's a firewall conecting the corporative LAN/WAN to the Internet with a set of rules that only allows specific traffic, such as HTTP, HTTPS. FTP or POP3 / SMTP. A malicious internal user, could take advantage of these open ports, and use them to access other services (sending through them, other protocols). For example, he could set up a ssh server on the Internet, listening port 443, and configure the internal ssh client to access that port. Such an arrangement, makes virtually imposible for any administrator to detect the real nature of the traffic. The same applies if there is a proxy working to provide Internet access to the LAN. By using tools like proxytunnel, it is possible to establish a connection to server on the Internet, without being detected. This snort patch, based on "tcpstatflow" tool and written to be compiled with snort-2.6.1.1 using stream4 preprocessor, is designed with the purpose of fighting these tecniques, by detecting traffic that is not HTTP / HTTPS / FTP / SMTP, with a reasonable margin of error. It's based on the fact that these protocols present a huge asymmetry in the amount of data transmitted in one way and the oposite (within a single TCP connection). As an example, you could consider HTTP requests, where you have the browser sending a small packet with a GET command (and some extra overhead) and as a response, receives a web page, an image, or a download. The same asymmetry takes place in reverse, with SMTP. Your mail client sends your composition, and a small ACK is sent back from the server. Asymmetry. Keep that in mind. To apply this patch, you must: - Download snort source (snort-2.6.1.1.tar.gz) - Download snort patch (snort_covered_channels_detection.txt) - Apply the patch: patch -p0 20) + { + LogMessage("%s...\n", buf); + return; + } + } + + LogMessage("%s\n", buf); LogMessage(" State Protection: %d\n", s4data.state_protection); LogMessage(" Self preservation threshold: %d\n", s4data.sp_threshold); LogMessage(" Self preservation period: %d\n", s4data.sp_period); @@ -1065,6 +1092,7 @@ void ParseStream4Args(char *args) s4data.path_mtu = 1460; s4data.ttl_limit = STREAM4_TTL_LIMIT; s4data.asynchronous_link = 0; + s4data.detect_covered_channels = 0; s4data.flush_data_diff_size = 500; s4data.zero_flushed_packets = 0; s4data.flush_on_alert = 0; @@ -1079,6 +1107,12 @@ void ParseStream4Args(char *args) s4data.flush_base = STREAM4_FLUSH_BASE; s4data.flush_seed = getpid() + time(NULL); + /* Covered Channels target ports */ + s4data.covered_channels_ports[21] = 1; + s4data.covered_channels_ports[25] = 1; + s4data.covered_channels_ports[80] = 1; + s4data.covered_channels_ports[443] = 1; + #ifdef STREAM4_UDP /* Ports on which to do UDP sessions. * Derived from rules that have "flow" keyword @@ -1113,6 +1147,65 @@ void ParseStream4Args(char *args) { s4data.asynchronous_link = 1; } + else if(!strcasecmp(stoks[0], "detect_covered_channels")) + { + if(isdigit((int)stoks[1][0])) + { + s4data.detect_covered_channels = atol(stoks[1]); + } + else + { + LogMessage("WARNING %s(%d) => Bad threshold in config file, " + "defaulting to %d bytes\n", file_name, file_line, + COVERED_CHANNELD_THRESHOLD); + + s4data.detect_covered_channels = COVERED_CHANNELD_THRESHOLD; + } + } + else if(!strcasecmp(stoks[0], "covered_channels_ports")) + { + char **ports; + char *port; + int j = 0; + u_int32_t portnum; + + for(j = 0;j<65535;j++) + { + s4data.covered_channels_ports[j] = 0; + } + + j = 1; + + while(j < s_toks) + { + port = stoks[j]; + + if(isdigit((int)port[0])) + { + portnum = atoi(port); + + if(portnum > 65535) + { + FatalError("%s(%d) => Bad port list to " + "reassembler\n", file_name, file_line); + } + + s4data.covered_channels_ports[portnum] = 1; + } + else if(!strncasecmp(port, "all", 3)) + { + memset(&s4data.covered_channels_ports, 1, 65536); + } + else if(!strncasecmp(port, "default", 7)) + { + s4data.covered_channels_ports[21] = 1; + s4data.covered_channels_ports[80] = 1; + s4data.covered_channels_ports[443] = 1; + } + + j++; + } + } else if(!strcasecmp(stoks[0], "keepstats")) { s4data.track_stats_flag = STATS_HUMAN_READABLE; @@ -3328,6 +3421,31 @@ int UpdateState2(Session *ssn, Packet *p StreamSegmentAdd(talker, p->dsize); + /* Logic test: + * detect_covered_channels option is set in the config + * Packet direction is from client + * Server to client flow exists + * Port server is a possible target of covered channel (with asymetric behaviour) + * Alert was not send yet for this connection + * Bytes sended and received for this connnection + */ + if(s4data.detect_covered_channels && + direction == FROM_CLIENT && + &ssn->server && + s4data.covered_channels_ports[(ssn->server).port] && + (ssn->client).covered == 0 && + (ssn->client).bytes_sent > s4data.detect_covered_channels && + (ssn->server).bytes_sent > s4data.detect_covered_channels) { + SnortEventqAdd(GENERATOR_SPP_STREAM4, /* GID */ + STREAM4_COVERED_CHANNEL, /* SID */ + 1, /* Rev */ + 0, /* classification */ + 3, /* priority (low) */ + STREAM4_COVERED_CHANNEL_STR, /* msg string */ + 0); + (ssn->client).covered = 1; + } + if(talker->state == ESTABLISHED) { listener->win_size = ntohs(p->tcph->th_win);