Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/smbclient.py: 22%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

841 statements  

1# SPDX-License-Identifier: GPL-2.0-only 

2# This file is part of Scapy 

3# See https://scapy.net/ for more information 

4# Copyright (C) Gabriel Potter 

5 

6""" 

7SMB 1 / 2 Client Automaton 

8 

9 

10.. note:: 

11 You will find more complete documentation for this layer over at 

12 `SMB <https://scapy.readthedocs.io/en/latest/layers/smb.html#client>`_ 

13""" 

14 

15import io 

16import os 

17import pathlib 

18import socket 

19import time 

20import threading 

21 

22from scapy.automaton import ATMT, Automaton, ObjectPipe 

23from scapy.base_classes import Net 

24from scapy.config import conf 

25from scapy.error import Scapy_Exception 

26from scapy.fields import UTCTimeField 

27from scapy.supersocket import SuperSocket 

28from scapy.utils import ( 

29 CLIUtil, 

30 pretty_list, 

31 human_size, 

32 valid_ip, 

33 valid_ip6, 

34) 

35from scapy.volatile import RandUUID 

36 

37from scapy.layers.dcerpc import NDRUnion, find_dcerpc_interface 

38from scapy.layers.gssapi import ( 

39 GSS_S_COMPLETE, 

40 GSS_S_CONTINUE_NEEDED, 

41 GSS_C_FLAGS, 

42) 

43from scapy.layers.inet6 import Net6 

44from scapy.layers.kerberos import ( 

45 KerberosSSP, 

46 krb_as_and_tgs, 

47 _parse_upn, 

48) 

49from scapy.layers.msrpce.raw.ms_srvs import ( 

50 LPSHARE_ENUM_STRUCT, 

51 NetrShareEnum_Request, 

52 NetrShareEnum_Response, 

53 SHARE_INFO_1_CONTAINER, 

54) 

55from scapy.layers.ntlm import ( 

56 NTLMSSP, 

57 MD4le, 

58) 

59from scapy.layers.smb import ( 

60 SMBNegotiate_Request, 

61 SMBNegotiate_Response_Extended_Security, 

62 SMBNegotiate_Response_Security, 

63 SMBSession_Null, 

64 SMBSession_Setup_AndX_Request, 

65 SMBSession_Setup_AndX_Request_Extended_Security, 

66 SMBSession_Setup_AndX_Response, 

67 SMBSession_Setup_AndX_Response_Extended_Security, 

68 SMB_Dialect, 

69 SMB_Header, 

70) 

71from scapy.layers.smb2 import ( 

72 DirectTCP, 

73 FileAllInformation, 

74 FileIdBothDirectoryInformation, 

75 SECURITY_DESCRIPTOR, 

76 SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2, 

77 SMB2_CREATE_REQUEST_LEASE, 

78 SMB2_CREATE_REQUEST_LEASE_V2, 

79 SMB2_Change_Notify_Request, 

80 SMB2_Change_Notify_Response, 

81 SMB2_Close_Request, 

82 SMB2_Close_Response, 

83 SMB2_Create_Context, 

84 SMB2_Create_Request, 

85 SMB2_Create_Response, 

86 SMB2_ENCRYPTION_CIPHERS, 

87 SMB2_Encryption_Capabilities, 

88 SMB2_Error_Response, 

89 SMB2_Header, 

90 SMB2_IOCTL_Request, 

91 SMB2_IOCTL_Response, 

92 SMB2_Negotiate_Context, 

93 SMB2_Negotiate_Protocol_Request, 

94 SMB2_Negotiate_Protocol_Response, 

95 SMB2_Netname_Negotiate_Context_ID, 

96 SMB2_Preauth_Integrity_Capabilities, 

97 SMB2_Query_Directory_Request, 

98 SMB2_Query_Directory_Response, 

99 SMB2_Query_Info_Request, 

100 SMB2_Query_Info_Response, 

101 SMB2_Read_Request, 

102 SMB2_Read_Response, 

103 SMB2_SIGNING_ALGORITHMS, 

104 SMB2_Session_Setup_Request, 

105 SMB2_Session_Setup_Response, 

106 SMB2_Signing_Capabilities, 

107 SMB2_Tree_Connect_Request, 

108 SMB2_Tree_Connect_Response, 

109 SMB2_Tree_Disconnect_Request, 

110 SMB2_Tree_Disconnect_Response, 

111 SMB2_Write_Request, 

112 SMB2_Write_Response, 

113 SMBStreamSocket, 

114 SMB_DIALECTS, 

115 SRVSVC_SHARE_TYPES, 

116 STATUS_ERREF, 

117) 

118from scapy.layers.spnego import SPNEGOSSP 

119 

120 

121class SMB_Client(Automaton): 

122 """ 

123 SMB client automaton 

124 

125 :param sock: the SMBStreamSocket to use 

126 :param ssp: the SSP to use 

127 

128 All other options (in caps) are optional, and SMB specific: 

129 

130 :param REQUIRE_SIGNATURE: set 'Require Signature' 

131 :param REQUIRE_ENCRYPTION: set 'Requite Encryption' 

132 :param MIN_DIALECT: minimum SMB dialect. Defaults to 0x0202 (2.0.2) 

133 :param MAX_DIALECT: maximum SMB dialect. Defaults to 0x0311 (3.1.1) 

134 :param DIALECTS: list of supported SMB2 dialects. 

135 Constructed from MIN_DIALECT, MAX_DIALECT otherwise. 

136 """ 

137 

138 port = 445 

139 cls = DirectTCP 

140 

141 def __init__(self, sock, ssp=None, *args, **kwargs): 

142 # Various SMB client arguments 

143 self.EXTENDED_SECURITY = kwargs.pop("EXTENDED_SECURITY", True) 

144 self.USE_SMB1 = kwargs.pop("USE_SMB1", False) 

145 self.REQUIRE_SIGNATURE = kwargs.pop("REQUIRE_SIGNATURE", None) 

146 self.REQUIRE_ENCRYPTION = kwargs.pop("REQUIRE_ENCRYPTION", False) 

147 self.RETRY = kwargs.pop("RETRY", 0) # optionally: retry n times session setup 

148 self.SMB2 = kwargs.pop("SMB2", False) # optionally: start directly in SMB2 

149 self.SERVER_NAME = kwargs.pop("SERVER_NAME", "") 

150 # Store supported dialects 

151 if "DIALECTS" in kwargs: 

152 self.DIALECTS = kwargs.pop("DIALECTS") 

153 else: 

154 MIN_DIALECT = kwargs.pop("MIN_DIALECT", 0x0202) 

155 self.MAX_DIALECT = kwargs.pop("MAX_DIALECT", 0x0311) 

156 self.DIALECTS = sorted( 

157 [ 

158 x 

159 for x in [0x0202, 0x0210, 0x0300, 0x0302, 0x0311] 

160 if x >= MIN_DIALECT and x <= self.MAX_DIALECT 

161 ] 

162 ) 

163 # Internal Session information 

164 self.ErrorStatus = None 

165 self.NegotiateCapabilities = None 

166 self.GUID = RandUUID()._fix() 

167 self.SequenceWindow = (0, 0) # keep track of allowed MIDs 

168 if ssp is None: 

169 # We got no SSP. Assuming the server allows anonymous 

170 ssp = SPNEGOSSP( 

171 [ 

172 NTLMSSP( 

173 UPN="guest", 

174 HASHNT=b"", 

175 ) 

176 ] 

177 ) 

178 # Initialize 

179 kwargs["sock"] = sock 

180 Automaton.__init__( 

181 self, 

182 *args, 

183 **kwargs, 

184 ) 

185 if self.is_atmt_socket: 

186 self.smb_sock_ready = threading.Event() 

187 # Set session options 

188 self.session.ssp = ssp 

189 self.session.SigningRequired = ( 

190 self.REQUIRE_SIGNATURE if self.REQUIRE_SIGNATURE is not None else bool(ssp) 

191 ) 

192 self.session.Dialect = self.MAX_DIALECT 

193 

194 @classmethod 

195 def from_tcpsock(cls, sock, **kwargs): 

196 return cls.smblink( 

197 None, 

198 SMBStreamSocket(sock, DirectTCP), 

199 **kwargs, 

200 ) 

201 

202 @property 

203 def session(self): 

204 # session shorthand 

205 return self.sock.session 

206 

207 def send(self, pkt): 

208 # Calculate what CreditCharge to send. 

209 if self.session.Dialect > 0x0202 and isinstance(pkt.payload, SMB2_Header): 

210 # [MS-SMB2] sect 3.2.4.1.5 

211 typ = type(pkt.payload.payload) 

212 if typ is SMB2_Negotiate_Protocol_Request: 

213 # See [MS-SMB2] 3.2.4.1.2 note 

214 pkt.CreditCharge = 0 

215 elif typ in [ 

216 SMB2_Read_Request, 

217 SMB2_Write_Request, 

218 SMB2_IOCTL_Request, 

219 SMB2_Query_Directory_Request, 

220 SMB2_Change_Notify_Request, 

221 SMB2_Query_Info_Request, 

222 ]: 

223 # [MS-SMB2] 3.1.5.2 

224 # "For READ, WRITE, IOCTL, and QUERY_DIRECTORY requests" 

225 # "CHANGE_NOTIFY, QUERY_INFO, or SET_INFO" 

226 if typ == SMB2_Read_Request: 

227 Length = pkt.payload.Length 

228 elif typ == SMB2_Write_Request: 

229 Length = len(pkt.payload.Data) 

230 elif typ == SMB2_IOCTL_Request: 

231 # [MS-SMB2] 3.3.5.15 

232 Length = max(len(pkt.payload.Input), pkt.payload.MaxOutputResponse) 

233 elif typ in [ 

234 SMB2_Query_Directory_Request, 

235 SMB2_Change_Notify_Request, 

236 SMB2_Query_Info_Request, 

237 ]: 

238 Length = pkt.payload.OutputBufferLength 

239 else: 

240 raise RuntimeError("impossible case") 

241 pkt.CreditCharge = 1 + (Length - 1) // 65536 

242 else: 

243 # "For all other requests, the client MUST set CreditCharge to 1" 

244 pkt.CreditCharge = 1 

245 # [MS-SMB2] 3.2.4.1.2 

246 pkt.CreditRequest = pkt.CreditCharge + 1 # this code is a bit lazy 

247 # Get first available message ID: [MS-SMB2] 3.2.4.1.3 and 3.2.4.1.5 

248 pkt.MID = self.SequenceWindow[0] 

249 return super(SMB_Client, self).send(pkt) 

250 

251 @ATMT.state(initial=1) 

252 def BEGIN(self): 

253 pass 

254 

255 @ATMT.condition(BEGIN) 

256 def continue_smb2(self): 

257 if self.SMB2: # Directly started in SMB2 

258 self.smb_header = DirectTCP() / SMB2_Header(PID=0xFEFF) 

259 raise self.SMB2_NEGOTIATE() 

260 

261 @ATMT.condition(BEGIN, prio=1) 

262 def send_negotiate(self): 

263 raise self.SENT_NEGOTIATE() 

264 

265 @ATMT.action(send_negotiate) 

266 def on_negotiate(self): 

267 # [MS-SMB2] sect 3.2.4.2.2.1 - Multi-Protocol Negotiate 

268 self.smb_header = DirectTCP() / SMB_Header( 

269 Flags2=( 

270 "LONG_NAMES+EAS+NT_STATUS+UNICODE+" 

271 "SMB_SECURITY_SIGNATURE+EXTENDED_SECURITY" 

272 ), 

273 TID=0xFFFF, 

274 PIDLow=0xFEFF, 

275 UID=0, 

276 MID=0, 

277 ) 

278 if self.EXTENDED_SECURITY: 

279 self.smb_header.Flags2 += "EXTENDED_SECURITY" 

280 pkt = self.smb_header.copy() / SMBNegotiate_Request( 

281 Dialects=[ 

282 SMB_Dialect(DialectString=x) 

283 for x in [ 

284 "PC NETWORK PROGRAM 1.0", 

285 "LANMAN1.0", 

286 "Windows for Workgroups 3.1a", 

287 "LM1.2X002", 

288 "LANMAN2.1", 

289 "NT LM 0.12", 

290 ] 

291 + (["SMB 2.002", "SMB 2.???"] if not self.USE_SMB1 else []) 

292 ], 

293 ) 

294 if not self.EXTENDED_SECURITY: 

295 pkt.Flags2 -= "EXTENDED_SECURITY" 

296 pkt[SMB_Header].Flags2 = ( 

297 pkt[SMB_Header].Flags2 

298 - "SMB_SECURITY_SIGNATURE" 

299 + "SMB_SECURITY_SIGNATURE_REQUIRED+IS_LONG_NAME" 

300 ) 

301 self.send(pkt) 

302 

303 @ATMT.state() 

304 def SENT_NEGOTIATE(self): 

305 pass 

306 

307 @ATMT.state() 

308 def SMB2_NEGOTIATE(self): 

309 pass 

310 

311 @ATMT.condition(SMB2_NEGOTIATE) 

312 def send_negotiate_smb2(self): 

313 raise self.SENT_NEGOTIATE() 

314 

315 @ATMT.action(send_negotiate_smb2) 

316 def on_negotiate_smb2(self): 

317 # [MS-SMB2] sect 3.2.4.2.2.2 - SMB2-Only Negotiate 

318 pkt = self.smb_header.copy() / SMB2_Negotiate_Protocol_Request( 

319 Dialects=self.DIALECTS, 

320 SecurityMode=( 

321 "SIGNING_ENABLED+SIGNING_REQUIRED" 

322 if self.session.SigningRequired 

323 else "SIGNING_ENABLED" 

324 ), 

325 ) 

326 if self.MAX_DIALECT >= 0x0210: 

327 # "If the client implements the SMB 2.1 or SMB 3.x dialect, ClientGuid 

328 # MUST be set to the global ClientGuid value" 

329 pkt.ClientGUID = self.GUID 

330 # Capabilities: same as [MS-SMB2] 3.3.5.4 

331 self.NegotiateCapabilities = "+".join( 

332 [ 

333 "DFS", 

334 "LEASING", 

335 "LARGE_MTU", 

336 ] 

337 ) 

338 if self.MAX_DIALECT >= 0x0300: 

339 # "if Connection.Dialect belongs to the SMB 3.x dialect family ..." 

340 self.NegotiateCapabilities += "+" + "+".join( 

341 [ 

342 "MULTI_CHANNEL", 

343 "PERSISTENT_HANDLES", 

344 "DIRECTORY_LEASING", 

345 "ENCRYPTION", 

346 ] 

347 ) 

348 if self.MAX_DIALECT >= 0x0311: 

349 # "If the client implements the SMB 3.1.1 dialect, it MUST do" 

350 pkt.NegotiateContexts = [ 

351 SMB2_Negotiate_Context() 

352 / SMB2_Preauth_Integrity_Capabilities( 

353 # As for today, no other hash algorithm is described by the spec 

354 HashAlgorithms=["SHA-512"], 

355 Salt=self.session.Salt, 

356 ), 

357 SMB2_Negotiate_Context() 

358 / SMB2_Encryption_Capabilities( 

359 Ciphers=self.session.SupportedCipherIds, 

360 ), 

361 # TODO support compression and RDMA 

362 SMB2_Negotiate_Context() 

363 / SMB2_Netname_Negotiate_Context_ID( 

364 NetName=self.SERVER_NAME, 

365 ), 

366 SMB2_Negotiate_Context() 

367 / SMB2_Signing_Capabilities( 

368 SigningAlgorithms=self.session.SupportedSigningAlgorithmIds, 

369 ), 

370 ] 

371 pkt.Capabilities = self.NegotiateCapabilities 

372 # Send 

373 self.send(pkt) 

374 # If required, compute sessions 

375 self.session.computeSMBConnectionPreauth( 

376 bytes(pkt[SMB2_Header]), # nego request 

377 ) 

378 

379 @ATMT.receive_condition(SENT_NEGOTIATE) 

380 def receive_negotiate_response(self, pkt): 

381 if ( 

382 SMBNegotiate_Response_Extended_Security in pkt 

383 or SMB2_Negotiate_Protocol_Response in pkt 

384 ): 

385 # Extended SMB1 / SMB2 

386 try: 

387 ssp_blob = pkt.SecurityBlob # eventually SPNEGO server initiation 

388 except AttributeError: 

389 ssp_blob = None 

390 if ( 

391 SMB2_Negotiate_Protocol_Response in pkt 

392 and pkt.DialectRevision & 0xFF == 0xFF 

393 ): 

394 # Version is SMB X.??? 

395 # [MS-SMB2] 3.2.5.2 

396 # If the DialectRevision field in the SMB2 NEGOTIATE Response is 

397 # 0x02FF ... the client MUST allocate sequence number 1 from 

398 # Connection.SequenceWindow, and MUST set MessageId field of the 

399 # SMB2 header to 1. 

400 self.SequenceWindow = (1, 1) 

401 self.smb_header = DirectTCP() / SMB2_Header(PID=0xFEFF, MID=1) 

402 self.SMB2 = True # We're now using SMB2 to talk to the server 

403 raise self.SMB2_NEGOTIATE() 

404 else: 

405 if SMB2_Negotiate_Protocol_Response in pkt: 

406 # SMB2 was negotiated ! 

407 self.session.Dialect = pkt.DialectRevision 

408 # If required, compute sessions 

409 self.session.computeSMBConnectionPreauth( 

410 bytes(pkt[SMB2_Header]), # nego response 

411 ) 

412 # Process max sizes 

413 self.session.MaxReadSize = pkt.MaxReadSize 

414 self.session.MaxTransactionSize = pkt.MaxTransactionSize 

415 self.session.MaxWriteSize = pkt.MaxWriteSize 

416 # Process SecurityMode 

417 if pkt.SecurityMode.SIGNING_REQUIRED: 

418 self.session.SigningRequired = True 

419 # Process capabilities 

420 if self.session.Dialect >= 0x0300: 

421 self.session.SupportsEncryption = pkt.Capabilities.ENCRYPTION 

422 # Process NegotiateContext 

423 if self.session.Dialect >= 0x0311 and pkt.NegotiateContextsCount: 

424 for ngctx in pkt.NegotiateContexts: 

425 if ngctx.ContextType == 0x0002: 

426 # SMB2_ENCRYPTION_CAPABILITIES 

427 if ngctx.Ciphers[0] != 0: 

428 self.session.CipherId = SMB2_ENCRYPTION_CIPHERS[ 

429 ngctx.Ciphers[0] 

430 ] 

431 self.session.SupportsEncryption = True 

432 elif ngctx.ContextType == 0x0008: 

433 # SMB2_SIGNING_CAPABILITIES 

434 self.session.SigningAlgorithmId = ( 

435 SMB2_SIGNING_ALGORITHMS[ngctx.SigningAlgorithms[0]] 

436 ) 

437 if self.REQUIRE_ENCRYPTION and not self.session.SupportsEncryption: 

438 self.ErrorStatus = "NEGOTIATE FAILURE: encryption." 

439 raise self.NEGO_FAILED() 

440 self.update_smbheader(pkt) 

441 raise self.NEGOTIATED(ssp_blob) 

442 elif SMBNegotiate_Response_Security in pkt: 

443 # Non-extended SMB1 

444 # Never tested. FIXME. probably broken 

445 raise self.NEGOTIATED(pkt.Challenge) 

446 

447 @ATMT.state(final=1) 

448 def NEGO_FAILED(self): 

449 self.smb_sock_ready.set() 

450 

451 @ATMT.state() 

452 def NEGOTIATED(self, ssp_blob=None): 

453 # Negotiated ! We now know the Dialect 

454 if self.session.Dialect > 0x0202: 

455 # [MS-SMB2] sect 3.2.5.1.4 

456 self.smb_header.CreditRequest = 1 

457 # Begin session establishment 

458 ssp_tuple = self.session.ssp.GSS_Init_sec_context( 

459 self.session.sspcontext, 

460 token=ssp_blob, 

461 req_flags=( 

462 GSS_C_FLAGS.GSS_C_MUTUAL_FLAG 

463 | (GSS_C_FLAGS.GSS_C_INTEG_FLAG if self.session.SigningRequired else 0) 

464 ), 

465 ) 

466 return ssp_tuple 

467 

468 def update_smbheader(self, pkt): 

469 """ 

470 Called when receiving a SMB2 packet to update the current smb_header 

471 """ 

472 # Some values should not be updated when ASYNC 

473 if not pkt.Flags.SMB2_FLAGS_ASYNC_COMMAND: 

474 # Update IDs 

475 self.smb_header.SessionId = pkt.SessionId 

476 self.smb_header.TID = pkt.TID 

477 self.smb_header.PID = pkt.PID 

478 # [MS-SMB2] 3.2.5.1.4 

479 self.SequenceWindow = ( 

480 self.SequenceWindow[0] + max(pkt.CreditCharge, 1), 

481 self.SequenceWindow[1] + pkt.CreditRequest, 

482 ) 

483 

484 # DEV: add a condition on NEGOTIATED with prio=0 

485 

486 @ATMT.condition(NEGOTIATED, prio=1) 

487 def should_send_session_setup_request(self, ssp_tuple): 

488 _, _, negResult = ssp_tuple 

489 if negResult not in [GSS_S_COMPLETE, GSS_S_CONTINUE_NEEDED]: 

490 raise ValueError("Internal error: the SSP completed with an error.") 

491 raise self.SENT_SESSION_REQUEST().action_parameters(ssp_tuple) 

492 

493 @ATMT.state() 

494 def SENT_SESSION_REQUEST(self): 

495 pass 

496 

497 @ATMT.action(should_send_session_setup_request) 

498 def send_setup_session_request(self, ssp_tuple): 

499 self.session.sspcontext, token, negResult = ssp_tuple 

500 if self.SMB2 and negResult == GSS_S_CONTINUE_NEEDED: 

501 # New session: force 0 

502 self.SessionId = 0 

503 if self.SMB2 or self.EXTENDED_SECURITY: 

504 # SMB1 extended / SMB2 

505 if self.SMB2: 

506 # SMB2 

507 pkt = self.smb_header.copy() / SMB2_Session_Setup_Request( 

508 Capabilities="DFS", 

509 SecurityMode=( 

510 "SIGNING_ENABLED+SIGNING_REQUIRED" 

511 if self.session.SigningRequired 

512 else "SIGNING_ENABLED" 

513 ), 

514 ) 

515 else: 

516 # SMB1 extended 

517 pkt = ( 

518 self.smb_header.copy() 

519 / SMBSession_Setup_AndX_Request_Extended_Security( 

520 ServerCapabilities=( 

521 "UNICODE+NT_SMBS+STATUS32+LEVEL_II_OPLOCKS+" 

522 "DYNAMIC_REAUTH+EXTENDED_SECURITY" 

523 ), 

524 NativeOS=b"", 

525 NativeLanMan=b"", 

526 ) 

527 ) 

528 pkt.SecurityBlob = token 

529 else: 

530 # Non-extended security. 

531 pkt = self.smb_header.copy() / SMBSession_Setup_AndX_Request( 

532 ServerCapabilities="UNICODE+NT_SMBS+STATUS32+LEVEL_II_OPLOCKS", 

533 NativeOS=b"", 

534 NativeLanMan=b"", 

535 OEMPassword=b"\0" * 24, 

536 UnicodePassword=token, 

537 ) 

538 self.send(pkt) 

539 if self.SMB2: 

540 # If required, compute sessions 

541 self.session.computeSMBSessionPreauth( 

542 bytes(pkt[SMB2_Header]), # session request 

543 ) 

544 

545 @ATMT.receive_condition(SENT_SESSION_REQUEST) 

546 def receive_session_setup_response(self, pkt): 

547 if ( 

548 SMBSession_Null in pkt 

549 or SMBSession_Setup_AndX_Response_Extended_Security in pkt 

550 or SMBSession_Setup_AndX_Response in pkt 

551 ): 

552 # SMB1 

553 if SMBSession_Null in pkt: 

554 # Likely an error 

555 raise self.NEGOTIATED() 

556 # Logging 

557 if pkt.Status != 0 and pkt.Status != 0xC0000016: 

558 # Not SUCCESS nor MORE_PROCESSING_REQUIRED: log 

559 self.ErrorStatus = pkt.sprintf("%SMB2_Header.Status%") 

560 self.debug( 

561 lvl=1, 

562 msg=conf.color_theme.red( 

563 pkt.sprintf("SMB Session Setup Response: %SMB2_Header.Status%") 

564 ), 

565 ) 

566 if self.SMB2: 

567 self.update_smbheader(pkt) 

568 # Cases depending on the response packet 

569 if ( 

570 SMBSession_Setup_AndX_Response_Extended_Security in pkt 

571 or SMB2_Session_Setup_Response in pkt 

572 ): 

573 # The server assigns us a SessionId 

574 self.smb_header.SessionId = pkt.SessionId 

575 # SMB1 extended / SMB2 

576 if pkt.Status == 0: # Authenticated 

577 if SMB2_Session_Setup_Response in pkt: 

578 # [MS-SMB2] sect 3.2.5.3.1 

579 if pkt.SessionFlags.IS_GUEST: 

580 # "If the security subsystem indicates that the session 

581 # was established by a guest user, Session.SigningRequired 

582 # MUST be set to FALSE and Session.IsGuest MUST be set to TRUE." 

583 self.session.IsGuest = True 

584 self.session.SigningRequired = False 

585 elif self.session.Dialect >= 0x0300: 

586 if pkt.SessionFlags.ENCRYPT_DATA or self.REQUIRE_ENCRYPTION: 

587 self.session.EncryptData = True 

588 self.session.SigningRequired = False 

589 raise self.AUTHENTICATED(pkt.SecurityBlob) 

590 else: 

591 if SMB2_Header in pkt: 

592 # If required, compute sessions 

593 self.session.computeSMBSessionPreauth( 

594 bytes(pkt[SMB2_Header]), # session response 

595 ) 

596 # Ongoing auth 

597 raise self.NEGOTIATED(pkt.SecurityBlob) 

598 elif SMBSession_Setup_AndX_Response_Extended_Security in pkt: 

599 # SMB1 non-extended 

600 pass 

601 elif SMB2_Error_Response in pkt: 

602 # Authentication failure 

603 self.session.sspcontext.clifailure() 

604 # Reset Session preauth (SMB 3.1.1) 

605 self.session.SessionPreauthIntegrityHashValue = None 

606 if not self.RETRY: 

607 raise self.AUTH_FAILED() 

608 self.debug(lvl=2, msg="RETRY: %s" % self.RETRY) 

609 self.RETRY -= 1 

610 raise self.NEGOTIATED() 

611 

612 @ATMT.state(final=1) 

613 def AUTH_FAILED(self): 

614 self.smb_sock_ready.set() 

615 

616 @ATMT.state() 

617 def AUTHENTICATED(self, ssp_blob=None): 

618 self.session.sspcontext, _, status = self.session.ssp.GSS_Init_sec_context( 

619 self.session.sspcontext, 

620 token=ssp_blob, 

621 ) 

622 if status != GSS_S_COMPLETE: 

623 raise ValueError("Internal error: the SSP completed with an error.") 

624 # Authentication was successful 

625 self.session.computeSMBSessionKeys(IsClient=True) 

626 

627 # DEV: add a condition on AUTHENTICATED with prio=0 

628 

629 @ATMT.condition(AUTHENTICATED, prio=1) 

630 def authenticated_post_actions(self): 

631 raise self.SOCKET_BIND() 

632 

633 # Plain SMB Socket 

634 

635 @ATMT.state() 

636 def SOCKET_BIND(self): 

637 self.smb_sock_ready.set() 

638 

639 @ATMT.condition(SOCKET_BIND) 

640 def start_smb_socket(self): 

641 raise self.SOCKET_MODE_SMB() 

642 

643 @ATMT.state() 

644 def SOCKET_MODE_SMB(self): 

645 pass 

646 

647 @ATMT.receive_condition(SOCKET_MODE_SMB) 

648 def incoming_data_received_smb(self, pkt): 

649 raise self.SOCKET_MODE_SMB().action_parameters(pkt) 

650 

651 @ATMT.action(incoming_data_received_smb) 

652 def receive_data_smb(self, pkt): 

653 resp = pkt[SMB2_Header].payload 

654 if isinstance(resp, SMB2_Error_Response): 

655 if pkt.Status == 0x00000103: # STATUS_PENDING 

656 # answer is coming later.. just wait... 

657 return 

658 if pkt.Status == 0x0000010B: # STATUS_NOTIFY_CLEANUP 

659 # this is a notify cleanup. ignore 

660 return 

661 self.update_smbheader(pkt) 

662 # Add the status to the response as metadata 

663 resp.NTStatus = pkt.sprintf("%SMB2_Header.Status%") 

664 self.oi.smbpipe.send(resp) 

665 

666 @ATMT.ioevent(SOCKET_MODE_SMB, name="smbpipe", as_supersocket="smblink") 

667 def outgoing_data_received_smb(self, fd): 

668 raise self.SOCKET_MODE_SMB().action_parameters(fd.recv()) 

669 

670 @ATMT.action(outgoing_data_received_smb) 

671 def send_data(self, d): 

672 self.send(self.smb_header.copy() / d) 

673 

674 

675class SMB_SOCKET(SuperSocket): 

676 """ 

677 Mid-level wrapper over SMB_Client.smblink that provides some basic SMB 

678 client functions, such as tree connect, directory query, etc. 

679 """ 

680 

681 def __init__(self, smbsock, use_ioctl=True, timeout=3): 

682 self.ins = smbsock 

683 self.timeout = timeout 

684 if not self.ins.atmt.smb_sock_ready.wait(timeout=timeout): 

685 # If we have a SSP, tell it we failed. 

686 if self.ins.atmt.session.sspcontext: 

687 self.ins.atmt.session.sspcontext.clifailure() 

688 raise TimeoutError( 

689 "The SMB handshake timed out ! (enable debug=1 for logs)" 

690 ) 

691 if self.ins.atmt.ErrorStatus: 

692 raise Scapy_Exception( 

693 "SMB Session Setup failed: %s" % self.ins.atmt.ErrorStatus 

694 ) 

695 

696 @classmethod 

697 def from_tcpsock(cls, sock, **kwargs): 

698 """ 

699 Wraps the tcp socket in a SMB_Client.smblink first, then into the 

700 SMB_SOCKET/SMB_RPC_SOCKET 

701 """ 

702 return cls( 

703 use_ioctl=kwargs.pop("use_ioctl", True), 

704 timeout=kwargs.pop("timeout", 3), 

705 smbsock=SMB_Client.from_tcpsock(sock, **kwargs), 

706 ) 

707 

708 @property 

709 def session(self): 

710 return self.ins.atmt.session 

711 

712 def set_TID(self, TID): 

713 """ 

714 Set the TID (Tree ID). 

715 This can be called before sending a packet 

716 """ 

717 self.ins.atmt.smb_header.TID = TID 

718 

719 def get_TID(self): 

720 """ 

721 Get the current TID from the underlying socket 

722 """ 

723 return self.ins.atmt.smb_header.TID 

724 

725 def tree_connect(self, name): 

726 """ 

727 Send a TreeConnect request 

728 """ 

729 resp = self.ins.sr1( 

730 SMB2_Tree_Connect_Request( 

731 Buffer=[ 

732 ( 

733 "Path", 

734 "\\\\%s\\%s" 

735 % ( 

736 self.session.sspcontext.ServerHostname, 

737 name, 

738 ), 

739 ) 

740 ] 

741 ), 

742 verbose=False, 

743 timeout=self.timeout, 

744 ) 

745 if not resp: 

746 raise ValueError("TreeConnect timed out !") 

747 if SMB2_Tree_Connect_Response not in resp: 

748 raise ValueError("Failed TreeConnect ! %s" % resp.NTStatus) 

749 # [MS-SMB2] sect 3.2.5.5 

750 if self.session.Dialect >= 0x0300: 

751 if resp.ShareFlags.ENCRYPT_DATA and self.session.SupportsEncryption: 

752 self.session.TreeEncryptData = True 

753 else: 

754 self.session.TreeEncryptData = False 

755 return self.get_TID() 

756 

757 def tree_disconnect(self): 

758 """ 

759 Send a TreeDisconnect request 

760 """ 

761 resp = self.ins.sr1( 

762 SMB2_Tree_Disconnect_Request(), 

763 verbose=False, 

764 timeout=self.timeout, 

765 ) 

766 if not resp: 

767 raise ValueError("TreeDisconnect timed out !") 

768 if SMB2_Tree_Disconnect_Response not in resp: 

769 raise ValueError("Failed TreeDisconnect ! %s" % resp.NTStatus) 

770 

771 def create_request( 

772 self, 

773 name, 

774 mode="r", 

775 type="pipe", 

776 extra_create_options=[], 

777 extra_desired_access=[], 

778 ): 

779 """ 

780 Open a file/pipe by its name 

781 

782 :param name: the name of the file or named pipe. e.g. 'srvsvc' 

783 """ 

784 ShareAccess = [] 

785 DesiredAccess = [] 

786 # Common params depending on the access 

787 if "r" in mode: 

788 ShareAccess.append("FILE_SHARE_READ") 

789 DesiredAccess.extend(["FILE_READ_DATA", "FILE_READ_ATTRIBUTES"]) 

790 if "w" in mode: 

791 ShareAccess.append("FILE_SHARE_WRITE") 

792 DesiredAccess.extend(["FILE_WRITE_DATA", "FILE_WRITE_ATTRIBUTES"]) 

793 if "d" in mode: 

794 ShareAccess.append("FILE_SHARE_DELETE") 

795 # Params depending on the type 

796 FileAttributes = [] 

797 CreateOptions = [] 

798 CreateContexts = [] 

799 CreateDisposition = "FILE_OPEN" 

800 if type == "folder": 

801 FileAttributes.append("FILE_ATTRIBUTE_DIRECTORY") 

802 CreateOptions.append("FILE_DIRECTORY_FILE") 

803 elif type in ["file", "pipe"]: 

804 CreateOptions = ["FILE_NON_DIRECTORY_FILE"] 

805 if "r" in mode: 

806 DesiredAccess.extend(["FILE_READ_EA", "READ_CONTROL", "SYNCHRONIZE"]) 

807 if "w" in mode: 

808 CreateDisposition = "FILE_OVERWRITE_IF" 

809 DesiredAccess.append("FILE_WRITE_EA") 

810 if "d" in mode: 

811 DesiredAccess.append("DELETE") 

812 CreateOptions.append("FILE_DELETE_ON_CLOSE") 

813 if type == "file": 

814 FileAttributes.append("FILE_ATTRIBUTE_NORMAL") 

815 elif type: 

816 raise ValueError("Unknown type: %s" % type) 

817 # [MS-SMB2] 3.2.4.3.8 

818 RequestedOplockLevel = 0 

819 if self.session.Dialect >= 0x0300: 

820 RequestedOplockLevel = "SMB2_OPLOCK_LEVEL_LEASE" 

821 elif self.session.Dialect >= 0x0210 and type == "file": 

822 RequestedOplockLevel = "SMB2_OPLOCK_LEVEL_LEASE" 

823 # SMB 3.X 

824 if self.session.Dialect >= 0x0300 and type in ["file", "folder"]: 

825 CreateContexts.extend( 

826 [ 

827 # [SMB2] sect 3.2.4.3.5 

828 SMB2_Create_Context( 

829 Name=b"DH2Q", 

830 Data=SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2( 

831 CreateGuid=RandUUID()._fix() 

832 ), 

833 ), 

834 # [SMB2] sect 3.2.4.3.9 

835 SMB2_Create_Context( 

836 Name=b"MxAc", 

837 ), 

838 # [SMB2] sect 3.2.4.3.10 

839 SMB2_Create_Context( 

840 Name=b"QFid", 

841 ), 

842 # [SMB2] sect 3.2.4.3.8 

843 SMB2_Create_Context( 

844 Name=b"RqLs", 

845 Data=SMB2_CREATE_REQUEST_LEASE_V2(LeaseKey=RandUUID()._fix()), 

846 ), 

847 ] 

848 ) 

849 elif self.session.Dialect == 0x0210 and type == "file": 

850 CreateContexts.extend( 

851 [ 

852 # [SMB2] sect 3.2.4.3.8 

853 SMB2_Create_Context( 

854 Name=b"RqLs", 

855 Data=SMB2_CREATE_REQUEST_LEASE(LeaseKey=RandUUID()._fix()), 

856 ), 

857 ] 

858 ) 

859 # Extra options 

860 if extra_create_options: 

861 CreateOptions.extend(extra_create_options) 

862 if extra_desired_access: 

863 DesiredAccess.extend(extra_desired_access) 

864 # Request 

865 resp = self.ins.sr1( 

866 SMB2_Create_Request( 

867 ImpersonationLevel="Impersonation", 

868 DesiredAccess="+".join(DesiredAccess), 

869 CreateDisposition=CreateDisposition, 

870 CreateOptions="+".join(CreateOptions), 

871 ShareAccess="+".join(ShareAccess), 

872 FileAttributes="+".join(FileAttributes), 

873 CreateContexts=CreateContexts, 

874 RequestedOplockLevel=RequestedOplockLevel, 

875 Name=name, 

876 ), 

877 verbose=0, 

878 timeout=self.timeout, 

879 ) 

880 if not resp: 

881 raise ValueError("CreateRequest timed out !") 

882 if SMB2_Create_Response not in resp: 

883 raise ValueError("Failed CreateRequest ! %s" % resp.NTStatus) 

884 return resp[SMB2_Create_Response].FileId 

885 

886 def close_request(self, FileId): 

887 """ 

888 Close the FileId 

889 """ 

890 pkt = SMB2_Close_Request(FileId=FileId) 

891 resp = self.ins.sr1(pkt, verbose=0, timeout=self.timeout) 

892 if not resp: 

893 raise ValueError("CloseRequest timed out !") 

894 if SMB2_Close_Response not in resp: 

895 raise ValueError("Failed CloseRequest ! %s" % resp.NTStatus) 

896 

897 def read_request(self, FileId, Length, Offset=0): 

898 """ 

899 Read request 

900 """ 

901 resp = self.ins.sr1( 

902 SMB2_Read_Request( 

903 FileId=FileId, 

904 Length=Length, 

905 Offset=Offset, 

906 ), 

907 verbose=0, 

908 timeout=self.timeout, 

909 ) 

910 if not resp: 

911 raise ValueError("ReadRequest timed out !") 

912 if SMB2_Read_Response not in resp: 

913 raise ValueError("Failed ReadRequest ! %s" % resp.NTStatus) 

914 return resp.Data 

915 

916 def write_request(self, Data, FileId, Offset=0): 

917 """ 

918 Write request 

919 """ 

920 resp = self.ins.sr1( 

921 SMB2_Write_Request( 

922 FileId=FileId, 

923 Data=Data, 

924 Offset=Offset, 

925 ), 

926 verbose=0, 

927 timeout=self.timeout, 

928 ) 

929 if not resp: 

930 raise ValueError("WriteRequest timed out !") 

931 if SMB2_Write_Response not in resp: 

932 raise ValueError("Failed WriteRequest ! %s" % resp.NTStatus) 

933 return resp.Count 

934 

935 def query_directory(self, FileId, FileName="*"): 

936 """ 

937 Query the Directory with FileId 

938 """ 

939 results = [] 

940 Flags = "SMB2_RESTART_SCANS" 

941 while True: 

942 pkt = SMB2_Query_Directory_Request( 

943 FileInformationClass="FileIdBothDirectoryInformation", 

944 FileId=FileId, 

945 FileName=FileName, 

946 Flags=Flags, 

947 ) 

948 resp = self.ins.sr1(pkt, verbose=0, timeout=self.timeout) 

949 Flags = 0 # only the first one is RESTART_SCANS 

950 if not resp: 

951 raise ValueError("QueryDirectory timed out !") 

952 if SMB2_Error_Response in resp: 

953 break 

954 elif SMB2_Query_Directory_Response not in resp: 

955 raise ValueError("Failed QueryDirectory ! %s" % resp.NTStatus) 

956 res = FileIdBothDirectoryInformation(resp.Output) 

957 results.extend( 

958 [ 

959 ( 

960 x.FileName, 

961 x.FileAttributes, 

962 x.EndOfFile, 

963 x.LastWriteTime, 

964 ) 

965 for x in res.files 

966 ] 

967 ) 

968 return results 

969 

970 def query_info(self, FileId, InfoType, FileInfoClass, AdditionalInformation=0): 

971 """ 

972 Query the Info 

973 """ 

974 pkt = SMB2_Query_Info_Request( 

975 InfoType=InfoType, 

976 FileInfoClass=FileInfoClass, 

977 OutputBufferLength=65535, 

978 FileId=FileId, 

979 AdditionalInformation=AdditionalInformation, 

980 ) 

981 resp = self.ins.sr1(pkt, verbose=0, timeout=self.timeout) 

982 if not resp: 

983 raise ValueError("QueryInfo timed out !") 

984 if SMB2_Query_Info_Response not in resp: 

985 raise ValueError("Failed QueryInfo ! %s" % resp.NTStatus) 

986 return resp.Output 

987 

988 def changenotify(self, FileId): 

989 """ 

990 Register change notify 

991 """ 

992 pkt = SMB2_Change_Notify_Request( 

993 Flags="SMB2_WATCH_TREE", 

994 OutputBufferLength=65535, 

995 FileId=FileId, 

996 CompletionFilter=0x0FFF, 

997 ) 

998 # we can wait forever, not a problem in this one 

999 resp = self.ins.sr1(pkt, verbose=0, chainCC=True) 

1000 if SMB2_Change_Notify_Response not in resp: 

1001 raise ValueError("Failed ChangeNotify ! %s" % resp.NTStatus) 

1002 return resp.Output 

1003 

1004 

1005class SMB_RPC_SOCKET(ObjectPipe, SMB_SOCKET): 

1006 """ 

1007 Extends SMB_SOCKET (which is a wrapper over SMB_Client.smblink) to send 

1008 DCE/RPC messages (bind, reqs, etc.) 

1009 

1010 This is usable as a normal SuperSocket (sr1, etc.) and performs the 

1011 wrapping of the DCE/RPC messages into SMB2_Write/Read packets. 

1012 """ 

1013 

1014 def __init__(self, smbsock, use_ioctl=True, timeout=3): 

1015 self.use_ioctl = use_ioctl 

1016 ObjectPipe.__init__(self, "SMB_RPC_SOCKET") 

1017 SMB_SOCKET.__init__(self, smbsock, timeout=timeout) 

1018 

1019 def open_pipe(self, name): 

1020 self.PipeFileId = self.create_request(name, mode="rw", type="pipe") 

1021 

1022 def close_pipe(self): 

1023 self.close_request(self.PipeFileId) 

1024 self.PipeFileId = None 

1025 

1026 def send(self, x): 

1027 """ 

1028 Internal ObjectPipe function. 

1029 """ 

1030 # Reminder: this class is an ObjectPipe, it's just a queue. 

1031 

1032 # Detect if DCE/RPC is fragmented. Then we must use Read/Write 

1033 is_frag = x.pfc_flags & 3 != 3 

1034 

1035 if self.use_ioctl and not is_frag: 

1036 # Use IOCTLRequest 

1037 pkt = SMB2_IOCTL_Request( 

1038 FileId=self.PipeFileId, 

1039 Flags="SMB2_0_IOCTL_IS_FSCTL", 

1040 CtlCode="FSCTL_PIPE_TRANSCEIVE", 

1041 ) 

1042 pkt.Input = bytes(x) 

1043 resp = self.ins.sr1(pkt, verbose=0) 

1044 if SMB2_IOCTL_Response not in resp: 

1045 raise ValueError("Failed reading IOCTL_Response ! %s" % resp.NTStatus) 

1046 data = bytes(resp.Output) 

1047 # Handle BUFFER_OVERFLOW (big DCE/RPC response) 

1048 while resp.NTStatus == "STATUS_BUFFER_OVERFLOW": 

1049 # Retrieve DCE/RPC full size 

1050 resp = self.ins.sr1( 

1051 SMB2_Read_Request( 

1052 FileId=self.PipeFileId, 

1053 ), 

1054 verbose=0, 

1055 ) 

1056 data += resp.Data 

1057 super(SMB_RPC_SOCKET, self).send(data) 

1058 else: 

1059 # Use WriteRequest/ReadRequest 

1060 pkt = SMB2_Write_Request( 

1061 FileId=self.PipeFileId, 

1062 ) 

1063 pkt.Data = bytes(x) 

1064 # We send the Write Request 

1065 resp = self.ins.sr1(pkt, verbose=0) 

1066 if SMB2_Write_Response not in resp: 

1067 raise ValueError("Failed sending WriteResponse ! %s" % resp.NTStatus) 

1068 # If fragmented, only read if it's the last. 

1069 if is_frag and not x.pfc_flags.PFC_LAST_FRAG: 

1070 return 

1071 # We send a Read Request afterwards 

1072 resp = self.ins.sr1( 

1073 SMB2_Read_Request( 

1074 FileId=self.PipeFileId, 

1075 ), 

1076 verbose=0, 

1077 ) 

1078 if SMB2_Read_Response not in resp: 

1079 raise ValueError("Failed reading ReadResponse ! %s" % resp.NTStatus) 

1080 super(SMB_RPC_SOCKET, self).send(resp.Data) 

1081 # Handle fragmented response 

1082 while resp.Data[3] & 2 != 2: # PFC_LAST_FRAG not set 

1083 # Retrieve DCE/RPC full size 

1084 resp = self.ins.sr1( 

1085 SMB2_Read_Request( 

1086 FileId=self.PipeFileId, 

1087 ), 

1088 verbose=0, 

1089 ) 

1090 super(SMB_RPC_SOCKET, self).send(resp.Data) 

1091 

1092 def close(self): 

1093 SMB_SOCKET.close(self) 

1094 ObjectPipe.close(self) 

1095 

1096 

1097@conf.commands.register 

1098class smbclient(CLIUtil): 

1099 r""" 

1100 A simple smbclient CLI 

1101 

1102 :param target: can be a hostname, the IPv4 or the IPv6 to connect to 

1103 :param UPN: the upn to use (DOMAIN/USER, DOMAIN\USER, USER@DOMAIN or USER) 

1104 :param guest: use guest mode (over NTLM) 

1105 :param ssp: if provided, use this SSP for auth. 

1106 :param kerberos: if available, whether to use Kerberos or not 

1107 :param kerberos_required: require kerberos 

1108 :param port: the TCP port. default 445 

1109 :param password: (string) if provided, used for auth 

1110 :param HashNt: (bytes) if provided, used for auth (NTLM) 

1111 :param ST: if provided, the service ticket to use (Kerberos) 

1112 :param KEY: if provided, the session key associated to the ticket (Kerberos) 

1113 :param cli: CLI mode (default True). False to use for scripting 

1114 

1115 Some additional SMB parameters are available under help(SMB_Client). Some of 

1116 them include the following: 

1117 

1118 :param REQUIRE_ENCRYPTION: requires encryption. 

1119 """ 

1120 

1121 def __init__( 

1122 self, 

1123 target: str, 

1124 UPN: str = None, 

1125 password: str = None, 

1126 guest: bool = False, 

1127 kerberos: bool = True, 

1128 kerberos_required: bool = False, 

1129 HashNt: str = None, 

1130 port: int = 445, 

1131 timeout: int = 2, 

1132 debug: int = 0, 

1133 ssp=None, 

1134 ST=None, 

1135 KEY=None, 

1136 cli=True, 

1137 # SMB arguments 

1138 REQUIRE_ENCRYPTION=False, 

1139 **kwargs, 

1140 ): 

1141 if cli: 

1142 self._depcheck() 

1143 hostname = None 

1144 # Check if target is a hostname / Check IP 

1145 if ":" in target: 

1146 family = socket.AF_INET6 

1147 if not valid_ip6(target): 

1148 hostname = target 

1149 target = str(Net6(target)) 

1150 else: 

1151 family = socket.AF_INET 

1152 if not valid_ip(target): 

1153 hostname = target 

1154 target = str(Net(target)) 

1155 assert UPN or ssp or guest, "Either UPN, ssp or guest must be provided !" 

1156 # Do we need to build a SSP? 

1157 if ssp is None: 

1158 # Create the SSP (only if not guest mode) 

1159 if not guest: 

1160 # Check UPN 

1161 try: 

1162 _, realm = _parse_upn(UPN) 

1163 if realm == ".": 

1164 # Local 

1165 kerberos = False 

1166 except ValueError: 

1167 # not a UPN: NTLM 

1168 kerberos = False 

1169 # Do we need to ask the password? 

1170 if HashNt is None and password is None and ST is None: 

1171 # yes. 

1172 from prompt_toolkit import prompt 

1173 

1174 password = prompt("Password: ", is_password=True) 

1175 ssps = [] 

1176 # Kerberos 

1177 if kerberos and hostname: 

1178 if ST is None: 

1179 resp = krb_as_and_tgs( 

1180 upn=UPN, 

1181 spn="cifs/%s" % hostname, 

1182 password=password, 

1183 debug=debug, 

1184 ) 

1185 if resp is not None: 

1186 ST, KEY = resp.tgsrep.ticket, resp.sessionkey 

1187 if ST: 

1188 ssps.append(KerberosSSP(UPN=UPN, ST=ST, KEY=KEY, debug=debug)) 

1189 elif kerberos_required: 

1190 raise ValueError( 

1191 "Kerberos required but target isn't a hostname !" 

1192 ) 

1193 elif kerberos_required: 

1194 raise ValueError( 

1195 "Kerberos required but domain not specified in the UPN, " 

1196 "or target isn't a hostname !" 

1197 ) 

1198 # NTLM 

1199 if not kerberos_required: 

1200 if HashNt is None and password is not None: 

1201 HashNt = MD4le(password) 

1202 ssps.append(NTLMSSP(UPN=UPN, HASHNT=HashNt)) 

1203 # Build the SSP 

1204 ssp = SPNEGOSSP(ssps) 

1205 else: 

1206 # Guest mode 

1207 ssp = None 

1208 # Open socket 

1209 sock = socket.socket(family, socket.SOCK_STREAM) 

1210 # Configure socket for SMB: 

1211 # - TCP KEEPALIVE, TCP_KEEPIDLE and TCP_KEEPINTVL. Against a Windows server this 

1212 # isn't necessary, but samba kills the socket VERY fast otherwise. 

1213 # - set TCP_NODELAY to disable Nagle's algorithm (we're streaming data) 

1214 sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 

1215 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 

1216 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 10) 

1217 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) 

1218 # Timeout & connect 

1219 sock.settimeout(timeout) 

1220 sock.connect((target, port)) 

1221 self.extra_create_options = [] 

1222 # Wrap with the automaton 

1223 self.timeout = timeout 

1224 kwargs.setdefault("SERVER_NAME", target) 

1225 self.sock = SMB_Client.from_tcpsock( 

1226 sock, 

1227 ssp=ssp, 

1228 debug=debug, 

1229 REQUIRE_ENCRYPTION=REQUIRE_ENCRYPTION, 

1230 timeout=timeout, 

1231 **kwargs, 

1232 ) 

1233 try: 

1234 # Wrap with SMB_SOCKET 

1235 self.smbsock = SMB_SOCKET(self.sock, timeout=self.timeout) 

1236 # Wait for either the atmt to fail, or the smb_sock_ready to timeout 

1237 _t = time.time() 

1238 while True: 

1239 if self.sock.atmt.smb_sock_ready.is_set(): 

1240 # yay 

1241 break 

1242 if not self.sock.atmt.isrunning(): 

1243 status = self.sock.atmt.get("Status") 

1244 raise Scapy_Exception( 

1245 "%s with status %s" 

1246 % ( 

1247 self.sock.atmt.state.state, 

1248 STATUS_ERREF.get(status, hex(status)), 

1249 ) 

1250 ) 

1251 if time.time() - _t > timeout: 

1252 self.sock.close() 

1253 raise TimeoutError("The SMB handshake timed out.") 

1254 time.sleep(0.1) 

1255 except Exception: 

1256 # Something bad happened, end the socket/automaton 

1257 self.sock.close() 

1258 raise 

1259 

1260 # For some usages, we will also need the RPC wrapper 

1261 from scapy.layers.msrpce.rpcclient import DCERPC_Client 

1262 

1263 self.rpcclient = DCERPC_Client.from_smblink(self.sock, ndr64=False, verb=False) 

1264 # We have a valid smb connection ! 

1265 print( 

1266 "%s authentication successful using %s%s !" 

1267 % ( 

1268 SMB_DIALECTS.get( 

1269 self.smbsock.session.Dialect, 

1270 "SMB %s" % self.smbsock.session.Dialect, 

1271 ), 

1272 repr(self.smbsock.session.sspcontext), 

1273 " as GUEST" if self.smbsock.session.IsGuest else "", 

1274 ) 

1275 ) 

1276 # Now define some variables for our CLI 

1277 self.pwd = pathlib.PureWindowsPath("/") 

1278 self.localpwd = pathlib.Path(".").resolve() 

1279 self.current_tree = None 

1280 self.ls_cache = {} # cache the listing of the current directory 

1281 self.sh_cache = [] # cache the shares 

1282 # Start CLI 

1283 if cli: 

1284 self.loop(debug=debug) 

1285 

1286 def ps1(self): 

1287 return r"smb: \%s> " % self.normalize_path(self.pwd) 

1288 

1289 def close(self): 

1290 print("Connection closed") 

1291 self.smbsock.close() 

1292 

1293 def _require_share(self, silent=False): 

1294 if self.current_tree is None: 

1295 if not silent: 

1296 print("No share selected ! Try 'shares' then 'use'.") 

1297 return True 

1298 

1299 def collapse_path(self, path): 

1300 # the amount of pathlib.wtf you need to do to resolve .. on all platforms 

1301 # is ridiculous 

1302 return pathlib.PureWindowsPath(os.path.normpath(path.as_posix())) 

1303 

1304 def normalize_path(self, path): 

1305 """ 

1306 Normalize path for CIFS usage 

1307 """ 

1308 return str(self.collapse_path(path)).lstrip("\\") 

1309 

1310 @CLIUtil.addcommand() 

1311 def shares(self): 

1312 """ 

1313 List the shares available 

1314 """ 

1315 # Poll cache 

1316 if self.sh_cache: 

1317 return self.sh_cache 

1318 # One of the 'hardest' considering it's an RPC 

1319 self.rpcclient.open_smbpipe("srvsvc") 

1320 self.rpcclient.bind(find_dcerpc_interface("srvsvc")) 

1321 req = NetrShareEnum_Request( 

1322 InfoStruct=LPSHARE_ENUM_STRUCT( 

1323 Level=1, 

1324 ShareInfo=NDRUnion( 

1325 tag=1, 

1326 value=SHARE_INFO_1_CONTAINER(Buffer=None), 

1327 ), 

1328 ), 

1329 PreferedMaximumLength=0xFFFFFFFF, 

1330 ) 

1331 resp = self.rpcclient.sr1_req(req, timeout=self.timeout) 

1332 self.rpcclient.close_smbpipe() 

1333 if not isinstance(resp, NetrShareEnum_Response): 

1334 raise ValueError("NetrShareEnum_Request failed !") 

1335 results = [] 

1336 for share in resp.valueof("InfoStruct.ShareInfo.Buffer"): 

1337 shi1_type = share.valueof("shi1_type") & 0x0FFFFFFF 

1338 results.append( 

1339 ( 

1340 share.valueof("shi1_netname").decode(), 

1341 SRVSVC_SHARE_TYPES.get(shi1_type, shi1_type), 

1342 share.valueof("shi1_remark").decode(), 

1343 ) 

1344 ) 

1345 self.sh_cache = results # cache 

1346 return results 

1347 

1348 @CLIUtil.addoutput(shares) 

1349 def shares_output(self, results): 

1350 """ 

1351 Print the output of 'shares' 

1352 """ 

1353 print(pretty_list(results, [("ShareName", "ShareType", "Comment")])) 

1354 

1355 @CLIUtil.addcommand() 

1356 def use(self, share): 

1357 """ 

1358 Open a share 

1359 """ 

1360 self.current_tree = self.smbsock.tree_connect(share) 

1361 self.pwd = pathlib.PureWindowsPath("/") 

1362 self.ls_cache.clear() 

1363 

1364 @CLIUtil.addcomplete(use) 

1365 def use_complete(self, share): 

1366 """ 

1367 Auto-complete 'use' 

1368 """ 

1369 return [ 

1370 x[0] for x in self.shares() if x[0].startswith(share) and x[0] != "IPC$" 

1371 ] 

1372 

1373 def _parsepath(self, arg, remote=True): 

1374 """ 

1375 Parse a path. Returns the parent folder and file name 

1376 """ 

1377 # Find parent directory if it exists 

1378 elt = (pathlib.PureWindowsPath if remote else pathlib.Path)(arg) 

1379 eltpar = (pathlib.PureWindowsPath if remote else pathlib.Path)(".") 

1380 eltname = elt.name 

1381 if arg.endswith("/") or arg.endswith("\\"): 

1382 eltpar = elt 

1383 eltname = "" 

1384 elif elt.parent and elt.parent.name or elt.is_absolute(): 

1385 eltpar = elt.parent 

1386 return eltpar, eltname 

1387 

1388 def _fs_complete(self, arg, cond=None): 

1389 """ 

1390 Return a listing of the remote files for completion purposes 

1391 """ 

1392 if cond is None: 

1393 cond = lambda _: True 

1394 eltpar, eltname = self._parsepath(arg) 

1395 # ls in that directory 

1396 try: 

1397 files = self.ls(parent=eltpar) 

1398 except ValueError: 

1399 return [] 

1400 return [ 

1401 str(eltpar / x[0]) 

1402 for x in files 

1403 if ( 

1404 x[0].lower().startswith(eltname.lower()) 

1405 and x[0] not in [".", ".."] 

1406 and cond(x[1]) 

1407 ) 

1408 ] 

1409 

1410 def _dir_complete(self, arg): 

1411 """ 

1412 Return a directories of remote files for completion purposes 

1413 """ 

1414 results = self._fs_complete( 

1415 arg, 

1416 cond=lambda x: x.FILE_ATTRIBUTE_DIRECTORY, 

1417 ) 

1418 if len(results) == 1 and results[0].startswith(arg): 

1419 # skip through folders 

1420 return [results[0] + "\\"] 

1421 return results 

1422 

1423 @CLIUtil.addcommand(spaces=True) 

1424 def ls(self, parent=None): 

1425 """ 

1426 List the files in the remote directory 

1427 -t: sort by timestamp 

1428 -S: sort by size 

1429 -r: reverse while sorting 

1430 """ 

1431 if self._require_share(): 

1432 return 

1433 # Get pwd of the ls 

1434 pwd = self.pwd 

1435 if parent is not None: 

1436 pwd /= parent 

1437 pwd = self.normalize_path(pwd) 

1438 # Poll the cache 

1439 if self.ls_cache and pwd in self.ls_cache: 

1440 return self.ls_cache[pwd] 

1441 self.smbsock.set_TID(self.current_tree) 

1442 # Open folder 

1443 fileId = self.smbsock.create_request( 

1444 pwd, 

1445 type="folder", 

1446 extra_create_options=self.extra_create_options, 

1447 ) 

1448 # Query the folder 

1449 files = self.smbsock.query_directory(fileId) 

1450 # Close the folder 

1451 self.smbsock.close_request(fileId) 

1452 self.ls_cache[pwd] = files # Store cache 

1453 return files 

1454 

1455 @CLIUtil.addoutput(ls) 

1456 def ls_output(self, results, *, t=False, S=False, r=False): 

1457 """ 

1458 Print the output of 'ls' 

1459 """ 

1460 fld = UTCTimeField( 

1461 "", None, fmt="<Q", epoch=[1601, 1, 1, 0, 0, 0], custom_scaling=1e7 

1462 ) 

1463 if t: 

1464 # Sort by time 

1465 results.sort(key=lambda x: -x[3]) 

1466 if S: 

1467 # Sort by size 

1468 results.sort(key=lambda x: -x[2]) 

1469 if r: 

1470 # Reverse sort 

1471 results = results[::-1] 

1472 results = [ 

1473 ( 

1474 x[0], 

1475 "+".join(y.lstrip("FILE_ATTRIBUTE_") for y in str(x[1]).split("+")), 

1476 human_size(x[2]), 

1477 fld.i2repr(None, x[3]), 

1478 ) 

1479 for x in results 

1480 ] 

1481 print( 

1482 pretty_list( 

1483 results, 

1484 [("FileName", "FileAttributes", "EndOfFile", "LastWriteTime")], 

1485 sortBy=None, 

1486 ) 

1487 ) 

1488 

1489 @CLIUtil.addcomplete(ls) 

1490 def ls_complete(self, folder): 

1491 """ 

1492 Auto-complete ls 

1493 """ 

1494 if self._require_share(silent=True): 

1495 return [] 

1496 return self._dir_complete(folder) 

1497 

1498 @CLIUtil.addcommand(spaces=True) 

1499 def cd(self, folder): 

1500 """ 

1501 Change the remote current directory 

1502 """ 

1503 if self._require_share(): 

1504 return 

1505 if not folder: 

1506 # show mode 

1507 return str(self.pwd) 

1508 self.pwd /= folder 

1509 self.pwd = self.collapse_path(self.pwd) 

1510 self.ls_cache.clear() 

1511 

1512 @CLIUtil.addcomplete(cd) 

1513 def cd_complete(self, folder): 

1514 """ 

1515 Auto-complete cd 

1516 """ 

1517 if self._require_share(silent=True): 

1518 return [] 

1519 return self._dir_complete(folder) 

1520 

1521 def _lfs_complete(self, arg, cond): 

1522 """ 

1523 Return a listing of local files for completion purposes 

1524 """ 

1525 eltpar, eltname = self._parsepath(arg, remote=False) 

1526 eltpar = self.localpwd / eltpar 

1527 return [ 

1528 # trickery so that ../<TAB> works 

1529 str(eltpar / x.name) 

1530 for x in eltpar.resolve().glob("*") 

1531 if (x.name.lower().startswith(eltname.lower()) and cond(x)) 

1532 ] 

1533 

1534 @CLIUtil.addoutput(cd) 

1535 def cd_output(self, result): 

1536 """ 

1537 Print the output of 'cd' 

1538 """ 

1539 if result: 

1540 print(result) 

1541 

1542 @CLIUtil.addcommand() 

1543 def lls(self): 

1544 """ 

1545 List the files in the local directory 

1546 """ 

1547 return list(self.localpwd.glob("*")) 

1548 

1549 @CLIUtil.addoutput(lls) 

1550 def lls_output(self, results): 

1551 """ 

1552 Print the output of 'lls' 

1553 """ 

1554 results = [ 

1555 ( 

1556 x.name, 

1557 human_size(stat.st_size), 

1558 time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(stat.st_mtime)), 

1559 ) 

1560 for x, stat in ((x, x.stat()) for x in results) 

1561 ] 

1562 print( 

1563 pretty_list(results, [("FileName", "File Size", "Last Modification Time")]) 

1564 ) 

1565 

1566 @CLIUtil.addcommand(spaces=True) 

1567 def lcd(self, folder): 

1568 """ 

1569 Change the local current directory 

1570 """ 

1571 if not folder: 

1572 # show mode 

1573 return str(self.localpwd) 

1574 self.localpwd /= folder 

1575 self.localpwd = self.localpwd.resolve() 

1576 

1577 @CLIUtil.addcomplete(lcd) 

1578 def lcd_complete(self, folder): 

1579 """ 

1580 Auto-complete lcd 

1581 """ 

1582 return self._lfs_complete(folder, lambda x: x.is_dir()) 

1583 

1584 @CLIUtil.addoutput(lcd) 

1585 def lcd_output(self, result): 

1586 """ 

1587 Print the output of 'lcd' 

1588 """ 

1589 if result: 

1590 print(result) 

1591 

1592 def _get_file(self, file, fd): 

1593 """ 

1594 Gets the file bytes from a remote host 

1595 """ 

1596 # Get pwd of the ls 

1597 fpath = self.pwd / file 

1598 self.smbsock.set_TID(self.current_tree) 

1599 # Open file 

1600 fileId = self.smbsock.create_request( 

1601 self.normalize_path(fpath), 

1602 type="file", 

1603 extra_create_options=[ 

1604 "FILE_SEQUENTIAL_ONLY", 

1605 ] 

1606 + self.extra_create_options, 

1607 ) 

1608 # Get the file size 

1609 info = FileAllInformation( 

1610 self.smbsock.query_info( 

1611 FileId=fileId, 

1612 InfoType="SMB2_0_INFO_FILE", 

1613 FileInfoClass="FileAllInformation", 

1614 ) 

1615 ) 

1616 length = info.StandardInformation.EndOfFile 

1617 offset = 0 

1618 # Read the file 

1619 while length: 

1620 lengthRead = min(self.smbsock.session.MaxReadSize, length) 

1621 fd.write( 

1622 self.smbsock.read_request(fileId, Length=lengthRead, Offset=offset) 

1623 ) 

1624 offset += lengthRead 

1625 length -= lengthRead 

1626 # Close the file 

1627 self.smbsock.close_request(fileId) 

1628 return offset 

1629 

1630 def _send_file(self, fname, fd): 

1631 """ 

1632 Send the file bytes to a remote host 

1633 """ 

1634 # Get destination file 

1635 fpath = self.pwd / fname 

1636 self.smbsock.set_TID(self.current_tree) 

1637 # Open file 

1638 fileId = self.smbsock.create_request( 

1639 self.normalize_path(fpath), 

1640 type="file", 

1641 mode="w", 

1642 extra_create_options=self.extra_create_options, 

1643 ) 

1644 # Send the file 

1645 offset = 0 

1646 while True: 

1647 data = fd.read(self.smbsock.session.MaxWriteSize) 

1648 if not data: 

1649 # end of file 

1650 break 

1651 offset += self.smbsock.write_request( 

1652 Data=data, 

1653 FileId=fileId, 

1654 Offset=offset, 

1655 ) 

1656 # Close the file 

1657 self.smbsock.close_request(fileId) 

1658 return offset 

1659 

1660 def _getr(self, directory, _root, _verb=True): 

1661 """ 

1662 Internal recursive function to get a directory 

1663 

1664 :param directory: the remote directory to get 

1665 :param _root: locally, the directory to store any found files 

1666 """ 

1667 size = 0 

1668 if not _root.exists(): 

1669 _root.mkdir() 

1670 # ls the directory 

1671 for x in self.ls(parent=directory): 

1672 if x[0] in [".", ".."]: 

1673 # Discard . and .. 

1674 continue 

1675 remote = directory / x[0] 

1676 local = _root / x[0] 

1677 try: 

1678 if x[1].FILE_ATTRIBUTE_DIRECTORY: 

1679 # Sub-directory 

1680 size += self._getr(remote, local) 

1681 else: 

1682 # Sub-file 

1683 size += self.get(remote, local)[1] 

1684 if _verb: 

1685 print(remote) 

1686 except ValueError as ex: 

1687 if _verb: 

1688 print(conf.color_theme.red(remote), "->", str(ex)) 

1689 return size 

1690 

1691 @CLIUtil.addcommand(spaces=True, globsupport=True) 

1692 def get(self, file, _dest=None, _verb=True, *, r=False): 

1693 """ 

1694 Retrieve a file 

1695 -r: recursively download a directory 

1696 """ 

1697 if self._require_share(): 

1698 return 

1699 if r: 

1700 dirpar, dirname = self._parsepath(file) 

1701 return file, self._getr( 

1702 dirpar / dirname, # Remotely 

1703 _root=self.localpwd / dirname, # Locally 

1704 _verb=_verb, 

1705 ) 

1706 else: 

1707 fname = pathlib.PureWindowsPath(file).name 

1708 # Write the buffer 

1709 if _dest is None: 

1710 _dest = self.localpwd / fname 

1711 with _dest.open("wb") as fd: 

1712 size = self._get_file(file, fd) 

1713 return fname, size 

1714 

1715 @CLIUtil.addoutput(get) 

1716 def get_output(self, info): 

1717 """ 

1718 Print the output of 'get' 

1719 """ 

1720 print("Retrieved '%s' of size %s" % (info[0], human_size(info[1]))) 

1721 

1722 @CLIUtil.addcomplete(get) 

1723 def get_complete(self, file): 

1724 """ 

1725 Auto-complete get 

1726 """ 

1727 if self._require_share(silent=True): 

1728 return [] 

1729 return self._fs_complete(file) 

1730 

1731 @CLIUtil.addcommand(spaces=True, globsupport=True) 

1732 def cat(self, file): 

1733 """ 

1734 Print a file 

1735 """ 

1736 if self._require_share(): 

1737 return 

1738 # Write the buffer to buffer 

1739 buf = io.BytesIO() 

1740 self._get_file(file, buf) 

1741 return buf.getvalue() 

1742 

1743 @CLIUtil.addoutput(cat) 

1744 def cat_output(self, result): 

1745 """ 

1746 Print the output of 'cat' 

1747 """ 

1748 print(result.decode(errors="backslashreplace")) 

1749 

1750 @CLIUtil.addcomplete(cat) 

1751 def cat_complete(self, file): 

1752 """ 

1753 Auto-complete cat 

1754 """ 

1755 if self._require_share(silent=True): 

1756 return [] 

1757 return self._fs_complete(file) 

1758 

1759 @CLIUtil.addcommand(spaces=True) 

1760 def put(self, file): 

1761 """ 

1762 Upload a file 

1763 """ 

1764 if self._require_share(): 

1765 return 

1766 local_file = self.localpwd / file 

1767 if local_file.is_dir(): 

1768 # Directory 

1769 raise ValueError("put on dir not impl") 

1770 else: 

1771 fname = pathlib.Path(file).name 

1772 with local_file.open("rb") as fd: 

1773 size = self._send_file(fname, fd) 

1774 self.ls_cache.clear() 

1775 return fname, size 

1776 

1777 @CLIUtil.addcomplete(put) 

1778 def put_complete(self, folder): 

1779 """ 

1780 Auto-complete put 

1781 """ 

1782 return self._lfs_complete(folder, lambda x: not x.is_dir()) 

1783 

1784 @CLIUtil.addcommand(spaces=True) 

1785 def rm(self, file): 

1786 """ 

1787 Delete a file 

1788 """ 

1789 if self._require_share(): 

1790 return 

1791 # Get pwd of the ls 

1792 fpath = self.pwd / file 

1793 self.smbsock.set_TID(self.current_tree) 

1794 # Open file 

1795 fileId = self.smbsock.create_request( 

1796 self.normalize_path(fpath), 

1797 type="file", 

1798 mode="d", 

1799 extra_create_options=self.extra_create_options, 

1800 ) 

1801 # Close the file 

1802 self.smbsock.close_request(fileId) 

1803 self.ls_cache.clear() 

1804 return fpath.name 

1805 

1806 @CLIUtil.addcomplete(rm) 

1807 def rm_complete(self, file): 

1808 """ 

1809 Auto-complete rm 

1810 """ 

1811 if self._require_share(silent=True): 

1812 return [] 

1813 return self._fs_complete(file) 

1814 

1815 @CLIUtil.addcommand() 

1816 def backup(self): 

1817 """ 

1818 Turn on or off backup intent 

1819 """ 

1820 if "FILE_OPEN_FOR_BACKUP_INTENT" in self.extra_create_options: 

1821 print("Backup Intent: Off") 

1822 self.extra_create_options.remove("FILE_OPEN_FOR_BACKUP_INTENT") 

1823 else: 

1824 print("Backup Intent: On") 

1825 self.extra_create_options.append("FILE_OPEN_FOR_BACKUP_INTENT") 

1826 

1827 @CLIUtil.addcommand(spaces=True) 

1828 def watch(self, folder): 

1829 """ 

1830 Watch file changes in folder (recursively) 

1831 """ 

1832 if self._require_share(): 

1833 return 

1834 # Get pwd of the ls 

1835 fpath = self.pwd / folder 

1836 self.smbsock.set_TID(self.current_tree) 

1837 # Open file 

1838 fileId = self.smbsock.create_request( 

1839 self.normalize_path(fpath), 

1840 type="folder", 

1841 extra_create_options=self.extra_create_options, 

1842 ) 

1843 print("Watching '%s'" % fpath) 

1844 # Watch for changes 

1845 try: 

1846 while True: 

1847 changes = self.smbsock.changenotify(fileId) 

1848 for chg in changes: 

1849 print(chg.sprintf("%.time%: %Action% %FileName%")) 

1850 except KeyboardInterrupt: 

1851 pass 

1852 # Close the file 

1853 self.smbsock.close_request(fileId) 

1854 print("Cancelled.") 

1855 

1856 @CLIUtil.addcommand(spaces=True) 

1857 def getsd(self, file): 

1858 """ 

1859 Get the Security Descriptor 

1860 """ 

1861 if self._require_share(): 

1862 return 

1863 fpath = self.pwd / file 

1864 self.smbsock.set_TID(self.current_tree) 

1865 # Open file 

1866 fileId = self.smbsock.create_request( 

1867 self.normalize_path(fpath), 

1868 type="", 

1869 mode="", 

1870 extra_desired_access=["READ_CONTROL", "ACCESS_SYSTEM_SECURITY"], 

1871 ) 

1872 # Get the file size 

1873 info = self.smbsock.query_info( 

1874 FileId=fileId, 

1875 InfoType="SMB2_0_INFO_SECURITY", 

1876 FileInfoClass=0, 

1877 AdditionalInformation=( 

1878 0x00000001 

1879 | 0x00000002 

1880 | 0x00000004 

1881 | 0x00000008 

1882 | 0x00000010 

1883 | 0x00000020 

1884 | 0x00000040 

1885 | 0x00010000 

1886 ), 

1887 ) 

1888 self.smbsock.close_request(fileId) 

1889 return info 

1890 

1891 @CLIUtil.addcomplete(getsd) 

1892 def getsd_complete(self, file): 

1893 """ 

1894 Auto-complete getsd 

1895 """ 

1896 if self._require_share(silent=True): 

1897 return [] 

1898 return self._fs_complete(file) 

1899 

1900 @CLIUtil.addoutput(getsd) 

1901 def getsd_output(self, results): 

1902 """ 

1903 Print the output of 'getsd' 

1904 """ 

1905 sd = SECURITY_DESCRIPTOR(results) 

1906 print("Owner:", sd.OwnerSid.summary()) 

1907 print("Group:", sd.GroupSid.summary()) 

1908 if getattr(sd, "DACL", None): 

1909 print("DACL:") 

1910 for ace in sd.DACL.Aces: 

1911 print(" - ", ace.toSDDL()) 

1912 if getattr(sd, "SACL", None): 

1913 print("SACL:") 

1914 for ace in sd.SACL.Aces: 

1915 print(" - ", ace.toSDDL()) 

1916 

1917 

1918if __name__ == "__main__": 

1919 from scapy.utils import AutoArgparse 

1920 

1921 AutoArgparse(smbclient)