Intro and other options
It's been like 5.5 years, so I'm mostly leaving this answer for people, trying to solve the same problems with modern Cisco Anyconnect 4.x. In my case Anyconnect was wrapping traffic to local Kubernetes cluster brought up by Minikube on macOS.
Methods like _ZN27CInterfaceRouteMonitorLinux20routeCallbackHandlerEv
or __ZN25CInterfaceRouteMonitorMac20routeCallbackHandlerEv
described in:
Does not seem to work any longer. Also, a lot of guides are related to fixing OS X firewall represented in previous versions by ipfw
utility like here, so they are irrelevant.
Patching vpnagentd
In 2019 we're still battling both problems for companies avoiding split routing and bringing up unreasonable firewall rules. Here's the fix.
You need to patch the calling of the method
CHostConfigMgr::StartInterfaceAndRouteMonitoring()
in vpnagentd
binary, which in my version is located at 0x09cbf6
. It's a simple jmp qword
command, which is never being returned to this place, so single nop
can be enough. However, it could be worth completely wiping the command with 6 nop
s.
Here the Python script which can automate this procedure for you, however any disassembly utility can help you there. In my original research and hack I used radare2
which is quite handy for people who do not perform such actions on a daily basis:
#!/usr/bin/python3
MAGIC_OFFSET = "0x09cbf6"
MAGIC_BYTE = 144
def eff_anyconnect(file):
print("Opening {} to patch it".format(file))
with open(file, "rb+") as f:
print("Going to {}".format(MAGIC_OFFSET))
print("Current command to call method for watching our routing table")
f.seek(int(MAGIC_OFFSET, 16))
print(hex(int.from_bytes(f.read(6), "big")))
f.seek(int(MAGIC_OFFSET, 16))
f.write(bytes([MAGIC_BYTE]))
print("NOP any longer:")
f.seek(int(MAGIC_OFFSET, 16))
print(hex(int.from_bytes(f.read(6), "big")))
eff_anyconnect("/your/path/to/cisco/bin/vpnagentd")
Next step, after you patch the binary, you should kill current vpnagent
process and reconnect to your VPN. You might find that desired routes are still affected, but the hack above will unlock the routing table, so you can override routes.
Routes
I would suggest adding -static
ones, those are not interfered by AnyConnect at all, while non-static still being duplicated by tunneled ones. I have no good solution here, for my case single static route was enough:
sudo route -n delete $(minikube ip)
sudo route -n add $(minikube ip) -interface bridge100 -static
Firewall
Final, firewall step. That's pretty straightforward, you need to check what are the rules denying or allowing only AnyConnect tagged ones. In my case, everything that is not tagged was blocked, so I created a file with following entries:
nat on utun1 proto {tcp, udp, icmp} from 192.168.64.0/24 to any -> utun1
pass in log on bridge0 inet all flags S/SA keep state tag cisco_anyconnect_vpn_pass
pass in log on bridge100 inet all flags S/SA keep state tag cisco_anyconnect_vpn_pass
Mind the following:
utun1
is your AnyConnect interface
bridge0
and bridge100
are your Minikube/Docker bridges. For some reason AnyConnect renames bridges.
192.168.64.0/24
is your Minikube subnet.
Then run:
sudo pfctl -e enable packet-filtering
sudo pfctl -f <your_file_with_rules> -v
From now on, until next reconnect you should be good in terms of routes and firewall.