Talos Vulnerability Report


WellinTech KingHistorian SORBAx64.dll RecvPacket integer conversion vulnerability

March 20, 2023
CVE Number



An integer conversion vulnerability exists in the SORBAx64.dll RecvPacket functionality of WellinTech KingHistorian A specially crafted network packet can lead to a buffer overflow. An attacker can send a malicious packet to trigger this vulnerability.


The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

WellinTech KingHistorian


KingHistorian - https://www.wellintech.com/product/kinghistorian


8.1 - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H


CWE-195 - Signed to Unsigned Conversion Error


KingHistorian is a time-series database used for ingesting and analyzing industrial control system data. KingHistorian is designed to be high performance and highly reliable for process data.

Within the RecvPacket functionality of SORBAx64.dll, an integer conversion issue exists that can lead to a buffer overflow. The assembly related to the conversion issue can be seen below.

18002e81c  // 0x18002cd50 - SORBA::TcpTransceiver::Recv
18002e81c  ff5028             call    qword [rax+0x28]
18002e81f  85c0               test    eax, eax
18002e821  // Check if recv successfully retrieved data
18002e821  0f8566010000       jne     0x18002e98d
18002e827  // Get the first byte of data
18002e827  440fb69c24409000…movzx   r11d, byte [rsp+0x9040 {stack_recvBuffer}]
18002e830  // sign extend the packet data size after the header (reported in header)
18002e830  4863c7             movsxd  rax, edi
18002e833  ba01000000         mov     edx, 0x1
18002e838  48014328           add     qword [rbx+0x28 {SORBA::TransceiverInputStream::m_nRecvLength}], rax {SORBA::TransceiverInputStream::m_nRecvLength}
18002e83c  410fb6c3           movzx   eax, r11b
18002e840  41b904000000       mov     r9d, 0x4
18002e846  2402               and     al, 0x2
18002e848  3c02               cmp     al, 0x2                                                   [1]
18002e84a  // First byte of recv buffer has 2 bit set
18002e84a  410f44d1           cmove   edx, r9d  {0x4}                                           [2]
18002e84e  8d42ff             lea     eax, [rdx-0x1]
18002e851  83f803             cmp     eax, 0x3
18002e854  770a               ja      0x18002e860
18002e856  // offset into packet to find length
18002e856  448b841441900000   mov     r8d, dword [rsp+rdx+0x9041 {var_fa17}]
18002e85e  eb03               jmp     0x18002e863
18002e863  418bc9             mov     ecx, r9d  {0x4}
18002e866  bfffffffff         mov     edi, 0xffffffff
18002e86b  2bca               sub     ecx, edx  // rcx = 0
18002e86d  8bef               mov     ebp, edi  {0xffffffff}
18002e86f  c1e103             shl     ecx, 0x3  // rcx = 0
18002e872  d3ed               shr     ebp, cl  // rbp = 0xFFFF_FFFF
18002e874  // rbp = packet_data & 0xFFFFFFFF (so no change to packet data)
18002e874  4123e8             and     ebp, r8d
18002e877  81fdecf90000       cmp     ebp, 0xf9ec                                               [3]
18002e87d  // signed comparison of extracted length
18002e87d  // This is an issue because this guard is here to protect lengths too large to 
18002e87d  // fit in the buffer
18002e87d  0f8f14ffffff       jg      0x18002e797
18002e8b3  442bca             sub     r9d, edx                                                  [4]
18002e8b6  418bc4             mov     eax, r12d
18002e8b9  418bc9             mov     ecx, r9d
18002e8bc  c1e103             shl     ecx, 0x3
18002e8bf  48d3ef             shr     rdi, cl
18002e8c2  4823f8             and     rdi, rax
18002e8c5  41f6c301           test    r11b, 0x1                                                 [5]
18002e8c9  7422               je      0x18002e8ed
18002e8ed  8d441201           lea     eax, [rdx+rdx+0x1]
18002e8f1  4c8bc7             mov     r8, rdi
18002e8f4  4863c8             movsxd  rcx, eax
18002e8f7  488d940c40900000   lea     rdx, [rsp+rcx+0x9040] {var_18a58}
18002e8ff  498bca             mov     rcx, r10
18002e902  e82f150000         call    memcpy                                                    [6]

At [1] a flag is extracted from the received packet that is used to determine how large the size field is later in the packet: if the second bit is set in the first byte, then the size field is 4 bytes, which is seen at [2]. Using this size field, some shifting is done to properly extract the size field from the packet for all possible values (only 1 or 4 in this code base). The main issue occurs at [3], where the size is extracted by AND’ing the packet data with 0xFFFFFFFF. If the packet data contains a value greater than 0x80000000, then the signed comparison will pass. It is then used to determine if the allocated stack buffer is large enough to hold all the data. The buffer is statically sized at 0xfa00 bytes. At [4] and [5] the same feature extraction is used to grab the size field again, but at [5] a different flag is checked, this one corresponding to if the payload is compressed or not. Regardless of if the packet is compressed, a multitude of bugs will occur. One such instance is at [6], where the extracted value is used directly as the size of memcpy, which can result in a buffer overflow.


2022-12-16 - Initial Vendor Contact
2022-12-22 - Vendor Disclosure
2022-12-22 - Initial Vendor Contact
2023-03-17 - Vendor Patch Release
2023-03-20 - Public Release


Discovered by Carl Hurd of Cisco Talos.