Hide keyboard shortcuts

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# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

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

7# for more information. 

8# 

9# Description: 

10# Initial [MS-RCPH] Interface implementation 

11# 

12# Author: 

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

14# 

15 

16import re 

17import binascii 

18from struct import unpack 

19 

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

21from impacket.dcerpc.v5.rpcrt import DCERPCException 

22 

23from impacket.uuid import EMPTY_UUID 

24from impacket.http import HTTPClientSecurityProvider, AUTH_BASIC 

25from impacket.structure import Structure 

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

27 MSRPC_RTS, PFC_FIRST_FRAG, PFC_LAST_FRAG 

28 

29class RPCProxyClientException(DCERPCException): 

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

31 

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

33 rpc_error_code = None 

34 

35 if proxy_error is not None: 

36 try: 

37 search = self.parser.search(proxy_error) 

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

39 except: 

40 error_string += ': ' + proxy_error 

41 

42 DCERPCException.__init__(self, error_string, rpc_error_code) 

43 

44 def __str__(self): 

45 if self.error_code is not None: 

46 key = self.error_code 

47 if key in system_errors.ERROR_MESSAGES: 

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

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

50 elif key in nt_errors.ERROR_MESSAGES: 

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

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

53 else: 

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

55 else: 

56 return self.error_string 

57 

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

59# CONSTANTS 

60################################################################################ 

61 

62RPC_OVER_HTTP_v1 = 1 

63RPC_OVER_HTTP_v2 = 2 

64 

65# Errors which might need handling 

66 

67# RPCProxyClient internal errors 

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

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

70 

71# Errors below contain a part of server responses 

72RPC_PROXY_INVALID_RPC_PORT_ERR = 'Invalid RPC Port' 

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

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

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

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

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

78 

79 

80# 2.2.3.3 Forward Destinations 

81FDClient = 0x00000000 

82FDInProxy = 0x00000001 

83FDServer = 0x00000002 

84FDOutProxy = 0x00000003 

85 

86RTS_FLAG_NONE = 0x0000 

87RTS_FLAG_PING = 0x0001 

88RTS_FLAG_OTHER_CMD = 0x0002 

89RTS_FLAG_RECYCLE_CHANNEL = 0x0004 

90RTS_FLAG_IN_CHANNEL = 0x0008 

91RTS_FLAG_OUT_CHANNEL = 0x0010 

92RTS_FLAG_EOF = 0x0020 

93RTS_FLAG_ECHO = 0x0040 

94 

95# 2.2.3.5 RTS Commands 

96RTS_CMD_RECEIVE_WINDOW_SIZE = 0x00000000 

97RTS_CMD_FLOW_CONTROL_ACK = 0x00000001 

98RTS_CMD_CONNECTION_TIMEOUT = 0x00000002 

99RTS_CMD_COOKIE = 0x00000003 

100RTS_CMD_CHANNEL_LIFETIME = 0x00000004 

101RTS_CMD_CLIENT_KEEPALIVE = 0x00000005 

102RTS_CMD_VERSION = 0x00000006 

103RTS_CMD_EMPTY = 0x00000007 

104RTS_CMD_PADDING = 0x00000008 

105RTS_CMD_NEGATIVE_ANCE = 0x00000009 

106RTS_CMD_ANCE = 0x0000000A 

107RTS_CMD_CLIENT_ADDRESS = 0x0000000B 

108RTS_CMD_ASSOCIATION_GROUP_ID = 0x0000000C 

109RTS_CMD_DESTINATION = 0x0000000D 

110RTS_CMD_PING_TRAFFIC_SENT_NOTIFY = 0x0000000E 

111 

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

113# STRUCTURES 

114################################################################################ 

115 

116# 2.2.3.1 RTS Cookie 

117class RTSCookie(Structure): 

118 structure = ( 

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

120 ) 

121 

122# 2.2.3.2 Client Address 

123class EncodedClientAddress(Structure): 

124 structure = ( 

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

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

127 ('ClientAddress',':'), 

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

129 ) 

130 

131# 2.2.3.4 Flow Control Acknowledgment 

132class Ack(Structure): 

133 structure = ( 

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

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

136 ('ChannelCookie',':',RTSCookie), 

137 ) 

138 

139# 2.2.3.5.1 ReceiveWindowSize 

140class ReceiveWindowSize(Structure): 

141 structure = ( 

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

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

144 ) 

145 

146# 2.2.3.5.2 FlowControlAck 

147class FlowControlAck(Structure): 

148 structure = ( 

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

150 ('Ack',':',Ack), 

151 ) 

152 

153# 2.2.3.5.3 ConnectionTimeout 

154class ConnectionTimeout(Structure): 

155 structure = ( 

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

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

158 ) 

159 

160# 2.2.3.5.4 Cookie 

161class Cookie(Structure): 

162 structure = ( 

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

164 ('Cookie',':',RTSCookie), 

165 ) 

166 

167# 2.2.3.5.5 ChannelLifetime 

168class ChannelLifetime(Structure): 

169 structure = ( 

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

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

172 ) 

173 

174# 2.2.3.5.6 ClientKeepalive 

175# 

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

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

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

179# 

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

181class ClientKeepalive(Structure): 

182 structure = ( 

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

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

185 ) 

186 

187# 2.2.3.5.7 Version 

188class Version(Structure): 

189 structure = ( 

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

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

192 ) 

193 

194# 2.2.3.5.8 Empty 

195class Empty(Structure): 

196 structure = ( 

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

198 ) 

199 

200# 2.2.3.5.9 Padding 

201class Padding(Structure): 

202 structure = ( 

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

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

205 ('Padding','*ConformanceCount'), 

206 ) 

207 

208# 2.2.3.5.10 NegativeANCE 

209class NegativeANCE(Structure): 

210 structure = ( 

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

212 ) 

213 

214# 2.2.3.5.11 ANCE 

215class ANCE(Structure): 

216 structure = ( 

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

218 ) 

219 

220# 2.2.3.5.12 ClientAddress 

221class ClientAddress(Structure): 

222 structure = ( 

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

224 ('ClientAddress',':',EncodedClientAddress), 

225 ) 

226 

227# 2.2.3.5.13 AssociationGroupId 

228class AssociationGroupId(Structure): 

229 structure = ( 

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

231 ('AssociationGroupId',':',RTSCookie), 

232 ) 

233 

234# 2.2.3.5.14 Destination 

235class Destination(Structure): 

236 structure = ( 

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

238 ('Destination','<L'), 

239 ) 

240 

241# 2.2.3.5.15 PingTrafficSentNotify 

242class PingTrafficSentNotify(Structure): 

243 structure = ( 

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

245 ('PingTrafficSent','<L'), 

246 ) 

247 

248COMMANDS = { 

249 0x0: ReceiveWindowSize, 

250 0x1: FlowControlAck, 

251 0x2: ConnectionTimeout, 

252 0x3: Cookie, 

253 0x4: ChannelLifetime, 

254 0x5: ClientKeepalive, 

255 0x6: Version, 

256 0x7: Empty, 

257 0x8: Padding, 

258 0x9: NegativeANCE, 

259 0xA: ANCE, 

260 0xB: ClientAddress, 

261 0xC: AssociationGroupId, 

262 0xD: Destination, 

263 0xE: PingTrafficSentNotify, 

264} 

265 

266# 2.2.3.6.1 RTS PDU Header 

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

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

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

270class RTSHeader(MSRPCHeader): 

271 _SIZE = 20 

272 commonHdr = MSRPCHeader.commonHdr + ( 

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

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

275 ) 

276 

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

278 MSRPCHeader.__init__(self, data, alignment) 

279 self['type'] = MSRPC_RTS 

280 self['flags'] = PFC_FIRST_FRAG | PFC_LAST_FRAG 

281 self['auth_length'] = 0 

282 self['call_id'] = 0 

283 

284# 2.2.4.2 CONN/A1 RTS PDU 

285# 

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

287# initiate the establishment of a virtual connection. 

288class CONN_A1_RTS_PDU(Structure): 

289 structure = ( 

290 ('Version',':',Version), 

291 ('VirtualConnectionCookie',':',Cookie), 

292 ('OutChannelCookie',':',Cookie), 

293 ('ReceiveWindowSize',':',ReceiveWindowSize), 

294 ) 

295 

296# 2.2.4.5 CONN/B1 RTS PDU 

297# 

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

299# initiate the establishment of a virtual connection. 

300class CONN_B1_RTS_PDU(Structure): 

301 structure = ( 

302 ('Version',':',Version), 

303 ('VirtualConnectionCookie',':',Cookie), 

304 ('INChannelCookie',':',Cookie), 

305 ('ChannelLifetime',':',ChannelLifetime), 

306 ('ClientKeepalive',':',ClientKeepalive), 

307 ('AssociationGroupId',':',AssociationGroupId), 

308 ) 

309 

310# 2.2.4.4 CONN/A3 RTS PDU 

311# 

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

313# continue the establishment of the virtual connection. 

314class CONN_A3_RTS_PDU(Structure): 

315 structure = ( 

316 ('ConnectionTimeout',':',ConnectionTimeout), 

317 ) 

318 

319# 2.2.4.9 CONN/C2 RTS PDU 

320# 

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

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

323class CONN_C2_RTS_PDU(Structure): 

324 structure = ( 

325 ('Version',':',Version), 

326 ('ReceiveWindowSize',':',ReceiveWindowSize), 

327 ('ConnectionTimeout',':',ConnectionTimeout), 

328 ) 

329 

330# 2.2.4.51 FlowControlAckWithDestination RTS PDU 

331class FlowControlAckWithDestination_RTS_PDU(Structure): 

332 structure = ( 

333 ('Destination',':',Destination), 

334 ('FlowControlAck',':',FlowControlAck), 

335 ) 

336 

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

338# HELPERS 

339################################################################################ 

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

341 conn_a1 = CONN_A1_RTS_PDU() 

342 conn_a1['Version'] = Version() 

343 conn_a1['VirtualConnectionCookie'] = Cookie() 

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

345 conn_a1['OutChannelCookie'] = Cookie() 

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

347 conn_a1['ReceiveWindowSize'] = ReceiveWindowSize() 

348 conn_a1['ReceiveWindowSize']['ReceiveWindowSize'] = receiveWindowSize 

349 

350 packet = RTSHeader() 

351 packet['Flags'] = RTS_FLAG_NONE 

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

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

354 

355 return packet.getData() 

356 

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

358 conn_b1 = CONN_B1_RTS_PDU() 

359 conn_b1['Version'] = Version() 

360 conn_b1['VirtualConnectionCookie'] = Cookie() 

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

362 conn_b1['INChannelCookie'] = Cookie() 

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

364 conn_b1['ChannelLifetime'] = ChannelLifetime() 

365 conn_b1['ClientKeepalive'] = ClientKeepalive() 

366 conn_b1['AssociationGroupId'] = AssociationGroupId() 

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

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

369 

370 packet = RTSHeader() 

371 packet['Flags'] = RTS_FLAG_NONE 

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

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

374 

375 return packet.getData() 

376 

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

378 rts_pdu = FlowControlAckWithDestination_RTS_PDU() 

379 rts_pdu['Destination'] = Destination() 

380 rts_pdu['Destination']['Destination'] = destination 

381 rts_pdu['FlowControlAck'] = FlowControlAck() 

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

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

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

385 

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

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

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

389 

390 packet = RTSHeader() 

391 packet['Flags'] = RTS_FLAG_OTHER_CMD 

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

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

394 

395 return packet.getData() 

396 

397def hPing(): 

398 packet = RTSHeader() 

399 packet['Flags'] = RTS_FLAG_PING 

400 

401 return packet.getData() 

402 

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

404# CLASSES 

405################################################################################ 

406class RPCProxyClient(HTTPClientSecurityProvider): 

407 RECV_SIZE = 8192 

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

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

410 'Connection' : 'Keep-Alive', 

411 'Expect' : '100-continue', 

412 'Accept' : 'application/rpc', 

413 'Pragma' : 'No-cache' 

414 } 

415 

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

417 HTTPClientSecurityProvider.__init__(self) 

418 self.__remoteName = remoteName 

419 self.__dstport = dstport 

420 

421 # Chosen auth type 

422 self.__auth_type = None 

423 

424 self.init_state() 

425 

426 def init_state(self): 

427 self.__channels = {} 

428 

429 self.__inChannelCookie = uuid.generate() 

430 self.__outChannelCookie = uuid.generate() 

431 self.__associationGroupId = uuid.generate() 

432 self.__virtualConnectionCookie = uuid.generate() 

433 

434 self.__serverConnectionTimeout = None 

435 self.__serverReceiveWindowSize = None 

436 self.__availableWindowAdvertised = 262144 # 256k 

437 self.__receiverAvailableWindow = self.__availableWindowAdvertised 

438 self.__bytesReceived = 0 

439 

440 self.__serverChunked = False 

441 self.__readBuffer = b'' 

442 self.__chunkLeft = 0 

443 

444 self.rts_ping_received = False 

445 

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

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

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

449 

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

451 HTTPClientSecurityProvider.set_credentials(self, username, password, 

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

453 

454 def create_rpc_in_channel(self): 

455 headers = self.default_headers.copy() 

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

457 

458 self.create_channel('RPC_IN_DATA', headers) 

459 

460 def create_rpc_out_channel(self): 

461 headers = self.default_headers.copy() 

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

463 

464 self.create_channel('RPC_OUT_DATA', headers) 

465 

466 def create_channel(self, method, headers): 

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

468 self._rpcProxyUrl.netloc) 

469 

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

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

472 

473 headers_final = {} 

474 headers_final.update(headers) 

475 headers_final.update(auth_headers) 

476 

477 self.__auth_type = HTTPClientSecurityProvider.get_auth_type(self) 

478 

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

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

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

482 # service. 

483 # 

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

485 # 

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

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

488 # 

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

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

491 # 

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

493 # 

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

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

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

497 # 

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

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

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

501 # we are not able to use them. 

502 # 

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

504 # allow connections by their own mechanisms: 

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

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

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

508 # Allowed ports: 6001-6004 

509 # 

510 # 6001 is used for MS-OXCRPC 

511 # 6002 is used for MS-OXABREF 

512 # 6003 is not used 

513 # 6004 is used for MS-OXNSPI 

514 # 

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

516 # on both 6002 and 6004. 

517 # 

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

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

520 # backward compatibility. 

521 # 

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

523 # 

524 # Allowed ports: 6001-6004 

525 # 

526 # 6001 is used for MS-OXCRPC 

527 # 6002 is used for MS-OXABREF 

528 # 6003 is not used 

529 # 6004 is used for MS-OXNSPI 

530 # 

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

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

533 # 

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

535 # 

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

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

538 # endpoints vulnerable to NTLM-Relaying attacks. 

539 # 

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

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

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

543 # a non-outlook User-Agent. 

544 # 

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

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

547 # the following channels. 

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

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

550 raise RPCProxyClientException(RPC_PROXY_REMOTE_NAME_NEEDED_ERR) 

551 

552 if not self.__remoteName: 

553 ntlmssp = self.get_ntlmssp_info() 

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

555 self._stringbinding.set_network_address(self.__remoteName) 

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

557 

558 if not self._rpcProxyUrl.query: 

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

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

561 

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

563 

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

565 self._read_100_continue(method) 

566 

567 def _read_100_continue(self, method): 

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

569 

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

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

572 

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

574 # 

575 # HTTP/1.1 100 Continue 

576 # Via: 1.1 FIREWALL1 

577 # 

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

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

580 try: 

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

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

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

584 

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

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

587 except (IndexError, KeyError, AttributeError): 

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

589 (self.__auth_type, method)) 

590 

591 def create_tunnel(self): 

592 # 3.2.1.5.3.1 Connection Establishment 

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

594 self.get_socket_out().send(packet) 

595 

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

597 self.get_socket_in().send(packet) 

598 

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

600 

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

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

603 

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

605 try: 

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

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

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

609 

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

611 except (IndexError, KeyError, AttributeError): 

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

613 

614 resp_ascii = resp.decode("ASCII", errors='replace') 

615 if "transfer-encoding: chunked" in resp_ascii.lower(): 

616 self.__serverChunked = True 

617 

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

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

620 

621 # Recieving and parsing CONN/A3 

622 conn_a3_rpc = self.rpc_out_read_pkt() 

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

624 conn_a3 = CONN_A3_RTS_PDU(conn_a3_pdu) 

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

626 

627 # Recieving and parsing CONN/C2 

628 conn_c2_rpc = self.rpc_out_read_pkt() 

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

630 conn_c2 = CONN_C2_RTS_PDU(conn_c2_pdu) 

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

632 

633 def get_socket_in(self): 

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

635 

636 def get_socket_out(self): 

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

638 

639 def close_rpc_in_channel(self): 

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

641 

642 def close_rpc_out_channel(self): 

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

644 

645 def check_http_error(self, buffer): 

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

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

648 

649 def rpc_out_recv1(self, amt=None): 

650 # Read with at most one underlying system call. 

651 # The function MUST return the maximum amt bytes. 

652 # 

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

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

655 sock = self.get_socket_out() 

656 

657 if self.__serverChunked is False: 

658 if len(self.__readBuffer) > 0: 

659 buffer = self.__readBuffer 

660 self.__readBuffer = b'' 

661 else: 

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

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

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

665 buffer = sock.recv(self.RECV_SIZE) 

666 

667 self.check_http_error(buffer) 

668 

669 if len(buffer) <= amt: 

670 return buffer 

671 

672 # We received more than we need 

673 self.__readBuffer = buffer[amt:] 

674 return buffer[:amt] 

675 

676 # Check if the previous chunk is still there 

677 if self.__chunkLeft > 0: 

678 # If the previous chunk is still there, 

679 # just give the caller what we already have 

680 if amt >= self.__chunkLeft: 

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

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

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

684 self.__chunkLeft = 0 

685 

686 return buffer 

687 else: 

688 buffer = self.__readBuffer[:amt] 

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

690 self.__chunkLeft -= amt 

691 

692 return buffer 

693 

694 # Let's start to process a new chunk 

695 buffer = self.__readBuffer 

696 self.__readBuffer = b'' 

697 

698 self.check_http_error(buffer) 

699 

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

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

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

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

704 self.check_http_error(buffer) 

705 

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

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

708 

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

710 while len(buffer) - 2 < chunksize: 

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

712 

713 # We should not be using any information from 

714 # the TCP level to determine HTTP boundaries. 

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

716 if len(buffer) - 2 > chunksize: 

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

718 buffer = buffer[:chunksize + 2] 

719 

720 # Checking the amt 

721 if len(buffer) - 2 > amt: 

722 self.__chunkLeft = chunksize - amt 

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

724 # so the concatenation is crucual 

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

726 

727 return buffer[:amt] 

728 else: 

729 # Removing CRLF 

730 return buffer[:-2] 

731 

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

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

734 # Microsoft software is developed this way. 

735 # If you do this, it may fail. 

736 self.get_socket_in().send(data) 

737 

738 def rpc_out_read_pkt(self, handle_rts=False): 

739 while True: 

740 response_data = b'' 

741 

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

743 # 

744 # C706 

745 # 12.4 Common Fields 

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

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

748 # interpretation across the two protocols. 

749 # 

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

751 # 

752 while len(response_data) < MSRPCHeader._SIZE: 

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

754 

755 response_header = MSRPCHeader(response_data) 

756 

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

758 # MSRPC and RTS 

759 frag_len = response_header['frag_len'] 

760 

761 # Receiving the full pkt and no more 

762 while len(response_data) < frag_len: 

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

764 

765 # We need to do the Flow Control procedures 

766 # 

767 # 3.2.1.1.4 

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

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

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

771 self.flow_control(frag_len) 

772 

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

774 self.handle_out_of_sequence_rts(response_data) 

775 else: 

776 return response_data 

777 

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

779 return self.rpc_out_read_pkt(handle_rts=True) 

780 

781 def handle_out_of_sequence_rts(self, response_data): 

782 packet = RTSHeader(response_data) 

783 

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

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

786 #packet.dump() 

787 # 

788 #pduData = packet['pduData'] 

789 #numberOfCommands = packet['NumberOfCommands'] 

790 # 

791 #server_cmds = [] 

792 #while numberOfCommands > 0: 

793 # numberOfCommands -= 1 

794 # 

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

796 # cmd = COMMANDS[cmd_type](pduData) 

797 # server_cmds.append(cmd) 

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

799 # 

800 #for cmd in server_cmds: 

801 # cmd.dump() 

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

803 

804 # 2.2.4.49 Ping RTS PDU 

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

806 # 3.2.1.2.1 PingTimer 

807 # 

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

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

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

811 # half of the value of the KeepAliveInterval. 

812 

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

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

815 self.rts_ping_received = True 

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

817 

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

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

820 packet = hPing() 

821 self.send(packet) 

822 self.send(packet) 

823 # 2.2.4.24 OUT_R1/A2 RTS PDU 

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

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

826 "but this function is not supported!") 

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

828 else: 

829 pass 

830 

831 def flow_control(self, frag_len): 

832 self.__bytesReceived += frag_len 

833 self.__receiverAvailableWindow -= frag_len 

834 

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

836 self.__receiverAvailableWindow = self.__availableWindowAdvertised 

837 packet = hFlowControlAckWithDestination(FDOutProxy, self.__bytesReceived, 

838 self.__availableWindowAdvertised, self.__outChannelCookie) 

839 self.send(packet) 

840 

841 def connect(self): 

842 self.create_rpc_in_channel() 

843 self.create_rpc_out_channel() 

844 self.create_tunnel() 

845 

846 def disconnect(self): 

847 self.close_rpc_in_channel() 

848 self.close_rpc_out_channel() 

849 self.init_state()