CVE-2019-5159
An exploitable improper input validation vulnerability exists in the firmware update functionality of WAGO e!COCKPIT automation software. A specially crafted firmware update file can allow an attacker to write arbitrary files to arbitrary locations on WAGO controllers as a part of executing a firmware update, potentially resulting in code execution. An attacker can create a malicious firmware update package file using any zip utility. The user must initiate a firmware update through e!COCKPIT and choose the malicious wup
file using the file browser to trigger the vulnerability.
WAGO e!COCKPIT 1.6.0.7
https://www.wago.com/us/ecockpit-engineering-software
8.6 - CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
CWE-73 External Control of File Name or Path
WAGO is a manufacturer of programmable automation controllers that are used in many industries including automotive, rail, power engineering, manufacturing, and building management. WAGO’s e!COCKPIT automation software provides an all in one utility that enables Programming, Visualization and Diagnostics for WAGO’s entire family of PLC’s.
The e!COCKPIT software supports updating WAGO controllers’ firmware via wup
(WAGO update package). Typically these wup
files are downloaded automatically by e!COCKPIT from WAGO servers. However, the user also has the option of choosing any file on disk to be used by the firmware update mechanism as long as it conforms to the expected data format of a wup
file.
The wup
file format consists of a zip file archive that is optionally encrypted with ZipCrypto. A hard-coded password is used to encrypt this zip archive, however an un-encrypted file is also accepted by the software. Each directory in the archive contains an xml file referred to as the control file. This control file specifies information about the firmware contained in the zip archive. It also lists additional files in within the zip archive that will be written to the device.
The Control File is expected to be called package-info.xml
and exist at the top-level directory of the archive. Inside, it contains an XML node <AssociatedFiles>
which contains any number of children nodes of the type <File>
. These <File>
nodes contain a Name
property which is the name of the file within the archive. The <File>
node also contains a TargetPath
property which specifies the location on the controller to write the file.
While performing a firmware update, e!COCKPIT will loop through each <File>
node and write it to the specified TargetPath
location on the device via SFTP. The user of e!COCKPIT must enter administrator or root credentials for the controller in order to perform a firmware update. With a malicious wup
file, the specified files will be written to the device with the level of credentials that the user has entered into e!COCKPIT. The files are written with global read permissions. If the file already exists on the device, it will retain it’s original file permissions.
This code snippet is from the .NET assembly Wago.FirmwareUpdate.Series750.dll
version 1.2.0.0
FirmwareUpdateSequnce
class of Wago.FirmwareUpdate.Series750
namespace. The function FirmwareUpdate.DownloadFirmware()
loops over each file listed in the firmware control file and calls UploadFile to the specified TargetPath.
public bool DownloadFirmware(IUpdatePackage updatePackage, CancellationToken cancellationToken, CancellationToken waitCancellationToken)
{
this.LogSequence(SequenceState.DownloadingFirmware);
this._targetName = "";
if (!cancellationToken.IsCancellationRequested)
{
using (this.Controller.UseConnection())
{
foreach (AssociatedFile associatedFile in updatePackage.GetAssociatedFiles())
{
using (Stream stream = this.CacheStream(associatedFile.Name, updatePackage.GetFileStream(associatedFile)))
{
if (this.Controller.UploadFile(stream, associatedFile.TargetPath, cancellationToken))
{
this._targetName = associatedFile.TargetPath;
}
else
{
if (cancellationToken.IsCancellationRequested)
{
break;
}
return false;
}
}
}
}
}
if (cancellationToken.IsCancellationRequested)
{
FwUpdate fwu = new FwUpdate(this.Controller, CancellationToken.None, this.Timing.RebootTimeout, this.Timing.MinimumPollInterval);
this.CancelUpdateAndWait(fwu, false, waitCancellationToken, false);
cancellationToken.ThrowIfCancellationRequested();
}
return true;
}
This code snippet is from the .NET assembly Wago.FirmwareUpdate.Series750.SshNet.dll
version 1.2.0.0
SshNetController
class of Wago.FirmwareUpdate.Series750
namespace. The function SshNetController.UploadFile()
loops over each file listed in the firmware control file and calls UploadFile to the specified TargetPath.
public virtual bool UploadFile(Stream source, string targetpath, CancellationToken cancellationToken)
{
for (int i = 0; i <= 3; i++)
{
using (SftpClient sftpClient = new SftpClient(this._host, this._username, this._password))
{
try
{
sftpClient.Connect();
cancellationToken.ThrowIfCancellationRequested();
int fileSize = sftpClient.GetFileSize(targetpath);
if ((long)fileSize == source.Length)
{
return true;
}
if (fileSize > 0)
{
bool flag = false;
SftpFileStream sftpFileStream = sftpClient.Open(targetpath, FileMode.Append, FileAccess.Write);
source.Seek((long)fileSize, SeekOrigin.Begin);
byte[] buffer = new byte[1048576];
for (;;)
{
int num = source.Read(buffer, 0, 1048576);
if (num == 0)
{
break;
}
IAsyncResult asyncResult = sftpFileStream.BeginWrite(buffer, 0, num, null, null);
Users should execute firmware updates via e!COCKPIT using administrator credentials rather than root for the controller. This will restrict the writable locations on the device to only those writable by the admin user.
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.