/*
* "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)
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
.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
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).
#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;
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))
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 */
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 */
/* 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 &&
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)
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)
--- /dev/null
+#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 */