NetMask NPM包,由270k +项目使用,易受八进制输入数据

2021-03-30 00:33:18

以下研究概述了当前使用的NetMask NPM包中发现的漏洞,目前由278,7222个其他项目使用。该漏洞已有9年。

由于此程序包非常令人难以置信,我会建议每个NodeJS开发人员检查他们的package.jsons以查看它们是否使用NetMask ...并立即升级!

显然,大多数下载都没有由人类完成,而是通过连续的整合和连续递送管道(CI / CD)进行。

对于不知道的读者,它只是“自动代码”。

虽然它似乎很明显,大多数30,000,000,000,000多个下载是某人的Docker图像或单元测试的一部分,它应该同样显而易见,几乎所有这些包都不会在运行时在最终用户中“检查”。

几个月前,我们在称为私有IP的下游封装中发现了不同的漏洞(CVE-2020-28360)。

当我们修复漏洞时,我们添加了一个名为“netmask”的新包。包装非常受欢迎,每周有数百万下载。

虽然我喜欢Regex,但是当时修复私有IP包的表达数量会很长,所以我们决定使用更强大的东西:网络掩码。

修复包括添加一个名为NetMask的另一个包,然后使用更简单的表示法定义IP地址范围/块。

它似乎完全是完全评估IP是否在任何给定的IPv4范围内或外部的包装。

让私人= [' 0.0.0.0 / 8' ;,' 10.0.0.0/8' ;,' 100.64.0.0/10' ;,' 127.0.0.0 / 8&#39 ;,' 169.254.0.0/16' ;,' 172.16.0.0/12' ;,' 192.0.0.0/24' ;,' 192.0 .0.0 / 29&#39 ;,' 192.0.0.8/32' ;,' 192.0.0.9/32' ;,' 192.0.0.10/32' ;,&#39 ; 192.0.0.170/32' ;,' 192.0.0.171/32' ;,' 192.0.2.0/24' ;,' 192.31.196.0/24',& #39; 192.52.193.0/24' ;,' 192.88.99.0/24' ;,' 192.168.0.0/16' ;,' 192.175.48.0/24' ,' 198.18.0.0/15' ;,' 198.51.100.0/24' ;,' 203.0.113.0/24' ;,' 240.0.0.0/4&# 39 ;,' 255.255.255.255/32' ]

对于给定IP,它将返回true或false关于IP是否在定义的“块”中。

例如,网络掩码有许多其他功能,例如,它会向您展示其中有多少IP在该块中:

几个月过去了,然后大约两周前,两个研究人员参与了私人IP固定,约翰杰克逊& Nick Sahler,联系了自己,生病的代码,与新的SSRF旁路相关。

开发人员& 研究员,Victor Viale,已经发现另一个旁路,他向我们展示如下: 如果您想知道此问题的问题,可以在以下示例中演示: 在浏览器中输入0127.0.0.1,或者只是访问浏览器中的以下链接:http://0127.0.0.1。 问题是,私有IP思考0127是127,因为它不是评估为以八进制格式的第一个八位字节,如真正十进制值87。 私有IP将看到0177为177,但实际位置是... 127在您的环回范围内! 如果您的浏览器识别八进制文字,但NodeJS应用程序没有,用户可以提交似乎内部的所有类型的恶性URL,但真的转到远程文件。 另一方面,用户还可以提交似乎公众的URL,但它们实际上非常私密!

伟大的...私人IP易于绕过,又重要,导致潜在的服务器端请求伪造(SSRF)或远程文件包含(RFI)。 起初,我认为另外的开发人员在添加网络掩码后恢复了私有IP返回Regex。 尼克再次立即检查代码并提醒我们只使用正则表达式过滤IPv6。 如果你失去了汽车钥匙,那么有人通常会告诉你站在某些东西上,得到了不同的房间的视角。 从不同的角度看代码,就像鹰眼的方法一样,有时你必须捏住自己并重新审视代码的明显部分,就像是非常基本的包裹。 记得shellshock? inck排除了IPv4正则表达式问题后,我最终排除了整个私有IP。 私有IP中的SSRF旁路实际上是由我们引入的新上游包引起的,我们已被称为网络掩码。

近十年来,网络掩码一直错误地将八进制输入数据作为字符串读取; 只需在前面剥离0并使用其余数据作为合法。 所有270,000个项目易受攻击吗? 好吧,很可能不是:它完全取决于项目如何使用它。 我个人没有重点检查278,000个代码基础,但其中一些项目是API,安全软件,加密项目,后端项目以及前端项目。 此时,另一个开发商凯莉,加入我们,因为我们手上有一个重大问题。 我们需要弄清楚谁在这里发生故障:是NetMask的错,因为不关心八进制输入数据? 或者它由使用网络掩码的每个单独的项目,在网扫描之前正确地将IP地址划分为十进制值,或者将八进制数据解析为十进制值? 每个其他项目怎么样,并在该项目的基础或范围内使用NetMask使用?

有一个无限量的问题,没有人可以真正计算,而不检查所有270k下游代码基础。

“如果您提交012,网络掩码将看到12(公共),但它真的是10(私人)”

“如果您提交010,网络掩码将看到10(私人),但它真的是一个8(公众)”

如果NetMask定义最终目标,则有人可以从第一列输入IP地址,但实际上从第二列获取数据。

0254.17.0.1读为254.17.0.1。但它进入了Docker Bridge Gateway,172.17.0.1(私人),通常是localhost!

同样,任何人都可以从第二个/第三列提交IP地址,并显示为私有,但实际上可以通过欺骗网络掩码来追溯思维方式来传送恶意软件。

控制87.0.0.1的个人使用Telecom Italia的意大利人,实际上可以将恶意软件提供给通过使用网络掩码的应用程序所做的请求,以查看请求是否为本地。

有人,就在巴西中部,某人有IP地址:177.0.0.1,这是0177.0.0.1的真正目的地。 其他有趣的其他包括010.0.0.1,这是8.0.0.1,由3级通信控制。 使用NetMask的一些VPC / VPN适应可能会考虑010.0.0.1私有,但真的达到8.0.0.1。 同样,012.0.0.1(AT& T服务)看起来是网克的,但它真的是您的私人网络的单向机票! 您不需要特殊的IP地址来执行此操作,您可以简单地提交公共网址并获取本地文件。 从字面上有很多漏洞导致它会让你的头部旋转。 如何将CNAME鞭打到一些蜘蛛蜜罐,并欺骗使用NetMask将自己电子邮件发送到顽皮列表的应用程序?

如何将Azure应用程序欺骗到服务邻居的请求中!

实际用CoffeeScript编写的节点网络掩码包的维护者是Netflix的工程总监,这是意外的! @rs是超级响应,并与我们合作在修复程序上,特别是在我们报告后的几天内获得第一个修补程序。

IP2Long将输入字符串IP分割为字符串数组,然后最终返回单个长度。

IP2Long =(IP) - > B =(IP +'')。分裂('');如果b.length为0或b.length> 4然后抛出新的错误('无效的IP'),如果ISNAN Parseint(字节,10),则在B中抛出新错误("无效的字节:#{byte}" )如果byte< 0或字节> 255然后抛出新的错误("无效的字节:#{byte}")返回((b [0]或0)< 24 |(b [1]或0)< 16 |(B [2]或0)<(b [3]或0)))>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 0.

由于我们知道0-Prefixed JavaScript表单中的Base-8整数,由于在使用Parseint(字节,10)中的显式基本呼叫,我们最初选择在处理输入之前选择将它们脱离基地10。

...对于byte,i在b如果是iSNAN解析int(字节,10),那么抛出新的错误("无效的字节:#{byte}")如果byte和byte [0] ==&#39 ; 0' #确认0前缀字节被解析为八进制字节= parseint(字节,8)else byte = parseint(字节,10)如果是isnan(byte),则抛出新的错误("无效的byte:#{byte}&# 34;)如果byte< 0或字节> 255然后抛出新的错误("无效的字节:#{byte}")b [i] = bytereturn((b [0]或0)> 0.

以上不与十进制相同的0 - 前缀八位字节。我们用以下测试验证,然后向@RS发送了我们建议的补丁以供考虑:

'框31.0.0/8' ;:主题: - >新(' 31.0.0.0/8')'包含IP 31.5.5.5&#39 ;:(块) - > assert.ok block.Contains(' 31.5.5.5')'不包含IP 031.5.5.5(25.5.5.5)&#39 ;:(块) - > Assert.ok不是块。附件(' 031.5.5.5')'框127.0.0.0/8'主题: - >新(' 127.0.0.0/8')'包含IP 127.0.0.2&#39 ;:(块) - > assert.ok block.Contains(' 127.0.0.2')'包含IP 0177.0.0.2(127.0.0.2)&#39 ;:(块) - > assert.ok block.Contains(' 0177.0.0.2')

我们以前考虑过NetMask如何解释基础-8整数的主要问题,尽管围绕使用内置NodeJS Parseint函数有一些辩论。

虽然我们的第一次尝试解决了我们最初看到的问题,但它也在网络掩码中创造了另一个漏洞。

现在代码将解析基本16整数,在JavaScript中以0x(即0xFF)开头,如0-前缀八进制......

这个错误只会过大约几个小时,这是一个升级到最新版本需要278,000多个项目的十亿日。

...#禁用十六进制inputif /\d/.test(字节)然后抛出新的错误("无效的字节:#{byte}")如果ISNAN Parseint(字节,10)抛出新错误( "无效的字节:#{byte}")如果byte和byte [0] ==' 0' #确认0前缀字节被解析为八进制字节= parseint(字节,8)else byte = parseint(字节,10)如果是isnan(byte),则抛出新的错误("无效的byte:#{byte}&# 34;)如果byte< 0或字节> 255然后抛出新的错误("无效的字节:#{byte}")b [i] = byte ...

现在我们在传统的“点十进制”表示法中表达了IP地址,其在十六进制中被解析为长期,正如我们在基础-8和Base-10中所能。为了符合NodeJS生态系统的其余部分评估IP地址的方式,NetMask不得禁用Base-16中的IP地址或位掩码输入。这也是一个破坏的变化......由于网络掩码先前允许十六进制输入(如README所示)。

在这一点上,我向节点JS跨堆积代码添加了一系列Mocha测试到节点网络掩码存储库,因为我们知道CoffeScript和节点可以评估一些事情......

...如果byte和byte [0] ==' 0'如果byte.length> 2和(字节[1] ==' x'或byte [1] ==' x')#确保0x前缀字节被解析为hex字节= parseint(字节,16 )否则#确保0前缀字节被解析为八进制字节= Parseint(字节,8)else字节= parseint(字节,10)...

我们尚未考虑解释的事实,这是一个Nodejs内置,条纹空白空间......

Victor和我自己(Kelly)经过验证的验证肯定会发现在IP八位字节之前或之后的白色空间。

否则,如果byte和(byte [0] ==''或byte [byte.length-1] =='')抛出新的错误('无效的ip&# 39;)

CoffeScript版本的NetMask中的最终修补版IP2Long的版本如下所示:

IP2Long =(IP) - &gt; B =(IP +&#39;&#39;)。分裂(&#39;&#39;);如果b.length为0或b.length&gt; 4然后抛出新的错误(&#39;无效的IP&#39;)对于byte,如果byte和byte [0] ==&#39; 0&#39;如果byte.length&gt; 2和(字节[1] ==&#39; x&#39;或byte [1] ==&#39; x&#39;)#确保0x前缀字节被解析为hex字节= parseint(字节,16 )else#确保0前缀字节被解析为八进制字节= parseint(字节,8)否则如果byte和(byte [0] ==&#39;&#39;或byte.length-1] == &#39;&#39;)抛出新的错误(&#39;无效的ip&#39;)byte = parseint(字节,10)如果是isnan(byte),则抛出新的错误(&#34;无效的byte:# {byte}&#34;)如果byte&lt; 0或字节&gt; 255然后抛出新的错误(&#34;无效的字节:#{byte}&#34;)b [i] = byte,而b.length&lt; 4 B.Unshift(0)返回(B [0]&lt; <1]&lt; <2]&lt;&lt;&lt;&lt;&lt; [3]&gt;&gt;&gt;&gt;&gt;&gt;&lt; <3]&lt;&lt;&lt;&lt;&lt; 0.

唯一合理的修复是考虑前导零使IP无效。 试图说服一些人应该被认为是八进制人或其他不应该被认为是八万的其他人是徒劳的。