有时我会发现一些有趣的兔子洞。今晚,事情就这样过去了。 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