Firewall

Task 1: Using Firewall

Linux有一个叫做iptables的工具,它本质上是防火墙。 在此任务中,目标是使用iptables设置一些防火墙策略,并在策略生效后观察系统的行为。

Prevent A from doing telnet to Machine B.

本项实验需要用iptables实现阻挡机器A用telnet 连接机器B,命令如下,在机器A上阻止目的ip是机器B且端口是23的数据包发出

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

如下图,可以看到,连接不能成功

image-20210120113359384

Prevent B from doing telnet to Machine A.

本项实验需要用iptables实现阻挡机器B用telnet 连接机器A,命令如下,在机器A上阻止来源IP是机器A且端口是23的数据包进入

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

image-20210120113413269

如下图,可以看到,连接不能成功

image-20210120113422776

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.

这里我们选择百度为我们实验的目标网站,我们用iptables阻挡机器A往www.baidu.com发包

根据下面图片可以看到,实验前可以打开页面,实验之后页面不能打开。

image-20210120113435857

1
sudo iptables -A OUTPUT -p tcp -d www.baidu.com --dport 80 -j DROP

image-20210120113447672

Task 2: Implementing a Simple Firewall

您在前面的任务中使用的防火墙是防火墙的包过滤类型。 这种类型的防火墙的主要部分是过滤部分,它检查每个传入和传出的数据包,并强制执行管理员设置的防火墙策略。 由于包处理是在内核中完成的,所以过滤也必须在内核中完成。 因此,实现这样的防火墙似乎需要我们修改Linux内核。 在过去,这必须通过修改和重建内核来完成。 现代Linux操作系统提供了几种新的机制,以方便在不重建内核映像的情况下操作数据包。 这两种机制是可加载内核模块(LKM)和Netfilter。

LKM允许我们在运行时向内核添加一个新模块。 这个新模块使我们能够扩展内核的功能,而不重建内核,甚至不重新启动计算机。 防火墙的数据包过滤部分可以作为LKM实现。 然而,这还不够。 为了使过滤模块阻止传入/传出数据包,必须将该模块插入到数据包处理路径中。 在Netfilter被引入Linux之前,这是不容易做到的。

Netfilter的设计是为了方便授权用户操作数据包。 Netfilter通过在Linux内核中实现多个钩子来实现这个目标。 这些钩子被插入到各种地方,包括数据包传入和传出路径。 如果我们想操作传入的数据包,我们只需要将我们自己的程序(在LKM内)连接到相应的钩子上。 一旦传入的数据包到达,我们的程序将被调用。 我们的程序可以决定这个数据包是否应该被阻塞;此外,我们还可以修改程序中的数据包。

在此任务中,需要使用LKM和Netfilter来实现包过滤模块。 该模块将从数据结构中获取防火墙策略,并使用策略来决定数据包是否应该被阻塞。 为了让您的生活更轻松,所以您可以专注于过滤部分,防火墙的核心,我们允许您在程序中硬编码防火墙策略。

在用 netfilter编程的过程中,我们用以下两个函数匹配IP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
bool check_address_src(struct iphdr *ip_header, int a, int b, int c, int d)
{
if (((ip_header->saddr & 0xff000000) >> 24) != d)
return false;
if (((ip_header->saddr & 0x00ff0000) >> 16) != c)
return false;
if (((ip_header->saddr & 0x0000ff00) >> 8) != b)
return false;
if ((ip_header->saddr & 0x000000ff) != a)
return false;
return true;
}

bool check_address_dst(struct iphdr *ip_header, int a, int b, int c, int d)
{
if (((ip_header->daddr & 0xff000000) >> 24) != d)
return false;
if (((ip_header->daddr & 0x00ff0000) >> 16) != c)
return false;
if (((ip_header->daddr & 0x0000ff00) >> 8) != b)
return false;
if ((ip_header->daddr & 0x000000ff) != a)
return false;
return true;
}

然后写过滤数据包的钩子函数,见下面截图,然后是钩子函数的注册和清除,我选择将入口和出口的过滤钩子注册在NF_INET_PRE_ROUTINGNF_INET_POST_ROUTING两个位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int init_module(void)
{
nfho_in.hook = hook_func_in;
nfho_in.hooknum = NF_INET_PRE_ROUTING;
nfho_in.pf = PF_INET;
nfho_in.priority = NF_IP_PRI_FIRST;
nf_register_hook(&nfho_in);

nfho_out.hook = hook_func_out;
nfho_out.hooknum = NF_INET_POST_ROUTING;
nfho_out.pf = PF_INET;
nfho_out.priority = NF_IP_PRI_FIRST;
nf_register_hook(&nfho_out);
return 0;
}

void cleanup_module(void)
{
printk(KERN_INFO "\nbye");
nf_unregister_hook(&nfho_in);
nf_unregister_hook(&nfho_out);
}

Prevent A from doing telnet to Machine B.

为了实现功能,我们在出口钩子函数实现以下代码,我们判断目的端口是不是23,然后判断目的ip是不是我们的过滤目标。

image-20201208165253375

如图,实验成功,连接不成功,而且在dmesg中看到了右边的记录

image-20210120113458912

Prevent B from doing telnet to Machine A.

为了实现功能,在入口钩子函数里实现以下代码,我们判断目的端口是不是23,然后判断目的和来源ip是不是我们的过滤目标。

image-20201208170901200

如图,实验成功,连接不成功,而且在dmesg中看到了右边的记录

image-20210120113508200

Prevent A from doing ping to Machine B.

为了实现功能,在出口钩子函数里实现以下代码,我们判断协议是不是icmp的请求包,然后判断目的和来源ip是不是我们的过滤目标。

image-20201208170837324

如图,实验成功,连接不成功,而且在dmesg中看到了右边的记录

image-20210120113516760

Task 3: Evading Egress Filtering

许多公司和学校实施出口过滤,这阻止了他们网络中的用户接触到某些网站或互联网服务。 它们确实允许用户访问其他网站。 在许多情况下,这种类型的防火墙检查传出数据包中的目标IP地址和端口号。 如果一个数据包符合限制,它将被丢弃。 由于性能原因,它们通常不进行深度分组检查(即查看数据包的数据部分。 在这个任务中,我们展示了如何使用隧道机制绕过这种出口过滤。 建立隧道有很多方法;在这项任务中,我们只关注SSH隧道。

image-20210120113525846

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

image-20210120113533013

下面的命令在本地主机(端口8000)和机器B(使用默认端口22)之间建立SSH隧道;当数据包从B端出来时,它将被转发到机器C的端口23(telnet端口)。

1
ssh -L 8000:192.168.164.128:23 seed@192.168.164.128

当我们将telnet传输到localhost的端口8000时,SSH将将我们所有的TCP数据包从localhost:8000上的隧道的一端传输到机器B上的隧道的另一端;从那里,数据包将被转发到机器B:23。

image-20210120113543262

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

在这里,我们使用动态端口转发的方式,所以我们只指定了本地的端口,当B机器接收到我们本地机器发送过去的数据包后,根据数据包的信息动态确定转发到哪个端口上

1
ssh -D 9000 seed@192.168.164.128

如下图,我们开启ssh

image-20210120113553576

在Firefox里面我们用SOCKS代理,将数据包发送到本地的9000端口,然后数据会通过我们的SSH隧道

image-20210120113603986

如下图,我们成功绕过了过滤

image-20210120113617917

image-20210120113625808

image-20210120113634397

Task 4: Evading Ingress Filtering

这里为了绕过INPUT形式的包过滤,我们采用了SSH反向代理的方式,首先我们在A机器上执行下命令,相当于将主动发起了连接,将机器192.168.164.131的7000端口转发到了A机器的22端口

1
ssh -p 22 -qngfNTR 7000:localhost:22 seed@192.168.164.131

image-20210120113644441

然后我们在机器192.168.164.131中执行下列命令,连接自己的7000端口,然后数据会通过隧道转发到A机器的22端口。

1
ssh -p 7000 seed@localhost

image-20210120113653325

image-20210120113702372

反向代理原理可用上图理解。