A Journey into Synology NAS 系列二: findhostd服务分析
前言
上一篇文章主要对群晖NAS进行了简单介绍,并给出了搭建群晖NAS环境的方法。在前面的基础上,本篇文章将从局域网的视角出发,对群晖NAS设备上开放的部分服务进行分析。由于篇幅原因,下面将重点对findhostd服务进行分析,介绍对应的通信机制和协议格式,并分享在其中发现的部分安全问题。
服务探测
由于NAS设备是网络可达的,假设我们与其处于同一个局域网中,首先对设备上开放的端口和服务进行探测。简单起见,这里直接通过netstat命令进行查看,如下。

可以看到,除了一些常见的服务如smbd、nginx、minissdpd和snmpd等,还有一些自定义的服务如synovncrelayd、iscsi_snapshot_comm_core、synosnmpd和findhostd等。与常见服务相比,这些自定义的服务可能less tested and more vulnerable,因此这里主要对这些自定义服务进行分析,包括findhostd和iscsi_snapshot_comm_core。
findhostd服务分析
findhostd服务主要负责和Synology Assistant进行通信,而Synology Assistant则用于在局域网内搜索、配置和管理对应的DiskStation,比如安装DSM系统、设置管理员账号/密码、设置设备获取IP地址的方式,以及映射网络硬盘等。
通过抓包分析可知,Synology Assistant和findhostd之间主要通过9999/udp端口(9998/udp、9997/udp)进行通信,一个简单的通信流程如下。具体地,Synology Assistant首先发送一个广播query数据包,之后findhostd会同时发送一个广播包和单播包作为响应。在发现对应的设备后,Synology Assistant可以进一步发送其他广播包如quickconf、memory test等,同样findhostd会发送一个广播包和单播包作为响应。

抓取的部分数据包如上图右侧所示。可以看到,两者之间通过9999/udp端口进行通信,且数据似乎以明文方式进行传输,其中包括mac地址、序列号和型号等信息。
协议格式分析
为了了解具体的协议格式,需要对findhostd(或Synology Assistant客户端)进行逆向分析和调试。经过分析可知,消息开头部分是magic (\x12\x34\x56\x78\x53\x59\x4e\x4f),然后存在一大段与协议格式相关的数据grgfieldAttribs,表明消息剩余部分的格式和含义。具体地,下图右侧中的每一行对应结构data_chunk,其包含6个字段。其中,pkt_id字段表明对应数据的含义,如数据包类型、用户名、mac地址等;offset字段对应将数据放到内部缓冲区的起始偏移;max_length字段则表示对应数据的最大长度。

根据上述信息,可以将数据包按下图格式进行解析。具体地,消息开头部分为magic (\x12\x34\x56\x78\x53\x59\x4e\x4f),后面的部分由一系列的TLV组成,TLV分别对应pkt_id、data_length和data。

进一步地,为了更方便地对数据包格式进行分析,编写了一个wireshark协议解析插件syno_finder,便于在wireshark中直接对数据包进行解析,效果如下图所示。

需要说明的是,在较新版本的Synology Assistant和DSM中,增加了对数据包加密的支持(因为其中可能会包含敏感信息)。对应地,存在两个magic,分别用于标识明文消息和密文消息。同时,引入了几个新的pkt_id,用于传递与加解密相关的参数。
1 | // magic |
协议fuzzing
在了解了协议的格式之后,为了测试协议解析代码的健壮性,很自然地会想到采用fuzz的方式。这里采用Kitty和Scapy框架,来快速构建一个基于生成的黑盒fuzzer。Scapy是一个强大的交互式数据包处理程序,借助它可以方便快速地定义对应的协议格式,示例如下。
1 | class IDPacket(Packet): |
Kitty是一个开源、模块化且易于扩展的fuzz框架,灵感来自于Sulley和Peach Fuzzer。基于前面定义的协议格式,借助Kitty框架,可以快速地构建一个基于生成的黑盒fuzzer。另外,考虑到findhostd和Synology Assistant之间的通信机制,可以同时对两端进行fuzz。
1 | host = '<broadcast>' |
此外,基于前面定义好的协议格式,也可以实现一个简易的Synology Assistant客户端。
1 | class DSAssistantClient: |
安全问题
密码泄露
前面提到,pkt_id字段表明对应数据的含义,如数据包类型、用户名、mac地址等。其中,pkt_id为0x1时对应的值表示整个数据包的类型,常见的数据包类型如下。其中,netsetting、quickconf和memory test数据包中包含加密后的管理员密码信息,对应的pkt_id为0x2a。

以quickconf数据包为例,如上图所示。可以看到,pkt_id为0x1时对应的值为0x4,同时pkt_id为0x2a时对应的内容为BnvPxUcU5P1nE01UG07BTUen1XPPKPZX。通过逆向分析可知,函数MatrixDecode()用于对加密后的密码进行解密。因此,可以很容易地获取到管理员的明文密码。
1 | sudo chroot . ./call_export_func -d BnvPxUcU5P1nE01UG07BTUen1XPPKPZX |
由于Synology Assistant和findhostd之间以广播的方式进行通信,且数据包以明文形式进行传输,在某些情形下,通过监听广播数据包,局域网内的用户可以很容易地获取到管理员的明文密码。
密码窃取
在对findhostd进行fuzz的过程中,注意到Synology Assistant中显示的DiskStation状态变为了"Not configured"。难道是某些畸形数据包对DiskStation进行了重置?经过分析后发现,是由于某些数据包欺骗了Synology Assistant:DiskStation是正常的,而Synology Assistant却认为其处于未配置状态。

通常情况下,管理员会选择通过Synology Assistant对设备进行重新配置,并设置之前用过的用户名和密码。此时,由于Synology Assistant和findhostd之间以广播的方式进行通信,且数据包以明文形式进行传输,故密码泄露问题又出现了。因此,在某些情形下,通过发送特定的广播数据包,局域网内的用户可以欺骗管理员对DiskStation进行”重新配置”,通过监听局域网内的广播数据包,从而窃取管理员的明文密码。另外,即使Synology Assistant和DSM版本都支持通信加密,由于向下兼容性,这种方式针对最新的版本仍然适用。
null byte off-by-one
这个问题同样也和Synology Assistant有关。在fuzz的过程中,发现Synology Assistant中显示的一些内容比较奇怪。其中,"%n"、"%x"和"%p"等是针对string类型预置的一些fuzz元素。注意到,在"Server name"中显示的内容除了"%n"之外,尾部还有一些额外的内容如"00:11:32:8Fxxx",这些多余的内容对应的是"MAC address"。正常情况下,"MAC address"对应的内容不会显示到"Server name"中。

通过对6.1-15030版本的DSAssistant.exe进行分析和调试,函数sub_1272E10()负责对string类型的数据进行处理,将其从接收的数据包中拷贝到对应的内部缓冲区。前面提到过,针对每个pkt_id项,都有一个对应的offset字段和max_length字段。当对应数据长度的大小正好为max_length时,额外的'\x00'在(1)处被追加到缓冲区末尾,而此时该'\x00'其实是写入了邻近缓冲区的起始处,从而造成null byte off-by-one。
1 | size_t __cdecl sub_1272E10(int a1, _BYTE *a2, int a3, int a4, size_t a5, int a6, int a7) |
The
_snprintf()function formats and stores count or fewer characters and values (including a terminating null character that is always appended unless count is zero or the formatted string length is greater than or equal to count characters) in buffer. // WindowsThe functions
snprintf()andvsnprintf()write at most size bytes (including the terminating null byte (‘\0’)) to str. // Linux
因此,对于某些在内部缓冲区中处于邻近的pkt_id(如0x5b和0x5c),通过构造特殊的数据包,可以使得前一项内容末尾的'\x00'被下一项内容覆盖,从而可能会泄露邻近缓冲区中的内容。
1 | pkt_id offset max_len |
小结
本文从局域网的视角出发,对群晖NAS设备上的findhostd服务进行了分析,包括Synology Assistant与findhostd之间的通信机制、syno_finder协议格式的解析、协议fuzzing等。最后,分享了在其中发现的部分问题。
相关链接
- Create Wireshark Dissector in Lua
- syno_finder
- Kitty Fuzzing Framework
- Synology-SA-19:38 Synology Assistant
本文首发于安全客,文章链接:https://www.anquanke.com/post/id/251909