]> git.ipfire.org Git - thirdparty/xtables-addons.git/commitdiff
xt_TARPIT: honeypot and reset modes
authorMartin Barrow Cliff <martinbarrowcliff@gmail.com>
Fri, 27 May 2011 22:53:02 +0000 (18:53 -0400)
committerJan Engelhardt <jengelh@medozas.de>
Tue, 31 May 2011 20:41:51 +0000 (22:41 +0200)
Honeypot mode attempts to maintain a normal connection for the purpose
of capturing payload packets.

Reset mode provides the ability to send a reset packet in lieu of
using the DROP or REJECT targets.

extensions/libxt_TARPIT.c
extensions/libxt_TARPIT.man
extensions/xt_TARPIT.Kconfig
extensions/xt_TARPIT.c
extensions/xt_TARPIT.h [new file with mode: 0644]

index da6307ec606ed978231add1ec240a96d37803053..59c190f8cd660dc2ba21e1d2eb4eaf85c2dae398 100644 (file)
 /*
  *     "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 <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <getopt.h>
+#include <string.h>
 #include <xtables.h>
+#include <linux/netfilter/x_tables.h>
+#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)
index 2a760d5d09dceb628ec23c746e5541fee38246ea..4f958c5a416e09b7b788186df8d6d02ce1d0f309 100644 (file)
@@ -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
 <http://www.hackbusters.net/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
index 69ae7a27198a7f19a55af74e6b9b549f1f1f1738..488456649001193442672f154615bab38b0be0de 100644 (file)
@@ -13,4 +13,10 @@ config NETFILTER_XT_TARGET_TARPIT
        This offers similar functionality to LaBrea
        <http://www.hackbusters.net/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).
index 16887d9d2821caffd3251297a15c9bc967a7e8f8..d807180363a084c64122fc9ea77831ceba905ec6 100644 (file)
 #include <net/route.h>
 #include <net/tcp.h>
 #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 (file)
index 0000000..5fb7e9f
--- /dev/null
@@ -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 */