被加密的移动光猫 PPPoE 密码
最近帮朋友家新装的宽带配网时,登录超级管理员 CMCCAdmin 后,发现光猫管理页面上的 PPPoE 密码是 6 个 *
星号 —— 好在这台光猫不需要拆机就可以开启 Telnet,不需要大动干戈地拆机拿示波器去找 TTL 了。然而高兴了没多久,登录进 Telnet 之后才发现,虽然是熟悉的 pppd
拨号,但配置里的拨号密码被加密了……
光猫的型号是 ZXHN F7005MV3,可以使用 zteOnu 工具开启 Telnet:
./zteOnu --ip "192.168.1.1" --new --user "CMCCAdmin" --pass "CMCCAdminXXXXXXXX" --port 80 --tp 23
ZteONU dev, built at unknown
source: https://github.com/thank243/zteOnu
-----------------------------------
step [0] reset factory: ok
step [1] request factory mode: ok
step [2] send sq: ok
step [3] check login auth: ok
step [4] enter factory mode: ok
-----------------------------------
user: aaaaaaaa
pass: bbbbbbbb
登录 Telnet 后,使用 ps
命令看到光猫使用 pppd
进行 PPPoE 拨号:
ps | grep pppd
5718 user 7568 2376 S <1> pppd 0 oe normal file /var/tmp/ppp/options.oe0 nbif1
6187 root 6276 948 S <1> grep pppd
查看 pppd
的配置文件 /var/tmp/ppp/options.oe0
(其中地址/用户名/密码已被替换为样例):
cat /var/tmp/ppp/options.oe0
plugin rp-pppoe.so
+ipv6
ipv6 ::d2dd:ffff:ffff:ffff,
user 11111111111
plugin passpppoe.so
encrypasswd 5876a52b309edc013d37d8c857e11db1
encrypasswdlen 16
lcp-echo-failure 3
lcp-echo-interval 10
mru 1480
mtu 1480
dscp -1
priority -1
queuenum 15
padi-nterval 5
usepeerdns
可见拨号密码加密为 encrypasswd
字段,注意到可疑的 passpppoe.so
插件,猜测和处理解密有关,在 /lib
找到了这个文件(下载),使用 curl 提取出来:
file passpppoe.so
passpppoe.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=6bd5fa48300fa20933647df62747c97128e52e70, stripped
使用 IDA 打开,找到 plugin_init
函数:
int plugin_init()
{
int result; // r0
result = add_options(&off_11170);
pap_check_hook = sub_978;
pap_passwd_hook = sub_AB0;
chap_check_hook = sub_978;
chap_passwd_hook = sub_AB0;
return result;
}
解密逻辑在 pap_passwd_hook
和 chap_passwd_hook
所指向的 sub_AB0
函数中,对变量名进行还原后的逻辑如下:
int __fastcall sub_AB0(int a1, char *result)
{
int dec_len; // r4
size_t s_len; // r0
bool has_extra_block; // zf
int i; // r9
unsigned int dec_len_; // r4
char *decrypted_block; // r1
char *decoded_block; // r0
char s_sha256[32]; // [sp+10h] [bp-2E4h] BYREF
char s[36]; // [sp+30h] [bp-2C4h] BYREF
char decoded[196]; // [sp+54h] [bp-2A0h] BYREF
char decrypted[196]; // [sp+118h] [bp-1DCh] BYREF
_BYTE aes_key[280]; // [sp+1DCh] [bp-118h] BYREF
memset(s, 0, 0x21u);
memset(decoded, 0, 0xC3u);
memset(decrypted, 0, 0xC3u);
if ( !memcmp(&encrypasswd, decrypted, 0xC3u) )
{
ProcUserLog("passdecry.c", 113, "pwdecrypt_passwd", 4, 0, "the encrypasswd is NULL");
return -1;
}
else
{
if ( result )
{
j_HexToStr((int)&encrypasswd, pass_dec_len, (int)decoded);
strncpy(s, "608158c36497b00221db14afb845c9e3", 0x20u);
dec_len = pass_dec_len;
memset(s_sha256, 0, sizeof(s_sha256));
memset(aes_key, 0, 0xF4u);
s_len = strlen(s);
// 密钥是上述字符串的 SHA256 值
SHA256((int)s, s_len, s_sha256);
AES_set_decrypt_key((int)s_sha256, 256, (int)aes_key);
// AES padding 的处理
has_extra_block = (dec_len & 0xF) == 0;
if ( (dec_len & 0xF) != 0 )
dec_len &= 0xFFFFFFF0;
i = 0;
if ( !has_extra_block )
dec_len += 16;
dec_len_ = dec_len & 0xFFFFFFF0;
// AES-ECB 解密
while ( dec_len_ != i )
{
decrypted_block = &decrypted[i];
decoded_block = &decoded[i];
i += 16;
AES_decrypt(decoded_block, decrypted_block, aes_key);
}
strncpy(result, decrypted, 0xFFu);
}
return 1;
}
}
可见,插件调用了 OpenSSL 进行解密,所用的加密方式是 AES-256-ECB,密钥是一段字符串的 SHA256 哈希值。由此编写出解密脚本:
import crypto from "crypto";
const encrypasswd = Buffer.from("d6122e4ad26fc0c611159cfb99dc9535", "hex"); // 该密码为样例
const aesKey = crypto.createHash("sha256").update("5876a52b309edc013d37d8c857e11db1").digest();
const decipher = crypto.createDecipheriv("aes-256-ecb", aesKey, null);
decipher.setAutoPadding(false);
const decrypted = Buffer.concat([
decipher.update(encrypasswd),
decipher.final()
]);
console.log(decrypted);
console.log(decrypted.toString("utf8"));
得到密码后,删除 PPPoE 和 TR069 网络,把 PPPoE 和 TR069 的 VLAN ID 桥出来,并在 LAN 口上配置 VLAN 绑定,这之后就是熟悉的配网流程了。