Talos Vulnerability Report

TALOS-2020-1173

Synology DSM findhostd unencrypted credentials disclosure vulnerability

February 25, 2020
CVE Number

Summary

An information disclosure vulnerability exists in the findhostd authentication functionality of Synology DSM 6.2.3 25426 DS120j. Using an application (e.g. Synology Assistant) can lead to information disclosure of administrator credentials. An attacker can sniff network traffic to trigger this vulnerability.

Tested Versions

Synology DSM 6.2.3 25426-2 DS120j

Product URLs

https://www.synology.com/en-global/dsm

CVSSv3 Score

8.0 - CVSS:3.0/AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

CWE

CWE-319 - Cleartext Transmission of Sensitive Information

Details

Synology DiskStation Manager (DSM) is the Linux-based operating system for every Synology NAS.

For easy configuration and setup of different Synology NAS’s running Synology DSM, tools such as the Synology Assistant are provided: https://www.synology.com/en-us/releaseNote/Assistant. This tool (and perhaps others) interact with Synology NAS’s by communicating over UDP port 9997, 9998, and 9999 to the findhostd daemon.

Avoiding diving too deeply into the protocol, there are certain commands that can be sent to findhostd that require authentication via pam_unix.so. In the case of Synology Assistant, there is a Memory Test command that requires this, but based on the fact that there’s three cross-references of the pam_authentication codepath we’re concerned with, we surmise that there are other tools or binaries that also perform vulnerable actions. Let us now examine the packet sent upon trying to perform a Memory Test from Synology Assistant:

0000   12 34 56 78 53 59 4e 4f a4 04 00 00 02 01 a6 04   .4VxSYNO........
0010   78 00 00 00 01 04 0c 00 00 00 19 11 30 30 3a 31   x...........00:1
0020   31 3a 33 32 3a 62 38 3a 63 33 3a 35 38 2a 10 76   1:32:b8:c3:58*.v
0030   6e 73 6e 48 50 34 50 75 55 37 55 42 30 65 6e 4a   nsnHP4PuU7UB0enJ
0040   05 61 64 6d 69 6e c2 04 5a 61 00 00 7c 11 31 31   .admin..Za..|.11
0050   3a 65 30 3a 34 63 3a 33 36 3a 30 30 3a 32 63      :e0:4c:36:00:2c

Not too complicated, findhostd’s protocol is a simple tlv. Breaking it down to the individual elements:

buf = ''"
#magic bytes 
buf+= "\x12\x34\x56\x78SYNO"

# not sure, but required
buf+= '\xa4"               [1]
buf+= "\x04"               [2]
buf+= "\x00\x00\x02\x01'   [3]

# not sure, but required
buf+= '\xa6\x04'
buf+= '\x78\x00\x00\x00'

# not sure, but required 
buf+= '\x01\x04'
buf+='\x0c\x00\x00\x00' 

# dst mac addr 00:11:32:b8:c3:58
buf+= '\x19\x11\x30\x30\x3a\x31\x31\x3a\x33\x32\x3a\x62\x38\x3a\x63\x33\x3a\x35\x38'
  
# password ???
buf+= '\x2a\x10'  
buf+= 'vnsnHP4PuU7UB0en'   [4]

# username (admin)
buf+= '\x4a\x05\x61\x64\x6d\x69\x6e'

#src mac addr 11:e0:4c:36:00:2c
buf+=' \x7c\x11\x31\x31\x3a\x65\x30\x3a\x34\x63\x3a\x33\x36\x3a\x30\x30\x3a\x31\x62'

As an example of a singular tlv, we see a uint8_t type at [1], a uint8_t len at [2], and then the value at [3]. Each given type correspond to a different parameter is usually only an integer (which must be length 4) or a string. In examining the previously mentioned authentication opcodes, we quickly see that type \x4a is passed as the username, whilst type \x2a is passed as an obfuscated password of some sort, the text input to generate the bytes at [4] was ‘password’. Looking back at the authentication codepath, we quickly see a singular function which handles this decoding process:

=>0x7f7bb62e28 : stp    x29, x30, [sp, #-64]!        //start of MatrixDecode(input,output)
0x7f7bb62e2c : mov      x29, sp
0x7f7bb62e30 : stp      x19, x20, [sp, #16]
0x7f7bb62e34 : stp      x21, x22, [sp, #32]
0x7f7bb62e38 : stp      x23, x24, [sp, #48]
0x7f7bb62e3c : cbz      x0, 0x7f7bb62f94
***********************************************************************************
#0 0x0000007f7bb62e28: (0x7f7bb5f000 0x7f7bb6a000 0xb000 0x0 /usr/lib/libfindhost.so.1)
#1 0x000000000040c000: (0x400000 0x40f000 0xf000 0x0 /usr/syno/bin/findhostd)
***********************************************************************************
[-.-]> x/1s $x0
0x7ff3acb1d4:   "vnsnHP4PuU7UB0en"

[^.^]> x/1s $x1
0x7ff3ac9198:   ""

[o.o]> fin
Run till exit from #0  0x0000007f7bb62e28 in ?? ()

[^.^]> x/1s 0x7ff3ac9198
0x7ff3ac9198:   "password"

The name of the function essentially gives up the vulnerability, the word “Decode” is always fun when it deals with credentials. Giving a quick run-through to verify that the entire MatrixDecode and MatrixEncode process is in fact entirely hard coded and has no secrets:

7f7bb62ed0  e4f047f9   ldr     x4, [x7, #0xfe0]  {mapc2d@GOT}     [1]
7f7bb62ed4  62004039   ldrb    w2, [x3]                           [2]
7f7bb62ed8  66f05f38   ldurb   w6, [x3, #-0x1]
7f7bb62edc  82686238   ldrb    w2, [x4, x2]                       [3]
7f7bb62ee0  84686638   ldrb    w4, [x4, x6]             
7f7bb62ee4  8218022a   orr     w2, w4, w2, lsl #0x6  
7f7bb62ee8  414c1432   orr     w1, w2, #0xfffff000
7f7bb62eec  82fe5f37   tbnz    w2, #0xb, 0x7f7bb62ebc

7f7bb62ef0  4000621e   scvtf   d0, w2
7f7bb62ef4  63080091   add     x3, x3, #0x2
7f7bb62ef8  7f0000eb   cmp     x3, x0                             [4]
7f7bb62efc  a08400fc   str     d0, [x5], #0x8                     [5]
7f7bb62f00  81feff54   b.ne    0x7f7bb62ed0

At [1], we load in a hard coded matrix (just 0x100 hardcoded bytes) that we then index into at [3] with bytes taken directly from a pointer in our input buffer, x3 at [2]. After performing basic operations on these sets of bytes, we then store it into a different matrix at [5], and continue to do so until x3 reaches the end of our input buffer at [4].

After this, another reversible matrix operation occurs, but with a different hardcoded matrix:

7f7bb62f04  a10000f0   adrp    x1, 0x7f7bb79000
7f7bb62f08  e20318aa   mov     x2, x24
7f7bb62f0c  e00313aa   mov     x0, x19             // transformed buffer
7f7bb62f10  21cc47f9   ldr     x1, [x1, #0xf98]  {pmti64@GOT}
7f7bb62f14  210040f9   ldr     x1, [x1] 
7f7bb62f18  6efcff97   bl      MatrixMultiply
7f7bb62f1c  6105005c   ldr     d1, 0x7f7bb62fc8
7f7bb62f20  03230091   add     x3, x24, #0x8
7f7bb62f24  020080d2   mov     x2, #0

After performing a MatrixMultiply with a transformed version of the input buffer and a hardcoded matrix, the output buffer can be read as the decoded password.

A few other things to note for understanding the totality of this vulnerability, in order to get to MatrixDecode, the user account authenticating must be a part of the admin group. Also, because this is pam_authentication, this password must be the plaintext of the actual account’s normal unix password. Furthermore, the library functions used by Synology Assistant to send this authentication message are broadcast to 255.255.255.255, so one doesn’t even need to perform a MITM attack, one simply needs to be able to receive broadcast traffic from the device authenticating in order to gain an administrative credential.

Timeline

2020-10-21 - Vendor Disclosure
2021-02-25 - Public Release

Credit

Discovered by Lilith >_> and Claudio Bozzato of Cisco Talos.