Linux 中流量劫持的几个常见姿势

在 istio 中我们见识过流量劫持的厉害,我们今天就盘算下相关的技术。

Use Iptables

使用 Iptables 是最常见的劫持方式,无论是常规的 KubeProxy 还是 istio 都使用了这种模式

1
iptables -t nat -A PREROUTING -s 192.168.0.10 -p tcp --dport 80 -j REDIRECT --to-port 8080

这样我们就可以将本身发往 192.168.0.10:80 的请求转到到 192.168.0.10:8080
这种技术一般被称之为 DNAT

Use epbf

使用比较新的 Linux 版本可以 sockmap 完成在 本机 上的更高级的转发方式

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
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>

struct bpf_map_def SEC("maps") sock_map = {
.type = BPF_MAP_TYPE_SOCKMAP,
.max_entries = 2,
};

SEC("sockops")
int handle_tcp(struct __sk_buff *skb) {
struct ethhdr *eth = bpf_hdr_pointer(skb);
struct iphdr *ip = (struct iphdr *)(eth + 1);
struct tcphdr *tcp = (struct tcphdr *)(ip + 1);

// Check if the packet is TCP
if (ip->protocol != IPPROTO_TCP)
return 0;

// Redirect packets to another socket using the sock_map
bpf_sock_ops_redirect(skb, &sock_map, BPF_F_INGRESS);

return 0;
}

Use Tun/Tap

相对复杂点

  1. 常见一个 tun 虚拟网卡,分配地址,并且激活
1
2
3
4
5
ip tuntap add mode tun dev tun0

ip addr add 10.0.0.1/24 dev tun0

ip link set dev tun0 up
  1. 配置路由

这里比如我们将 192.168.0.0/24 都转到这里

1
ip route add 192.168.0.0/24 dev tun0
  1. 编写代码读取
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
26
27
28
29
30
31
int tun_fd = open("/dev/net/tun", O_RDWR);
if (tun_fd < 0) {
perror("Failed to open TUN device");
exit(1);
}

struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN; // Set as TUN device

if (ioctl(tun_fd, TUNSETIFF, (void *)&ifr) < 0) {
perror("Failed to set TUN device options");
close(tun_fd);
exit(1);
}

char buffer[4096];
ssize_t num_bytes;

while (1) {
num_bytes = read(tun_fd, buffer, sizeof(buffer));
if (num_bytes < 0) {
perror("Error while reading from TUN device");
close(tun_fd);
exit(1);
}

// Process the received data
// ...

}

这里有个完整的例子 tuntap-packet-dumper

当我们尝试 Ping 的时候,就可以从 Tun0 的另外一头获得数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Flags: 0x0000  Protocol: 0x86dd
To: 60:00:00:00:00:08
From: 3a:ff:fe:80:00:00
IP Version 0 Priority: 0
Flow Label: 0x001077
Payload length: 26488
Next header: 0xd2
Hop limit: 68
Source IP: 9c62:ff02::
Dest IP: 2:8500:96a0::
48: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0: 00 00 10 77 67 78 d2 44 9c 62 ff 02 00 00 00 00
16: 00 00 00 00 00 00 00 00 00 02 85 00 96 a0 00 00
32: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

USE LD_PRELOAD

这是 chatgpt 给的一种方式,原以为很离谱,真的有这样的 torsocks

1
export LD_PRELOAD=/lib/libtorsocks.so

本质上编写自定义共享库:创建一个共享库来拦截与 TCP 相关的系统调用并修改它们的行为。您可以使用 C 或 C++ 等编程语言来编写库。一些通常被拦截的函数包括 socket、connect、bind、listen、accept、send 和 recv。在这些拦截函数中,您可以根据需要实施重定向 TCP 流量的逻辑。