有时我会发现一些有趣的兔子洞。今晚,事情就这样过去了。 Ubiquiti 为他们的 USG 设备发布了一个新版本的软件,因为他们有这个东西,他们的 dhcpv6-pd 实现可以被坐在网络上正确位置的人(即,你的“WAN”端口)利用来运行任意命令.
自从他们为这些设备推出新版本以来已经有一段时间了,我想知道还有什么变化。我发现,当公司本应发布“只是一个安全修复程序”补丁时,他们通常最终会发布更多的补丁,而且可能还会破坏一些东西。 (我仍然对 Mac 的 2020-002“安全更新”感到不满。)
无论如何,这让我开始思考:你能区分这些东西吗?事实证明,你当然可以。这是两个 squashfs 文件系统,所以挂载它们并比较它们,然后挖掘结果,然后……嘿。
/opt/vyatta/sbin/dhcpv6-pd-response.pl: 如果(定义$域){ $域 =~ s/\.\s+$//; + $domain =~ s/[^A-Za-z0-9.]+/-/g; $dn = $域名; } 别的 { $dn = "";
是的。这就是改变。就是这样。 *捂脸*。然后我感到无聊并继续查看输出以查看还发生了什么。那时我看到整个 /etc/shadow 都发生了变化。一堆数值改变了,像这样:
-root:!:18920:0:99999:7::: +root:!:19369:0:99999:7:::
我必须查一下才能确定,但那是“上次更改密码的日期”。将它们除以 365,你会发现其中一个大约是 51 年,另一个大约是 53 年。因此,2021 年和 2023 年分别是上一版本和新版本的日期。他们的发布过程显然重建了影子文件。
但这还不是兔子洞的尽头。想起上周的时间帖子,我开始查看那个数字。它是如此之小。它适合 16 位(但不是 15 位)。我想知道他们用什么类型来固定它。我进入了阴影源。
我发现的第一件事是名为 strtoday() 的东西。它看起来像这样(稍微调整以适合此处):
long strtoday (const char *str) { 时间_t; [...] t = get_date (str, NULL); 如果 ((time_t) - 1 == t) { 返回-2; } /* 将秒数转换为自 1970-01-01 以来的天数 */ 返回(长)(t + DAY / 2)/ DAY; }
嗯。它返回一个长。在 32 位机器上,long 是 4 个字节,即使在 glibc 执行了他们的“time_t 现在是 64 位”的事情之后,它仍然是 4 个字节,最终从管道中下来。多头不会改变。
那么,这什么时候中断?事实证明……在其他一切爆炸前 12 小时。 “DAY”在源代码中定义为 (24L*3600L),因此 86400 – 一天中的秒数。它占用了其中的一半(因此 43200 – 12 小时的秒数)并将其添加到从 get_date 返回的值中。这使得它提前 12 小时爆炸。
2038-01-18 15:14:08Z 是该代码开始返回负数的时间。那会很有趣。
请记住,带符号的 32 位 time_t 的实际“结束时间”是 12 小时后:2038-01-19 03:14:08Z。
这里的教训是:如果你花时间对其进行数学运算并将其推入另一种数据类型,你最好确保它不会溢出那些*不会*在现在和那时扩展的类型之一.
…
$猫t.cc #include <stdio.h> #include <sys/time.h> #include <cinttypes> #定义日期 (24L*3600L) 长 strtoday_tt(time_t t) { 返回(长)(t + DAY / 2)/ DAY; } 诠释主要(){ printf("2147440447 -> %ld\n", strtoday_tt(2147440447)); printf("2147440448 -> %ld\n", strtoday_tt(2147440448)); 返回 0; } $./吨 2147440447 -> 24855 2147440448 -> -24855