mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-06 04:35:12 +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
|
||||
* 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)
|
||||
|
@@ -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
|
||||
|
@@ -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).
|
||||
|
@@ -48,12 +48,15 @@
|
||||
#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)
|
||||
|
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