CVE-2019-5161
An exploitable remote code execution vulnerability exists in the Cloud Connectivity functionality of WAGO PFC200. A specially crafted XML file will direct the Cloud Connectivity service to download and execute a shell script with root privileges.
WAGO PFC200 Firmware version 03.02.02(14) WAGO PFC200 Firmware version 03.01.07(13) WAGO PFC200 Firmware version 03.00.39(12)
https://www.wago.com/us/pfc200
9.1 - CVSS:3.0/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
CWE-646 Reliance on File Name or Extension of Externally-Supplied File
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 Cloud Connectivity service of the WAGO PFC200 Controller allows for bi-directional communication with a variety of cloud providers including the Wago Cloud application, Microsoft Azure, IBM Cloud, AWS and SAP IoT Services. The Cloud Connectivity service enables the device to send telemetry data to the cloud and act on commands received from the cloud provider.
The Cloud Connectivity service supports Remote Firmware update through the Wago Cloud Azure IoT Hub application. One step in the remote firmware update process is to parse an XML file containing remote URL’s and local paths for files to be downloaded. The Cloud Connectivity service does not validate the path, file name or file type of the downloaded files. After downloading the files specified in the Firmware Control XML file, the next step in the firmware update process is to look for a shell script with the name /tmp/fwupdate/fwupdate_hook.sh
. If that file exists, it is marked as executable and then executed. This script is executed before any signature verification is performed on the firmware update file.
When the Cloud Connectivity service is configured to connect to an attacker controlled Azure IoT Hub node, the attacker has the ability to write a shell script to the specific location /tmp/fwupdate/fwupdate_hook.sh
and it will be executed as root. Web administrator privileges are required to configure the Cloud Connectivity service.
Here, if the XML File Type property is equal to “rauc” it will continue on to download it below
.text:0002DEC8 LDR R3, =(aFile_0 - 0x2DED8) ; "File"
.text:0002DECC LDR R7, [R9,#0xC]
.text:0002DED0 ADD R3, PC, R3 ; "File"
.text:0002DED4 STR R3, [SP,#0xC68+var_C38]
.text:0002DED8 LDR R3, =(aCloudtype+5 - 0x2DEE4) ; "Type"
.text:0002DEDC ADD R3, PC, R3 ; "Type"
.text:0002DEE0 STR R3, [SP,#0xC68+var_C3C]
.text:0002DEE4
.text:0002DEE4 loc_2DEE4 ; CODE XREF: sub_2D488+D98↓j
.text:0002DEE4 CMP R7, #0
.text:0002DEE8 BEQ loc_2E224
.text:0002DEEC LDR R1, [SP,#0xC68+var_C38]
.text:0002DEF0 LDR R0, [R7,#8]
.text:0002DEF4 BL xmlStrcmp
.text:0002DEF8 CMP R0, #0
.text:0002DEFC BNE loc_2E21C
.text:0002DF00 LDR R3, [SP,#0xC68+var_C3C]
.text:0002DF04 ADD R1, SP, #0xC68+var_1F8
.text:0002DF08 LDR R0, [R3]
.text:0002DF0C LDRB R3, [R3,#4]
.text:0002DF10 STR R0, [SP,#0xC68+var_1F8]
.text:0002DF14 MOV R0, R7
.text:0002DF18 STRB R3, [R1,#4]
.text:0002DF1C BL xmlGetProp
.text:0002DF20 ADD R4, SP, #0xC68+var_1B8
.text:0002DF24 STR R0, [SP,#0xC68+var_C48]
.text:0002DF28 ADD R4, R4, #8
.text:0002DF2C MOV R1, R0
.text:0002DF30 MOV R0, R4
.text:0002DF34 BL sub_516D4
.text:0002DF38 LDR R1, =(aRauc - 0x2DF48); "rauc"
.text:0002DF3C MOV R0, R4
.text:0002DF40 ADD R1, PC, R1 ; "rauc"
.text:0002DF44 BL sub_280BC
Here, the TargetPath property is extracted, but not validated
.text:0002DF9C LDR R2, =(aTargetpath - 0x2DFAC) ; "TargetPath"
.text:0002DFA0 ADD R3, SP, #0xC68+var_1C8
.text:0002DFA4 ADD R2, PC, R2 ; "TargetPath"
.text:0002DFA8 LDR R0, [R2] ; "TargetPath"
.text:0002DFAC LDR R1, [R2,#(aTargetpath+4 - 0x9D0A1)]
.text:0002DFB0 STM R3!, {R0,R1}
.text:0002DFB4 MOV R0, R7
.text:0002DFB8 LDRH R1, [R2,#(aTargetpath+8 - 0x9D0A1)]
.text:0002DFBC LDRB R2, [R2,#(aTargetpath+0xA - 0x9D0A1)]
.text:0002DFC0 STRH R1, [R3]
.text:0002DFC4 ADD R1, SP, #0xC68+var_1C8
.text:0002DFC8 STRB R2, [R3,#2]
.text:0002DFCC BL xmlGetProp
Next, sub_62E38 is called with the first parameter being the value of TargetPath from the XML file, and the second is the url of the file to be downloaded
.text:00062E38 sub_62E38 ; CODE XREF: sub_2D488+768↑p
.text:00062E38 ; sub_2D488+1024↑p
.text:00062E38
.text:00062E38 var_150 = -0x150
.text:00062E38 var_14C = -0x14C
.text:00062E38 var_148 = -0x148
.text:00062E38 var_144 = -0x144
.text:00062E38 var_140 = -0x140
.text:00062E38 var_13C = -0x13C
.text:00062E38 var_138 = -0x138
.text:00062E38 var_120 = -0x120
.text:00062E38 var_108 = -0x108
.text:00062E38 var_F0 = -0xF0
.text:00062E38 var_EC = -0xEC
.text:00062E38 var_28 = -0x28
.text:00062E38
.text:00062E38 PUSH {R4-R11,LR}
.text:00062E3C MOV R6, R1
.text:00062E40 LDR R1, =(aStateBlinkingC_1+0x1A - 0x62E58) ; "w"
.text:00062E44 SUB SP, SP, #0x12C
.text:00062E48 MOV R5, R0
.text:00062E4C LDR R0, [R0] ; filename - TargetPath from XML file
.text:00062E50 ADD R1, PC, R1 ; "w" ; modes
.text:00062E54 BL fopen
.text:00062E58 SUBS R7, R0, #0
.text:00062E5C BNE loc_62EB8
sub_62E38 continued - the second parameter is set as the URL and downloaded
.text:00062EB8 loc_62EB8 ; CODE XREF: sub_62E38+24↑j
.text:00062EB8 BL curl_easy_init
.text:00062EBC SUBS R4, R0, #0
.text:00062EC0 BEQ loc_6310C
.text:00062EC4 LDR R2, [R6] ; setting curl URL to second argument to function
.text:00062EC8 MOV R1, #0x2712 ; CURLOPT_URL
.text:00062ECC BL curl_easy_setopt
.text:00062ED0 LDR R2, =(aEtcSslCertsCaC - 0x62EE4) ; "/etc/ssl/certs/ca-certificates.crt"
.text:00062ED4 MOV R1, #0x2751
.text:00062ED8 MOV R0, R4
.text:00062EDC ADD R2, PC, R2 ; "/etc/ssl/certs/ca-certificates.crt"
.text:00062EE0 BL curl_easy_setopt
.text:00062EE4 MOV R2, #1
.text:00062EE8 MOV R1, #0x40 ; '@'
.text:00062EEC MOV R0, R4
.text:00062EF0 BL curl_easy_setopt
.text:00062EF4 MOV R2, #2
.text:00062EF8 MOV R1, #0x51 ; 'Q'
.text:00062EFC MOV R0, R4
.text:00062F00 BL curl_easy_setopt
.text:00062F04 LDR R2, =(sub_3C3F8 - 0x62F18)
.text:00062F08 MOV R1, #0x4E2B
.text:00062F0C MOV R0, R4
.text:00062F10 ADD R2, PC, R2 ; sub_3C3F8
.text:00062F14 BL curl_easy_setopt
.text:00062F18 MOV R2, R7
.text:00062F1C MOV R1, #0x2711
.text:00062F20 MOV R0, R4
.text:00062F24 BL curl_easy_setopt
.text:00062F28 MOV R2, #1
.text:00062F2C MOV R1, #0x34 ; '4'
.text:00062F30 MOV R0, R4
.text:00062F34 BL curl_easy_setopt
.text:00062F38 MOV R2, #0x258
.text:00062F3C MOV R1, #0xD
.text:00062F40 MOV R0, R4
.text:00062F44 BL curl_easy_setopt
.text:00062F48 MOV R0, R4
.text:00062F4C BL curl_easy_perform
After the file is downloaded, the next step is for the script /usr/sbin/fwupdate_background_service
to execute the shell script located at /tmp/fwupdate/fwupdate_hook.sh
# Call pre install update hook if present
if [[ -e "$WAGO_FW_UPDATE_PREINSTALL_HOOK" ]]; then
fwupdate_report_info "Found pre install update hook to call: \"$WAGO_FW_UPDATE_PREINSTALL_HOOK\""
chmod a+x "$WAGO_FW_UPDATE_PREINSTALL_HOOK"
"$WAGO_FW_UPDATE_PREINSTALL_HOOK"
local HOOK_RESULT=$?
if [[ $HOOK_RESULT -ne 0 ]]; then
fwupdate_report_warning "Pre install update hook failed on call: \"$WAGO_FW_UPDATE_PREINSTALL_HOOK\" (exit code $HOOK_RESULT)"
fi
fi
Environment variable definitions from /usr/sbin/fwupdate_basic_defines
readonly WAGO_FW_UPDATE_DEFAULT_TMP_DIR="/tmp/fwupdate"
readonly WAGO_FW_UPDATE_DEFAULT_WORK_DIR="$WAGO_FW_UPDATE_DEFAULT_TMP_DIR"
readonly WAGO_FW_UPDATE_PREINSTALL_HOOK="$WAGO_FW_UPDATE_DEFAULT_WORK_DIR/fwupdate_hook_preinstall.sh"
This vulnerability could be mitigated by disabling the Cloud Connectivity feature via the Web-based management web application.
2019-10-31 - Vendor Disclosure
2019-10-31 - Vendor acknowledged and passed to CERT@VDE for coordination/handling
2020-01-28 - Talos discussion with vendor; disclosure deadline extended
2020-03-09 - Public Release
Discovered by Kelly Leuschner of Cisco Talos.