Linux防火墙实验(iptables)Linux Firewall Exploration Lab

网络安全课程实验报告

《Linux Firewall Exploration Lab》

Overview

​   了解防火墙是如何工作的,并实现一个简化的数据包过滤防火墙。

​   防火墙有多种类型,在这个实验中,我们专注于数据包过滤器。数据包过滤器检查数据包,并根据防火墙规则决定是丢弃还是转发数据包。通常是无状态的,只根据该包中包含的信息来过滤每个包,而不注意一个包是否是现有流量的一部分。包过滤器通常组合使用包的源地址和目标地址、协议,对于TCP和UDP流量,还使用端口号。此外,学生们还将学习如何使用SSH隧道来绕过防火墙。

​    **防火墙(英语:Firewall)**是目前最重要的一种网络防护设备,是位于两个或多个设备间,实行网络间访问控制的硬件或软件。可以隔离网络,制定出不同区域之间的访问控制策略来控制不同信任程度区域间传送的数据流。

Lab Tasks

Task 1: Using Firewall

任务说明:

​   使用Linux的iptables软件,在两台虚拟机之间进行防火墙实验。

iptables介绍:

​   iptabels是与Linux内核集成的包过滤防火墙系统,软件的结构是
i p t a b l e s → T a b l e s → C h a i n s → R u l e s . iptables \to Tables \to Chains \to Rules. iptablesTablesChainsRules.
​    iptables有四种内建表table,分别是Filiter,NAT,Mangle,Raw。每种表内有多种链chain,每个链上可以定义多个规则rule。
在这里插入图片描述

​   规则(rules)其实就是网络管理员预定义的条件,规则一般的定义为“如果数据包头符合这样的条件,就这样处理这个数据包”。规则存储在内核空间的信息包过滤表中,这些规则分别指定了源地址、目的地址、传输协议(如TCP、UDP、ICMP)和服务类型(如HTTP、FTP和SMTP)等。当数据包与规则匹配时,iptables就根据规则所定义的方法来处理这些数据包,如放行(accept)、拒绝(reject)和丢弃(drop)等。

​   配置防火墙的 主要工作就是添加、修改和删除这些规则。

实验过程:

  • Prevent A from doing telnet to Machine B

    命令:

    sudo iptables -A OUTPUT -d 10.0.2.5 -p tcp --dport 23 -j DROP
    

   先在主机A上telnet 主机B 10.0.2.5,可以正常通信,然后添加一条rule,在OUTPUT链上,阻止A发送到B的端口号为23的tcp包,然后再次尝试telnet,连接失败。

在这里插入图片描述

  • Prevent B from doing telnet to Machine A

    命令:

    sudo iptables -A INPUT -s 10.0.2.5 -p tcp --dport 23 -j DROP
    

     先用B主机telnet A,可以正常和A通信,然后在A主机添加rule,在INPUT链上,阻止B主机访问A主机的23端口,再次尝试,telnet连接失败。

在这里插入图片描述

  • Prevent A from visiting an external web site. You can choose any web site that you like to block, but keep in mind, some web servers have multiple IP addresses

    命令:

    sudo iptables -A OUTPUT -d baidu.com -p tcp --dport 80/443 -j DROP
    

 我们尝试在A主机上添加rule,禁止主机A访问百度的网站,注意在iptables的命令中,目的地地址要填写baidu.com,而不是www.baidu.com:

  使用sudo iptables -t filter --list命令可以查看当前的表和链和rule的信息。

在这里插入图片描述

  打开浏览器尝试访问 baidu.com,可以看到连接超时:

在这里插入图片描述

Task 2: Implementing a Simple Firewall

任务说明:

​   学习使用LKM和Netfilter编写包过滤内核模块实现至少5条过滤规则。

Netfilter:

​   Netfilter 是一个由Linux 内核提供的框架,可以进行多种网络相关的自定义操作。Netfilter 在 Linux 内核中表现为一系列的hook, 并允许Linux 内核模块注册为回调函数,Linux内核模块通过回调函数操作网络报文。

​   Netfilter 在内核协议栈的包处理路径上提供了 5 个 hook 点,分别是:

enum nf_inet_hooks {
	NF_INET_PRE_ROUTING,
	NF_INET_LOCAL_IN,
	NF_INET_FORWARD,
	NF_INET_LOCAL_OUT,
	NF_INET_POST_ROUTING,
	NF_INET_NUMHOOKS
};

​ 这些hook点在内核处理包的路径上的位置如图所示:

在这里插入图片描述

​   netfilter的架构就是在整个网络流程的若干位置放置了一些检测点(HOOK),而在每个检测点上登记了一些处理函数进行处理。当有数据包经过 hook 点时, 就会调用相应的 handlers 进行处理。这就像有5个钓鱼台,在每个钓鱼台放了一个鱼钩(钩子函数),把经过的数据包钓上来处理。

​   当一个报文通过hook的时候,hook将会依据优先级调用钩子函数。钩子函数对数据包处理后,必须向netfilter框架返回以下返回值中的一个:

  • NF_ACCEPT: 继续正常处理此报文,即允许报文通过。

  • NF_DROP: 丢弃此报文,不再进行继续处理,即拒绝此报文。

  • NF_STOLEN: 取走这个报文,不再继续处理。

  • NF_QUEUE: 报文进行重新排队,可以将报文发到用户空间的程序,进行修改或者决定是拒绝或者允许。

  • NF_REPEAT: 报文重新调用hook。

​   如果我们想在对应的hook加入自己的钩子函数,需要使用如下函数:

//旧版内核的钩子函数注册方法
int nf_register_hook(struct nf_hook_ops *reg)

//新版内核的钩子函数注册方法,增加了网络命名空间,默认可以使用&init_net
void nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)

​   注册函数需要我们提供一个结构体nf_hook_ops,结构体的定义如下:

struct nf_hook_ops {
	nf_hookfn			*hook;	//需要注册的钩子函数
	struct net_device	*dev;	//网络设备
	void				*priv;	
	u_int8_t			pf;		//协议簇,对于ipv4而言,是PF_INET
	unsigned int		hooknum;//指明HOOK位置
	int			priority;		//优先级
};

​   我们注意到,钩子函数是一个nf_hookfn类型的函数指针,该函数有固定的形式的,钩子函数原型如下:

typedef unsigned int nf_hookfn(void *priv,	
			       struct sk_buff *skb,		// 网络数据的缓存栈
			       const struct nf_hook_state *state);

​   总结来说,就是自己写一个nf_hookfn类型的函数,然后填到一个nf_hook_ops结构体中,然后使用注册函数将该结构体注册到对应的hook点。

LKM

  • ​ LKM(Loadable Kernel Modules)“可加载内核模块程序”,这是一种区别于一般应用程序的系统级程序,它主要用于扩展linux的内核功能。LKM可以动态地加载到内存中,无须重新编译内核。由于LKM具有这样的特点,所以它经常被用于一些设备的驱动程序,例如声卡,网卡等。

  • ​ LKM没有main()函数,但是必须有以下两个函数:

    /*初始化函数*/ 
    int module_init(int) {... } 
    
    /*关闭函数*/
    void module_exit(int) {... } 
    

    ​    LKM编译之后可以得到一个二进制的可执行文件,然后我们就可以使用insmod <filename>命令将可执行文件加载到内存中(需要root权限)。还可以使用lsmod查看当前内存中的模块,卸载内存中的模块命令是 rmmod <file>

在这里插入图片描述

  • ​ 结合netfilter框架,我们就可以在module_init(int)函数中,完成钩子函数的注册,在module_exit(int)函数中完成钩子函数的注销。

实验过程:

参考如下博客编写代码:

  1. Linux Firewall Exploration Lab
  2. 利用netfilter抓包(二)抓包函数的实现
  3. 理解 Linux 下的 Netfilter/iptables
  • 规则一:

    ​   阻止B主机(10.0.2.5)通过telnet访问本机,需要在NF_INET_PRE_ROUTINGNF_INET_LOCAL_IN这两个hook点,添加钩子函数,如果数据包的是tcp,端口是23,源地址是10.0.2.5,那么就返回NF_DROP丢弃数据包。

    代码:

    ##############################################
    ## task2_1.c 
    ## LKM+netfilter 组织10.0.2.5向本机的telnet数据包
    ##############################################
    
    #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/in.h>
    #include <linux/tcp.h>
    #include <linux/if_ether.h>
    
    MODULE_LICENSE("GPL");  /*模块许可证明,描述内核模块的许可权限,必须*/
    MODULE_AUTHOR("zzp"); /*模块作者,可选*/
    MODULE_DESCRIPTION("task2 netfilter");/*模块说明,可选*/
    MODULE_VERSION("0.0.1");/*verison,可选*/
    
    static struct nf_hook_ops *hook_1 = NULL;	
    static struct nf_hook_ops *hook_2 = NULL;
    static struct nf_hook_ops *hook_3 = NULL;
    static struct nf_hook_ops *hook_4 = NULL;
    static struct nf_hook_ops *hook_5 = NULL; //了5个结构体,对应5个hook函数
    
    static unsigned int hookfunc_1(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
    {
      struct iphdr *iph;	//取出ip头
      struct tcphdr *tcph;	//取出tcp头
      if (!skb)
        return NF_ACCEPT;
      iph = ip_hdr(skb);
      tcph = tcp_hdr(skb);  // get tcph from iph
      unsigned char* saddr = (unsigned char*)&iph->saddr;	//源IP地址
      if (iph->protocol == IPPROTO_TCP && tcph->dest == htons(23) && 
        (int)saddr[0] == 10 && saddr[1] == 0 && saddr[2] == 2 && saddr[3] == 5) // filter的过滤条件 组织B telnet A
      { 
        printk(KERN_INFO "Drop telnet packet from 10.0.2.5\n");
        return NF_DROP;
      }
      return NF_ACCEPT;
    }
    void hook_1_reg(void)
    {
      hook_1 = (struct nf_hook_ops*)kcalloc(1, sizeof(struct nf_hook_ops), GFP_KERNEL);
      hook_1->hook      = (nf_hookfn*)hookfunc_1;       // hook函数指针 
      hook_1->hooknum   = NF_INET_PRE_ROUTING;     // hook点
      hook_1->pf        = PF_INET;                 // IPv4 
      hook_1->priority  = NF_IP_PRI_FIRST;         // hook函数的优先级,越小优先级越高
      nf_register_net_hook(&init_net, hook_1); //注册函数
    }
    
    void hook_1_unreg(void)
    {
      printk(KERN_INFO "Delete telnet filter from 10.0.2.5\n");
      nf_unregister_net_hook(&init_net, hook_1);
      kfree(hook_1);
    }
    
    ########################################################
    ## LKM函数(已经包括了5条规则的注册和注销,后续的代码中不再展示)
    ########################################################
    static int __init LKM_init(void)
    {
    	hook_1_reg();
        hook_2_reg();
        hook_3_reg();
        hook_4_reg();
        hook_5_reg();
    }
    
    static void __exit LKM_exit(void)
    {
    	hook_1_unreg();
        hook_2_unreg();
        hook_3_unreg();
        hook_4_unreg();
        hook_5_unreg();	
    }
    module_init(LKM_init);	// LKM初始化函数
    module_exit(LKM_exit);	// LKM关闭函数
    
    

  • ​ 使用Makefile进行编译,注意这里和手册提供的makefile略有不同,手册中的M=\$(PWD)在编译时会出错,参考stackoverflow:No rule to make target,将M=\$(PWD)修改为M=$(shell pwd)

    obj-m=task2_all.o
    all:
    	make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
    clean:
    	make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
    
    

    ​   然后再shell上使用sudo make命令,可以得到好几个文件,其中有一个task2_1.ko,再使用sudo insmod task2_1.ko命令将程序加载到内存中:

在这里插入图片描述

测试:

  ​ 在主机B上进行测试,注意在这之前需要删除任务一中通过iptables添加的过滤器,如图,删除任务一的过滤器之后,主机B上telnet可以连接A主机,然后当A主机将task2_1.ko加载到内存中后,B主机再使用telnet就不能通过telnet访问A了:

在这里插入图片描述

​   在主机A上使用dmesg命令可以查看log信息,就是我们printk的信息:

在这里插入图片描述

  • 规则二:

    ​   阻止主机通过telnet访问B(10.0.2.5),需要在NF_INET_POST_ROUTINGNF_INET_LOCAL_OUT这两个hook点,添加钩子函数,如果数据包的是tcp,端口是23,目的地址是10.0.2.5,那么就返回NF_DROP丢弃数据包。

    代码:

    static unsigned int hookfunc_2(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
    {
      struct iphdr *iph;
      struct tcphdr *tcph;
    
      if (!skb)
        return NF_ACCEPT;
    
      iph = ip_hdr(skb);
      tcph = tcp_hdr(skb);  // get tcph from iph
      unsigned char* daddr = (unsigned char*)&iph->daddr;
      if (iph->protocol == IPPROTO_TCP && tcph->dest == htons(23) && 
        (int)daddr[0] == 10 && daddr[1] == 0 && daddr[2] == 2 && daddr[3] == 5) // prevent A telnet B
      { 
        printk(KERN_INFO "Drop telnet packet to 10.0.2.5\n");
        return NF_DROP;
      }
      return NF_ACCEPT;
    }
    void hook_2_reg(void)
    {
      hook_2 = (struct nf_hook_ops*)kcalloc(1, sizeof(struct nf_hook_ops), GFP_KERNEL);
      /* Initialize netfilter hook */
      hook_2->hook      = (nf_hookfn*)hookfunc_2;  // hook function 
      hook_2->hooknum   = NF_INET_LOCAL_OUT;         // received packets 
      hook_2->pf        = PF_INET;                 // IPv4 */
      hook_2->priority  = NF_IP_PRI_FIRST;         // max hook priority 
      nf_register_net_hook(&init_net, hook_2);
    }
    void hook_2_unreg(void)
    {
      printk(KERN_INFO "Delete telnet filter to 10.0.2.5\n");
      nf_unregister_net_hook(&init_net, hook_2);
      kfree(hook_2);
    }
    

    运行结果:

      ​ 在主机A行telnet访问B,被阻止了:

在这里插入图片描述

   查看主机A的log信息,成功组织了A向B的telnet包:

在这里插入图片描述

其他规则就是简单重复,不再赘述,可以自定义规则,和包处理方法,实现更灵活的防火墙

Task 3: Evading Egress Filtering 使用SSH隧道绕过防火墙

​   在主机A上部署防火墙,①:drop所有通向主机B的telnet数据包,②:drop所有通向lol.qq.com(140.249.243.249)的数据包。使用ssh隧道,绕过防火墙。

​   iptables设置如下:

在这里插入图片描述

在这里插入图片描述

Task 3.a: Telnet to Machine B through the fifirewall

​   使用ssh隧道,命令如下:

#先在一个shell上开启ssh并连接到主机B
ssh -L 8000:10.0.2.5:23 seed@10.0.2.5

#然后再另一个shell上,telnet 本机的8000端口
telnet localhost 8000

​   这样所有发往本机8000端口的数据都会通过ssh发向10.0.2.5的23端口。

运行结果:

在这里插入图片描述

本机网卡抓包,可以看到有很多和8000端口交互的数据包:

在这里插入图片描述

外部网卡抓包,可以看到SSH在担当一个隧道作用:

在这里插入图片描述

Task 3.b: Connect to Facebook using SSH Tunnel.

​   建立动态ssh隧道:

ssh -D 8888 -C seed@10.0.2.5

​   所有发往本机8888端口的数据包都会通过ssh隧道发往10.0.2.5

运行结果:

​   建立隧道:
在这里插入图片描述

​   打开火狐浏览器,设置代理:

在这里插入图片描述

​   然后就可以登录被防火墙屏蔽的网页了:

在这里插入图片描述

​   如果断开了ssh连接,那么隧道就断开了:

在这里插入图片描述

Task 4: SSH建立反向隧道,绕过防火墙

​   通过ssh建立反向隧道,绕过防火墙。

​   先在主机B上,修改/etc/hosts文件,让主机B可以访问主机A上的网站(前面实验使用过的XSS或CSRF的网站)

在这里插入图片描述

​   然后在主机A上建立防火墙,阻止主机B访问本机的22和80端口,如图,可以看到建立防火墙后,主机B不能登录主机A的网站了,也不能访问ssh端口了:

命令:

sudo iptables -A INPUT -s 10.0.2.5 -p tcp --dport 80 -j DROP
sudo iptables -A INPUT -s 10.0.2.5 -p tcp --dport 22 -j DROP

在这里插入图片描述

为了绕过防火墙,我们可以先在主机A上运行如下命令,建立一个反向隧道:

#所有发往主机B 10.0.2.5:7777端口的数据包都会转发到主机A的80端口
ssh -R 7777:localhost:80 seed@10.0.2.5

然后在主机B上建立一个动态正向隧道:

ssh -D 8888:localhost:7777 -C locahost

然后设置一下主机B的浏览器的代理,让浏览器访问8888端口,如图,这样就可以通过反向隧道,访问到主机A上的网站了:

在这里插入图片描述

注意:

​   代理在设置的时候,一定要选上http协议,为了方便,可以把所有协议都勾选上,全设置成localhost:8888端口。但是对于某些公网的网站,只设置SOCK协议就可以。

  
  
  
  
❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀完结撒花❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值