From dd12afd7175ceb1dbb0f09cf2b6fe2426c7b6f67 Mon Sep 17 00:00:00 2001 From: zerosum0x0 Date: Thu, 20 Apr 2017 15:03:29 -0600 Subject: [PATCH 1/6] added DoublePulsar detection --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 98 ++++++++++++++++--- 1 file changed, 87 insertions(+), 11 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index c128996235..4b4fd33177 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -22,10 +22,16 @@ class MetasploitModule < Msf::Auxiliary If the status returned is "STATUS_INSUFF_SERVER_RESOURCES", the machine does not have the MS17-010 patch. + If the machine is missing the MS17-010 patch, the module will check for an + existing DoublePulsar (ring 0 shellcode/malware) infection. + This module does not require valid SMB credentials in default server configurations. It can log on as the user "\" and connect to IPC$. }, - 'Author' => [ 'Sean Dillon ' ], + 'Author' => + [ 'Sean Dillon ', + 'Luke Jennings' # DoublePulsar detection + ], 'References' => [ [ 'CVE', '2017-0143'], @@ -43,19 +49,37 @@ class MetasploitModule < Msf::Auxiliary def run_host(ip) begin - status = do_smb_probe(ip) + ipc_share = "\\\\#{ip}\\IPC$" + + tree_id = do_smb_setup_tree(ipc_share) + print_status("Connected to #{ipc_share} with TID = #{tree_id}") + + status = do_smb_ms17_010_probe(tree_id) + print_status("Received #{status} with FID = 0") if status == "STATUS_INSUFF_SERVER_RESOURCES" - print_warning("Host is likely VULNERABLE to MS17-010!") + print_good("Host is likely VULNERABLE to MS17-010!") report_vuln( host: ip, name: self.name, refs: self.references, info: 'STATUS_INSUFF_SERVER_RESOURCES for FID 0 against IPC$' ) + + # vulnerable to MS17-010, check for DoublePulsar infection + code = do_smb_doublepulsar_probe(tree_id) + if code == 0x51 + print_warning("Host is likely INFECTED with DoublePulsar!") + report_vuln( + host: ip, + name: "MS17-010 DoublePulsar Infection", + refs: self.references, + info: 'MultiPlexID = 0x51 on Trans2 request' + ) + end elsif status == "STATUS_ACCESS_DENIED" or status == "STATUS_INVALID_HANDLE" # STATUS_ACCESS_DENIED (Windows 10) and STATUS_INVALID_HANDLE (others) - print_good("Host does NOT appear vulnerable.") + print_bad("Host does NOT appear vulnerable.") else print_bad("Unable to properly detect if host is vulnerable.") end @@ -72,19 +96,34 @@ class MetasploitModule < Msf::Auxiliary end end - def do_smb_probe(ip) + def do_smb_setup_tree(ipc_share) connect # logon as user \ simple.login(datastore['SMBName'], datastore['SMBUser'], datastore['SMBPass'], datastore['SMBDomain']) # connect to IPC$ - ipc_share = "\\\\#{ip}\\IPC$" simple.connect(ipc_share) - tree_id = simple.shares[ipc_share] - print_status("Connected to #{ipc_share} with TID = #{tree_id}") + # return tree + return simple.shares[ipc_share] + end + def do_smb_doublepulsar_probe(tree_id) + # make doublepulsar knock + pkt = make_smb_trans2_doublepulsar(tree_id) + + sock.put(pkt) + bytes = sock.get_once + + # convert packet to response struct + pkt = Rex::Proto::SMB::Constants::SMB_TRANS_RES_HDR_PKT.make_struct + pkt.from_s(bytes[4..-1]) + + return pkt['SMB'].v['MultiplexID'] + end + + def do_smb_ms17_010_probe(tree_id) # request transaction with fid = 0 pkt = make_smb_trans_ms17_010(tree_id) sock.put(pkt) @@ -97,10 +136,47 @@ class MetasploitModule < Msf::Auxiliary # convert error code to string code = pkt['SMB'].v['ErrorClass'] smberr = Rex::Proto::SMB::Exceptions::ErrorCode.new - status = smberr.get_error(code) - print_status("Received #{status} with FID = 0") - status + return smberr.get_error(code) + end + + def make_smb_trans2_doublepulsar(tree_id) + # make a raw transaction packet + # this one is a trans2 packet, the checker is trans + pkt = Rex::Proto::SMB::Constants::SMB_TRANS2_PKT.make_struct + simple.client.smb_defaults(pkt['Payload']['SMB']) + + # opcode 0x0e = SESSION_SETUP + setup = "\x0e\x00\x00\x00" + setup_count = 1 # 2 words + trans = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + # calculate offsets to the SetupData payload + base_offset = pkt.to_s.length + (setup.length) - 4 + param_offset = base_offset + trans.length + data_offset = param_offset # + 0 + + # packet baselines + pkt['Payload']['SMB'].v['Command'] = Rex::Proto::SMB::Constants::SMB_COM_TRANSACTION2 + pkt['Payload']['SMB'].v['Flags1'] = 0x18 + pkt['Payload']['SMB'].v['MultiplexID'] = 65 + pkt['Payload']['SMB'].v['Flags2'] = 0xc007 # 0xc803 would unicode + pkt['Payload']['SMB'].v['TreeID'] = tree_id + pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count + pkt['Payload'].v['Timeout'] = 0x00a4d9a6 + pkt['Payload'].v['ParamCountTotal'] = 12 + pkt['Payload'].v['ParamCount'] = 12 + pkt['Payload'].v['ParamCountMax'] = 1 + pkt['Payload'].v['DataCountMax'] = 0 + pkt['Payload'].v['ParamOffset'] = 66 + pkt['Payload'].v['DataOffset'] = 78 + + # actual magic: PeekNamedPipe FID=0, \PIPE\ + pkt['Payload'].v['SetupCount'] = setup_count + pkt['Payload'].v['SetupData'] = setup + pkt['Payload'].v['Payload'] = trans + + pkt.to_s end def make_smb_trans_ms17_010(tree_id) From 9fab64c60e9fbce78b508d8e71af6d3dcf5cef41 Mon Sep 17 00:00:00 2001 From: zerosum0x0 Date: Thu, 20 Apr 2017 15:22:37 -0600 Subject: [PATCH 2/6] added references --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index 4b4fd33177..57507e7511 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -29,8 +29,9 @@ class MetasploitModule < Msf::Auxiliary configurations. It can log on as the user "\" and connect to IPC$. }, 'Author' => - [ 'Sean Dillon ', - 'Luke Jennings' # DoublePulsar detection + [ + 'Sean Dillon ', # @zerosum0x0 + 'Luke Jennings' # DoublePulsar detection Python code ], 'References' => [ @@ -41,6 +42,7 @@ class MetasploitModule < Msf::Auxiliary [ 'CVE', '2017-0147'], [ 'CVE', '2017-0148'], [ 'MSB', 'MS17-010'], + [ 'URL', 'https://github.com/countercept/doublepulsar-detection-script'], [ 'URL', 'https://technet.microsoft.com/en-us/library/security/ms17-010.aspx'] ], 'License' => MSF_LICENSE From 8a77bf7b60ef1eb1720f342144d9d4f34914eb1b Mon Sep 17 00:00:00 2001 From: zerosum0x0 Date: Fri, 21 Apr 2017 08:27:13 -0600 Subject: [PATCH 3/6] removed wrong comments --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index 57507e7511..7f44f1a2d8 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -150,7 +150,7 @@ class MetasploitModule < Msf::Auxiliary # opcode 0x0e = SESSION_SETUP setup = "\x0e\x00\x00\x00" - setup_count = 1 # 2 words + setup_count = 1 # 1 word trans = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # calculate offsets to the SetupData payload @@ -162,7 +162,7 @@ class MetasploitModule < Msf::Auxiliary pkt['Payload']['SMB'].v['Command'] = Rex::Proto::SMB::Constants::SMB_COM_TRANSACTION2 pkt['Payload']['SMB'].v['Flags1'] = 0x18 pkt['Payload']['SMB'].v['MultiplexID'] = 65 - pkt['Payload']['SMB'].v['Flags2'] = 0xc007 # 0xc803 would unicode + pkt['Payload']['SMB'].v['Flags2'] = 0xc007 pkt['Payload']['SMB'].v['TreeID'] = tree_id pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count pkt['Payload'].v['Timeout'] = 0x00a4d9a6 @@ -173,7 +173,6 @@ class MetasploitModule < Msf::Auxiliary pkt['Payload'].v['ParamOffset'] = 66 pkt['Payload'].v['DataOffset'] = 78 - # actual magic: PeekNamedPipe FID=0, \PIPE\ pkt['Payload'].v['SetupCount'] = setup_count pkt['Payload'].v['SetupData'] = setup pkt['Payload'].v['Payload'] = trans From a69aba0eaba813bc4c2d230816b32250e219cd1b Mon Sep 17 00:00:00 2001 From: zerosum0x0 Date: Sat, 22 Apr 2017 23:54:30 -0600 Subject: [PATCH 4/6] added XOR Key calculation --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index 7f44f1a2d8..07f351acf3 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -42,6 +42,7 @@ class MetasploitModule < Msf::Auxiliary [ 'CVE', '2017-0147'], [ 'CVE', '2017-0148'], [ 'MSB', 'MS17-010'], + [ 'URL', 'https://zerosum0x0.blogspot.com/2017/04/doublepulsar-initial-smb-backdoor-ring.html'], [ 'URL', 'https://github.com/countercept/doublepulsar-detection-script'], [ 'URL', 'https://technet.microsoft.com/en-us/library/security/ms17-010.aspx'] ], @@ -49,6 +50,12 @@ class MetasploitModule < Msf::Auxiliary )) end + # algorithm to calculate the XOR Key for DoublePulsar knocks + def calculate_doublepulsar_xor_key(s) + x = (2 * s ^ (((s & 0xff00 | (s << 16)) << 8) | (((s >> 16) | s & 0xff0000) >> 8))) + x & 0xffffffff # this line was added just to truncate to 32 bits + end + def run_host(ip) begin ipc_share = "\\\\#{ip}\\IPC$" @@ -69,14 +76,16 @@ class MetasploitModule < Msf::Auxiliary ) # vulnerable to MS17-010, check for DoublePulsar infection - code = do_smb_doublepulsar_probe(tree_id) + code, signature = do_smb_doublepulsar_probe(tree_id) + if code == 0x51 - print_warning("Host is likely INFECTED with DoublePulsar!") + xor_key = calculate_doublepulsar_xor_key(signature).to_s(16).upcase + print_warning("Host is likely INFECTED with DoublePulsar! - XOR Key: #{xor_key}") report_vuln( host: ip, name: "MS17-010 DoublePulsar Infection", refs: self.references, - info: 'MultiPlexID = 0x51 on Trans2 request' + info: 'MultiPlexID += 0x10 on Trans2 request - XOR Key: #{xor_key}' ) end elsif status == "STATUS_ACCESS_DENIED" or status == "STATUS_INVALID_HANDLE" @@ -122,7 +131,7 @@ class MetasploitModule < Msf::Auxiliary pkt = Rex::Proto::SMB::Constants::SMB_TRANS_RES_HDR_PKT.make_struct pkt.from_s(bytes[4..-1]) - return pkt['SMB'].v['MultiplexID'] + return pkt['SMB'].v['MultiplexID'], pkt['SMB'].v['Signature1'] end def do_smb_ms17_010_probe(tree_id) From 453ca6e3bf9d66c0da48223a4b692ac06ce00af8 Mon Sep 17 00:00:00 2001 From: zerosum0x0 Date: Mon, 24 Apr 2017 13:20:44 -0600 Subject: [PATCH 5/6] added OS printing on vulnerable systems --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index 07f351acf3..cec585acfd 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -67,12 +67,12 @@ class MetasploitModule < Msf::Auxiliary print_status("Received #{status} with FID = 0") if status == "STATUS_INSUFF_SERVER_RESOURCES" - print_good("Host is likely VULNERABLE to MS17-010!") + print_good("Host is likely VULNERABLE to MS17-010! (#{simple.client.peer_native_os})") report_vuln( host: ip, name: self.name, refs: self.references, - info: 'STATUS_INSUFF_SERVER_RESOURCES for FID 0 against IPC$' + info: 'STATUS_INSUFF_SERVER_RESOURCES for FID 0 against IPC$ -- (#{simple.client.peer_native_os})' ) # vulnerable to MS17-010, check for DoublePulsar infection From 55f01d3fc71cf26199ec0812cb418d8bb8878d21 Mon Sep 17 00:00:00 2001 From: zerosum0x0 Date: Mon, 24 Apr 2017 13:33:05 -0600 Subject: [PATCH 6/6] made the plugin less spammy with more vprintf --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index cec585acfd..9682e819e9 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -61,10 +61,10 @@ class MetasploitModule < Msf::Auxiliary ipc_share = "\\\\#{ip}\\IPC$" tree_id = do_smb_setup_tree(ipc_share) - print_status("Connected to #{ipc_share} with TID = #{tree_id}") + vprint_status("Connected to #{ipc_share} with TID = #{tree_id}") status = do_smb_ms17_010_probe(tree_id) - print_status("Received #{status} with FID = 0") + vprint_status("Received #{status} with FID = 0") if status == "STATUS_INSUFF_SERVER_RESOURCES" print_good("Host is likely VULNERABLE to MS17-010! (#{simple.client.peer_native_os})")