Für ein CTF musste ich letztens einen Netfilter bauen, der ein- und ausgehende IP-Pakete patcht. Genauer gesagt mussten Daten in TCP-Header geändert werden (mehrere Reserved-Bits mussten gesetzt werden).
Nachdem ich hoffnungslos versucht hab, irgendwelche random TCP/IP-Userspace-Stacks mit Raw-Sockets oder TUN/TAP-Devices zum Laufen zu bringen, war die “einfache” Lösung ein Netfilter-Kernelmodul. Da man dazu immer mal wieder verschiedene Varianten findet und 90% der Tech-Blogs da draußen nicht in der Lage sind, Zeilenumbrüche und Quotes ordentlich zu formatieren, hier eine “einfache” Lösung (basiert teilweise auf dieser):
ultra_hack.c
:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
MODULE_LICENSE("GPL-3.0");
MODULE_AUTHOR("Ultra Hacker 3000");
MODULE_DESCRIPTION("Super-TCP-Hack");
static unsigned int hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
if (!skb)
return NF_ACCEPT;
struct iphdr *iph = ip_hdr(skb);
if (iph->protocol != IPPROTO_TCP)
return NF_ACCEPT;
struct tcphdr *tcp_header = tcp_hdr(skb);
tcp_header->res1 = 0b1010;
printk(KERN_DEBUG "ultra hack is hacking.\n");
return NF_ACCEPT;
}
static struct nf_hook_ops nf_hook_data = {
.hook = hook_func,
.hooknum = NF_INET_POST_ROUTING,
.pf = PF_INET,
.priority = NF_IP_PRI_FIRST,
};
static int __init init_function(void)
{
printk(KERN_INFO "ultra hack module is loading.\n");
nf_register_net_hook(&init_net, &nf_hook_data);
return 0;
}
static void __exit exit_function(void)
{
printk(KERN_INFO "ultra hack module is unloading.\n");
nf_unregister_net_hook(&init_net, &nf_hook_data);
}
module_init(init_function);
module_exit(exit_function);
Ein paar Anmerkungen:
PF_INET
steht für IPv4- die
hooknum
gibt die Stelle an, an der der Filter eingesetzt wird (das kann man noch Pre-Routing machen etc, einfach nach anderen Konstanten schauen). - Die Signatur der
hook_func
ändert sich gerne mal bei Kernel-Updates. Die aus dem Beispiel funktioniert für5.11.0
.
Hier noch die Makefile dazu:
obj-m := ultra_hack.o
LKM-objs += ultra_hack.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
rm -r -f *.mod.c .*.cmd *.symvers *.o
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
Zum Laden, Entfernen etc:
# kompilieren
make all
# laden
sudo insmod ultra_hack.ko
# schauen, ob es da ist
lsmod
# schauen, ob printk-Nachrichten auftauchen
sudo dmesg -w
# entfernen
sudo rmmod ultra_hack
Man kann das auch mit nfqueue und Python machen.