diff --git a/extensions/libxt_TARPIT.c b/extensions/libxt_TARPIT.c index da6307e..59c190f 100644 --- a/extensions/libxt_TARPIT.c +++ b/extensions/libxt_TARPIT.c @@ -1,34 +1,120 @@ /* * "TARPIT" target extension to iptables - * this file is in the Public Domain + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License; either + * version 2 of the License, or any later version, as published by the + * Free Software Foundation. */ +#include +#include #include #include +#include #include +#include +#include "xt_TARPIT.h" #include "compat_user.h" +enum { + F_TARPIT = 1 << 0, + F_HONEYPOT = 1 << 1, + F_RESET = 1 << 2, +}; + +static const struct option tarpit_tg_opts[] = { + {.name = "tarpit", .has_arg = false, .val = 't'}, + {.name = "honeypot", .has_arg = false, .val = 'h'}, + {.name = "reset", .has_arg = false, .val = 'r'}, + {NULL}, +}; + static void tarpit_tg_help(void) { - printf("TARPIT takes no options\n\n"); + printf( + "TARPIT target options:\n" + " --tarpit Enable classic 0-window tarpit (default)\n" + " --honeypot Enable honeypot option\n" + " --reset Enable inline resets\n"); } static int tarpit_tg_parse(int c, char **argv, int invert, unsigned int *flags, const void *entry, struct xt_entry_target **target) { - return 0; + struct xt_tarpit_tginfo *info = (void *)(*target)->data; + + switch (c) { + case 't': + info->variant = XTTARPIT_TARPIT; + *flags |= F_TARPIT; + return true; + case 'h': + info->variant = XTTARPIT_HONEYPOT; + *flags |= F_HONEYPOT; + return true; + case 'r': + info->variant = XTTARPIT_RESET; + *flags |= F_RESET; + return true; + } + return false; } static void tarpit_tg_check(unsigned int flags) { + if (flags == (F_TARPIT | F_HONEYPOT | F_RESET)) + xtables_error(PARAMETER_PROBLEM, + "TARPIT: only one action can be used at a time"); +} + +static void tarpit_tg_print(const void *ip, + const struct xt_entry_target *target, int numeric) +{ + const struct xt_tarpit_tginfo *info = (void *)target->data; + + switch (info->variant) { + case XTTARPIT_HONEYPOT: + printf(" honeypot mode "); + break; + case XTTARPIT_RESET: + printf(" reset mode "); + break; + default: + printf(" tarpit mode "); + break; + } +} + +static void tarpit_tg_save(const void *ip, + const struct xt_entry_target *target) +{ + const struct xt_tarpit_tginfo *info = (const void *)target->data; + + switch (info->variant) { + case XTTARPIT_TARPIT: + printf(" --tarpit "); + break; + case XTTARPIT_HONEYPOT: + printf(" --honeypot "); + break; + case XTTARPIT_RESET: + printf(" --reset "); + break; + } } static struct xtables_target tarpit_tg_reg = { .version = XTABLES_VERSION, .name = "TARPIT", .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_tarpit_tginfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tarpit_tginfo)), .help = tarpit_tg_help, .parse = tarpit_tg_parse, .final_check = tarpit_tg_check, + .print = tarpit_tg_print, + .save = tarpit_tg_save, + .extra_opts = tarpit_tg_opts, }; static __attribute__((constructor)) void tarpit_tg_ldr(void) diff --git a/extensions/libxt_TARPIT.man b/extensions/libxt_TARPIT.man index 2a760d5..4f958c5 100644 --- a/extensions/libxt_TARPIT.man +++ b/extensions/libxt_TARPIT.man @@ -1,14 +1,38 @@ Captures and holds incoming TCP connections using no local per-connection -resources. Connections are accepted, but immediately switched to the persist -state (0 byte window), in which the remote side stops sending data and asks to -continue every 60-240 seconds. Attempts to close the connection are ignored, -forcing the remote side to time out the connection in 12-24 minutes. - +resources. +.PP +TARPIT only works at the TCP level, and is totally application agnostic. This +module will answer a TCP request and play along like a listening server, but +aside from sending an ACK or RST, no data is sent. Incoming packets are ignored +and dropped. The attacker will terminate the session eventually. This module +allows the initial packets of an attack to be captured by other software for +inspection. In most cases this is sufficient to determine the nature of the +attack. +.PP This offers similar functionality to LaBrea but does not require dedicated hardware or IPs. Any TCP port that you would normally DROP or REJECT can instead become a tarpit. - +.TP +\fB\-\-tarpit\fP +This mode completes a connection with the attacker but limits the window size +to 0, thus keeping the attacker waiting long periods of time. While he is +maintaining state of the connection and trying to continue every 60-240 +seconds, we keep none, so it is very lightweight. Attempts to close the +connection are ignored, forcing the remote side to time out the connection in +12-24 minutes. This mode is the default. +.TP +\fB\-\-honeypot\fP +This mode completes a connection with the attacker, but signals a normal window +size, so that the remote side will attempt to send data, often with some very +nasty exploit attempts. We can capture these packets for decoding and further +analysis. The module does not send any data, so if the remote expects an +application level response, the game is up. +.TP +\fB\-\-reset\fP +This mode is handy because we can send an inline RST (reset) from userspace. It +has no other function. +.PP To tarpit connections to TCP port 80 destined for the current machine: .IP \-A INPUT \-p tcp \-m tcp \-\-dport 80 \-j TARPIT @@ -30,4 +54,6 @@ port while using conntrack, you could: .IP \-t raw \-A PREROUTING \-p tcp \-\-dport 6667 \-j NOTRACK .IP +\-A INPUT \-p tcp \-\-dport 6667 \-j NFLOG +.IP \-A INPUT \-p tcp \-\-dport 6667 \-j TARPIT diff --git a/extensions/xt_TARPIT.Kconfig b/extensions/xt_TARPIT.Kconfig index 69ae7a2..4884566 100644 --- a/extensions/xt_TARPIT.Kconfig +++ b/extensions/xt_TARPIT.Kconfig @@ -13,4 +13,10 @@ config NETFILTER_XT_TARGET_TARPIT This offers similar functionality to LaBrea , but does not require dedicated hardware or IPs. Any TCP port that you would normally DROP or REJECT - can instead become a tar pit. + can instead become a tar pit or honeypot. All 3 modes may be used + in iptables rules interchangably and simultaneously. + + A honeypot option is available which will answer connections normally + and allow the remote to send data packets that may be captured in a + pcap for later analysis. A reset mode is also available that will only + send an inline reset (RST). diff --git a/extensions/xt_TARPIT.c b/extensions/xt_TARPIT.c index 16887d9..d807180 100644 --- a/extensions/xt_TARPIT.c +++ b/extensions/xt_TARPIT.c @@ -48,12 +48,15 @@ #include #include #include "compat_xtables.h" +#include "xt_TARPIT.h" -static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook) +static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook, + unsigned int mode) { struct tcphdr _otcph, *oth, *tcph; unsigned int addr_type = RTN_UNSPEC; struct sk_buff *nskb; + const struct iphdr *oldhdr; struct iphdr *niph; u_int16_t tmp; @@ -66,16 +69,28 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook) if (oth == NULL) return; - /* No replies for RST, FIN or !SYN,!ACK */ - if (oth->rst || oth->fin || (!oth->syn && !oth->ack)) - return; - - /* Rate-limit replies to !SYN,ACKs */ -#if 0 - if (!oth->syn && oth->ack) - if (!xrlim_allow(rt_dst(ort), HZ)) + if (mode == XTTARPIT_TARPIT) { + /* No replies for RST, FIN or !SYN,!ACK */ + if (oth->rst || oth->fin || (!oth->syn && !oth->ack)) return; +#if 0 + /* Rate-limit replies to !SYN,ACKs */ + if (!oth->syn && oth->ack) + if (!xrlim_allow(rt_dst(ort), HZ)) + return; #endif + } else if (mode == XTTARPIT_HONEYPOT) { + /* Do not answer any resets regardless of combination */ + if (oth->rst || oth->seq == 0xDEADBEEF) + return; + } else if (mode == XTTARPIT_RESET) { + tcph->window = 0; + tcph->ack = false; + tcph->syn = false; + tcph->rst = true; + tcph->seq = oth->ack_seq; + tcph->ack_seq = oth->seq; + } /* Check checksum. */ if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) @@ -102,6 +117,7 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook) skb_shinfo(nskb)->gso_type = 0; #endif + oldhdr = ip_hdr(oldskb); tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); /* Swap source and dest */ @@ -115,24 +131,63 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook) tcph->doff = sizeof(struct tcphdr) / 4; skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); niph->tot_len = htons(nskb->len); - - /* Use supplied sequence number or make a new one */ - tcph->seq = oth->ack ? oth->ack_seq : 0; - - /* Our SYN-ACKs must have a >0 window */ - tcph->window = (oth->syn && !oth->ack) ? htons(5) : 0; tcph->urg_ptr = 0; - /* Reset flags */ ((u_int8_t *)tcph)[13] = 0; - if (oth->syn && oth->ack) { - tcph->rst = true; - tcph->ack_seq = false; - } else { - tcph->syn = oth->syn; - tcph->ack = 1; - tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn); + if (mode == XTTARPIT_TARPIT) { + /* Use supplied sequence number or make a new one */ + tcph->seq = oth->ack ? oth->ack_seq : 0; + + /* Our SYN-ACKs must have a >0 window */ + tcph->window = (oth->syn && !oth->ack) ? htons(5) : 0; + if (oth->syn && oth->ack) { + tcph->rst = true; + tcph->ack_seq = false; + } else { + tcph->syn = oth->syn; + tcph->ack = true; + tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn); + } + } else if (mode == XTTARPIT_HONEYPOT) { + /* Send a reset to scanners. They like that. */ + if (oth->syn && oth->ack) { + tcph->window = 0; + tcph->ack = false; + tcph->psh = true; + tcph->ack_seq = 0xdeadbeef; /* see if they ack it */ + tcph->seq = oth->ack_seq; + tcph->rst = true; + } + /* SYN > SYN-ACK */ + if (oth->syn && !oth->ack) { + tcph->syn = true; + tcph->ack = true; + tcph->window = oth->window; + tcph->ack_seq = oth->seq; + tcph->seq = htonl(net_random() | ~oth->seq); + } + /* ACK > ACK */ + if (oth->ack && !oth->fin && !oth->syn) { + tcph->syn = false; + tcph->ack = true; + tcph->window = oth->window & + ((net_random() & 0x1f) - 0xf); + tcph->ack_seq = htonl(ntohl(oth->seq) + 1); + tcph->seq = oth->ack_seq; + } + /* + * FIN > RST. + * We cannot terminate gracefully so just be abrupt. + */ + if (oth->fin) { + tcph->window = 0; + tcph->seq = oth->ack_seq; + tcph->ack_seq = oth->ack_seq; + tcph->fin = false; + tcph->ack = false; + tcph->rst = true; + } } /* Adjust TCP checksum */ @@ -149,7 +204,10 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook) /* Set DF, id = 0 */ niph->frag_off = htons(IP_DF); - niph->id = 0; + if (mode == XTTARPIT_TARPIT) + niph->id = 0; + else if (mode == XTTARPIT_HONEYPOT) + niph->id = ~oldhdr->id + 1; #ifdef CONFIG_BRIDGE_NETFILTER if (hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL && @@ -193,6 +251,7 @@ tarpit_tg(struct sk_buff **pskb, const struct xt_action_param *par) const struct sk_buff *skb = *pskb; const struct iphdr *iph = ip_hdr(skb); const struct rtable *rt = skb_rtable(skb); + const struct xt_tarpit_tginfo *info = par->targinfo; /* Do we have an input route cache entry? (Not in PREROUTING.) */ if (rt == NULL) @@ -218,19 +277,20 @@ tarpit_tg(struct sk_buff **pskb, const struct xt_action_param *par) if (iph->frag_off & htons(IP_OFFSET)) return NF_DROP; - tarpit_tcp(*pskb, par->hooknum); + tarpit_tcp(*pskb, par->hooknum, info->variant); return NF_DROP; } static struct xt_target tarpit_tg_reg __read_mostly = { - .name = "TARPIT", - .revision = 0, - .family = NFPROTO_IPV4, - .table = "filter", - .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD), - .proto = IPPROTO_TCP, - .target = tarpit_tg, - .me = THIS_MODULE, + .name = "TARPIT", + .revision = 0, + .family = NFPROTO_IPV4, + .table = "filter", + .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD), + .proto = IPPROTO_TCP, + .target = tarpit_tg, + .targetsize = sizeof(struct xt_tarpit_tginfo), + .me = THIS_MODULE, }; static int __init tarpit_tg_init(void) diff --git a/extensions/xt_TARPIT.h b/extensions/xt_TARPIT.h new file mode 100644 index 0000000..5fb7e9f --- /dev/null +++ b/extensions/xt_TARPIT.h @@ -0,0 +1,14 @@ +#ifndef _LINUX_NETFILTER_XT_TARPIT_H +#define _LINUX_NETFILTER_XT_TARPIT_H 1 + +enum xt_tarpit_target_variant { + XTTARPIT_TARPIT, + XTTARPIT_HONEYPOT, + XTTARPIT_RESET, +}; + +struct xt_tarpit_tginfo { + uint8_t variant; +}; + +#endif /* _LINUX_NETFILTER_XT_TARPIT_H */