mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-06 12:45:13 +02:00
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:

committed by
Jan Engelhardt

parent
1a5c079e6b
commit
fa1348455d
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
@@ -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).
|
||||||
|
@@ -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
14
extensions/xt_TARPIT.h
Normal 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 */
|
Reference in New Issue
Block a user