Proxy Design and Implementation

Sharing for Network Security Engineering and Practice

Jinjun Peng, Weilin Zhao

Tsinghua University

2022.12

Framework Design - Client/Server

  • 正常流程:应用程序 – 内核协议栈 – 物理网卡 – 发出

  • TUN/TAP 是操作系统内核中的虚拟网络设备。

    • TAP 模拟以太网设备,操作第二层数据包。
    • TUN 模拟网络层设备,操作第三层数据包。

    Python 中可使用 pytun 包管理 tun 设备,其他语言有类似的封装

  • 虚拟网卡:应用程序 – 内核协议栈 – 虚拟网卡 – 文件描述符 – 应用程序处理 – 内核协议栈 – 物理网卡 – 发出

Framework Design - Client/Server

  • 通过 route 命令将去往某些网站的 IP 流量导向我们的虚拟网卡 tun0,使得程序可以统一处理。
  • 程序处理后从物理网卡 eth0 处发出。

Framework Design - Client/Server

  • Client 端和 Proxy 端:
    • 建立 Socket 连接(TCP)
    • 分别设立虚拟网卡
  • 程序同时拥有 socket 的 fd 和 tun 的 fd,能够进行读写

Framework Design - Forwarding

  • Client 端先与 Proxy 端进行通信,再由 Proxy 端转发至 Web Server
    • 开启 ip_forward echo 1 > /proc/sys/net/ipv4/ip_forward
    • 直接 ip_forward ,回程时包被拦截!

Framework Design - Forwarding

  • 修改 iptables ,使用 –j 在转发前,修改 IP src
  • MASQUERADE 指:修改 IP src 为当前转发网卡的 ip
    iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Framework Design - Overall

Datagram Design

| nonce<12> | encrypt( time_bytes<8> | magic_bytes<32> ) |
| encrypt( rand_len<8> | random_padding<rand_len> | data | digest<64> ) |
  1. nonce: required by ChaCha20Poly1305
  2. time_bytes<8>: timestamp in i64; Δt<120s\Delta t < 120s; always changing
  3. magic_bytes<32>: pre-shared string of 32 bytes; check consistency after the decryption of the first part

Datagram Design

| nonce<12> | encrypt( time_bytes<8> | magic_bytes<32> ) |
| encrypt( rand_len<8> | random_padding<rand_len> | data | digest<64> ) |
  1. rand_len<8> | random_padding<rand_len>: random content and its length in i64; to eliminate the characteristics of packet length
  2. data: raw data
  3. digest<64>: digest(rand_len<8> | random_padding<rand_len> | data) to ensure integrity

Real-world Test - ICMP

Real-world Test - HTTPS

Limitations

  • Cannot pretend to be normal traffics like HTTPS
    • Datagram structure
    • Server behavior

Limitations

  • DoS attack
    • Only establish the first connection to support single-user scenario
      • Support multi-user
    • Only has one state
      • More states are needed: authentication, connected, ...
      • Resource management: kill invalid connections

Attack

Attack - Group 1

  • Features: Rust; common security considerations (encryption, bloom filter, randomness of packet length and interval)
  • Weakness: UDP (does not need connection)
  • DoS: can lead to timeout

Attack - Group 1

  • DoS: can lead to timeout

Attack - Group 1

  • Found one bug in implementation: add nonce into bloom filter without checking packet validness
let nonce = unsafe { from_u8_array::<Nonce<Aes256Gcm>>(&buffer[len - NONCE_LEN..len]) };
// ...
if filters.0.check(&nonce) || filters.1.check(&nonce) {
    eprintln!("Replay suspected");
    continue;
}
filters.1.set(&nonce);
let res = cipher.decrypt(&nonce, &buffer[..len - NONCE_LEN]);
if res.is_err() {
    continue;
}

Attack - Group 2

  • Features: Rust; Support multi-client; Two-state design (handshake, connected)
  • Plain DoS: cannot affect it

Attack - Group 2

  • DoS with another legal user
# wgetflood.sh
for ((;;))
do
    wget 127.0.0.1:80/large.txt -O /dev/null > /dev/null 2>&1 &
done

Attack - Group 2

Attack - Group 2

Thanks