5

I'm attempting to find a solution to allowing split-tunnelling with the Cisco AnyConnect client for OSX. I've found how it is modifying the firewall, and that's possible to fix. The issue is however the vpnagentd daemon keeps hijacking the routing table.

Sasha Pachev proposed an elegant solution for Linux (https://superuser.com/a/546668/568559), however I'm having challenges in adapting it to OSX.

The hack.c written for Linux references a linux/netlink.h, which isn't present on OSX. I think this is where the AF_NETLINK comes from.

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

int __ZN25CInterfaceRouteMonitorMac20routeCallbackHandlerEv()
{
  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;
}

I am not familiar with this language, so I'm not sure where to look. I can see others made reference in the original question to adapting this to OSX, but I don't see the results published anywhere.

Has anyone had any luck adapting this method to OSX? Any help greatly appreciated.

Once I get this aspect working, I'm more than happy to share the full solution.

2 Answers 2

3

Just came across this post. Thought I'd share my results.

I just did this hack on OSX. The solution given by Rubio does indeed work (and also results in 100% CPU on one core, unfortunately).

I wasn't able to trick the loader into using my function since OSX doesn't use LD_PRELOAD and DYLD_INSERT_LIBRARIES wasn't working for this binary for some reason. If anyone else runs into that issue, I solved it by editing the original libvpnagentutilities.dylib assembly (a hex editor is your best friend here)

Replace the first 6 bytes of the function with the following instructions to achieve the same "return 0" effect as the C code given above:

__ZN25CInterfaceRouteMonitorMac20routeCallbackHandlerEv:
0007add0        movl    $0x0, %eax  
0007add5        retl

However, after some more function call tracing, I figured out a way to do it WITHOUT increasing CPU utilization. It's actually simpler than my solution above. There's another symbol that is actually delegated the task of modifying the route table called _ZN28CInterfaceRouteMonitorCommon20routeCallbackHandlerEv. Tracing that function's call stack, I found the function that it calls to effect the change at offset 67c06.

The solution? Ignore the changes I listed earlier and instead simply replace the call instruction at 67c06 with nop's!!

Here's the before:

00067c03        movl    %eax, (%esp) 
00067c06        calll   *0x8(%ecx)   
00067c09        addl    $0x4, %esp   

And here's the final version:

00067c03        movl    %eax, (%esp)
00067c06        nop
00067c07        nop
00067c08        nop
00067c09        addl    $0x4, %esp

That should be all that you change. Replace the original vpnagentutilities.dylib with this modified version and enjoy a babysitter-free route table. It will still modify the table during the initial connection, but then you are free to alter it however you see fit.

EFF YOU ANYCONNECT!

4
  • Hmm seems like since 2017 the code has changed. Could you elaborate more on how to do what you have done with vpnagentutilities.dylib in more details? It is ok if you don't have time. Any tips would be appreciated :)
    – yeralin
    Sep 16, 2020 at 20:35
  • 1
    I have tried the hack above but it appears that it's not working anymore, at least on MacOS Catalina and Cisco AnyConnect 4.9.00086. Any hints on that? Oct 14, 2020 at 15:45
  • Likewise @BogdanStoica
    – yeralin
    Oct 26, 2020 at 21:35
  • In the end I gave up on using cisco anyconnect since I discovered that you can use openconnect instead of cisco and the routing issues are gone Oct 26, 2020 at 21:38
2

(I wrote the expanded version of this function that drains the NETLINK data, following Sasha Pachev's awesome detective work finding what function to intercept. I'm glad to see people have found the code useful.)

From the other thread I see someone used "nm" to find that there is an analogous callback handler for OSX, and you're trying to create a suitable function to replace it. It's my understanding that OSX doesn't provide a NETLINK interface at all, so it's very unlikely that the OSX version of AnyConnect keeps control over the routing table the same way the Linux client does. I don't know what mechanism OSX provides to signal to AnyConnect that a routing change has happened, but since it's not NETLINK based, the code here to drain the netlink message is inapplicable.

Ironically enough the original stub function style provided by Sasha would most likely be all you'd need to stop it from replacing your routes with its own. That function looked like:

int __ZN25CInterfaceRouteMonitorMac20routeCallbackHandlerEv()
{
  return 0;
}

On linux, that original function led to high cpu usage because the NETLINK event that triggered the call to the callback handler would never be cleared by this do-nothing code. the same effect may happen for the OSX client, where whatever event DOES trigger calling this function isn't being cleared either. But if this function is the correct handler function to intercept, and you are able to make your own library to override that function, and get that library loaded instead of the real one, at least you'll stop it from resetting the routing table every time you try to change it yourself. If you get that far, sacrificing some CPU may be worthwhile.

Good luck!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .