Linux netfilter REDIRECT target 构建透明代理原理

构建透明代理至于需要满足两个条件:1. 能够劫持应用程序产生的需代理的网络数据包。2. 能够获得这些数据包的原目的地址。

Linux netfilter 有一个 REDIRECT target,可用于 nat 表的 PREROUTING 和 OUTPUT 链上,其工作流程是将上述的两个链上命中的数据包的目的地址修改成(即重定向)该包进入网卡的主 IP 地址,如果是本地回环网络产生的包即重定向到 127.0.0.1。目标端口从参数指定,协议支持 tcp, udp, dccp or sctp。

从上面的情况看,Linux netfilter 的 REDIRECT target 可以实现透明代理的第一个条件,而第二个条件在强大的 Linux netfilter 子系统中也有方法:

#include <linux/netfilter_ipv4.h>
 
struct sockaddr_in orig_addr;
socklen_t orig_addr_len = sizeof (orig_addr);
if (0 == getsockopt(client_fd, SOL_IP, SO_ORIGINAL_DST, (struct sockaddr*) &orig_addr, &orig_addr_len)) {
        printf ("This is original destination address => %s:%u\n", inet_ntoa (orig_addr.sin_addr), ntohs (orig_addr.sin_port));
}

Over!

Firefox for Android 代理设置

目前发现 Android 平台的浏览器原生支持代理配置的仅 Firefox for Android(官方下载),不仅支持 HTTP、HTTPS 和 Socks4、5,还支持 PAC 脚本。

全局代理配置
在 Firefox 中访问 about:config,使用 proxy 过滤器并的找到 network.proxy.socks, network.proxy.socks_port, network.proxy.socks_remote_dns, network.proxy.type 并更改成图中配置。
Screenshot_2014-09-19-13-34-16

Screenshot_2014-09-19-13-34-28

自动代理配置
将 PAC 脚本放至于 /sdcard/proxy.pac,在 Firefox 中访问 about:config,使用 proxy 过滤器并的找到 network.proxy.autoconfig_url, network.proxy.type 并更改成图中配置。
Screenshot_2014-09-19-13-33-51

Screenshot_2014-09-19-13-42-48

Over!

QEMU 高性能网络配置

QEMU 的网络配置最简单的是 -net user 模式,从 Guest OS 角度看 Host OS 为其提供了 NAT 和 DHCP 服务。此模式也有两点明显的不足之处:
1. 网络传输性能较差。
2. Host OS 访问 Guest OS 网络服务、Guest OS 互相访问对话网络服务都不方便。

这里推荐使用 -net tap 模式,大概的工作原理是 QEMU 进程会使用 Linux tun 子系统创建一个虚拟的 tap 类型的网络接口,这个虚拟的链路的一端在 Host OS 角度即是一个 Ethernet 类型的网络接口,另一端被 QEMU 连接到其在 Guest OS 中虚拟化的 Ethernet 网络接口上。Tap 类型的接口工作在链路层,链路上承载的协议与常规的 Ethernet 相比无多余的限制。这种情况看上去就像是 Host 与 Guest 都有一个网络接口直连着。

Host OS 还可以创建一个 Bridge,然后将 QEMU 创建的 tap 类型的网口都加入到同一个 Bridge 上面,这样就相当于将多个 Guest 及 Host 的一个网口都连接到同一个交换机上。当然创建多个 Bridge 进行分组管理也可以。

配置实例
Host OS

sudo brctl addbr br0
sudo brctl stp br0 off
sudo ifconfig br0 192.168.5.1/24 up
qemu-system-x86_64 --enable-kvm -net nic,model=virtio -net tap,helper=/usr/lib/qemu/qemu-bridge-helper ...

Guest OS

sudo ifconfig eth0 192.168.5.2/24 up
sudo route add default gw 192.168.5.1

外网配置
Guest OS 中如需连接外部网络,有两种方式:
1. NAT 模式,Host OS 视 Guest OS 所处的 Bridge 是一个内网,由 Host OS 提供 NAT 服务,配置与常规的 Linux NAT 配置一样。
2. Bridge 模式,只需将 Host OS 上连接外网的网口也增加到 Guest OS 所属的 Bridge 中。

Windows virtio 驱动
Guest 中如果安装 Windows 操作系统,需要安装 virtio (net) 驱动,详见:http://www.linux-kvm.org/page/WindowsGuestDrivers/Download_Drivers

Tips: 注意防火墻的配置。

Over!

一种 HTTP 会话劫持的防御方法

HTTP 会话劫持也是 TCP 会话劫持,它是利用了 TCP Seqnum 的可预见性来伪造有效的响应包,并抢先响应客户端同时使得客户端主动丢弃原服务器的响应包。在这个层面想区分哪一个响应包是伪造的是有困难的,但结合实际情况考虑问题也还有一些思路。

我们以劫持程序的设计者角度考虑:
1. 劫持程序不是什么都劫持了,只有符合条件被命中的 HTTP 请求才会劫持的,那也就是说劫持程序需要一个完整或至少包含它需要的信息的请求才可以劫持,我想 Host, QueryString, Referer 这些应该少不了。
2. 劫持程序不希望维护 TCP 上下文进行流重组。因为维护起来成本比较高。
3. 基于上一点来说,劫持程序也就不希望一个 HTTP 请求被拆分在两个或多个包中传输。

从实际检测来看,大多数的 HTTP 请求还是包含在一个独立的 TCP 包中传输的,只有很少的情况被拆分了。我想你一定想到了什么了,对!我们就假设当前在用的劫持程序都没有维护 TCP 传输上下文进行流重组,那么人为对前向链路上的流进行强制拆分,就应该能够逃过被劫持。

实现的方式应该会有很多,我在应用层代理层面实现了一个简单的原型,方式是将前向链路的 Buffer size 强制在了 16 字节。代码托管在 Github => https://github.com/heiher/hev-socks5-proxy/tree/anti-hijack

从抓包来看,HTTP 请求在传输时被拆分了:

packet 0:[syn]
packet 1: GET / HTTP/1.1\r\n
packet 2: Host: xxxx
packet 3: ...

Over!

Forward special packets

主机A与主机B之间建立了一条 IPIP 遂道,两个主机之间生成的TCP协议且源端口是8000的包需要通过遂道传输到对方后,通过对方的网关发送出去。

On Host A

TUN_IFACE="tun-b"
HOST_A_IP="10.0.0.3"
HOST_B_IP="10.0.3.2"
TUN_GATEWAY="192.168.4.2"
HOST_GATEWAY="10.0.0.1"
 
sudo iptunnel add ${TUN_IFACE} mode ipip remote ${HOST_B_IP} local ${HOST_A_IP}
sudo ifconfig ${TUN_IFACE} up
sudo route add -host ${TUN_GATEWAY} dev ${TUN_IFACE}
 
sudo iptables -t mangle -A POSTROUTING -p tcp -m tcp --sport 8000 -m mark ! --mark 0x8888 -j TEE --gateway ${TUN_GATEWAY}
sudo iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 8000 -j MARK --set-mark 0x8888
sudo iptables -t mangle -A PREROUTING -m mark --mark 0x8888 -j TEE --gateway ${HOST_GATEWAY}

On Host B

TUN_IFACE="tun-a"
HOST_A_IP="10.0.0.3"
HOST_B_IP="10.0.3.2"
TUN_GATEWAY="192.168.4.1"
HOST_GATEWAY="10.0.3.1"
 
sudo iptunnel add ${TUN_IFACE} mode ipip remote ${HOST_A_IP} local ${HOST_B_IP}
sudo ifconfig ${TUN_IFACE} up
sudo route add -host ${TUN_GATEWAY} dev ${TUN_IFACE}
 
sudo iptables -t mangle -A POSTROUTING -p tcp -m tcp --sport 8000 -m mark ! --mark 0x8888 -j TEE --gateway ${TUN_GATEWAY}
sudo iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 8000 -j MARK --set-mark 0x8888
sudo iptables -t mangle -A PREROUTING -m mark --mark 0x8888 -j TEE --gateway ${HOST_GATEWAY}

Over!