Headline
CVE-2019-5065: TALOS-2019-0854 || Cisco Talos Intelligence Group
An exploitable information disclosure vulnerability exists in the packet-parsing functionality of Blynk-Library v0.6.1. A specially crafted packet can cause an unterminated strncpy, resulting in information disclosure. An attacker can send a packet to trigger this vulnerability.
Summary
An exploitable information disclosure vulnerability exists in the packet-parsing functionality of Blynk-Library v0.6.1. A specially crafted packet can cause an unterminated strncpy, resulting in information disclosure. An attacker can send a packet to trigger this vulnerability.
Tested Versions
Blynk Blynk-Library v0.6.1
Product URLs
https://blynk.io/
CVSSv3 Score
5.3 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
CWE
CWE-125: Out-of-bounds Read
Details
Blynk-Library is a small library for connecting more than 400 different embedded device models into a private or enterprise Blynk-Server instance. According to the git repository, it is the “most popular internet-of-things platform for connecting any hardware to the cloud.”
When dealing with Blynk-Server to Blynk-Device communications (and not PhoneApp to Blynk-Server), the network protocol is surprisingly simple and light:
| 1byte | 2byteBE | 2byteBE | <len bytes>
| type | msg_id | len | <msg>
With the available message types being:
enum BlynkCmd
{
BLYNK_CMD_RESPONSE = 0,
BLYNK_CMD_LOGIN = 2,
BLYNK_CMD_PING = 6,
BLYNK_CMD_TWEET = 12,
BLYNK_CMD_EMAIL = 13,
BLYNK_CMD_NOTIFY = 14,
BLYNK_CMD_BRIDGE = 15,
BLYNK_CMD_HARDWARE_SYNC = 16,
BLYNK_CMD_INTERNAL = 17,
BLYNK_CMD_SMS = 18,
BLYNK_CMD_PROPERTY = 19,
BLYNK_CMD_HARDWARE = 20,
BLYNK_CMD_HW_LOGIN = 29,
BLYNK_CMD_REDIRECT = 41,
BLYNK_CMD_DEBUG_PRINT = 55,
BLYNK_CMD_EVENT_LOG = 64
};
The basic code flow for any packet is as such:
bool BlynkProtocol<Transp>::run(bool avail)
bool BlynkProtocol<Transp>::processInput(void)
if (state == CONNECTING && (1 == hdr.msg_id){...}
} else {
int BlynkProtocol<Transp>::readHeader(BlynkHeader& hdr)
switch (hdr.type){...}
Of most interest right now is the BLYNK_CMD_REDIRECT message, which is normally used by the Blynk-Server to direct the Blynk-Device to a more suitable spot for communications.
//~BlynkProtocol.h:300
case BLYNK_CMD_REDIRECT: {
if (!redir_serv) {
redir_serv = (char*)malloc(32); //[1]
}
BlynkParam param(inputBuffer, hdr.length);
uint16_t redir_port = BLYNK_DEFAULT_PORT;
BlynkParam::iterator it = param.begin(); //[2]
if (it >= param.end())
return false;
strncpy(redir_serv, it.asStr(), 32); //[3]
if (++it < param.end())
redir_port = it.asLong();
BLYNK_LOG4(BLYNK_F("Redirecting to "), redir_serv, ':', redir_port);
conn.disconnect();
conn.begin(redir_serv, redir_port); //[4]
state = CONNECTING;
lastHeartbeat = lastActivityIn = lastActivityOut = (BlynkMillis() - 5000UL);
} break;
At [1], the client will allocate 32 bytes to hold the name of the new server, which comes from the BlynkParam at [2]. The param.begin() function just returns a pointer to our string, nothing fancy. Next at [3], the server will copy exactly 32 bytes into the allocated buffer with strncpy. Now, if we take a quick look at the strncpy man page, we can see:
The strncpy() function is similar, except that at most “n” bytes of src are copied. If there is no null byte among the first “n” bytes of src, the string placed in dest will not be null-terminated.
Since we’re always copying 32 bytes (unless the redir_serv string length is less than 32), strncpy will never actually null terminate the buffer on the heap, and thus, when the device connects again at [4], getaddrinfo is called, and the device will do a DNS lookup for the redir_serv string, which, if the heap is laid out correctly, can end up leaking useful information to an attacker.
It should be noted that this only applies for certain versions of malloc (most notably jemalloc), as normal ptmalloc/dlmalloc versions will have either a NULL dword or the size of our 32-byte redir_serv in the following memory slot, and as such, renders this vulnerability useless for dlmalloc/ptmalloc. But since the Blink-Library is used for so many different devices, it is possible that different version of malloc will be used.
Crash Information
=================================================================
==109006==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000000030 at pc 0x0000004a12cc bp 0x7ffe2c5c6650 sp 0x7ffe2c5c5e00
READ of size 33 at 0x603000000030 thread T0
#0 0x4a12cb (/root/boop/blynk/asan_blynk+0x4a12cb)
#1 0x7fdb41b2d018 (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x114018)
#2 0x52f38e (/root/boop/blynk/asan_blynk+0x52f38e)
#3 0x52c25f (/root/boop/blynk/asan_blynk+0x52c25f)
#4 0x52b60a (/root/boop/blynk/asan_blynk+0x52b60a)
#5 0x7fdb40e8fb96 (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
#6 0x41b779 (/root/boop/blynk/asan_blynk+0x41b779)
0x603000000030 is located 0 bytes to the right of 32-byte region [0x603000000010,0x603000000030)
allocated by thread T0 here:
#0 0x4e5aa7 (/root/boop/blynk/asan_blynk+0x4e5aa7)
#1 0x52f056 (/root/boop/blynk/asan_blynk+0x52f056)
#2 0x52c25f (/root/boop/blynk/asan_blynk+0x52c25f)
#3 0x52b60a (/root/boop/blynk/asan_blynk+0x52b60a)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/root/boop/blynk/asan_blynk+0x4a12cb)
Shadow bytes around the buggy address:
0x0c067fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c067fff8000: fa fa 00 00 00 00[fa]fa fa fa fa fa fa fa fa fa
0x0c067fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
Timeline
2019-07-31 - Vendor Disclosure
2019-09-04 - Public Release
Discovered by Lilith ( ̄■ ̄;) Wyatt of Cisco Talos.