def _verify_dialect_negotiate(self): log_header = "Session: %s, Tree: %s" \ % (self.session.username, self.share_name) log.info("%s - Running secure negotiate process" % log_header) ioctl_request = SMB2IOCTLRequest() ioctl_request['ctl_code'] = \ CtlCode.FSCTL_VALIDATE_NEGOTIATE_INFO ioctl_request['file_id'] = b"\xff" * 16 val_neg = SMB2ValidateNegotiateInfoRequest() val_neg['capabilities'] = \ self.session.connection.client_capabilities val_neg['guid'] = self.session.connection.client_guid val_neg['security_mode'] = \ self.session.connection.client_security_mode val_neg['dialects'] = \ self.session.connection.negotiated_dialects ioctl_request['buffer'] = val_neg ioctl_request['max_output_response'] = len(val_neg) ioctl_request['flags'] = IOCTLFlags.SMB2_0_IOCTL_IS_FSCTL log.info("%s - Sending Secure Negotiate Validation message" % log_header) log.debug(ioctl_request) request = self.session.connection.send(ioctl_request, sid=self.session.session_id, tid=self.tree_connect_id) log.info("%s - Receiving secure negotiation response" % log_header) response = self.session.connection.receive(request) ioctl_resp = SMB2IOCTLResponse() ioctl_resp.unpack(response['data'].get_value()) log.debug(ioctl_resp) log.info("%s - Unpacking secure negotiate response info" % log_header) val_resp = SMB2ValidateNegotiateInfoResponse() val_resp.unpack(ioctl_resp['buffer'].get_value()) log.debug(val_resp) self._verify("server capabilities", val_resp['capabilities'].get_value(), self.session.connection.server_capabilities.get_value()) self._verify("server guid", val_resp['guid'].get_value(), self.session.connection.server_guid) self._verify("server security mode", val_resp['security_mode'].get_value(), self.session.connection.server_security_mode) self._verify("server dialect", val_resp['dialect'].get_value(), self.session.connection.dialect) log.info("Session: %d, Tree: %d - Secure negotiate complete" % (self.session.session_id, self.tree_connect_id))
def test_create_message(self): message = SMB2ValidateNegotiateInfoRequest() message['capabilities'] = 8 message['guid'] = b"\x11" * 16 message['security_mode'] = 1 message['dialect_count'] = 2 message['dialects'] = [Dialects.SMB_2_0_2, Dialects.SMB_2_1_0] expected = b"\x08\x00\x00\x00" \ b"\x11\x11\x11\x11\x11\x11\x11\x11" \ b"\x11\x11\x11\x11\x11\x11\x11\x11" \ b"\x01\x00" \ b"\x02\x00" \ b"\x02\x02\x10\x02" actual = message.pack() assert len(message) == 28 assert actual == expected
def test_parse_message(self): actual = SMB2ValidateNegotiateInfoRequest() data = b"\x08\x00\x00\x00" \ b"\x11\x11\x11\x11\x11\x11\x11\x11" \ b"\x11\x11\x11\x11\x11\x11\x11\x11" \ b"\x01\x00" \ b"\x02\x00" \ b"\x02\x02\x10\x02" actual.unpack(data) assert len(actual) == 28 assert actual['capabilities'].get_value() == 8 assert actual['guid'].get_value() == uuid.UUID(bytes=b"\x11" * 16) assert actual['security_mode'].get_value() == 1 assert actual['dialect_count'].get_value() == 2 assert actual['dialects'][0] == 514 assert actual['dialects'][1] == 528 assert len(actual['dialects'].get_value()) == 2
def _verify_dialect_negotiate(self): log_header = "Session: %s, Tree: %s" \ % (self.session.username, self.share_name) log.info("%s - Running secure negotiate process" % log_header) if not self.session.signing_key: # This will only happen if we authenticated with the guest or anonymous user. raise SMBException( 'Cannot verify negotiate information without a session signing key. Authenticate with ' 'a non-guest or anonymous account or set require_secure_negotiate=False to disable the ' 'negotiation info verification checks.') dialect = self.session.connection.dialect if dialect >= Dialects.SMB_3_1_1: # SMB 3.1.1+ uses the negotiation info to generate the signing key so doesn't need this extra exchange. return ioctl_request = SMB2IOCTLRequest() ioctl_request['ctl_code'] = \ CtlCode.FSCTL_VALIDATE_NEGOTIATE_INFO ioctl_request['file_id'] = b"\xff" * 16 val_neg = SMB2ValidateNegotiateInfoRequest() val_neg['capabilities'] = \ self.session.connection.client_capabilities val_neg['guid'] = self.session.connection.client_guid val_neg['security_mode'] = \ self.session.connection.client_security_mode val_neg['dialects'] = \ self.session.connection.negotiated_dialects ioctl_request['buffer'] = val_neg ioctl_request['max_output_response'] = len(val_neg) ioctl_request['flags'] = IOCTLFlags.SMB2_0_IOCTL_IS_FSCTL log.info("%s - Sending Secure Negotiate Validation message" % log_header) log.debug(ioctl_request) request = self.session.connection.send(ioctl_request, sid=self.session.session_id, tid=self.tree_connect_id, force_signature=True) log.info("%s - Receiving secure negotiation response" % log_header) try: response = self.session.connection.receive(request) except (FileClosed, InvalidDeviceRequest, NotSupported) as e: # https://docs.microsoft.com/en-us/archive/blogs/openspecification/smb3-secure-dialect-negotiation # Older dialects may respond with these exceptions, this is expected and we only want to fail if # they are not signed. Check that header signature was signed, fail if it wasn't. The signature, if # present, would have been verified when the connection received the data. if e.header['signature'].get_value() == b'\x00' * 16: raise return # If we received an actual response we want to validate the info provided matches with what was negotiated. ioctl_resp = SMB2IOCTLResponse() ioctl_resp.unpack(response['data'].get_value()) log.debug(ioctl_resp) log.info("%s - Unpacking secure negotiate response info" % log_header) val_resp = SMB2ValidateNegotiateInfoResponse() val_resp.unpack(ioctl_resp['buffer'].get_value()) log.debug(val_resp) self._verify("server capabilities", val_resp['capabilities'].get_value(), self.session.connection.server_capabilities.get_value()) self._verify("server guid", val_resp['guid'].get_value(), self.session.connection.server_guid) self._verify("server security mode", val_resp['security_mode'].get_value(), self.session.connection.server_security_mode) self._verify("server dialect", val_resp['dialect'].get_value(), self.session.connection.dialect) log.info("Session: %d, Tree: %d - Secure negotiate complete" % (self.session.session_id, self.tree_connect_id))