def _invoke(self, function_name, opnum, data): req = RequestPDU() req['pfx_flags'].set_flag(PFlags.PFC_FIRST_FRAG) req['pfx_flags'].set_flag(PFlags.PFC_LAST_FRAG) req['packed_drep'] = DataRepresentationFormat() req['call_id'] = self.call_id self.call_id += 1 req['opnum'] = opnum req['stub_data'] = data ioctl_request = SMB2IOCTLRequest() ioctl_request['ctl_code'] = CtlCode.FSCTL_PIPE_TRANSCEIVE ioctl_request['file_id'] = self.handle.file_id ioctl_request['max_output_response'] = 1024 ioctl_request['flags'] = IOCTLFlags.SMB2_0_IOCTL_IS_FSCTL ioctl_request['buffer'] = req session_id = self.tree.session.session_id tree_id = self.tree.tree_connect_id log.info("Sending svcctl RPC request for %s" % function_name) log.debug(str(req)) request = self.tree.session.connection.send(ioctl_request, sid=session_id, tid=tree_id) log.info("Receiving svcctl RPC response for %s" % function_name) resp = self.tree.session.connection.receive(request) ioctl_resp = SMB2IOCTLResponse() ioctl_resp.unpack(resp['data'].get_value()) log.debug(str(ioctl_resp)) pdu_resp = self._parse_pdu(ioctl_resp['buffer'].get_value(), opnum) return pdu_resp
def test_parse_message(self): actual = SMB2IOCTLResponse() data = b"\x31\x00\x00\x00" \ b"\x04\x02\x14\x00" \ b"\xff\xff\xff\xff\xff\xff\xff\xff" \ b"\xff\xff\xff\xff\xff\xff\xff\xff" \ b"\x00\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x70\x00\x00\x00" \ b"\x04\x00\x00\x00" \ b"\x01\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x20\x21\x22\x23" actual.unpack(data) assert len(actual) == 52 assert actual['structure_size'].get_value() == 49 assert actual['reserved'].get_value() == 0 assert actual['ctl_code'].get_value() == \ CtlCode.FSCTL_VALIDATE_NEGOTIATE_INFO assert actual['file_id'].pack() == b"\xff" * 16 assert actual['input_offset'].get_value() == 0 assert actual['input_count'].get_value() == 0 assert actual['output_offset'].get_value() == 112 assert actual['output_count'].get_value() == 4 assert actual['flags'].get_value() == IOCTLFlags.SMB2_0_IOCTL_IS_FSCTL assert actual['reserved2'].get_value() == 0 assert actual['buffer'].get_value() == b"\x20\x21\x22\x23"
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 dfs_request(tree, path): # type: (TreeConnect, str) -> DFSReferralResponse """ Send a DFS Referral request to the IPC tree and return the referrals. """ dfs_referral = DFSReferralRequest() dfs_referral['request_file_name'] = to_text(path) ioctl_req = SMB2IOCTLRequest() ioctl_req['ctl_code'] = CtlCode.FSCTL_DFS_GET_REFERRALS ioctl_req['file_id'] = b"\xFF" * 16 ioctl_req['max_output_response'] = 56 * 1024 ioctl_req['flags'] = IOCTLFlags.SMB2_0_IOCTL_IS_FSCTL ioctl_req['buffer'] = dfs_referral request = tree.session.connection.send(ioctl_req, sid=tree.session.session_id, tid=tree.tree_connect_id) response = tree.session.connection.receive(request) ioctl_resp = SMB2IOCTLResponse() ioctl_resp.unpack(response['data'].get_value()) dfs_response = DFSReferralResponse() dfs_response.unpack(ioctl_resp['buffer'].get_value()) return dfs_response
def test_create_message(self): message = SMB2IOCTLResponse() message['ctl_code'] = CtlCode.FSCTL_VALIDATE_NEGOTIATE_INFO message['file_id'] = b"\xff" * 16 message['input_offset'] = 0 message['input_count'] = 0 message['output_offset'] = 112 message['output_count'] = 4 message['flags'] = IOCTLFlags.SMB2_0_IOCTL_IS_FSCTL message['buffer'] = b"\x20\x21\x22\x23" expected = b"\x31\x00\x00\x00" \ b"\x04\x02\x14\x00" \ b"\xff\xff\xff\xff\xff\xff\xff\xff" \ b"\xff\xff\xff\xff\xff\xff\xff\xff" \ b"\x00\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x70\x00\x00\x00" \ b"\x04\x00\x00\x00" \ b"\x01\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x20\x21\x22\x23" actual = message.pack() assert len(message) == 52 assert actual == expected
def _receive_resp(request): response = transaction.raw.fd.connection.receive(request) query_resp = SMB2IOCTLResponse() query_resp.unpack(response['data'].get_value()) return query_resp['buffer'].get_value()
def enum_services_status_w(self, server_handle, service_type, service_state): """ Enumerates the services based on the criteria selected :param server_handle: A handle to SCMR :param service_type: ServiceType flags to filter by service type :param service_state: EnumServiceState enum value :return: List dictionaries with the following entries service_name: The service name of the service display_name: The display name of the service service_status: ServiceStatus structure of the service """ # https://msdn.microsoft.com/en-us/library/cc245933.aspx opnum = 14 # sent 0 bytes on the buffer size for the 1st request to get the # buffer size that is required req_data = server_handle req_data += struct.pack("<i", service_type) req_data += struct.pack("<i", service_state) req_data += struct.pack("<i", 0) req_data += b"\x00\x00\x00\x00" res = self._invoke("REnumServicesStatusW", opnum, req_data) # now send another request with the total buffer size sent buffer_size = struct.unpack("<i", res[4:8])[0] req_data = server_handle req_data += struct.pack("<i", service_type) req_data += struct.pack("<i", service_state) req_data += res[4:8] req_data += b"\x00\x00\x00\x00" try: res = self._invoke("REnumServicesStatusW", opnum, req_data) data = res except SMBResponseException as exc: if exc.status != NtStatus.STATUS_BUFFER_OVERFLOW: raise exc ioctl_resp = SMB2IOCTLResponse() ioctl_resp.unpack(exc.header['data'].get_value()) pdu_resp = self._parse_pdu(ioctl_resp['buffer'].get_value(), opnum) read_data = self.handle.read(0, 3256) # 4280 - 1024 data = pdu_resp + read_data while len(data) < buffer_size: read_data = self.handle.read(0, 4280) data += self._parse_pdu(read_data, opnum) return_code = struct.unpack("<i", data[-4:])[0] self._parse_error(return_code, "REnumServicesStatusW") # now we have all the data, let's unpack it services = [] services_returned = struct.unpack("<i", data[-12:-8])[0] offset = 4 for i in range(0, services_returned): name_offset = struct.unpack("<i", data[offset:4 + offset])[0] disp_offset = struct.unpack("<i", data[4 + offset:8 + offset])[0] service_status = ServiceStatus() service_name = data[name_offset + 4:].split(b"\x00\x00")[0] display_name = data[disp_offset + 4:].split(b"\x00\x00")[0] service_status.unpack(data[offset + 8:]) service_info = { "display_name": (display_name + b"\x00").decode('utf-16-le'), "service_name": (service_name + b"\x00").decode('utf-16-le'), "service_status": service_status } services.append(service_info) offset += 8 + len(service_status) return services
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))