AnyConnect/OpenConnect 客户端代理转发技术分析


Cisco AnyConnect/OpenConnect 客户端代理转发技术分析

普通的代理客户端如何转发客户端流量

本文编写时,较为流行的代理软件有 Clash, Shadowsocks, Tarjan, V2Ray 等,他们的 Windows 客户端无一例外地采用了 HTTP 代理或者 PAC 的方式来转发用户流量。

这样做的好处是: - 每次开启和关闭代理功能时,只需要调用系统级别的 HTTP代理开关即可 - PAC 可以容纳很大的域名和IP规则(GFW List)

当然了,也有缺点: - 部分软件不支持:终端 (需要配置环境变量), UWP 应用(需要配置 Local Loopback) - 对于某些用户来说,不够安全(并非所有流量都经由隧道转发)

Cisco AnyConnect 如何转发客户端流量

综上所述,Cisco AnyConnect 和各大厂商的 VPN 软件都采取了同一种方式。维护 Windows 的本地路由表(ROUTE.EXE)。

这个路由表是系统级别的,对所有软件生效。

这里我输出了我自己的路由表,作为例子。

pwsh> route PRINT -4                                                                                          
===========================================================================                                   
Interface List
 16...00 ff 76 77 13 94 ......TAP-Windows Adapter V9
 36...00 15 5d fb 59 43 ......Hyper-V Virtual Ethernet Adapter
 18...3c 2c 30 99 07 5e ......Realtek PCIe GbE Family Controller
 32...0a 00 27 00 00 20 ......VirtualBox Host-Only Ethernet Adapter
 25...b4 69 21 54 05 88 ......Intel(R) Wireless-AC 9462
 31...b4 69 21 54 05 89 ......Microsoft Wi-Fi Direct Virtual Adapter #4
 20...b6 69 21 54 05 88 ......Microsoft Wi-Fi Direct Virtual Adapter #5
  9...00 50 56 c0 00 01 ......VMware Virtual Ethernet Adapter for VMnet1
 29...00 50 56 c0 00 08 ......VMware Virtual Ethernet Adapter for VMnet8
  1...........................Software Loopback Interface 1
===========================================================================

IPv4 Route Table
===========================================================================
Active Routes:
Network Destination        Netmask          Gateway       Interface  Metric
          0.0.0.0          0.0.0.0  <My WAN IP>  <My WAN IP>     26
          0.0.0.0          0.0.0.0        10.12.0.1       10.12.0.68      2
        10.12.0.0    255.255.255.0         On-link        10.12.0.68    257
       10.12.0.68  255.255.255.255         On-link        10.12.0.68    257
      10.12.0.255  255.255.255.255         On-link        10.12.0.68    257
   <VPN EXTERNAL GW>  255.255.255.255  <My WAN GW>  <My WAN IP>     26
        127.0.0.0        255.0.0.0         On-link         127.0.0.1    331
        127.0.0.1  255.255.255.255         On-link         127.0.0.1    331
  127.255.255.255  255.255.255.255         On-link         127.0.0.1    331
      169.254.0.0      255.255.0.0         On-link     169.254.92.21    281
    169.254.92.21  255.255.255.255         On-link     169.254.92.21    281
  169.254.255.255  255.255.255.255         On-link     169.254.92.21    281
     172.18.240.0    255.255.240.0         On-link      172.18.240.1    271
     172.18.240.1  255.255.255.255         On-link      172.18.240.1    271
   172.18.255.255  255.255.255.255         On-link      172.18.240.1    271
     192.168.38.0    255.255.255.0         On-link      192.168.38.1    291
     192.168.38.1  255.255.255.255         On-link      192.168.38.1    291
   192.168.38.255  255.255.255.255         On-link      192.168.38.1    291
    192.168.232.0    255.255.255.0         On-link     192.168.232.1    291
    192.168.232.1  255.255.255.255         On-link     192.168.232.1    291
  192.168.232.255  255.255.255.255         On-link     192.168.232.1    291
    <My WAN SUBNET>    255.255.255.0         On-link   <My WAN IP>    281
  <My WAN IP>  255.255.255.255         On-link   <My WAN IP>    281
  <My WAN GW>  255.255.255.255         On-link   <My WAN IP>    281
        224.0.0.0        240.0.0.0         On-link         127.0.0.1    331
        224.0.0.0        240.0.0.0         On-link   <My WAN IP>    281
        224.0.0.0        240.0.0.0         On-link     169.254.92.21    281
        224.0.0.0        240.0.0.0         On-link     192.168.232.1    291
        224.0.0.0        240.0.0.0         On-link      192.168.38.1    291
        224.0.0.0        240.0.0.0         On-link      172.18.240.1    271
        224.0.0.0        240.0.0.0         On-link        10.12.0.68    257
  255.255.255.255  255.255.255.255         On-link         127.0.0.1    331
  255.255.255.255  255.255.255.255         On-link   <My WAN IP>   281
  255.255.255.255  255.255.255.255         On-link     169.254.92.21    281
  255.255.255.255  255.255.255.255         On-link     192.168.232.1    291
  255.255.255.255  255.255.255.255         On-link      192.168.38.1    291
  255.255.255.255  255.255.255.255         On-link      172.18.240.1    271
  255.255.255.255  255.255.255.255         On-link        10.12.0.68    257
===========================================================================
Persistent Routes:
  Network Address          Netmask  Gateway Address  Metric
          0.0.0.0          0.0.0.0        10.12.0.1       1
===========================================================================

路由表主要分为两大部分,上部是我们的网卡,又叫适配器(Adapter),又叫接口(Interface)。 分别拥有自己的 MAC,和 friendly-name.

而下方就是我们的路由表了。 我将逐行进行讲解。

Network Destination Netmask Gateway Interface Metric

这五个参数是一条路由的基本参数: - Network Destination 目标网段 - Netmask 其子网掩码 - Gateway 默认网关 - Interface 流量出接口 - Metric 路由距离 (网段的规则不唯一时相当于优先级,越小越优先)

直连流量出接口

0.0.0.0 0.0.0.0 <My WAN IP> <My WAN IP> 26

此路由指定了直连流量的出接口,为我们联网的网卡,在没连VPN时,它的路由距离是很小的,所以流量会从这里直接出去。这里 AnyConnect 将其改为 26,在 miss 掉所有 AnyConnect 的规则后,会匹配上此路由,进行直连。

VPN 流量出接口

0.0.0.0 0.0.0.0 10.12.0.1 10.12.0.68 2

VPN 的流量出接口,这里是 Remote 的 Inside IP

Remote LAN 流量代理路由

10.12.0.255 255.255.255.255 On-link 10.12.0.68 257

将 LAN IP 交由VPN隧道代理。

localhost

127.0.0.1 255.255.255.255 On-link 127.0.0.1 331

天经地义,不能动。

Cisco AnyConnect 的路由表保护

为了安全,AnyConnect 客户端会监听路由表,确保其完整性。完成这一工作的是其客户端中的 CHostConfigMgr::StartInterfaceAndRouteMonitoring() 函数。

如果出于某种原因,你想修改路由表的话,你需要用 IDA 或者 OllyDebug 修改这一函数,让他直接 return 掉。同时还需要绕过思科的 checksum 检查。(位于 vpnagentd.exe 中)。

一些黑魔法

如果你碰巧使用的是 Linux 系统,你可以尝试手写一个黑魔法函数——_ZN14CHostConfigMgr32StartInterfaceAndRouteMonitoringEv

1.黑魔法函数

#include <sys/socket.h>
#include <linux/netlink.h>

int _ZN27CInterfaceRouteMonitorLinux20routeCallbackHandlerEv()
{
  int fd=50;          // max fd to try
  char buf[8192];
  struct sockaddr_nl sa;
  socklen_t len = sizeof(sa);

  while (fd) {
     if (!getsockname(fd, (struct sockaddr *)&sa, &len)) {
        if (sa.nl_family == AF_NETLINK) {
           ssize_t n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
        }
     }
     fd--;
  }
  return 0;
}

2.编译

gcc -o libhack.so -shared -fPIC hack.c

  1. 安装 libhack.so 到 Cisco library path中:

sudo cp libhack.so /opt/cisco/anyconnect/lib/

  1. 关闭 AnyConnect

/etc/init.d/vpnagentd stop

  1. 检查进程

ps auxw | grep vpnagentd

如果它不肯体面,我们就帮它体面。

kill -9

  1. 如果存在 /etc/init.d/vpnagentd 的话,尝试添加 LD_PRELOAD=/opt/cisco/anyconnect/lib/libhack.so 到底层二进制文件被调用的地方,最后应该看起来像这样:

LD_PRELOAD=/opt/cisco/anyconnect/lib/libhack.so /opt/cisco/anyconnect/bin/vpnagentd

新版本的 AnyConnect 使用了 /etc/init.d/vpnagentd 而不是 /lib/systemd/system/vpnagentd.service

所以应该:

sudo mv /opt/cisco/anyconnect/bin/vpnagentd /opt/cisco/anyconnect/bin/vpnagentd.orig &&
{ echo '#!/bin/bash' &&
  echo "LD_PRELOAD=$HOME/vpn/libhack.so exec /opt/cisco/anyconnect/bin/vpnagentd.orig"
} | sudo tee /opt/cisco/anyconnect/bin/vpnagentd &&
sudo chmod +x /opt/cisco/anyconnect/bin/vpnagentd

  1. 扫清六合,天下归一,是时候启动了

/etc/init.d/vpnagentd start

  1. 注意此时 AnyConnect 已经把路由表干烂了,需要手动修复:

iptables-save | grep -v DROP | iptables-restore

  1. 如果你有本地LAN的访问需求的话,自己改路由表。

指定接口:

route add -net 192.168.1.0 netmask 255.255.255.0 dev wlan0

  1. 检查结果

route -n

(未完待续)

Reference

  1. route add no longer works when I connected to VPN via cisco anyconnect client - StackOverflow
  2. How to allow local LAN access while connected to Cisco VPN?

文章作者: sfc9982
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 sfc9982 !
  目录