调制解调器克隆:为了乐趣(而非利益!)
最近,我在垃圾箱旁边发现了一个旧的电缆调制解调器。一个邻居刚搬走,他们扔掉了几箱旧垃圾。我很兴奋,因为这个调制解调器比我目前使用的要好得多,并且具有内置5GHz WiFi和DOCSIS 3.0支持等高级功能。然而,当我致电我的互联网服务提供商激活它时,他们告诉我该调制解调器与另一个账户绑定,可能是因为邻居在扔掉设备之前没有停用它。技术人员无法访问他们的账户,所以我必须等待它变为非活动状态,或者以某种方式找到他们并说服他们帮我设置他们扔掉的调制解调器。
但黑客总能找到第三种选择。我以为我可以重新编程MAC地址并毫无问题地激活它。调制解调器/路由器因其总是使用过时的软件和未受保护的硬件而臭名昭著地容易被黑客攻击。几乎每个逆向工程博客在某个时候都有关于黑客攻击某个路由器的文章,每个硬件黑客"训练营"都在NETGEAR或Linksys设备上工作。所以这篇文章将是我撰写"真正"硬件黑客博客的入门仪式。
BPI+
获取shell访问权限容易得可笑,所以我甚至不会详细介绍。简而言之,我在贴纸上找到了FCC ID并通过谷歌搜索找到了板的完整原理图以及所有芯片的部件号(此类信息是FCC批准过程所要求的,但大多数公司要求保密)。通过原理图,我找到了UART控制台,它通过一些未填充的端口很好地暴露出来。事实上,我在这里做了太多工作,因为在打开设备后,我发现"CONSOLE"一词印在那些端口旁边的阻焊层上。焊接一些排针后,我能够将其连接到我的树莓派,并在不需要任何密码的情况下进入root shell。整个过程花了大约一个小时——最费时间的是试图物理打开塑料外壳,因为(这可能令人惊讶)黑客并非体力的典范。
一旦获得shell,我转储了闪存,并搜索了印在标签上的MAC地址(尝试了十六进制、ASCII和不同的分隔符)。我在一个标记为NVRAM的分区中找到了一个包含MAC地址的文件。该文件似乎没有任何校验和,所以我只是用新的MAC替换了它,重新启动然后……什么都没发生。调制解调器拒绝建立连接。那时真正的工作开始了……
第一个线索是在NVRAM分区中四处查看,并找到一组为调制解调器MAC地址签名的证书。谷歌搜索"DOCSIS certificate"让我陷入了调制解调器克隆、服务窃取、带宽解锁等的兔子洞。我了解到不久以前,人们会修改他们的调制解调器配置文件以解锁比他们支付的更高的速度(如果有的话)。随着ISP加强和保护其基础设施,黑客转向通过查找现有订阅者的MAC地址并重新编程他们的调制解调器以使用相同的MAC地址来"克隆"调制解调器以窃取服务。由于所有这些,DOCSIS 1.1规范建立了一个用于MAC地址验证的PKI系统。
首先,我为我的新MAC地址生成了一组自签名证书。令人惊讶的是,我能够配置调制解调器,并且我的ISP接受了证书并给了我一个IP地址。不幸的是,我无法访问互联网,甚至使用我旧路由器的MAC地址也不起作用。我的猜测是,自签名证书被工程师用来测试网络,因此不允许访问互联网。它可能还与针对"简单"克隆的保护措施有关。现在我计划从非活动设备获取一组新证书。我上eBay买了一个坏的SurfBoard SBG6580。选择这个型号纯粹是因为它是我能找到的最便宜的。既然它坏了,它更有可能被停用。
转储SBG6580
不幸的是,FCC没有公开此设备的原理图,但快速检查显示标记为Spansion FL128SAIF00的芯片是一个16MiB的基于SPI的闪存,其数据表很容易在线获取。作为一个TSOP芯片,焊接电线很容易,幸运的是我记得在我降级PS3时使用的NORway,并且它支持SPI转储。我连接了Teensy2++并修补了支持以检测此芯片。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
diff --git a/SPIway.py b/SPIway.py
index f3a48b7..f79e52c 100644
--- a/SPIway.py
+++ b/SPIway.py
@@ -176,6 +176,22 @@ class SPIFlasher(TeensySerial):
print "Chip type: unknown (0x%02x)"%self.DEVICE_ID
self.close()
sys.exit(1)
+
+ if self.MF_ID == 0x01:
+ print "Chip manufacturer: Spansion (0x%02x)"%self.MF_ID
+ if self.DEVICE_ID == 0x17:
+ print "Chip type: FL128SAIF00 (0x%02x)"%self.DEVICE_ID
+ self.SPI_BLOCK_COUNT = 256
+ self.SPI_SECTORS_PER_BLOCK = 16
+ self.SPI_SECTOR_SIZE = 0x1000
+ self.SPI_TOTAL_SECTORS = self.SPI_SECTORS_PER_BLOCK * self.SPI_BLOCK_COUNT
+ self.SPI_BLOCK_SIZE = self.SPI_SECTORS_PER_BLOCK * self.SPI_SECTOR_SIZE
+ self.SPI_ADDRESS_LENGTH = 4
+
+ else:
+ print "Chip type: unknown (0x%02x)"%self.DEVICE_ID
+ self.close()
+ sys.exit(1)
else:
print "Chip manufacturer: unknown (0x%02x)"%self.MF_ID
print "Chip type: unknown (0x%02x)"%self.DEVICE_ID
|
binwalk能够找到一些嵌入式证书
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
67322 0x106FA Certificate in DER format (x509 v3), header length: 4, sequence length: 803
68131 0x10A23 Certificate in DER format (x509 v3), header length: 4, sequence length: 1024
70249 0x11269 Certificate in DER format (x509 v3), header length: 4, sequence length: 808
71063 0x11597 Certificate in DER format (x509 v3), header length: 4, sequence length: 988
83445 0x145F5 Certificate in DER format (x509 v3), header length: 4, sequence length: 866
84317 0x1495D Certificate in DER format (x509 v3), header length: 4, sequence length: 983
85306 0x14D3A Certificate in DER format (x509 v3), header length: 4, sequence length: 864
100090 0x186FA Certificate in DER format (x509 v3), header length: 4, sequence length: 803
100899 0x18A23 Certificate in DER format (x509 v3), header length: 4, sequence length: 1024
103017 0x19269 Certificate in DER format (x509 v3), header length: 4, sequence length: 808
103831 0x19597 Certificate in DER format (x509 v3), header length: 4, sequence length: 988
116213 0x1C5F5 Certificate in DER format (x509 v3), header length: 4, sequence length: 866
117085 0x1C95D Certificate in DER format (x509 v3), header length: 4, sequence length: 983
118074 0x1CD3A Certificate in DER format (x509 v3), header length: 4, sequence length: 864
131164 0x2005C LZMA compressed data, properties: 0x5D, dictionary size: 16777216 bytes, uncompressed size: 2898643604054482944 bytes
8388700 0x80005C LZMA compressed data, properties: 0x5D, dictionary size: 16777216 bytes, uncompressed size: 2898643604054482944 bytes
|
这包括用于美国和欧洲地区的DOCSIS BPI+证书以及代码签名证书和根证书。但不幸的是,没有私钥。根据经验,私钥很可能存储在公钥附近,所以我在十六进制转储中查找了可能的候选者。在一些证书之间有看起来随机的数据块。此外,在每个证书之前似乎有一个两字节的DER文件长度。因此,我能够解析NV存储以转储证书、一些纯文本设置和设备信息,以及我之前看到的0x2A0大小的数据块。这些数据不能是RSA密钥的私钥指数,因为它太大了。它似乎也不包含任何结构,因此不能包含密钥的任何CRT组件。我的假设是它是一个加密的PKCS#8 RSA私钥,采用DER格式。证据是文件大小与加密块对齐,我的另一个调制解调器使用PKCS#8 DER,并且RSA1024密钥的PKCS#8 DER约为0x279字节,这与0x2A0非常接近(相比之下,PEM编码的密钥至少为0x394字节,而PKCS#1 DER由于额外的因子几乎为1KB)。
逆向固件
考虑到这一点,没有其他办法,只能逆向固件。binwalk中的最后两个条目显示了两个大的压缩块,这是一个好的开始。我发现另一个黑客以前处理过这种压缩,技巧在于头是非标准的(它缺少有效的未压缩大小字段)。谷歌搜索CPU给出了这个wiki,断言架构是大端MIPS。固件中有许多对0x8000….的引用,而没有对0x7fff….的引用,所以我假设加载地址是0x80000000。当然,加载地址是不正确的,但与其花时间逆向引导加载程序,我反而假设加载地址是页对齐的(因为不考虑安全的理智程序员不会这样做),并从代码中找到一个指向大段字符串的随机指针,并将指针递增0x1000,直到我找到一个从该地址开始的字符串。加载地址是0x80004000。
幸运的是,有足够的调试字符串来缩小在16MiB固件中搜索解密例程的范围。通过查找诸如"decrypt"、“bpi"和"private"之类的术语,我能够找到一个函数,该函数打印出******** Private Key Source is ENCRYPTED. (%d BytesUsed)\n以及@@@@@@@@@ des3ABC_CBC_decrypt() failed @@@@@@@。看起来很有希望。
从调试printf来看,很明显某个数据块被传递给一个名为des3ABC_CBC_decrypt的函数。我假设这意味着具有3密钥配置的3DES EDE。输入密钥是21字节,这是非标准的。事实证明,有一个简单的密钥派生过程(通过隐蔽实现安全性),涉及打乱密钥字节,并从每个字节中减去索引。然后,21字节的密钥,即8组7位,被转换为8组8位的标准表示,其中每组都有一个奇偶校验位。我在下面包含了逆向的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int convert_key(unsigned char *src, unsigned char *dst) {
unsigned char v0, v1, a2, *v1x, a0x;
// convert 7-bit groups to 8-bits
v0 = src[0];
v0 &= 0xFE;
dst[0] = v0;
v1 = src[1];
v1 >>= 1;
v1 &= 0x7E;
v0 = src[0];
v0 &= 1;
v0 <<= 7;
v1 += v0;
dst[1] = v1;
v1 = src[2];
v1 >>= 2;
v1 &= 0x3E;
v0 = src[1];
v0 &= 3;
v0 <<= 6;
v1 += v0;
dst[2] = v1;
v1 = src[3];
v1 >>= 3;
v1 &= 0x1E;
v0 = src[2];
v0 &= 7;
v0 <<= 5;
v1 += v0;
dst[3] = v1;
v1 = src[4];
v1 >>= 4;
v1 &= 0xE;
v0 = src[3];
v0 &= 0xF;
v0 <<= 4;
v1 += v0;
dst[4] = v1;
v1 = src[5];
v1 >>= 5;
v1 &= 6;
v0 = src[4];
v0 &= 0x1F;
v0 <<= 3;
v1 += v0;
dst[5] = v1;
v1 = src[6];
v1 >>= 6;
v1 &= 2;
v0 = src[5];
v0 &= 0x3F;
v0 <<= 2;
v1 += v0;
dst[6] = v1;
v0 = src[6];
v0 <<= 1;
dst[7] = v0;
// compute the parity bit
a2 = 0;
v1x = &dst[a2];
do {
v0 = v1x[0];
v0 ^= 1;
v1x[0] = v0;
a0x = 1;
v0 = v1x[0];
do {
v0 = (char)v0 >> a0x;
v0 &= 1;
a0x += 1;
if (v0 != 0) {
v0 = v1x[0];
v0 ^= 1;
v1x[0] = v0;
}
v0 = v1x[0];
} while (a0x < 8);
a2 += 1;
v1x = &dst[a2];
} while (a2 < 8);
return 0;
}
static int derive_key(unsigned char *src, unsigned char *dst) {
for (int j = 0; j < 7; j++) {
for (int i = 0; i < 3; i++) {
dst[7*i + j] = src[3*j + i] - j;
}
}
return 0;
}
int main(int argc, const char *argv[]) {
if (argc != 2) {
fprintf(stderr, "%s\n", "invalid args\n");
return 1;
}
if (strnlen(argv[1], 64) != 42) {
fprintf(stderr, "%s\n", "key must be 42 hex characters\n");
}
fprintf(stderr, "in : ");
unsigned char in[21];
for (int i = 0; i < 21; i++) {
char sym[3];
int cur;
int conv;
sym[0] = argv[1][2*i];
sym[1] = argv[1][2*i+1];
sym[2] = '\0';
cur = strtoul(sym, NULL, 16);
fprintf(stderr, "%02X", cur);
in[i] = cur;
}
fprintf(stderr, "\n");
unsigned char drv[21];
derive_key(in, drv);
printf("out: ");
for (int i = 0; i < 3; i++) {
unsigned char dst[8];
convert_key(&drv[7*i], dst);
for (int j = 0; j < 8; j++) {
printf("%02X", dst[j]);
}
}
printf("\n");
return 0;
}
|
使用正确的密钥,我能够解密0x2A0 blob,结果(如我所料)是一个DER编码的PKCS#8 RSA私钥,以及一个用于验证加密的SHA1哈希。
最后
这是一段有趣的旅程,但出于谨慎,我不会实际使用这个调制解调器。克隆MAC与窃取互联网服务过于交织在一起,尽管这不是我打算做的事情,但我不希望在我、政府和大ISP之间产生任何混淆。因此,这只是一个有趣的练习。作为对读者的建议,许多人因黑客攻击调制解调器而被捕。本网站不宽恕或推广任何非法活动,本文仅用于教育目的,更多是关于逆向硬件而不是绕过限制。
其他说明
以下是我在旅程中收集的一些"有趣事实”。
- 一些调制解调器有供你的ISP(通常是技术人员和客户服务代理)登录的后门。我查看的调制解调器有一个SSH服务器,在LAN(你的设备<->你的调制解调器)或WAN(你的调制解调器<->互联网)上不可见,但对CMTS(你的调制解调器<->你的ISP)可见。这是通过iptables强制执行的。他们还有一个单独的用户名/密码到路由器网关页面,密码较弱,你无法更改。如果你启用远程管理,此登录也可以从LAN和WAN工作。
- EAE(早期认证和加密)是DOCSIS 3.0中的一项功能,允许早期建立加密连接。我的ISP(美国顶级ISP之一)即使对于支持它的DOCSIS 3.0路由器也禁用了此功能。CM配置文件包含有关你的服务计划、你的上行/下行限制、你的带宽使用情况等信息。这是与DHCP请求一起以未加密方式发送的,以建立IP地址。
- 由于上述原因,可能通过DHCP对邻居执行MITM攻击。
- DOCSIS 3.0提供了使用AES-128每会话流量加密的能力,但我的ISP(再次是美国顶级ISP之一,所以我怀疑其他ISP在这方面有所不同)选择使用DES(顺便说一下,不是3DES)和56位密钥(因为他们仍然支持DOCSIS 2.0)。请注意,2015年提出了一种使用彩虹表使破解DOCSIS流量变得微不足道的攻击。阅读我的ISP的服务协议,他们似乎承认了这一点,并声明他们的服务没有隐私期望。我猜法律修复比技术修复容易得多。