There’s a clever “new” attack against VPNs, called TunnelVision, done by researchers at Leviathan Security. To explain why we put “new” in quotation marks, I’ll just share my note-to-self on this one written before reading the write-up: “Doesn’t using a more specific DHCP route do this already?” And indeed, that’s the secret here: in routing, the more specific route wins. I could not have told you that DHCP option 121 is used to set extra static routes, so that part was new to me. So let’s break this down a bit, for those that haven’t spent the last 20 years thinking about DHCP, networking, and VPNs.
So up first, a route is a collection of values that instruct your computer how to reach a given IP address, and the set of routes on a computer is the routing table. On one of my machines, the (slightly simplified) routing table looks like:
# ip route
default via 10.0.1.1 dev eth0
10.0.1.0/24 dev eth0
The first line there is the default route, where “default” is a short-hand for 0.0.0.0/0
. That indicate a network using the Classless Inter-Domain Routing (CIDR) notation. When the Internet was first developed, it was segmented into networks using network classes A, B, and C. The problem there was that the world was limited to just over 2.1 million networks on the Internet, which has since proven to be not nearly enough. CIDR came along, eliminated the classes, and gave us subnets instead.
In CIDR notation, the value after the slash is commonly called the netmask, and indicates the number of bits that are dedicated to the network identifier, and how many bits are dedicated to the address on the network. Put more simply, the bigger the number after the slash, the fewer usable IP addresses on the network. In the context of a route, the IP address here is going to refer to a network identifier, and the whole CIDR string identifies that network and its size.
Back to my routing table, the two routes are a bit different. The first one uses the “via” term to indicate we use a gateway to reach the indicated network. That doesn’t make any sense on its own, as the 10.0.1.1 address is on the 0.0.0.0/0 network. The second route saves the day, indicating that the 10.0.1.0/24 network is directly reachable out the eth0 device. This works because the more specific route — the one with the bigger netmask value, takes precedence.
The next piece to understand is DHCP, the Dynamic Host Configuration Protocol. That’s the way most machines get an IP address from the local network. DHCP not only assigns IP addresses, but it also sets additional information via numeric options. Option 1 is the subnet mask, option 6 advertises DNS servers, and option 3 sets the local router IP. That router is then generally used to construct the default route on the connecting machine — 0.0.0.0/0 via router_IP.
Remember the problem with the gateway IP address belonging to the default network? There’s a similar issue with VPNs. If you want all traffic to flow over the VPN device, tun0, how does the VPN traffic get routed across the Internet to the VPN server? And how does the VPN deal with the existence of the default route set by DHCP? By leaving those routes in place, and adding more specific routes. That’s usually 0.0.0.0/1
and 128.0.0.0/1
, neatly slicing the entire Internet into two networks, and routing both through the VPN. These routes are more specific than the default route, but leave the router-provided routes in place to keep the VPN itself online.
And now enter TunnelVision. The key here is DHCP option 121, which sets additional CIDR notation routes. The very same trick a VPN uses to override the network’s default route can be used against it. Yep, DHCP can simply inform a client that networks 0.0.0.0/2
, 64.0.0.0/2
, 128.0.0.0/2
, and 192.0.0.0/2
are routed through malicious_IP. You’d see it if you actually checked your routing table, but how often does anybody do that, when not working a problem?
There is a CVE assigned, CVE-2024-3661, but there’s an interesting question raised: Is this a vulnerability, and in which component? And what’s the right solution? To the first question, everything is basically working the way it is supposed to. The flaw is that some VPNs make the assumption that a /1 route is a bulletproof way to override the default route. The solution is a bit trickier.
Wireguard on Linux already has a very robust solution that users can opt in to, the use of network namespaces to further isolate traffic inside and outside the VPN. Another approach is to simply ignore DHCP option 121 like Android does, making it one of the few unaffected platforms. It seems reasonable that platforms that do need option 121 could re-use the existing trusted vs untrusted designation for networks, only honoring option 121 for trusted networks.
And one final thought before moving on, this can really be a problem on semi-trusted networks, where an adversary could set up a malicious rogue DHCP server. Proper host isolation seems like it would make that a challenge, but not every network does so. The biggest threat I see is an informed attacker using TunnelVision to capture traffic meant for internal traffic. Internal IPs don’t have valid HTTPS certificates, so this seems like it could be used in a highly targeted campaign to capture data intended for such a device.
A Scarecrow Would Have Saved You
We’re going to take a quick look at a new way the zEus malware is getting distributed, and then chat about a new tool that may have helped prevent infection. So first, it was embedded in a Minecraft Source Pack. Now as far as we can tell, this isn’t a vulnerability in Minecraft, it’s just your normal self-extracting RAR that runs the malware while extracting a copy of the files you actually want. For our purposes, the interesting part is the anti-analysis component.
This malware has a list of computer names and running programs that it checks for before deploying. If your computer is named george, or you’re running Wireshark, the payload won’t trigger. That’s not unique to this particular malware, and it’s exactly the malware quirk that Cyber Scarecrow uses. This might be one of those “dumb ideas” that works, and therefore isn’t a dumb idea. Regardless, Cyber Scarecrow runs on Windows, and launches multiple fake analysis indicators, trying to trick malware into leaving your computer alone.
Poutine
Boost Security has released poutine, a new Open Source security scanner for both GitHub and Gitlab actions/pipelines. The scanner looks for misconfigurations that allow things like arbitrary code execution from external contributors.
Running the tool appears to be pretty simple, and it’s available via docker, brew, or a binary download. On the roadmap is support for CircleCI and Azure, with more misconfigurations to be added.
Gitlab’s Deep Dive
Last for this week is Gitlab’s own coverage of a file write vulnerability. And it all starts with the Ruby Gem devfile
calling an external binary. That raised suspicions, as interfaces like those are prone to bugs. And here was a bug at the interface: the parent key worked by copying files to the local directory. That’s not what you want.
The good news is that this was guarded against in the Ruby code. But there was a bypass, by specifying a binary sequence, the safeguard in the Ruby code doesn’t trigger, but the binary code did see the unsafe entry. The only trick left to find was how to do path traversal to put the payload where it needed to go. And the answer was to use a devfile registry to pull a tarfile. A tarfile that can technically include both dots and slashes in its filename. Yep, you can put the ../
directory traversal right in the filename, for ultimate ease of use. The fixes landed back in January, and surely we’ve all updated out Gitlab instances since then, right?
The cyber scarecrow reminds me of another trick that was setting some locale in regedit as russian, as most hackers from there wouldn’t want to infect nationals
“Internal IPs don’t have valid HTTPS certificates”
Mine do. That’s basic security, it’s not hard to do.
You can’t get a signed cert for an IP, and definitely not for a local IP. What’s your approach?
Sorry – not on the IP address itself, you can’t get a cert for an IP (unless potentially you use your own root?).
But you can use hosts file or dnsmasq to point a domain at a local IP. It’s much safer than accessing something by IP as you’ll connect to completely the wrong machine if you’re on a different network.
I use a subdomain of a domain I own for each of my machines. I then use Acme.sh with DNS-01 to get a certificate for a subdomain.
so whats the matter with setting up a dns record that points to your local ip address then?
How do you get the SSL cert to validate? Gotta have the service exposed to the public Internet for Let’s encrypt. Or set up some weird system to copy the cert around. Neither is great, though admittedly workable.
And yet, where is Amazon’s WireGuard offering?
Author of Cyber scarecrow here. Thanks for the mention!
Very interesting article.