Coverage for /root/GitHubProjects/impacket/impacket/dcerpc/v5/rpch.py: 37%

# Coverage for /root/GitHubProjects/impacket/impacket/dcerpc/v5/rpch.py : 37%

## 359 statements

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1# SECUREAUTH LABS. Copyright 2020 SecureAuth Corporation. All rights reserved.

2#

3# This software is provided under under a slightly modified version

4# of the Apache Software License. See the accompanying LICENSE file

5# for more information.

6#

7# Description:

8# Initial [MS-RCPH] Interface implementation

9#

10# Authors:

11# Arseniy Sharoglazov <mohemiv@gmail.com> / Positive Technologies (https://www.ptsecurity.com/)

12#

13

14 import re

15 import binascii

16 from struct import unpack

17

18 from impacket import uuid, ntlm, system_errors, nt_errors, LOG

19 from impacket.dcerpc.v5.rpcrt import DCERPCException

20

21 from impacket.uuid import EMPTY_UUID

22 from impacket.http import HTTPClientSecurityProvider, AUTH_BASIC

23 from impacket.structure import Structure

24 from impacket.dcerpc.v5.rpcrt import MSRPCHeader, \

25 MSRPC_RTS, PFC_FIRST_FRAG, PFC_LAST_FRAG

26

27 class RPCProxyClientException(DCERPCException):

28 parser = re.compile(r'RPC Error: ([a-fA-F0-9]{1,8})')

29

30 def __init__(self, error_string=None, proxy_error=None):

31 rpc_error_code = None

32

33 if proxy_error is not None:

34 try:

35 search = self.parser.search(proxy_error)

36 rpc_error_code = int(search.group(1), 16)

37 except:

38 error_string += ': ' + proxy_error

39

40 DCERPCException.__init__(self, error_string, rpc_error_code)

41

42 def __str__(self):

43 if self.error_code is not None:

44 key = self.error_code

45 if key in system_errors.ERROR_MESSAGES:

46 error_msg_short = system_errors.ERROR_MESSAGES[key][0]

47 return '%s, code: 0x%x - %s' % (self.error_string, self.error_code, error_msg_short)

48 elif key in nt_errors.ERROR_MESSAGES:

49 error_msg_short = nt_errors.ERROR_MESSAGES[key][0]

50 return '%s, code: 0x%x - %s' % (self.error_string, self.error_code, error_msg_short)

51 else:

52 return '%s: unknown code: 0x%x' % (self.error_string, self.error_code)

53 else:

54 return self.error_string

55

56################################################################################

57# CONSTANTS

58################################################################################

59

60 RPC_OVER_HTTP_v1 = 1

61 RPC_OVER_HTTP_v2 = 2

62

63# Errors which might need handling

64

65# RPCProxyClient internal errors

66 RPC_PROXY_REMOTE_NAME_NEEDED_ERR = 'Basic authentication in RPC proxy is used, ' \

67 'so coudn\'t obtain a target NetBIOS name from NTLMSSP to connect.'

68

69# Errors below contain a part of server responses

70 RPC_PROXY_INVALID_RPC_PORT_ERR = 'Invalid RPC Port'

71 RPC_PROXY_CONN_A1_0X6BA_ERR = 'RPC Proxy CONN/A1 request failed, code: 0x6ba'

72 RPC_PROXY_CONN_A1_404_ERR = 'CONN/A1 request failed: HTTP/1.1 404 Not Found'

73 RPC_PROXY_RPC_OUT_DATA_404_ERR = 'RPC_OUT_DATA channel: HTTP/1.1 404 Not Found'

74 RPC_PROXY_CONN_A1_401_ERR = 'CONN/A1 request failed: HTTP/1.1 401 Unauthorized'

75 RPC_PROXY_HTTP_IN_DATA_401_ERR = 'RPC_IN_DATA channel: HTTP/1.1 401 Unauthorized'

76

77

78# 2.2.3.3 Forward Destinations

79 FDClient = 0x00000000

80 FDInProxy = 0x00000001

81 FDServer = 0x00000002

82 FDOutProxy = 0x00000003

83

84 RTS_FLAG_NONE = 0x0000

85 RTS_FLAG_PING = 0x0001

86 RTS_FLAG_OTHER_CMD = 0x0002

87 RTS_FLAG_RECYCLE_CHANNEL = 0x0004

88 RTS_FLAG_IN_CHANNEL = 0x0008

89 RTS_FLAG_OUT_CHANNEL = 0x0010

90 RTS_FLAG_EOF = 0x0020

91 RTS_FLAG_ECHO = 0x0040

92

93# 2.2.3.5 RTS Commands

94 RTS_CMD_RECEIVE_WINDOW_SIZE = 0x00000000

95 RTS_CMD_FLOW_CONTROL_ACK = 0x00000001

96 RTS_CMD_CONNECTION_TIMEOUT = 0x00000002

97 RTS_CMD_COOKIE = 0x00000003

98 RTS_CMD_CHANNEL_LIFETIME = 0x00000004

99 RTS_CMD_CLIENT_KEEPALIVE = 0x00000005

100 RTS_CMD_VERSION = 0x00000006

101 RTS_CMD_EMPTY = 0x00000007

102 RTS_CMD_PADDING = 0x00000008

103 RTS_CMD_NEGATIVE_ANCE = 0x00000009

104 RTS_CMD_ANCE = 0x0000000A

105 RTS_CMD_CLIENT_ADDRESS = 0x0000000B

106 RTS_CMD_ASSOCIATION_GROUP_ID = 0x0000000C

107 RTS_CMD_DESTINATION = 0x0000000D

108 RTS_CMD_PING_TRAFFIC_SENT_NOTIFY = 0x0000000E

109

110################################################################################

111# STRUCTURES

112################################################################################

113

114# 2.2.3.1 RTS Cookie

115 class RTSCookie(Structure):

116 structure = (

117 ('Cookie','16s=b"\\x00"*16'),

118 )

119

120# 2.2.3.2 Client Address

121 class EncodedClientAddress(Structure):

122 structure = (

123 ('AddressType','<L=(0 if len(ClientAddress) == 4 else 1)'),

124 ('_ClientAddress','_-ClientAddress','4 if AddressType == 0 else 16'),

125 ('ClientAddress',':'),

126 ('Padding','12s=b"\\x00"*12'),

127 )

128

129# 2.2.3.4 Flow Control Acknowledgment

130 class Ack(Structure):

131 structure = (

132 ('BytesReceived','<L=0'),

133 ('AvailableWindow','<L=0'),

134 ('ChannelCookie',':',RTSCookie),

135 )

136

137# 2.2.3.5.1 ReceiveWindowSize

138 class ReceiveWindowSize(Structure):

139 structure = (

140 ('CommandType','<L=0'),

141 ('ReceiveWindowSize','<L=262144'),

142 )

143

144# 2.2.3.5.2 FlowControlAck

145 class FlowControlAck(Structure):

146 structure = (

147 ('CommandType','<L=1'),

148 ('Ack',':',Ack),

149 )

150

151# 2.2.3.5.3 ConnectionTimeout

152 class ConnectionTimeout(Structure):

153 structure = (

154 ('CommandType','<L=2'),

155 ('ConnectionTimeout','<L=120000'),

156 )

157

158# 2.2.3.5.4 Cookie

159 class Cookie(Structure):

160 structure = (

161 ('CommandType','<L=3'),

162 ('Cookie',':',RTSCookie),

163 )

164

165# 2.2.3.5.5 ChannelLifetime

166 class ChannelLifetime(Structure):

167 structure = (

168 ('CommandType','<L=4'),

169 ('ChannelLifetime','<L=1073741824'),

170 )

171

172# 2.2.3.5.6 ClientKeepalive

173#

174# By the spec, ClientKeepalive value can be 0 or in the inclusive

175# range of 60,000 through 4,294,967,295.

176# If it is 0, it MUST be interpreted as 300,000.

177#

178# But do not set it to 0, it will cause 0x6c0 rpc error.

179 class ClientKeepalive(Structure):

180 structure = (

181 ('CommandType','<L=5'),

182 ('ClientKeepalive','<L=300000'),

183 )

184

185# 2.2.3.5.7 Version

186 class Version(Structure):

187 structure = (

188 ('CommandType','<L=6'),

189 ('Version','<L=1'),

190 )

191

192# 2.2.3.5.8 Empty

193 class Empty(Structure):

194 structure = (

195 ('CommandType','<L=7'),

196 )

197

198# 2.2.3.5.9 Padding

199 class Padding(Structure):

200 structure = (

201 ('CommandType','<L=8'),

202 ('ConformanceCount','<L=len(Padding)'),

203 ('Padding','*ConformanceCount'),

204 )

205

206# 2.2.3.5.10 NegativeANCE

207 class NegativeANCE(Structure):

208 structure = (

209 ('CommandType','<L=9'),

210 )

211

212# 2.2.3.5.11 ANCE

213 class ANCE(Structure):

214 structure = (

215 ('CommandType','<L=0xA'),

216 )

217

218# 2.2.3.5.12 ClientAddress

219 class ClientAddress(Structure):

220 structure = (

221 ('CommandType','<L=0xB'),

222 ('ClientAddress',':',EncodedClientAddress),

223 )

224

225# 2.2.3.5.13 AssociationGroupId

226 class AssociationGroupId(Structure):

227 structure = (

228 ('CommandType','<L=0xC'),

229 ('AssociationGroupId',':',RTSCookie),

230 )

231

232# 2.2.3.5.14 Destination

233 class Destination(Structure):

234 structure = (

235 ('CommandType','<L=0xD'),

236 ('Destination','<L'),

237 )

238

239# 2.2.3.5.15 PingTrafficSentNotify

240 class PingTrafficSentNotify(Structure):

241 structure = (

242 ('CommandType','<L=0xE'),

243 ('PingTrafficSent','<L'),

244 )

245

246 COMMANDS = {

247 0x0: ReceiveWindowSize,

248 0x1: FlowControlAck,

249 0x2: ConnectionTimeout,

250 0x3: Cookie,

251 0x4: ChannelLifetime,

252 0x5: ClientKeepalive,

253 0x6: Version,

254 0x7: Empty,

255 0x8: Padding,

256 0x9: NegativeANCE,

257 0xA: ANCE,

258 0xB: ClientAddress,

259 0xC: AssociationGroupId,

260 0xD: Destination,

261 0xE: PingTrafficSentNotify,

262}

263

264# 2.2.3.6.1 RTS PDU Header

265# The RTS PDU Header has the same layout as the common header of

266# the connection-oriented RPC PDU as specified in [C706] section 12.6.1,

267# with a few additional requirements around the contents of the header fields.

268 class RTSHeader(MSRPCHeader):

269 _SIZE = 20

270 commonHdr = MSRPCHeader.commonHdr + (

271 ('Flags','<H=0'), # 16

272 ('NumberOfCommands','<H=0'), # 18

273 )

274

275 def __init__(self, data=None, alignment=0):

276 MSRPCHeader.__init__(self, data, alignment)

277 self['type'] = MSRPC_RTS

278 self['flags'] = PFC_FIRST_FRAG | PFC_LAST_FRAG

279 self['auth_length'] = 0

280 self['call_id'] = 0

281

282# 2.2.4.2 CONN/A1 RTS PDU

283#

284# The CONN/A1 RTS PDU MUST be sent from the client to the outbound proxy on the OUT channel to

285# initiate the establishment of a virtual connection.

286 class CONN_A1_RTS_PDU(Structure):

287 structure = (

288 ('Version',':',Version),

289 ('VirtualConnectionCookie',':',Cookie),

290 ('OutChannelCookie',':',Cookie),

291 ('ReceiveWindowSize',':',ReceiveWindowSize),

292 )

293

294# 2.2.4.5 CONN/B1 RTS PDU

295#

296# The CONN/B1 RTS PDU MUST be sent from the client to the inbound proxy on the IN channel to

297# initiate the establishment of a virtual connection.

298 class CONN_B1_RTS_PDU(Structure):

299 structure = (

300 ('Version',':',Version),

301 ('VirtualConnectionCookie',':',Cookie),

302 ('INChannelCookie',':',Cookie),

303 ('ChannelLifetime',':',ChannelLifetime),

304 ('ClientKeepalive',':',ClientKeepalive),

305 ('AssociationGroupId',':',AssociationGroupId),

306 )

307

308# 2.2.4.4 CONN/A3 RTS PDU

309#

310# The CONN/A3 RTS PDU MUST be sent from the outbound proxy to the client on the OUT channel to

311# continue the establishment of the virtual connection.

312 class CONN_A3_RTS_PDU(Structure):

313 structure = (

314 ('ConnectionTimeout',':',ConnectionTimeout),

315 )

316

317# 2.2.4.9 CONN/C2 RTS PDU

318#

319# The CONN/C2 RTS PDU MUST be sent from the outbound proxy to the client on the OUT channel to

320# notify it that a virtual connection has been established.

321 class CONN_C2_RTS_PDU(Structure):

322 structure = (

323 ('Version',':',Version),

324 ('ReceiveWindowSize',':',ReceiveWindowSize),

325 ('ConnectionTimeout',':',ConnectionTimeout),

326 )

327

328# 2.2.4.51 FlowControlAckWithDestination RTS PDU

329 class FlowControlAckWithDestination_RTS_PDU(Structure):

330 structure = (

331 ('Destination',':',Destination),

332 ('FlowControlAck',':',FlowControlAck),

333 )

334

335################################################################################

336# HELPERS

337################################################################################

338 def hCONN_A1(virtualConnectionCookie=EMPTY_UUID, outChannelCookie=EMPTY_UUID, receiveWindowSize=262144):

339 conn_a1 = CONN_A1_RTS_PDU()

340 conn_a1['Version'] = Version()

341 conn_a1['VirtualConnectionCookie'] = Cookie()

342 conn_a1['VirtualConnectionCookie']['Cookie'] = virtualConnectionCookie

343 conn_a1['OutChannelCookie'] = Cookie()

344 conn_a1['OutChannelCookie']['Cookie'] = outChannelCookie

345 conn_a1['ReceiveWindowSize'] = ReceiveWindowSize()

346 conn_a1['ReceiveWindowSize']['ReceiveWindowSize'] = receiveWindowSize

347

348 packet = RTSHeader()

349 packet['Flags'] = RTS_FLAG_NONE

350 packet['NumberOfCommands'] = len(conn_a1.structure)

351 packet['pduData'] = conn_a1.getData()

352

353 return packet.getData()

354

355 def hCONN_B1(virtualConnectionCookie=EMPTY_UUID, inChannelCookie=EMPTY_UUID, associationGroupId=EMPTY_UUID):

356 conn_b1 = CONN_B1_RTS_PDU()

357 conn_b1['Version'] = Version()

358 conn_b1['VirtualConnectionCookie'] = Cookie()

359 conn_b1['VirtualConnectionCookie']['Cookie'] = virtualConnectionCookie

360 conn_b1['INChannelCookie'] = Cookie()

361 conn_b1['INChannelCookie']['Cookie'] = inChannelCookie

362 conn_b1['ChannelLifetime'] = ChannelLifetime()

363 conn_b1['ClientKeepalive'] = ClientKeepalive()

364 conn_b1['AssociationGroupId'] = AssociationGroupId()

365 conn_b1['AssociationGroupId']['AssociationGroupId'] = RTSCookie()

366 conn_b1['AssociationGroupId']['AssociationGroupId']['Cookie'] = associationGroupId

367

368 packet = RTSHeader()

369 packet['Flags'] = RTS_FLAG_NONE

370 packet['NumberOfCommands'] = len(conn_b1.structure)

371 packet['pduData'] = conn_b1.getData()

372

373 return packet.getData()

374

375 def hFlowControlAckWithDestination(destination, bytesReceived, availableWindow, channelCookie):

376 rts_pdu = FlowControlAckWithDestination_RTS_PDU()

377 rts_pdu['Destination'] = Destination()

378 rts_pdu['Destination']['Destination'] = destination

379 rts_pdu['FlowControlAck'] = FlowControlAck()

380 rts_pdu['FlowControlAck']['Ack'] = Ack()

381 rts_pdu['FlowControlAck']['Ack']['BytesReceived'] = bytesReceived

382 rts_pdu['FlowControlAck']['Ack']['AvailableWindow'] = availableWindow

383

384 # Cookie of the channel for which the traffic received is being acknowledged

385 rts_pdu['FlowControlAck']['Ack']['ChannelCookie'] = RTSCookie()

386 rts_pdu['FlowControlAck']['Ack']['ChannelCookie']['Cookie'] = channelCookie

387

388 packet = RTSHeader()

389 packet['Flags'] = RTS_FLAG_OTHER_CMD

390 packet['NumberOfCommands'] = len(rts_pdu.structure)

391 packet['pduData'] = rts_pdu.getData()

392

393 return packet.getData()

394

395 def hPing():

396 packet = RTSHeader()

397 packet['Flags'] = RTS_FLAG_PING

398

399 return packet.getData()

400

401################################################################################

402# CLASSES

403################################################################################

404 class RPCProxyClient(HTTPClientSecurityProvider):

405 RECV_SIZE = 8192

406 default_headers = {'User-Agent' : 'MSRPC',

407 'Cache-Control': 'no-cache',

408 'Connection' : 'Keep-Alive',

409 'Expect' : '100-continue',

410 'Accept' : 'application/rpc',

411 'Pragma' : 'No-cache'

412 }

413

414 def __init__(self, remoteName=None, dstport=593):

415 HTTPClientSecurityProvider.__init__(self)

416 self.__remoteName = remoteName

417 self.__dstport = dstport

418

419 # Chosen auth type

420 self.__auth_type = None

421

422 self.init_state()

423

424 def init_state(self):

425 self.__channels = {}

426

427 self.__inChannelCookie = uuid.generate()

428 self.__outChannelCookie = uuid.generate()

429 self.__associationGroupId = uuid.generate()

430 self.__virtualConnectionCookie = uuid.generate()

431

432 self.__serverConnectionTimeout = None

433 self.__serverReceiveWindowSize = None

434 self.__availableWindowAdvertised = 262144 # 256k

435 self.__receiverAvailableWindow = self.__availableWindowAdvertised

436 self.__bytesReceived = 0

437

438 self.__serverChunked = False

439 self.__readBuffer = b''

440 self.__chunkLeft = 0

441

442 self.rts_ping_received = False

443

444 def set_proxy_credentials(self, username, password, domain='', lmhash='', nthash=''):

445 LOG.error("DeprecationWarning: Call to deprecated method set_proxy_credentials (use set_credentials).")

446 self.set_credentials(username, password, domain, lmhash, nthash)

447

448 def set_credentials(self, username, password, domain='', lmhash='', nthash='', aesKey='', TGT=None, TGS=None):

449 HTTPClientSecurityProvider.set_credentials(self, username, password,

450 domain, lmhash, nthash, aesKey, TGT, TGS)

451

452 def create_rpc_in_channel(self):

453 headers = self.default_headers.copy()

454 headers['Content-Length'] = '1073741824'

455

456 self.create_channel('RPC_IN_DATA', headers)

457

458 def create_rpc_out_channel(self):

459 headers = self.default_headers.copy()

460 headers['Content-Length'] = '76'

461

462 self.create_channel('RPC_OUT_DATA', headers)

463

464 def create_channel(self, method, headers):

465 self.__channels[method] = HTTPClientSecurityProvider.connect(self, self._rpcProxyUrl.scheme,

466 self._rpcProxyUrl.netloc)

467

468 auth_headers = HTTPClientSecurityProvider.get_auth_headers(self, self.__channels[method],

469 method, self._rpcProxyUrl.path, headers)[0]

470

471 headers_final = {}

472 headers_final.update(headers)

473 headers_final.update(auth_headers)

474

475 self.__auth_type = HTTPClientSecurityProvider.get_auth_type(self)

476

477 # To connect to an RPC Server, we need to let the RPC Proxy know

478 # where to connect. The target RPC Server name and its port are passed

479 # in the query of the HTTP request. The target RPC Server must be the ncacn_http

480 # service.

481 #

482 # The utilized format: /rpc/rpcproxy.dll?RemoteName:RemotePort

483 #

484 # For RDG servers, you can specify localhost:3388, but in other cases you cannot

485 # use localhost as there will be no ACL for it.

486 #

487 # To know what RemoteName to use, we rely on Default ACL. It's specified

488 # in the HKLM\SOFTWARE\Microsoft\Rpc\RpcProxy key:

489 #

490 # ValidPorts REG_SZ COMPANYSERVER04:593;COMPANYSERVER04:49152-65535

491 #

492 # In this way, we can at least connect to the endpoint mapper on port 593.

493 # So, if the caller set remoteName to an empty string, we assume the target

494 # is the RPC Proxy server itself, and get its NetBIOS name from the NTLMSSP.

495 #

496 # Interestingly, if the administrator renames the server after RPC Proxy installation

497 # or joins the server to the domain after RPC Proxy installation, the ACL will remain

498 # the original. So, sometimes the ValidPorts values have the format WIN-JCKEDQVDOQU, and

499 # we are not able to use them.

500 #

501 # For Exchange servers, the value of the default ACL doesn't matter as they

502 # allow connections by their own mechanisms:

503 # - Exchange 2003 / 2007 / 2010 servers add their own ACL, which includes

504 # NetBIOS names of all Exchange servers (and some other servers).

505 # This ACL is regularly and automatically updated on each server.

506 # Allowed ports: 6001-6004

507 #

508 # 6001 is used for MS-OXCRPC

509 # 6002 is used for MS-OXABREF

510 # 6003 is not used

511 # 6004 is used for MS-OXNSPI

512 #

513 # Tests on Exchange 2010 show that MS-OXNSPI and MS-OXABREF are available

514 # on both 6002 and 6004.

515 #

516 # - Exchange 2013 / 2016 / 2019 servers process RemoteName on their own

517 # (via RpcProxyShim.dll), and the NetBIOS name format is supported only for

518 # backward compatibility.

519 #

520 # ! Default ACL is never used, so there is no way to connect to the endpoint mapper!

521 #

522 # Allowed ports: 6001-6004

523 #

524 # 6001 is used for MS-OXCRPC

525 # 6002 is used for MS-OXABREF

526 # 6003 is not used

527 # 6004 is used for MS-OXNSPI

528 #

529 # Tests show that all protocols are available on the 6001 / 6002 / 6004 ports via

530 # RPC over HTTP v2, and the separation is only used for backward compatibility.

531 #

532 # The pure ncacn_http endpoint is available only on the 6001 TCP/IP port.

533 #

534 # RpcProxyShim.dll allows you to skip authentication on the RPC level to get

535 # a faster connection, and it makes Exchange 2013 / 2016 / 2019 RPC over HTTP v2

536 # endpoints vulnerable to NTLM-Relaying attacks.

537 #

538 # If the target is Exchange behind Microsoft TMG, you most likely need to specify

539 # the remote name manually using the value from /autodiscover/autodiscover.xml.

540 # Note that /autodiscover/autodiscover.xml might not be available with

541 # a non-outlook User-Agent.

542 #

543 # There may be multiple RPC Proxy servers with different NetBIOS names on

544 # a single external IP. We store the first one's NetBIOS name and use it for all

545 # the following channels.

546 # It's acceptable to assume all RPC Proxies have the same ACLs (true for Exchange).

547 if not self.__remoteName and self.__auth_type == AUTH_BASIC:

548 raise RPCProxyClientException(RPC_PROXY_REMOTE_NAME_NEEDED_ERR)

549

550 if not self.__remoteName:

551 ntlmssp = self.get_ntlmssp_info()

552 self.__remoteName = ntlmssp[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')

553 self._stringbinding.set_network_address(self.__remoteName)

554 LOG.debug('StringBinding has been changed to %s' % self._stringbinding)

555

556 if not self._rpcProxyUrl.query:

557 query = self.__remoteName + ':' + str(self.__dstport)

558 self._rpcProxyUrl = self._rpcProxyUrl._replace(query=query)

559

560 path = self._rpcProxyUrl.path + '?' + self._rpcProxyUrl.query

561

562 self.__channels[method].request(method, path, headers=headers_final)

563 self._read_100_continue(method)

564

565 def _read_100_continue(self, method):

566 resp = self.__channels[method].sock.recv(self.RECV_SIZE)

567

568 while resp.find(b'\r\n\r\n') == -1:

569 resp += self.__channels[method].sock.recv(self.RECV_SIZE)

570

571 # Continue responses can have multiple lines, for example:

572 #

573 # HTTP/1.1 100 Continue

574 # Via: 1.1 FIREWALL1

575 #

576 # Don't expect the response to contain "100 Continue\r\n\r\n"

577 if resp[9:23] != b'100 Continue\r\n':

578 try:

579 # The server (IIS) may return localized error messages in

580 # the first line. Tests shown they are in UTF-8.

581 resp = resp.split(b'\r\n')[0].decode("UTF-8", errors='replace')

582

583 raise RPCProxyClientException('RPC Proxy Client: %s authentication failed in %s channel' %

584 (self.__auth_type, method), proxy_error=resp)

585 except (IndexError, KeyError, AttributeError):

586 raise RPCProxyClientException('RPC Proxy Client: %s authentication failed in %s channel' %

587 (self.__auth_type, method))

588

589 def create_tunnel(self):

590 # 3.2.1.5.3.1 Connection Establishment

591 packet = hCONN_A1(self.__virtualConnectionCookie, self.__outChannelCookie, self.__availableWindowAdvertised)

592 self.get_socket_out().send(packet)

593

594 packet = hCONN_B1(self.__virtualConnectionCookie, self.__inChannelCookie, self.__associationGroupId)

595 self.get_socket_in().send(packet)

596

597 resp = self.get_socket_out().recv(self.RECV_SIZE)

598

599 while resp.find(b'\r\n\r\n') == -1:

600 resp += self.get_socket_out().recv(self.RECV_SIZE)

601

602 if resp[9:12] != b'200':

603 try:

604 # The server (IIS) may return localized error messages in

605 # the first line. Tests shown they are in UTF-8.

606 resp = resp.split(b'\r\n')[0].decode("UTF-8", errors='replace')

607

608 raise RPCProxyClientException('RPC Proxy CONN/A1 request failed', proxy_error=resp)

609 except (IndexError, KeyError, AttributeError):

610 raise RPCProxyClientException('RPC Proxy CONN/A1 request failed')

611

612 if b'Transfer-Encoding: chunked' in resp:

613 self.__serverChunked = True

614

615 # If the body is here, let's send it to rpc_out_recv1()

616 self.__readBuffer = resp[resp.find(b'\r\n\r\n') + 4:]

617

618 # Recieving and parsing CONN/A3

619 conn_a3_rpc = self.rpc_out_read_pkt()

620 conn_a3_pdu = RTSHeader(conn_a3_rpc)['pduData']

621 conn_a3 = CONN_A3_RTS_PDU(conn_a3_pdu)

622 self.__serverConnectionTimeout = conn_a3['ConnectionTimeout']['ConnectionTimeout']

623

624 # Recieving and parsing CONN/C2

625 conn_c2_rpc = self.rpc_out_read_pkt()

626 conn_c2_pdu = RTSHeader(conn_c2_rpc)['pduData']

627 conn_c2 = CONN_C2_RTS_PDU(conn_c2_pdu)

628 self.__serverReceiveWindowSize = conn_c2['ReceiveWindowSize']['ReceiveWindowSize']

629

630 def get_socket_in(self):

631 return self.__channels['RPC_IN_DATA'].sock

632

633 def get_socket_out(self):

634 return self.__channels['RPC_OUT_DATA'].sock

635

636 def close_rpc_in_channel(self):

637 return self.__channels['RPC_IN_DATA'].close()

638

639 def close_rpc_out_channel(self):

640 return self.__channels['RPC_OUT_DATA'].close()

641

642 def check_http_error(self, buffer):

643 if buffer[:22] == b'HTTP/1.0 503 RPC Error':

644 raise RPCProxyClientException('RPC Proxy request failed', proxy_error=buffer)

645

646 def rpc_out_recv1(self, amt=None):

647 # Read with at most one underlying system call.

648 # The function MUST return the maximum amt bytes.

649 #

650 # Strictly speaking, it may cause more than one read,

651 # but that is ok, since that is to satisfy the chunked protocol.

652 sock = self.get_socket_out()

653

654 if self.__serverChunked is False:

655 if len(self.__readBuffer) > 0:

656 buffer = self.__readBuffer

657 self.__readBuffer = b''

658 else:

659 # Let's read RECV_SIZE bytes and not amt bytes.

660 # We would need to check the answer for HTTP errors, as

661 # they can just appear in the middle of the stream.

662 buffer = sock.recv(self.RECV_SIZE)

663

664 self.check_http_error(buffer)

665

666 if len(buffer) <= amt:

667 return buffer

668

669 # We received more than we need

670 self.__readBuffer = buffer[amt:]

671 return buffer[:amt]

672

673 # Check if the previous chunk is still there

674 if self.__chunkLeft > 0:

675 # If the previous chunk is still there,

676 # just give the caller what we already have

677 if amt >= self.__chunkLeft:

678 buffer = self.__readBuffer[:self.__chunkLeft]

679 # We may have recieved a part of a new chunk

680 self.__readBuffer = self.__readBuffer[self.__chunkLeft + 2:]

681 self.__chunkLeft = 0

682

683 return buffer

684 else:

685 buffer = self.__readBuffer[:amt]

686 self.__readBuffer = self.__readBuffer[amt:]

687 self.__chunkLeft -= amt

688

689 return buffer

690

691 # Let's start to process a new chunk

692 buffer = self.__readBuffer

693 self.__readBuffer = b''

694

695 self.check_http_error(buffer)

696

697 # Let's receive a chunk size field which ends with CRLF

698 # For Microsoft TMG 2010 it can cause more than one read

699 while buffer.find(b'\r\n') == -1:

700 buffer += sock.recv(self.RECV_SIZE)

701 self.check_http_error(buffer)

702

703 chunksize = int(buffer[:buffer.find(b'\r\n')], 16)

704 buffer = buffer[buffer.find(b'\r\n') + 2:]

705

706 # Let's read at least our chunk including final CRLF

707 while len(buffer) - 2 < chunksize:

708 buffer += sock.recv(chunksize - len(buffer) + 2)

709

710 # We should not be using any information from

711 # the TCP level to determine HTTP boundaries.

712 # So, we may have received more than we need.

713 if len(buffer) - 2 > chunksize:

714 self.__readBuffer = buffer[chunksize + 2:]

715 buffer = buffer[:chunksize + 2]

716

717 # Checking the amt

718 if len(buffer) - 2 > amt:

719 self.__chunkLeft = chunksize - amt

720 # We may have recieved a part of a new chunk before,

721 # so the concatenation is crucual

722 self.__readBuffer = buffer[amt:] + self.__readBuffer

723

724 return buffer[:amt]

725 else:

726 # Removing CRLF

727 return buffer[:-2]

728

729 def send(self, data, forceWriteAndx=0, forceRecv=0):

730 # We don't use chunked encoding for IN channel as

731 # Microsoft software is developed this way.

732 # If you do this, it may fail.

733 self.get_socket_in().send(data)

734

735 def rpc_out_read_pkt(self, handle_rts=False):

736 while True:

737 response_data = b''

738

739 # Let's receive common RPC header and no more

740 #

741 # C706

742 # 12.4 Common Fields

743 # Header encodings differ between connectionless and connection-oriented PDUs.

744 # However, certain fields use common sets of values with a consistent

745 # interpretation across the two protocols.

746 #

747 # This MUST recv MSRPCHeader._SIZE bytes, and not MSRPCRespHeader._SIZE bytes!

748 #

749 while len(response_data) < MSRPCHeader._SIZE:

750 response_data += self.rpc_out_recv1(MSRPCHeader._SIZE - len(response_data))

751

752 response_header = MSRPCHeader(response_data)

753

754 # frag_len contains the full length of the packet for both

755 # MSRPC and RTS

756 frag_len = response_header['frag_len']

757

758 # Receiving the full pkt and no more

759 while len(response_data) < frag_len:

760 response_data += self.rpc_out_recv1(frag_len - len(response_data))

761

762 # We need to do the Flow Control procedures

763 #

764 # 3.2.1.1.4

765 # This protocol specifies that only RPC PDUs are subject to the flow control abstract data

766 # model. RTS PDUs and the HTTP request and response headers are not subject to flow control.

767 if response_header['type'] != MSRPC_RTS:

768 self.flow_control(frag_len)

769

770 if handle_rts is True and response_header['type'] == MSRPC_RTS:

771 self.handle_out_of_sequence_rts(response_data)

772 else:

773 return response_data

774

775 def recv(self, forceRecv=0, count=0):

776 return self.rpc_out_read_pkt(handle_rts=True)

777

778 def handle_out_of_sequence_rts(self, response_data):

779 packet = RTSHeader(response_data)

780

781 #print("=========== RTS PKT ===========")

782 #print("RAW: %s" % binascii.hexlify(response_data))

783 #packet.dump()

784 #

785 #pduData = packet['pduData']

786 #numberOfCommands = packet['NumberOfCommands']

787 #

788 #server_cmds = []

789 #while numberOfCommands > 0:

790 # numberOfCommands -= 1

791 #

792 # cmd_type = unpack('<L', pduData[:4])[0]

793 # cmd = COMMANDS[cmd_type](pduData)

794 # server_cmds.append(cmd)

795 # pduData = pduData[len(cmd):]

796 #

797 #for cmd in server_cmds:

798 # cmd.dump()

799 #print("=========== / RTS PKT ===========")

800

801 # 2.2.4.49 Ping RTS PDU

802 if packet['Flags'] == RTS_FLAG_PING:

803 # 3.2.1.2.1 PingTimer

804 #

805 # If the SendingChannel is part of a Virtual Connection in the Outbound Proxy or Client roles, the

806 # SendingChannel maintains a PingTimer that on expiration indicates a PING PDU must be sent to the

807 # receiving channel. The PING PDU is sent to the receiving channel when no data has been sent within

808 # half of the value of the KeepAliveInterval.

809

810 # As we do not do long-term connections with no data transfer,

811 # it means something on the server-side is going wrong.

812 self.rts_ping_received = True

813 LOG.error("Ping RTS PDU packet received. Is the RPC Server alive?")

814

815 # Just in case it's a long operation, let's send PING PDU to IN Channel like in xfreerdp

816 # It's better to send more than one PING packet as it only 20 bytes long

817 packet = hPing()

818 self.send(packet)

819 self.send(packet)

820 # 2.2.4.24 OUT_R1/A2 RTS PDU

821 elif packet['Flags'] == RTS_FLAG_RECYCLE_CHANNEL:

822 raise RPCProxyClientException("The server requested recycling of a virtual OUT channel, " \

823 "but this function is not supported!")

824 # Ignore all other messages, most probably flow control acknowledgments

825 else:

826 pass

827

828 def flow_control(self, frag_len):

829 self.__bytesReceived += frag_len

830 self.__receiverAvailableWindow -= frag_len

831

832 if (self.__receiverAvailableWindow < self.__availableWindowAdvertised // 2):

833 self.__receiverAvailableWindow = self.__availableWindowAdvertised

834 packet = hFlowControlAckWithDestination(FDOutProxy, self.__bytesReceived,

835 self.__availableWindowAdvertised, self.__outChannelCookie)

836 self.send(packet)

837

838 def connect(self):

839 self.create_rpc_in_channel()

840 self.create_rpc_out_channel()

841 self.create_tunnel()

842

843 def disconnect(self):

844 self.close_rpc_in_channel()

845 self.close_rpc_out_channel()

846 self.init_state()