Talos Vulnerability Report

TALOS-2019-0871

WAGO PFC200 iocheckd service "I/O-Check" ReadPSN remote code execution vulnerability

December 16, 2019
CVE Number

CVE-2019-5079

WAGO PFC200 iocheckd service “I/O-Check” ReadPSN remote code execution vulnerability

Summary

An exploitable heap buffer overflow vulnerability exists in the iocheckd service “I/O-Check” functionality of WAGO PFC 200. A specially crafted set of packets can cause a heap buffer overflow, potentially resulting in code execution. An attacker can send unauthenticated packets to trigger this vulnerability.

Tested Versions

WAGO PFC200 Firmware version 03.01.07(13) WAGO PFC200 Firmware version 03.00.39(12) WAGO PFC100 Firmware version 03.00.39(12)

Product URLs

https://www.wago.com/us/pfc200 https://www.wago.com/us/pfc100

CVSSv3 Score

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

CWE

CWE-120 - Buffer Copy without Checking Size of Input (‘Classic Buffer Overflow’)

Details

The WAGO PFC200 Controller is one of WAGO’s programmable automation controllers that boasts high cybersecurity standards by including VPN, SSL and firewall software. WAGO controllers are used in many industries including automotive, rail, power engineering, manufacturing, and building management. The WAGO PFC200 Controller communicates via both standard and custom protocols.

The service protocol (port 6626) provided by the iocheckd service supports reading and writing key,value pairs to the EEPROM of the device. The ReadPSN message processing code calls strcpy with a fixed sized heap destination buffer, where the source data is the EEPROM value for PSN. An attacker can send an unauthenticated packet to overwrite the PSN value in EEPROM enabling attacker controlled data to overflow this heap buffer potentially resulting in arbitrary code execution. 

[Annotated Disassembly / Decompilation output]

Here, the buffer is allocated on the heap, size 0x28

.text:0001F520                 MOV             R1, #0x28
.text:0001F524                 LDR             R0, [R4]
.text:0001F528                 MOV             R5, R1
.text:0001F52C                 BL              realloc
.text:0001F530                 STR             R0, [R4]

Here, R5 contains the EEPROM value for PSN, R4 contains our heap buffer allocated above.

.text:0001F8F8                 LDR             R5, [SP,#4] ; value for PSN read from EEPROM
.text:0001F8FC                 LDR             R0, [R4] ; this is our heap buffer size 0x28
.text:0001F900                 MOV             R1, R5
.text:0001F904                 ADD             R0, R0, #4
.text:0001F908                 BL              strcpy ; overflow heap buffer if length of PSN value is > 0x28

Crash Information

These are the register values before the overflow. The heap pointer is actually -4 from r0: 0x424c8

r0             0x424cc  0x424cc ; this is the destination buffer that will be overflowed
r1             0x42cf8  0x42cf8
r2             0x80000000       0x80000000
r3             0x58585858       0x58585858
r4             0xbefffbc4       0xbefffbc4
r5             0x42cf8  0x42cf8
r6             0x1f87c  0x1f87c
r7             0x0      0x0
r8             0x91c08  0x91c08
r9             0xb6ff44e0       0xb6ff44e0
r10            0x0      0x0
r11            0x30005  0x30005
r12            0x42d24  0x42d24
sp             0xbefffba8       0xbefffba8
lr             0xb6f83b30       0xb6f83b30
pc             0x1f908  0x1f908
cpsr           0x60040010       0x60040010
fpscr          0x0      0x0

Heap chunk adjacent to destination buffer before overflow:

0x424f0:        0x110005        0x39    0x7     0x630101 ; chunk: prev_size, size, data

Heap chunk adjacent to destination buffer after overflow:

0x424f0:        0x42424242      0x58585858      0x0     0x630101 ; chunk: prev_size, size, data

Backtrace isn’t helpful here so here are the registers in malloc_printerr just before abort(). The crash is when the destination buffer is freed.

$r0  : 0x2
$r1  : 0xb6be65fc  →  "*** Error in `%s': %s: 0x%s ***"
$r2  : 0xbefffea3  →  "iocheckd"
$r3  : 0xb6be6738  →  "free(): invalid next size (fast)"
$r4  : 0x2
$r5  : 0xbefffb74  →  "000424c8"
$r6  : 0xb6be6738  →  "free(): invalid next size (fast)"
$r7  : 0x000424f0  →  "BBBBXXXX"
$r8  : 0x91c08
$r9  : 0x0
$r10 : 0x0
$r11 : 0x30005
$r12 : 0x0
$sp  : 0xbefffb68  →  0xbefffb74  →  "000424c8"
$lr  : 0xb6b38a90  →   add sp,  sp,  #24
$pc  : 0xb6b32d30  →   push {r1,  r2,  r3}

Backtrace

Program received signal SIGABRT, Aborted.
#0  0xb6af67a0 in raise () from target:/lib/libc.so.6
#1  0xb6af7b08 in abort () from target:/lib/libc.so.6
#2  0xb6b32fbc in ?? () from target:/lib/libc.so.6
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

Mitigation

This vulnerability could be mitigated by disabling the iocheckd service “I/O-Check” via the Web-based management web application.

Timeline

2019-07-30 - Vendor disclosure
2019-09-06 - 30+ day follow up
2019-10-02 - 60+ day follow up; vendor acknowledged
2019-10-31 - Vendor passed to CERT@VDE for coordination; Talos extended public disclosure deadline
2019-12-16 - Public Release

Credit

Discovered by Kelly Leuschner of Cisco Talos