Module: RubySMB::Dcerpc::Epm

Defined in:
lib/ruby_smb/dcerpc/epm.rb,
lib/ruby_smb/dcerpc/epm/epm_twrt.rb,
lib/ruby_smb/dcerpc/epm/epm_ept_map_request.rb,
lib/ruby_smb/dcerpc/epm/epm_ept_map_response.rb

Defined Under Namespace

Classes: EpmDecodedTowerOctetString, EpmEptMapRequest, EpmEptMapResponse, EpmFloorHostOrAddr, EpmFloorInterfaceOrDataIdentifier, EpmFloorPipeOrHost, EpmFloorPipeOrPort, EpmFloorProtocolIdentifier, EpmIpv4Address, EpmIpxSpxAddress, EpmTowerOctetString, EpmTwrpt, EpmTwrt

Constant Summary collapse

UUID =
'E1AF8308-5D1F-11C9-91A4-08002B14A0FA'
VER_MAJOR =
3
VER_MINOR =
0
EPT_MAP =

Operation numbers

0x0003
STATUS_NO_ELEMENTS =

MS-RPCE specific error codes

0x16C9A0D6

Instance Method Summary collapse

Instance Method Details

#ept_map(uuid:, maj_ver:, min_ver: 0, max_towers: 1, protocol: :ncacn_ip_tcp) ⇒ Array<Hash<Symbol,>>

Map a service to a connection end point.

Parameters:

  • uuid (String)

    The object UUID of the interface.

  • maj_ver (Integer)

    The major version number of the interface.

  • min_ver (Integer) (defaults to: 0)

    The minor version number of the interface.

  • max_towers (Integer) (defaults to: 1)

    The maximum number of results to obtain.

  • protocol (Symbol) (defaults to: :ncacn_ip_tcp)

    The protocol of endpoint to obtain.

Returns:

  • (Array<Hash<Symbol,>>)

    The mapped endpoints. The hash keys will depend on the protocol that was selected but an endpoint key will always be present.

Raises:

  • (NotImplementedError)

    Raised if the protocol argument is not supported.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/ruby_smb/dcerpc/epm.rb', line 32

def ept_map(uuid:, maj_ver:, min_ver: 0, max_towers: 1, protocol: :ncacn_ip_tcp)
  interface_identifier = {
    interface: uuid,
    major_version: maj_ver,
    minor_version: min_ver
  }
  data_representation = {
    interface: Ndr::UUID,
    major_version: Ndr::VER_MAJOR,
    minor_version: Ndr::VER_MINOR
  }

  case protocol
  when :ncacn_ip_tcp
    decoded_tower = EpmDecodedTowerOctetString.new(
      interface_identifier: interface_identifier,
      data_representation: data_representation,
      pipe_or_port: {
        identifier: 7, # 0x07: DOD TCP port
        pipe_or_port: 0
      },
      host_or_addr: {
        identifier: 9, # 0x09: DOD IP v4 address (big-endian)
        host_or_addr: 0
      }
    )

    process_tower = lambda do |tower|
      port = tower.pipe_or_port.pipe_or_port.value
      address = IPAddr.new(tower.host_or_addr.host_or_addr.value, Socket::AF_INET)
      {
        port: port,
        address: address,
        # https://learn.microsoft.com/en-us/windows/win32/midl/ncacn-ip-tcp
        endpoint: "ncacn_ip_tcp:#{address}[#{port}]"
      }
    end
  when :ncacn_np
    decoded_tower = EpmDecodedTowerOctetString.new(
      interface_identifier: interface_identifier,
      data_representation: data_representation,
      pipe_or_port: {
        identifier: 0x0f, # 0x0f: NetBIOS pipe name
        pipe_or_port: [0]
      },
      host_or_addr: {
        identifier: 0x11, # 0x11: MS NetBIOS host name
        host_or_addr: [0]
      }
    )

    process_tower = lambda do |tower|
      pipe = tower.pipe_or_port.pipe_or_port[...-1].pack('C*')
      host = tower.host_or_addr.host_or_addr[...-1].pack('C*')
      {
        pipe: pipe,
        host: host,
        # https://learn.microsoft.com/en-us/windows/win32/midl/ncacn-nb-nb
        endpoint: "ncacn_np:#{host}[#{pipe}]"
      }
    end
  else
    raise NotImplementedError, "Unsupported protocol: #{protocol}"
  end

  tower = EpmTwrt.new(decoded_tower)
  ept_map_request = EpmEptMapRequest.new(
    obj: Uuid.new,
    map_tower: tower,
    entry_handle: Ndr::NdrContextHandle.new,
    max_towers: max_towers
  )
  response = dcerpc_request(ept_map_request)
  begin
    ept_map_response = EpmEptMapResponse.read(response)
  rescue IOError
    raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading EptMapResponse'
  end

  if ept_map_response.error_status == STATUS_NO_ELEMENTS
    raise RubySMB::Dcerpc::Error::EpmError,
      "Error returned with ept_map: "\
      "(0x16c9a0d6) STATUS_NO_ELEMENTS: There are no elements that satisfy the specified search criteria."
  elsif ept_map_response.error_status != WindowsError::NTStatus::STATUS_SUCCESS
    raise RubySMB::Dcerpc::Error::EpmError,
      "Error returned with ept_map: "\
      "#{WindowsError::NTStatus.find_by_retval(ept_map_response.error_status.value).join(',')}"
  end

  ept_map_response.towers.map do |tower|
    tower_binary = tower.tower_octet_string.to_binary_s
    begin
      decoded_tower = EpmDecodedTowerOctetString.read(tower_binary)
    rescue IOError
      raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading EpmDecodedTowerOctetString'
    end

    process_tower.(decoded_tower)
  end
end

#ept_map_endpoint(endpoint, **kwargs) ⇒ Object



133
134
135
# File 'lib/ruby_smb/dcerpc/epm.rb', line 133

def ept_map_endpoint(endpoint, **kwargs)
  ept_map(uuid: endpoint::UUID, maj_ver: endpoint::VER_MAJOR, min_ver: endpoint::VER_MINOR, **kwargs)
end