xt_TARPIT: honeypot and reset modes

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.
This commit is contained in:
Martin Barrow Cliff
2011-05-27 18:53:02 -04:00
committed by Jan Engelhardt
parent 1a5c079e6b
commit fa1348455d
5 changed files with 235 additions and 43 deletions

View File

@@ -1,34 +1,120 @@
/* /*
* "TARPIT" target extension to iptables * "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 <stdio.h>
#include <getopt.h> #include <getopt.h>
#include <string.h>
#include <xtables.h> #include <xtables.h>
#include <linux/netfilter/x_tables.h>
#include "xt_TARPIT.h"
#include "compat_user.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) 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, static int tarpit_tg_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target) 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) 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 = { static struct xtables_target tarpit_tg_reg = {
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
.name = "TARPIT", .name = "TARPIT",
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_tarpit_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_tarpit_tginfo)),
.help = tarpit_tg_help, .help = tarpit_tg_help,
.parse = tarpit_tg_parse, .parse = tarpit_tg_parse,
.final_check = tarpit_tg_check, .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) static __attribute__((constructor)) void tarpit_tg_ldr(void)

View File

@@ -1,14 +1,38 @@
Captures and holds incoming TCP connections using no local per-connection Captures and holds incoming TCP connections using no local per-connection
resources. Connections are accepted, but immediately switched to the persist resources.
state (0 byte window), in which the remote side stops sending data and asks to .PP
continue every 60-240 seconds. Attempts to close the connection are ignored, TARPIT only works at the TCP level, and is totally application agnostic. This
forcing the remote side to time out the connection in 12-24 minutes. 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 This offers similar functionality to LaBrea
<http://www.hackbusters.net/LaBrea/> but does not require dedicated hardware or <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 IPs. Any TCP port that you would normally DROP or REJECT can instead become a
tarpit. 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: To tarpit connections to TCP port 80 destined for the current machine:
.IP .IP
\-A INPUT \-p tcp \-m tcp \-\-dport 80 \-j TARPIT \-A INPUT \-p tcp \-m tcp \-\-dport 80 \-j TARPIT
@@ -30,4 +54,6 @@ port while using conntrack, you could:
.IP .IP
\-t raw \-A PREROUTING \-p tcp \-\-dport 6667 \-j NOTRACK \-t raw \-A PREROUTING \-p tcp \-\-dport 6667 \-j NOTRACK
.IP .IP
\-A INPUT \-p tcp \-\-dport 6667 \-j NFLOG
.IP
\-A INPUT \-p tcp \-\-dport 6667 \-j TARPIT \-A INPUT \-p tcp \-\-dport 6667 \-j TARPIT

View File

@@ -13,4 +13,10 @@ config NETFILTER_XT_TARGET_TARPIT
This offers similar functionality to LaBrea This offers similar functionality to LaBrea
<http://www.hackbusters.net/LaBrea/>, but does not require dedicated <http://www.hackbusters.net/LaBrea/>, but does not require dedicated
hardware or IPs. Any TCP port that you would normally DROP or REJECT 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).

View File

@@ -48,12 +48,15 @@
#include <net/route.h> #include <net/route.h>
#include <net/tcp.h> #include <net/tcp.h>
#include "compat_xtables.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; struct tcphdr _otcph, *oth, *tcph;
unsigned int addr_type = RTN_UNSPEC; unsigned int addr_type = RTN_UNSPEC;
struct sk_buff *nskb; struct sk_buff *nskb;
const struct iphdr *oldhdr;
struct iphdr *niph; struct iphdr *niph;
u_int16_t tmp; u_int16_t tmp;
@@ -66,16 +69,28 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook)
if (oth == NULL) if (oth == NULL)
return; return;
if (mode == XTTARPIT_TARPIT) {
/* No replies for RST, FIN or !SYN,!ACK */ /* No replies for RST, FIN or !SYN,!ACK */
if (oth->rst || oth->fin || (!oth->syn && !oth->ack)) if (oth->rst || oth->fin || (!oth->syn && !oth->ack))
return; return;
/* Rate-limit replies to !SYN,ACKs */
#if 0 #if 0
/* Rate-limit replies to !SYN,ACKs */
if (!oth->syn && oth->ack) if (!oth->syn && oth->ack)
if (!xrlim_allow(rt_dst(ort), HZ)) if (!xrlim_allow(rt_dst(ort), HZ))
return; return;
#endif #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. */ /* Check checksum. */
if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) 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; skb_shinfo(nskb)->gso_type = 0;
#endif #endif
oldhdr = ip_hdr(oldskb);
tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb));
/* Swap source and dest */ /* Swap source and dest */
@@ -115,25 +131,64 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook)
tcph->doff = sizeof(struct tcphdr) / 4; tcph->doff = sizeof(struct tcphdr) / 4;
skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr));
niph->tot_len = htons(nskb->len); niph->tot_len = htons(nskb->len);
tcph->urg_ptr = 0;
/* Reset flags */
((u_int8_t *)tcph)[13] = 0;
if (mode == XTTARPIT_TARPIT) {
/* Use supplied sequence number or make a new one */ /* Use supplied sequence number or make a new one */
tcph->seq = oth->ack ? oth->ack_seq : 0; tcph->seq = oth->ack ? oth->ack_seq : 0;
/* Our SYN-ACKs must have a >0 window */ /* Our SYN-ACKs must have a >0 window */
tcph->window = (oth->syn && !oth->ack) ? htons(5) : 0; 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) { if (oth->syn && oth->ack) {
tcph->rst = true; tcph->rst = true;
tcph->ack_seq = false; tcph->ack_seq = false;
} else { } else {
tcph->syn = oth->syn; tcph->syn = oth->syn;
tcph->ack = 1; tcph->ack = true;
tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn); 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 */ /* Adjust TCP checksum */
tcph->check = 0; tcph->check = 0;
@@ -149,7 +204,10 @@ static void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook)
/* Set DF, id = 0 */ /* Set DF, id = 0 */
niph->frag_off = htons(IP_DF); niph->frag_off = htons(IP_DF);
if (mode == XTTARPIT_TARPIT)
niph->id = 0; niph->id = 0;
else if (mode == XTTARPIT_HONEYPOT)
niph->id = ~oldhdr->id + 1;
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
if (hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL && 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 sk_buff *skb = *pskb;
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
const struct rtable *rt = skb_rtable(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.) */ /* Do we have an input route cache entry? (Not in PREROUTING.) */
if (rt == NULL) if (rt == NULL)
@@ -218,7 +277,7 @@ tarpit_tg(struct sk_buff **pskb, const struct xt_action_param *par)
if (iph->frag_off & htons(IP_OFFSET)) if (iph->frag_off & htons(IP_OFFSET))
return NF_DROP; return NF_DROP;
tarpit_tcp(*pskb, par->hooknum); tarpit_tcp(*pskb, par->hooknum, info->variant);
return NF_DROP; return NF_DROP;
} }
@@ -230,6 +289,7 @@ static struct xt_target tarpit_tg_reg __read_mostly = {
.hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD), .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD),
.proto = IPPROTO_TCP, .proto = IPPROTO_TCP,
.target = tarpit_tg, .target = tarpit_tg,
.targetsize = sizeof(struct xt_tarpit_tginfo),
.me = THIS_MODULE, .me = THIS_MODULE,
}; };

14
extensions/xt_TARPIT.h Normal file
View File

@@ -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 */