Skip to content

Instantly share code, notes, and snippets.

@jackson5sec
Created July 19, 2018 18:36
Show Gist options
  • Save jackson5sec/39a823899bf8bca5408654b3b72c6166 to your computer and use it in GitHub Desktop.
Save jackson5sec/39a823899bf8bca5408654b3b72c6166 to your computer and use it in GitHub Desktop.
A make-shift SMB2 replacement for Metasploit's auxiliary/scanner/smb_version
#!/usr/bin/python
'''
This is a make-shift replacement for metasploit's auxiliary/scanner/smb_version for clients that have disabled/removed
SMBv1. This grabs the hostname, domain name, and Windows version from the NTLMv2 challenge response
@Quickbreach
'''
import argparse
import struct
from binascii import hexlify, unhexlify
import socket
import random
import string
from netaddr import IPNetwork
from threading import Thread
from multiprocessing import Queue
from impacket.spnego import SPNEGO_NegTokenResp
from impacket.ntlm import NTLMAuthChallenge, AV_PAIRS, NTLMSSP_AV_HOSTNAME
from impacket.smb3structs import SMB2Packet, SMB2Negotiate, \
SMB2SessionSetup_Response, SMB2_DIALECT_002, SMB2_DIALECT_21, \
SMB2_DIALECT_30, SMB2_DIALECT_302
import logging
from impacket.examples import logger
logger.init()
logging.getLogger().setLevel(logging.INFO)
class VChecker(Thread):
def __init__(self, IPQueue, Timeout = 3):
Thread.__init__(self)
self.daemon = True
self.IPQueue = IPQueue
self.Timeout = 3
self.suicide = False
self.start()
def join(self, timeout):
if(timeout == -1):
return
else:
self.suicide = True
super(VChecker, self).join(timeout)
def run(self):
while True:
if self.suicide: return
nextIP = None # need this declared outside of the try/catch
try:
if not self.IPQueue.empty():
nextIP = self.IPQueue.get()
else:
self.suicide = True
return
except Exception, e:
return
if nextIP == None: return
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.settimeout(self.Timeout)
s.connect((nextIP, 445))
except Exception, e:
continue
if(s == None):
continue
try:
# Negotiate
negProto = SMB2Negotiate(unhexlify("24000500010000007f000000cb78cd146438e7119168000c291232a370000000020000000202100200030203110300000100260000000000010020000100c8c31f28d43563c829b9070423e96a98701ac3ec788a3ac01573ee03d07d942600000200060000000000020002000100"))
negProto['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30, SMB2_DIALECT_302, 0, 0]
negProto['ClientGuid'] = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8))
rawData = str(SMB2Packet()) + str(negProto)
netbios = struct.pack('>i', len(str(rawData)))
rpkt = str(netbios) + str(rawData)
s.sendall(rpkt)
data = s.recv(4096)
negResp = SMB2Packet(data[4:])
# NTLMSSP Negotiate
sessionSetup = unhexlify("000000a2fe534d42400001000000000001001f0000000000000000000200000000000000fffe00000000000000000000000000000000000000000000000000000000000019000001010000000000000058004a000000000000000000604806062b0601050502a03e303ca00e300c060a2b06010401823702020aa22a04284e544c4d5353500001000000978208e2000000000000000000000000000000000601b11d0000000f")
test = SMB2Packet(sessionSetup[4:])
test['Reserved'] = 0
test['MessageID'] = 1
netbios = str(struct.pack('>i', len(str(test))))
s.sendall(netbios + str(test))
# Get NTLMSSP challenge response
data = s.recv(4096)
s.close()
packet = SMB2Packet(data[4:])
resp = SMB2SessionSetup_Response(packet['Data'])
securityBlob = SPNEGO_NegTokenResp(resp['Buffer'])
authData = NTLMAuthChallenge(securityBlob['ResponseToken'])
serverInfo = AV_PAIRS(authData['TargetInfoFields'])
majorVersion = int(hexlify(struct.unpack('<c', str(authData['Version'])[0])[0]), 16)
minorVersion = int(hexlify(struct.unpack('<c', str(authData['Version'])[1])[0]), 16)
buildVersion = struct.unpack('<h', str(authData['Version'])[2:4])[0]
hostname = serverInfo[NTLMSSP_AV_HOSTNAME][1].decode("utf-16-le").encode("utf-8")
domain = authData['domain_name'].decode("utf-16-le").encode("utf-8")
except Exception, e:
logging.error("Failed to get data from " + nextIP + " (Does it support NTLM?)")
logging.error(e)
continue
OS = "(Unknown OS version)"
if majorVersion == 5:
# Major 5, minor 1: Windows XP SP2
if minorVersion == 1: OS = "Windows XP SP2"
# Major 5, minor 2: Windows server 2003
if minorVersion == 2: OS = "Windows Server 2003"
if majorVersion == 6:
if minorVersion == 0: OS = "Windows Vista or Server 2008"
if minorVersion == 1: OS = "Windows 7 or Server 2008 R2"
if minorVersion == 2: OS = "Windows 8 or Server 2012"
if minorVersion == 3: OS = "Windows 8.1 or Server 2012 R2"
if majorVersion == 10:
if minorVersion == 0: OS = "Windows 10 or Server 2016"
logging.info(nextIP + ":445\t" + OS + " (build:" + str(buildVersion) + ") (name:" + hostname + ") (Domain:" + domain + ")")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser._optionals.title = "Standard arguments"
parser.add_argument('-t', type=str, help='Target IP or CIDR', required=True)
parser.add_argument('--threads', default=1, type=int, help='Maximum number of concurrent threads (default: 1)', required=False)
parser.add_argument('--timeout', default=1, type=int, help='Connection timeout in seconds (default: 1)', required=False)
args = parser.parse_args()
targetList = Queue()
workers = []
for ip in IPNetwork(args.t):
targetList.put(str(ip))
for x in range(0, args.threads):
workers.append(VChecker(targetList, args.timeout))
try:
for worker in workers:
while worker.isAlive():
worker.join(-1)
except KeyboardInterrupt:
logging.error("Recieved interrupt - cleaning up...")
for worker in workers:
worker.join(0)
exit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment