The popular npm netmask library recently encountered a serious problem, explained as follows:
The npm netmask package incorrectly evaluates individual ipv4 octets that contain octal strings as left-stripped integers, leading to an inordinate attack surface on hundreds of thousands of projects that rely on netmask to filter or evaluate ipv4 block ranges, both inbound and outbound.
Got that?
In case you can’t read mumbo jumbo, hold on, and I’ll try to explain.
The basics
The npm library netmask is used by hundreds of thousands of applications and amasses over 3 million weekly downloads. It is used to read and manipulate IP addresses.
If you understand IP addresses and octals, you can skip the next section.
IP address
An IP address tells us how to find a certain device within a network. For each network a computer is connected to, it has an IP-address on that network. The IP address for this website is 130.211.198.3
, for example.
Some things that happen inside a computer rely on an IP address too. For that we can use either 0.0.0.0
or
127.0.0.1
, which is why that one is called “home” or “localhost”. Domains, names used to address computers, are associated with IP-addresses. The Domain Name System (DNS) translates domain names used by people, like blog.malwarebytes.com
into the IP addresses used by computers, like
130.211.198.3
. The DNS system is often compared to a phone book where you can look up a person’s name to find their phone number. IPv4 octets
When you see an IP address you will probably recognize it for what it is. The typical format of an IP version 4 address is very familiar: Four numbers between 0 and 255 separated by three dots.
In fact, an IP address is a decimal representation of a 32-bit number. The 32-bit number is grouped 8 bits at a time, each group of 8 bits is an octet. The octets are separated by a dot, and represented in decimal format, this is known as dotted decimal notation. The possibilities range from 0.0.0.0
to
255.255.255.255
. The difference between decimal and octal
Decimal means a number expressed in the base-ten system which is the system that we use every day that uses the digits 0 to 9, whereas octal means the number system that uses the eight digits 0, 1, 2, 3, 4, 5, 6, 7.
Since an IP address is a 32 bit number it makes a lot of sense to use the octal number system. In that system the dotted octal 127.0.0.1
looks like
0177.0000.0000.0001
. Here’s why: In decimal, numbers are written according to how many ones they have, how many tens, how many hundreds, and so on. So the number 127 is 1 * 100, 2 * 10 and 7 * 1.
In octal, numbers are written according to how many ones they have, how many eights, how many 64s, and so on. So the number 127 is represented as 0177, which is 0 * 128, 1 * 64, 7 * 8 and 7 * 1.
Using different numerical systems is no problem for computers, as long as it’s clear which one you are using. Allowing mixed input for an application is asking for problems, however.
The netmask vulnerability
Zeroes
To understand the problem it helps to understand how things are supposed to work, copy the octal IP address 0177.0000.0000.0001
into your browser address bar. It should get correctly translated to
127.0.0.1
. And try 0177.0.0.1
in the same browser you used before. And act surprised when it still takes you to 127.0.0.1
despite the fact that we did not write out the last three octets in full. 127.0.0.1
and
0177.0.0.1
look like they are in the same notation but they are not. The first zero on 0177.0.0.1
makes all the difference, and your browser knows this. The bug
The problem with tnetmask was that it stripped leading zeroes from IP addresses. So, if you fed it an address that starts with a zero, like 0177.0000.0000.0001
, it will not recognise it as an octal address and turn it into the decimal version,
127.0.0.1
like your browser. Instead it would treat it a decimal address, 177.0.0.1
, which is an address for a completely different computer. While this may seem more of an inconvenience than a security problem at first sight, but when an attacker is able to influence the IP address input being parsed by the application, the bug can give rise to various vulnerabilities.
Private IP or not?
Remember when I wrote that your computer has an IP address in every network it is connected to? Some IP address ranges are reserved for internal networks and can’t be used on the Internet. The most well-known is probably 192.168.1.0
to
192.168.1.255
, often written as 192.168.1.xxx
. Many home networks use the 10.0.1.xxx
network range. Importantly, many systems are set up to be more trusting towards traffic coming from inside a private network. See how that might pose a problem? If an attacker fed a vulnerable version of netmask the address 012.0.0.1
, netmask would read it as the public address
12.0.0.1
instead of the private address 10.0.0.1
. According to netmask’s own maintainer, the vulnerability could have allowed an attacker to abuse this trust and gain access to all kinds of things they shouldn’t:
A remote authenticated or unauthenticated attacker can bypass packages that rely on netmask to filter IP address blocks to reach intranets, VPNs, containers, adjacent VPC instances, or LAN hosts
CVE-2021-28918
Publicly disclosed computer security flaws are listed in the Common Vulnerabilities and Exposures (CVE) database. Its goal is to make it easier to share data across separate vulnerability capabilities (tools, databases, and services). This zero-day is listed as CVE-2021-28918.
The fix for CVE-2021-28918 has been released in version 2.0.1 of netmask on npm downloads. The Perl component Net::Netmask also suffered from this flaw, and its maintainer, Joelle Maslak has released a fix in the 2.0000 version today.
Stay safe, everyone!