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

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

812 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.config import conf 

24from scapy.error import Scapy_Exception 

25from scapy.fields import UTCTimeField 

26from scapy.supersocket import SuperSocket 

27from scapy.utils import ( 

28 CLIUtil, 

29 pretty_list, 

30 human_size, 

31) 

32from scapy.volatile import RandUUID 

33 

34from scapy.layers.dcerpc import NDRUnion, find_dcerpc_interface 

35from scapy.layers.gssapi import ( 

36 GSS_C_FLAGS, 

37 GSS_S_COMPLETE, 

38 GSS_S_CONTINUE_NEEDED, 

39 GSS_S_DEFECTIVE_TOKEN, 

40) 

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

42 LPSHARE_ENUM_STRUCT, 

43 NetrShareEnum_Request, 

44 SHARE_INFO_1_CONTAINER, 

45) 

46from scapy.layers.ntlm import ( 

47 NTLMSSP, 

48) 

49from scapy.layers.smb import ( 

50 SMBNegotiate_Request, 

51 SMBNegotiate_Response_Extended_Security, 

52 SMBNegotiate_Response_Security, 

53 SMBSession_Null, 

54 SMBSession_Setup_AndX_Request, 

55 SMBSession_Setup_AndX_Request_Extended_Security, 

56 SMBSession_Setup_AndX_Response, 

57 SMBSession_Setup_AndX_Response_Extended_Security, 

58 SMB_Dialect, 

59 SMB_Header, 

60) 

61from scapy.layers.windows.security import SECURITY_DESCRIPTOR 

62from scapy.layers.smb2 import ( 

63 DirectTCP, 

64 FileAllInformation, 

65 FileIdBothDirectoryInformation, 

66 SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2, 

67 SMB2_CREATE_REQUEST_LEASE, 

68 SMB2_CREATE_REQUEST_LEASE_V2, 

69 SMB2_Change_Notify_Request, 

70 SMB2_Change_Notify_Response, 

71 SMB2_Close_Request, 

72 SMB2_Close_Response, 

73 SMB2_Create_Context, 

74 SMB2_Create_Request, 

75 SMB2_Create_Response, 

76 SMB2_ENCRYPTION_CIPHERS, 

77 SMB2_Encryption_Capabilities, 

78 SMB2_Error_Response, 

79 SMB2_Header, 

80 SMB2_IOCTL_Request, 

81 SMB2_IOCTL_Response, 

82 SMB2_Negotiate_Context, 

83 SMB2_Negotiate_Protocol_Request, 

84 SMB2_Negotiate_Protocol_Response, 

85 SMB2_Netname_Negotiate_Context_ID, 

86 SMB2_Preauth_Integrity_Capabilities, 

87 SMB2_Query_Directory_Request, 

88 SMB2_Query_Directory_Response, 

89 SMB2_Query_Info_Request, 

90 SMB2_Query_Info_Response, 

91 SMB2_Read_Request, 

92 SMB2_Read_Response, 

93 SMB2_SIGNING_ALGORITHMS, 

94 SMB2_Session_Setup_Request, 

95 SMB2_Session_Setup_Response, 

96 SMB2_Signing_Capabilities, 

97 SMB2_Tree_Connect_Request, 

98 SMB2_Tree_Connect_Response, 

99 SMB2_Tree_Disconnect_Request, 

100 SMB2_Tree_Disconnect_Response, 

101 SMB2_Write_Request, 

102 SMB2_Write_Response, 

103 SMBStreamSocket, 

104 SMB_DIALECTS, 

105 SRVSVC_SHARE_TYPES, 

106 STATUS_ERREF, 

107) 

108from scapy.layers.spnego import SPNEGOSSP 

109 

110 

111class SMB_Client(Automaton): 

112 """ 

113 SMB client automaton 

114 

115 :param sock: the SMBStreamSocket to use 

116 :param ssp: the SSP to use 

117 

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

119 

120 :param REQUIRE_SIGNATURE: set 'Require Signature' 

121 :param REQUIRE_ENCRYPTION: set 'Requite Encryption' 

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

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

124 :param DIALECTS: list of supported SMB2 dialects. 

125 Constructed from MIN_DIALECT, MAX_DIALECT otherwise. 

126 """ 

127 

128 port = 445 

129 cls = DirectTCP 

130 

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

132 # Various SMB client arguments 

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

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

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

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

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

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

139 self.HOST = kwargs.pop("HOST", "") 

140 # Store supported dialects 

141 if "DIALECTS" in kwargs: 

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

143 else: 

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

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

146 self.DIALECTS = sorted( 

147 [ 

148 x 

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

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

151 ] 

152 ) 

153 # Internal Session information 

154 self.ErrorStatus = None 

155 self.NegotiateCapabilities = None 

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

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

158 self.CurrentCreditCount = 0 

159 self.MaxCreditCount = 128 

160 if ssp is None: 

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

162 ssp = SPNEGOSSP( 

163 [ 

164 NTLMSSP( 

165 UPN="guest", 

166 HASHNT=b"", 

167 ) 

168 ] 

169 ) 

170 # Initialize 

171 kwargs["sock"] = sock 

172 Automaton.__init__( 

173 self, 

174 *args, 

175 **kwargs, 

176 ) 

177 if self.is_atmt_socket: 

178 self.smb_sock_ready = threading.Event() 

179 # Set session options 

180 self.session.ssp = ssp 

181 self.session.SigningRequired = ( 

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

183 ) 

184 self.session.Dialect = self.MAX_DIALECT 

185 

186 @classmethod 

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

188 return cls.smblink( 

189 None, 

190 SMBStreamSocket(sock, DirectTCP), 

191 **kwargs, 

192 ) 

193 

194 @property 

195 def session(self): 

196 # session shorthand 

197 return self.sock.session 

198 

199 def send(self, pkt): 

200 # Calculate what CreditCharge to send. 

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

202 # [MS-SMB2] sect 3.2.4.1.5 

203 typ = type(pkt.payload.payload) 

204 if typ is SMB2_Negotiate_Protocol_Request: 

205 # See [MS-SMB2] 3.2.4.1.2 note 

206 pkt.CreditCharge = 0 

207 elif typ in [ 

208 SMB2_Read_Request, 

209 SMB2_Write_Request, 

210 SMB2_IOCTL_Request, 

211 SMB2_Query_Directory_Request, 

212 SMB2_Change_Notify_Request, 

213 SMB2_Query_Info_Request, 

214 ]: 

215 # [MS-SMB2] 3.1.5.2 

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

217 # "CHANGE_NOTIFY, QUERY_INFO, or SET_INFO" 

218 if typ == SMB2_Read_Request: 

219 Length = pkt.payload.Length 

220 elif typ == SMB2_Write_Request: 

221 Length = len(pkt.payload.Data) 

222 elif typ == SMB2_IOCTL_Request: 

223 # [MS-SMB2] 3.3.5.15 

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

225 elif typ in [ 

226 SMB2_Query_Directory_Request, 

227 SMB2_Change_Notify_Request, 

228 SMB2_Query_Info_Request, 

229 ]: 

230 Length = pkt.payload.OutputBufferLength 

231 else: 

232 raise RuntimeError("impossible case") 

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

234 else: 

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

236 pkt.CreditCharge = 1 

237 # Keep track of our credits 

238 self.CurrentCreditCount -= pkt.CreditCharge 

239 # [MS-SMB2] note <110> 

240 # "The Windows-based client will request credits up to a configurable 

241 # maximum of 128 by default." 

242 pkt.CreditRequest = self.MaxCreditCount - self.CurrentCreditCount 

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

244 pkt.MID = self.SequenceWindow[0] 

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

246 

247 @ATMT.state(initial=1) 

248 def BEGIN(self): 

249 pass 

250 

251 @ATMT.condition(BEGIN) 

252 def continue_smb2(self): 

253 if self.SMB2: # Directly started in SMB2 

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

255 raise self.SMB2_NEGOTIATE() 

256 

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

258 def send_negotiate(self): 

259 raise self.SENT_NEGOTIATE() 

260 

261 @ATMT.action(send_negotiate) 

262 def on_negotiate(self): 

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

264 self.smb_header = DirectTCP() / SMB_Header( 

265 Flags2=( 

266 "LONG_NAMES+EAS+NT_STATUS+UNICODE+" 

267 "SMB_SECURITY_SIGNATURE+EXTENDED_SECURITY" 

268 ), 

269 TID=0xFFFF, 

270 PIDLow=0xFEFF, 

271 UID=0, 

272 MID=0, 

273 ) 

274 if self.EXTENDED_SECURITY: 

275 self.smb_header.Flags2 += "EXTENDED_SECURITY" 

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

277 Dialects=[ 

278 SMB_Dialect(DialectString=x) 

279 for x in [ 

280 "PC NETWORK PROGRAM 1.0", 

281 "LANMAN1.0", 

282 "Windows for Workgroups 3.1a", 

283 "LM1.2X002", 

284 "LANMAN2.1", 

285 "NT LM 0.12", 

286 ] 

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

288 ], 

289 ) 

290 if not self.EXTENDED_SECURITY: 

291 pkt.Flags2 -= "EXTENDED_SECURITY" 

292 pkt[SMB_Header].Flags2 = ( 

293 pkt[SMB_Header].Flags2 

294 - "SMB_SECURITY_SIGNATURE" 

295 + "SMB_SECURITY_SIGNATURE_REQUIRED+IS_LONG_NAME" 

296 ) 

297 self.send(pkt) 

298 

299 @ATMT.state() 

300 def SENT_NEGOTIATE(self): 

301 pass 

302 

303 @ATMT.state() 

304 def SMB2_NEGOTIATE(self): 

305 pass 

306 

307 @ATMT.condition(SMB2_NEGOTIATE) 

308 def send_negotiate_smb2(self): 

309 raise self.SENT_NEGOTIATE() 

310 

311 @ATMT.action(send_negotiate_smb2) 

312 def on_negotiate_smb2(self): 

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

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

315 Dialects=self.DIALECTS, 

316 SecurityMode=( 

317 "SIGNING_ENABLED+SIGNING_REQUIRED" 

318 if self.session.SigningRequired 

319 else "SIGNING_ENABLED" 

320 ), 

321 ) 

322 if self.MAX_DIALECT >= 0x0210: 

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

324 # MUST be set to the global ClientGuid value" 

325 pkt.ClientGUID = self.GUID 

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

327 self.NegotiateCapabilities = "+".join( 

328 [ 

329 "DFS", 

330 "LEASING", 

331 "LARGE_MTU", 

332 ] 

333 ) 

334 if self.MAX_DIALECT >= 0x0300: 

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

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

337 [ 

338 "MULTI_CHANNEL", 

339 "PERSISTENT_HANDLES", 

340 "DIRECTORY_LEASING", 

341 "ENCRYPTION", 

342 ] 

343 ) 

344 if self.MAX_DIALECT >= 0x0311: 

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

346 pkt.NegotiateContexts = [ 

347 SMB2_Negotiate_Context() 

348 / SMB2_Preauth_Integrity_Capabilities( 

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

350 HashAlgorithms=["SHA-512"], 

351 Salt=self.session.Salt, 

352 ), 

353 SMB2_Negotiate_Context() 

354 / SMB2_Encryption_Capabilities( 

355 Ciphers=self.session.SupportedCipherIds, 

356 ), 

357 # TODO support compression and RDMA 

358 SMB2_Negotiate_Context() 

359 / SMB2_Netname_Negotiate_Context_ID( 

360 NetName=self.HOST, 

361 ), 

362 SMB2_Negotiate_Context() 

363 / SMB2_Signing_Capabilities( 

364 SigningAlgorithms=self.session.SupportedSigningAlgorithmIds, 

365 ), 

366 ] 

367 pkt.Capabilities = self.NegotiateCapabilities 

368 # Send 

369 self.send(pkt) 

370 # If required, compute sessions 

371 self.session.computeSMBConnectionPreauth( 

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

373 ) 

374 

375 @ATMT.receive_condition(SENT_NEGOTIATE) 

376 def receive_negotiate_response(self, pkt): 

377 if ( 

378 SMBNegotiate_Response_Extended_Security in pkt 

379 or SMB2_Negotiate_Protocol_Response in pkt 

380 ): 

381 # Extended SMB1 / SMB2 

382 try: 

383 ssp_blob = pkt.SecurityBlob # eventually SPNEGO server initiation 

384 except AttributeError: 

385 ssp_blob = None 

386 if ( 

387 SMB2_Negotiate_Protocol_Response in pkt 

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

389 ): 

390 # Version is SMB X.??? 

391 # [MS-SMB2] 3.2.5.2 

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

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

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

395 # SMB2 header to 1. 

396 self.SequenceWindow = (1, 1) 

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

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

399 raise self.SMB2_NEGOTIATE() 

400 else: 

401 if SMB2_Negotiate_Protocol_Response in pkt: 

402 # SMB2 was negotiated ! 

403 self.session.Dialect = pkt.DialectRevision 

404 # If required, compute sessions 

405 self.session.computeSMBConnectionPreauth( 

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

407 ) 

408 # Process max sizes 

409 self.session.MaxReadSize = pkt.MaxReadSize 

410 self.session.MaxTransactionSize = pkt.MaxTransactionSize 

411 self.session.MaxWriteSize = pkt.MaxWriteSize 

412 # Process SecurityMode 

413 if pkt.SecurityMode.SIGNING_REQUIRED: 

414 self.session.SigningRequired = True 

415 # Process capabilities 

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

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

418 # Process NegotiateContext 

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

420 for ngctx in pkt.NegotiateContexts: 

421 if ngctx.ContextType == 0x0002: 

422 # SMB2_ENCRYPTION_CAPABILITIES 

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

424 self.session.CipherId = SMB2_ENCRYPTION_CIPHERS[ 

425 ngctx.Ciphers[0] 

426 ] 

427 self.session.SupportsEncryption = True 

428 elif ngctx.ContextType == 0x0008: 

429 # SMB2_SIGNING_CAPABILITIES 

430 self.session.SigningAlgorithmId = ( 

431 SMB2_SIGNING_ALGORITHMS[ngctx.SigningAlgorithms[0]] 

432 ) 

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

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

435 raise self.NEGO_FAILED() 

436 self.update_smbheader(pkt) 

437 raise self.NEGOTIATED(ssp_blob) 

438 elif SMBNegotiate_Response_Security in pkt: 

439 # Non-extended SMB1 

440 # Never tested. FIXME. probably broken 

441 raise self.NEGOTIATED(pkt.Challenge) 

442 

443 @ATMT.state(final=1) 

444 def NEGO_FAILED(self): 

445 self.smb_sock_ready.set() 

446 

447 @ATMT.state() 

448 def NEGOTIATED(self, ssp_blob=None): 

449 # Negotiated ! We now know the Dialect 

450 if self.session.Dialect > 0x0202: 

451 # [MS-SMB2] sect 3.2.5.1.4 

452 self.smb_header.CreditRequest = 1 

453 # Begin session establishment 

454 ssp_tuple = self.session.ssp.GSS_Init_sec_context( 

455 self.session.sspcontext, 

456 input_token=ssp_blob, 

457 target_name="cifs/" + self.HOST if self.HOST else None, 

458 req_flags=( 

459 GSS_C_FLAGS.GSS_C_MUTUAL_FLAG 

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

461 ), 

462 ) 

463 return ssp_tuple 

464 

465 def update_smbheader(self, pkt): 

466 """ 

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

468 """ 

469 # Some values should not be updated when ASYNC 

470 if not pkt.Flags.SMB2_FLAGS_ASYNC_COMMAND: 

471 # Update IDs 

472 self.smb_header.SessionId = pkt.SessionId 

473 self.smb_header.TID = pkt.TID 

474 self.smb_header.PID = pkt.PID 

475 # Update credits 

476 self.CurrentCreditCount += pkt.CreditRequest 

477 # [MS-SMB2] 3.2.5.1.4 

478 self.SequenceWindow = ( 

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

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

481 ) 

482 

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

484 

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

486 def should_retry_without_blob(self, ssp_tuple): 

487 _, _, status = ssp_tuple 

488 if status == GSS_S_DEFECTIVE_TOKEN: 

489 # Token was defective. This could be that we passed a SPNEGO initial token 

490 # to a NTLM SSP (not using SPNEGO). Retry using no input blob 

491 raise self.NEGOTIATED() 

492 

493 @ATMT.condition(NEGOTIATED, prio=2) 

494 def should_send_session_setup_request(self, ssp_tuple): 

495 _, _, status = ssp_tuple 

496 if status not in [GSS_S_COMPLETE, GSS_S_CONTINUE_NEEDED]: 

497 raise ValueError( 

498 "Internal error: the SSP completed with error: %s" % status 

499 ) 

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

501 

502 @ATMT.state() 

503 def SENT_SESSION_REQUEST(self): 

504 pass 

505 

506 @ATMT.action(should_send_session_setup_request) 

507 def send_setup_session_request(self, ssp_tuple): 

508 self.session.sspcontext, token, status = ssp_tuple 

509 if self.SMB2 and status == GSS_S_CONTINUE_NEEDED: 

510 # New session: force 0 

511 self.SessionId = 0 

512 if self.SMB2 or self.EXTENDED_SECURITY: 

513 # SMB1 extended / SMB2 

514 if self.SMB2: 

515 # SMB2 

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

517 Capabilities="DFS", 

518 SecurityMode=( 

519 "SIGNING_ENABLED+SIGNING_REQUIRED" 

520 if self.session.SigningRequired 

521 else "SIGNING_ENABLED" 

522 ), 

523 ) 

524 else: 

525 # SMB1 extended 

526 pkt = ( 

527 self.smb_header.copy() 

528 / SMBSession_Setup_AndX_Request_Extended_Security( 

529 ServerCapabilities=( 

530 "UNICODE+NT_SMBS+STATUS32+LEVEL_II_OPLOCKS+" 

531 "DYNAMIC_REAUTH+EXTENDED_SECURITY" 

532 ), 

533 NativeOS=b"", 

534 NativeLanMan=b"", 

535 ) 

536 ) 

537 pkt.SecurityBlob = token 

538 else: 

539 # Non-extended security. 

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

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

542 NativeOS=b"", 

543 NativeLanMan=b"", 

544 OEMPassword=b"\0" * 24, 

545 UnicodePassword=token, 

546 ) 

547 self.send(pkt) 

548 if self.SMB2: 

549 # If required, compute sessions 

550 self.session.computeSMBSessionPreauth( 

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

552 ) 

553 

554 @ATMT.receive_condition(SENT_SESSION_REQUEST) 

555 def receive_session_setup_response(self, pkt): 

556 if ( 

557 SMBSession_Null in pkt 

558 or SMBSession_Setup_AndX_Response_Extended_Security in pkt 

559 or SMBSession_Setup_AndX_Response in pkt 

560 ): 

561 # SMB1 

562 if SMBSession_Null in pkt: 

563 # Likely an error 

564 raise self.NEGOTIATED() 

565 # Logging 

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

567 # Not SUCCESS nor MORE_PROCESSING_REQUIRED: log 

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

569 self.debug( 

570 lvl=1, 

571 msg=conf.color_theme.red( 

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

573 ), 

574 ) 

575 if self.SMB2: 

576 self.update_smbheader(pkt) 

577 # Cases depending on the response packet 

578 if ( 

579 SMBSession_Setup_AndX_Response_Extended_Security in pkt 

580 or SMB2_Session_Setup_Response in pkt 

581 ): 

582 # The server assigns us a SessionId 

583 self.smb_header.SessionId = pkt.SessionId 

584 # SMB1 extended / SMB2 

585 if pkt.Status == 0: # Authenticated 

586 if SMB2_Session_Setup_Response in pkt: 

587 # [MS-SMB2] sect 3.2.5.3.1 

588 if pkt.SessionFlags.IS_GUEST: 

589 # "If the security subsystem indicates that the session 

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

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

592 self.session.IsGuest = True 

593 self.session.SigningRequired = False 

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

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

596 self.session.EncryptData = True 

597 self.session.SigningRequired = False 

598 raise self.AUTHENTICATED(pkt.SecurityBlob) 

599 else: 

600 if SMB2_Header in pkt: 

601 # If required, compute sessions 

602 self.session.computeSMBSessionPreauth( 

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

604 ) 

605 # Ongoing auth 

606 raise self.NEGOTIATED(pkt.SecurityBlob) 

607 elif SMBSession_Setup_AndX_Response_Extended_Security in pkt: 

608 # SMB1 non-extended 

609 pass 

610 elif SMB2_Error_Response in pkt: 

611 # Authentication failure 

612 self.session.sspcontext.clifailure() 

613 # Reset Session preauth (SMB 3.1.1) 

614 self.session.SessionPreauthIntegrityHashValue = None 

615 if not self.RETRY: 

616 raise self.AUTH_FAILED() 

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

618 self.RETRY -= 1 

619 raise self.NEGOTIATED() 

620 

621 @ATMT.state(final=1) 

622 def AUTH_FAILED(self): 

623 self.smb_sock_ready.set() 

624 

625 @ATMT.state() 

626 def AUTHENTICATED(self, ssp_blob=None): 

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

628 self.session.sspcontext, 

629 input_token=ssp_blob, 

630 target_name="cifs/" + self.HOST if self.HOST else None, 

631 ) 

632 if status != GSS_S_COMPLETE: 

633 raise ValueError( 

634 "Internal error: the SSP completed with error: %s" % status 

635 ) 

636 # Authentication was successful 

637 self.session.computeSMBSessionKeys(IsClient=True) 

638 

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

640 

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

642 def authenticated_post_actions(self): 

643 raise self.SOCKET_BIND() 

644 

645 # Plain SMB Socket 

646 

647 @ATMT.state() 

648 def SOCKET_BIND(self): 

649 self.smb_sock_ready.set() 

650 

651 @ATMT.condition(SOCKET_BIND) 

652 def start_smb_socket(self): 

653 raise self.SOCKET_MODE_SMB() 

654 

655 @ATMT.state() 

656 def SOCKET_MODE_SMB(self): 

657 pass 

658 

659 @ATMT.receive_condition(SOCKET_MODE_SMB) 

660 def incoming_data_received_smb(self, pkt): 

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

662 

663 @ATMT.action(incoming_data_received_smb) 

664 def receive_data_smb(self, pkt): 

665 resp = pkt[SMB2_Header].payload 

666 if isinstance(resp, SMB2_Error_Response): 

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

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

669 return 

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

671 # this is a notify cleanup. ignore 

672 return 

673 self.update_smbheader(pkt) 

674 # Add the status to the response as metadata 

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

676 self.oi.smbpipe.send(resp) 

677 

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

679 def outgoing_data_received_smb(self, fd): 

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

681 

682 @ATMT.action(outgoing_data_received_smb) 

683 def send_data(self, d): 

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

685 

686 

687class SMB_SOCKET(SuperSocket): 

688 """ 

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

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

691 """ 

692 

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

694 self.ins = smbsock 

695 self.timeout = timeout 

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

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

698 if self.session.sspcontext: 

699 self.session.sspcontext.clifailure() 

700 raise TimeoutError( 

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

702 ) 

703 if self.ins.atmt.ErrorStatus: 

704 raise Scapy_Exception( 

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

706 ) 

707 

708 @classmethod 

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

710 """ 

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

712 SMB_SOCKET/SMB_RPC_SOCKET 

713 """ 

714 return cls( 

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

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

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

718 ) 

719 

720 @property 

721 def session(self): 

722 return self.ins.atmt.session 

723 

724 def set_TID(self, TID): 

725 """ 

726 Set the TID (Tree ID). 

727 This can be called before sending a packet 

728 """ 

729 self.ins.atmt.smb_header.TID = TID 

730 

731 def get_TID(self): 

732 """ 

733 Get the current TID from the underlying socket 

734 """ 

735 return self.ins.atmt.smb_header.TID 

736 

737 def tree_connect(self, name): 

738 """ 

739 Send a TreeConnect request 

740 """ 

741 resp = self.ins.sr1( 

742 SMB2_Tree_Connect_Request( 

743 Buffer=[ 

744 ( 

745 "Path", 

746 "\\\\%s\\%s" 

747 % ( 

748 self.session.sspcontext.ServerHostname, 

749 name, 

750 ), 

751 ) 

752 ] 

753 ), 

754 verbose=False, 

755 timeout=self.timeout, 

756 ) 

757 if not resp: 

758 raise ValueError("TreeConnect timed out !") 

759 if SMB2_Tree_Connect_Response not in resp: 

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

761 # [MS-SMB2] sect 3.2.5.5 

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

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

764 self.session.TreeEncryptData = True 

765 else: 

766 self.session.TreeEncryptData = False 

767 return self.get_TID() 

768 

769 def tree_disconnect(self): 

770 """ 

771 Send a TreeDisconnect request 

772 """ 

773 resp = self.ins.sr1( 

774 SMB2_Tree_Disconnect_Request(), 

775 verbose=False, 

776 timeout=self.timeout, 

777 ) 

778 if not resp: 

779 raise ValueError("TreeDisconnect timed out !") 

780 if SMB2_Tree_Disconnect_Response not in resp: 

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

782 

783 def create_request( 

784 self, 

785 name, 

786 mode="r", 

787 type="pipe", 

788 extra_create_options=[], 

789 extra_desired_access=[], 

790 ): 

791 """ 

792 Open a file/pipe by its name 

793 

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

795 """ 

796 ShareAccess = [] 

797 DesiredAccess = [] 

798 # Common params depending on the access 

799 if "r" in mode: 

800 ShareAccess.append("FILE_SHARE_READ") 

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

802 if "w" in mode: 

803 ShareAccess.append("FILE_SHARE_WRITE") 

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

805 if "d" in mode: 

806 ShareAccess.append("FILE_SHARE_DELETE") 

807 # Params depending on the type 

808 FileAttributes = [] 

809 CreateOptions = [] 

810 CreateContexts = [] 

811 CreateDisposition = "FILE_OPEN" 

812 if type == "folder": 

813 FileAttributes.append("FILE_ATTRIBUTE_DIRECTORY") 

814 CreateOptions.append("FILE_DIRECTORY_FILE") 

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

816 CreateOptions = ["FILE_NON_DIRECTORY_FILE"] 

817 if "r" in mode: 

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

819 if "w" in mode: 

820 CreateDisposition = "FILE_OVERWRITE_IF" 

821 DesiredAccess.append("FILE_WRITE_EA") 

822 if "d" in mode: 

823 DesiredAccess.append("DELETE") 

824 CreateOptions.append("FILE_DELETE_ON_CLOSE") 

825 if type == "file": 

826 FileAttributes.append("FILE_ATTRIBUTE_NORMAL") 

827 elif type: 

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

829 # [MS-SMB2] 3.2.4.3.8 

830 RequestedOplockLevel = 0 

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

832 RequestedOplockLevel = "SMB2_OPLOCK_LEVEL_LEASE" 

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

834 RequestedOplockLevel = "SMB2_OPLOCK_LEVEL_LEASE" 

835 # SMB 3.X 

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

837 CreateContexts.extend( 

838 [ 

839 # [SMB2] sect 3.2.4.3.5 

840 SMB2_Create_Context( 

841 Name=b"DH2Q", 

842 Data=SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2( 

843 CreateGuid=RandUUID()._fix() 

844 ), 

845 ), 

846 # [SMB2] sect 3.2.4.3.9 

847 SMB2_Create_Context( 

848 Name=b"MxAc", 

849 ), 

850 # [SMB2] sect 3.2.4.3.10 

851 SMB2_Create_Context( 

852 Name=b"QFid", 

853 ), 

854 # [SMB2] sect 3.2.4.3.8 

855 SMB2_Create_Context( 

856 Name=b"RqLs", 

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

858 ), 

859 ] 

860 ) 

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

862 CreateContexts.extend( 

863 [ 

864 # [SMB2] sect 3.2.4.3.8 

865 SMB2_Create_Context( 

866 Name=b"RqLs", 

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

868 ), 

869 ] 

870 ) 

871 # Extra options 

872 if extra_create_options: 

873 CreateOptions.extend(extra_create_options) 

874 if extra_desired_access: 

875 DesiredAccess.extend(extra_desired_access) 

876 # Request 

877 resp = self.ins.sr1( 

878 SMB2_Create_Request( 

879 ImpersonationLevel="Impersonation", 

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

881 CreateDisposition=CreateDisposition, 

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

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

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

885 CreateContexts=CreateContexts, 

886 RequestedOplockLevel=RequestedOplockLevel, 

887 Name=name, 

888 ), 

889 verbose=0, 

890 timeout=self.timeout, 

891 ) 

892 if not resp: 

893 raise ValueError("CreateRequest timed out !") 

894 if SMB2_Create_Response not in resp: 

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

896 return resp[SMB2_Create_Response].FileId 

897 

898 def close_request(self, FileId): 

899 """ 

900 Close the FileId 

901 """ 

902 pkt = SMB2_Close_Request(FileId=FileId) 

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

904 if not resp: 

905 raise ValueError("CloseRequest timed out !") 

906 if SMB2_Close_Response not in resp: 

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

908 

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

910 """ 

911 Read request 

912 """ 

913 resp = self.ins.sr1( 

914 SMB2_Read_Request( 

915 FileId=FileId, 

916 Length=Length, 

917 Offset=Offset, 

918 ), 

919 verbose=0, 

920 timeout=self.timeout * 10, 

921 ) 

922 if not resp: 

923 raise ValueError("ReadRequest timed out !") 

924 if SMB2_Read_Response not in resp: 

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

926 return resp.Data 

927 

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

929 """ 

930 Write request 

931 """ 

932 resp = self.ins.sr1( 

933 SMB2_Write_Request( 

934 FileId=FileId, 

935 Data=Data, 

936 Offset=Offset, 

937 ), 

938 verbose=0, 

939 timeout=self.timeout * 10, 

940 ) 

941 if not resp: 

942 raise ValueError("WriteRequest timed out !") 

943 if SMB2_Write_Response not in resp: 

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

945 return resp.Count 

946 

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

948 """ 

949 Query the Directory with FileId 

950 """ 

951 results = [] 

952 Flags = "SMB2_RESTART_SCANS" 

953 while True: 

954 pkt = SMB2_Query_Directory_Request( 

955 FileInformationClass="FileIdBothDirectoryInformation", 

956 FileId=FileId, 

957 FileName=FileName, 

958 Flags=Flags, 

959 ) 

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

961 Flags = 0 # only the first one is RESTART_SCANS 

962 if not resp: 

963 raise ValueError("QueryDirectory timed out !") 

964 if SMB2_Error_Response in resp: 

965 break 

966 elif SMB2_Query_Directory_Response not in resp: 

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

968 res = FileIdBothDirectoryInformation(resp.Output) 

969 results.extend( 

970 [ 

971 ( 

972 x.FileName, 

973 x.FileAttributes, 

974 x.EndOfFile, 

975 x.LastWriteTime, 

976 ) 

977 for x in res.files 

978 ] 

979 ) 

980 return results 

981 

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

983 """ 

984 Query the Info 

985 """ 

986 pkt = SMB2_Query_Info_Request( 

987 InfoType=InfoType, 

988 FileInfoClass=FileInfoClass, 

989 OutputBufferLength=65535, 

990 FileId=FileId, 

991 AdditionalInformation=AdditionalInformation, 

992 ) 

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

994 if not resp: 

995 raise ValueError("QueryInfo timed out !") 

996 if SMB2_Query_Info_Response not in resp: 

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

998 return resp.Output 

999 

1000 def changenotify(self, FileId): 

1001 """ 

1002 Register change notify 

1003 """ 

1004 pkt = SMB2_Change_Notify_Request( 

1005 Flags="SMB2_WATCH_TREE", 

1006 OutputBufferLength=65535, 

1007 FileId=FileId, 

1008 CompletionFilter=0x0FFF, 

1009 ) 

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

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

1012 if SMB2_Change_Notify_Response not in resp: 

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

1014 return resp.Output 

1015 

1016 

1017class SMB_RPC_SOCKET(ObjectPipe, SMB_SOCKET): 

1018 """ 

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

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

1021 

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

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

1024 """ 

1025 

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

1027 self.use_ioctl = use_ioctl 

1028 ObjectPipe.__init__(self, "SMB_RPC_SOCKET") 

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

1030 

1031 def open_pipe(self, name): 

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

1033 

1034 def close_pipe(self): 

1035 self.close_request(self.PipeFileId) 

1036 self.PipeFileId = None 

1037 

1038 def send(self, x, is_sr1=True): 

1039 # Reminder: this class is an ObjectPipe ! It doesn't act as a real socket 

1040 # but just a queue. When someone calls the "send" function, they pipe 

1041 # some data that we must send, and tell us if they expect an answer through 

1042 # the is_sr1 flag. 

1043 

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

1045 is_frag = x.pfc_flags & 3 != 3 

1046 

1047 if self.use_ioctl and is_sr1 and not is_frag and self.session.Dialect >= 0x0210: 

1048 # Use IOCTLRequest 

1049 pkt = SMB2_IOCTL_Request( 

1050 FileId=self.PipeFileId, 

1051 Flags="SMB2_0_IOCTL_IS_FSCTL", 

1052 CtlCode="FSCTL_PIPE_TRANSCEIVE", 

1053 ) 

1054 pkt.Input = bytes(x) 

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

1056 if SMB2_IOCTL_Response not in resp: 

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

1058 data = bytes(resp.Output) 

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

1060 

1061 # Handle BUFFER_OVERFLOW (big DCE/RPC response) 

1062 while resp.NTStatus == "STATUS_BUFFER_OVERFLOW" or data[3] & 2 != 2: 

1063 # Retrieve DCE/RPC full size 

1064 resp = self.ins.sr1( 

1065 SMB2_Read_Request( 

1066 FileId=self.PipeFileId, 

1067 ), 

1068 verbose=0, 

1069 ) 

1070 data = resp.Data 

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

1072 else: 

1073 # Use WriteRequest/ReadRequest 

1074 pkt = SMB2_Write_Request( 

1075 FileId=self.PipeFileId, 

1076 ) 

1077 pkt.Data = bytes(x) 

1078 # We send the Write Request 

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

1080 if SMB2_Write_Response not in resp: 

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

1082 

1083 # We may not be expecting an answer 

1084 if not is_sr1: 

1085 return 

1086 

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

1088 if is_frag and not x.pfc_flags.PFC_LAST_FRAG: 

1089 return 

1090 

1091 # We send a Read Request afterwards 

1092 resp = self.ins.sr1( 

1093 SMB2_Read_Request( 

1094 FileId=self.PipeFileId, 

1095 ), 

1096 verbose=0, 

1097 ) 

1098 if SMB2_Read_Response not in resp: 

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

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

1101 # Handle fragmented response 

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

1103 # Retrieve DCE/RPC full size 

1104 resp = self.ins.sr1( 

1105 SMB2_Read_Request( 

1106 FileId=self.PipeFileId, 

1107 ), 

1108 verbose=0, 

1109 ) 

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

1111 

1112 def close(self): 

1113 SMB_SOCKET.close(self) 

1114 ObjectPipe.close(self) 

1115 

1116 

1117@conf.commands.register 

1118class smbclient(CLIUtil): 

1119 r""" 

1120 A simple SMB client CLI powered by Scapy 

1121 

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

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

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

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

1126 :param kerberos_required: require kerberos 

1127 :param port: the TCP port. default 445 

1128 :param password: if provided, used for auth 

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

1130 :param HashAes256Sha96: if provided, used for auth (Kerberos) 

1131 :param HashAes128Sha96: if provided, used for auth (Kerberos) 

1132 :param use_krb5ccname: (bool) if true, the KRB5CCNAME environment variable will 

1133 be used if available. 

1134 :param use_winssp: (bool) (only works on Windows). Use implicit authentication 

1135 through WinSSP. 

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

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

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

1139 

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

1141 them include the following: 

1142 

1143 :param REQUIRE_ENCRYPTION: requires encryption. 

1144 """ 

1145 

1146 def __init__( 

1147 self, 

1148 target: str, 

1149 UPN: str = None, 

1150 password: str = None, 

1151 guest: bool = False, 

1152 kerberos_required: bool = False, 

1153 HashNt: bytes = None, 

1154 HashAes256Sha96: bytes = None, 

1155 HashAes128Sha96: bytes = None, 

1156 use_krb5ccname: bool = False, 

1157 use_winssp: bool = False, 

1158 port: int = 445, 

1159 timeout: int = 5, 

1160 debug: int = 0, 

1161 ssp=None, 

1162 ST=None, 

1163 KEY=None, 

1164 cli=True, 

1165 # SMB arguments 

1166 REQUIRE_ENCRYPTION=False, 

1167 **kwargs, 

1168 ): 

1169 if cli: 

1170 self._depcheck() 

1171 assert ( 

1172 UPN or ssp or guest or use_winssp 

1173 ), "Either UPN, ssp or guest must be provided !" 

1174 # Do we need to build a SSP? 

1175 if ssp is None: 

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

1177 if not guest: 

1178 ssp = SPNEGOSSP.from_cli_arguments( 

1179 UPN=UPN, 

1180 target=target, 

1181 password=password, 

1182 HashNt=HashNt, 

1183 HashAes256Sha96=HashAes256Sha96, 

1184 HashAes128Sha96=HashAes128Sha96, 

1185 ST=ST, 

1186 KEY=KEY, 

1187 kerberos_required=kerberos_required, 

1188 use_krb5ccname=use_krb5ccname, 

1189 use_winssp=use_winssp, 

1190 ) 

1191 else: 

1192 # Guest mode 

1193 ssp = None 

1194 # Check if target is IPv4 or IPv6 

1195 if ":" in target: 

1196 family = socket.AF_INET6 

1197 else: 

1198 family = socket.AF_INET 

1199 # Open socket 

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

1201 # Configure socket for SMB: 

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

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

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

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

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

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

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

1209 # Timeout & connect 

1210 sock.settimeout(timeout) 

1211 if debug: 

1212 print("Connecting to %s:%s" % (target, port)) 

1213 sock.connect((target, port)) 

1214 self.extra_create_options = [] 

1215 # Wrap with the automaton 

1216 self.timeout = timeout 

1217 kwargs.setdefault("HOST", target) 

1218 self.sock = SMB_Client.from_tcpsock( 

1219 sock, 

1220 ssp=ssp, 

1221 debug=debug, 

1222 REQUIRE_ENCRYPTION=REQUIRE_ENCRYPTION, 

1223 timeout=timeout, 

1224 **kwargs, 

1225 ) 

1226 try: 

1227 # Wrap with SMB_SOCKET 

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

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

1230 _t = time.time() 

1231 while True: 

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

1233 # yay 

1234 break 

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

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

1237 raise Scapy_Exception( 

1238 "%s with status %s" 

1239 % ( 

1240 self.sock.atmt.state.state, 

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

1242 ) 

1243 ) 

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

1245 self.sock.close() 

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

1247 time.sleep(0.1) 

1248 except Exception: 

1249 # Something bad happened, end the socket/automaton 

1250 self.sock.close() 

1251 raise 

1252 

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

1254 from scapy.layers.msrpce.rpcclient import DCERPC_Client 

1255 

1256 self.rpcclient = DCERPC_Client.from_smblink( 

1257 self.sock, 

1258 ndr64=False, 

1259 verb=bool(debug), 

1260 ) 

1261 # We have a valid smb connection ! 

1262 print( 

1263 "%s authentication successful using %s%s !" 

1264 % ( 

1265 SMB_DIALECTS.get( 

1266 self.smbsock.session.Dialect, 

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

1268 ), 

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

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

1271 ) 

1272 ) 

1273 # Now define some variables for our CLI 

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

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

1276 self.current_tree = None 

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

1278 self.sh_cache = [] # cache the shares 

1279 # Start CLI 

1280 if cli: 

1281 self.loop(debug=debug) 

1282 

1283 def ps1(self): 

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

1285 

1286 def close(self): 

1287 print("Connection closed") 

1288 self.smbsock.close() 

1289 

1290 def _require_share(self, silent=False): 

1291 if self.current_tree is None: 

1292 if not silent: 

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

1294 return True 

1295 

1296 def collapse_path(self, path): 

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

1298 # is ridiculous 

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

1300 

1301 def normalize_path(self, path): 

1302 """ 

1303 Normalize path for CIFS usage 

1304 """ 

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

1306 

1307 @CLIUtil.addcommand() 

1308 def shares(self): 

1309 """ 

1310 List the shares available 

1311 """ 

1312 # Poll cache 

1313 if self.sh_cache: 

1314 return self.sh_cache 

1315 # It's an RPC 

1316 self.rpcclient.open_smbpipe("srvsvc") 

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

1318 req = NetrShareEnum_Request( 

1319 InfoStruct=LPSHARE_ENUM_STRUCT( 

1320 Level=1, 

1321 ShareInfo=NDRUnion( 

1322 tag=1, 

1323 value=SHARE_INFO_1_CONTAINER(Buffer=None), 

1324 ), 

1325 ), 

1326 PreferedMaximumLength=0xFFFFFFFF, 

1327 ndr64=self.rpcclient.ndr64, 

1328 ) 

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

1330 self.rpcclient.close_smbpipe() 

1331 if resp.status != 0: 

1332 resp.show() 

1333 raise ValueError("NetrShareEnum_Request failed !") 

1334 results = [] 

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

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

1337 results.append( 

1338 ( 

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

1340 SRVSVC_SHARE_TYPES.get(shi1_type, shi1_type), 

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

1342 ) 

1343 ) 

1344 self.sh_cache = results # cache 

1345 return results 

1346 

1347 @CLIUtil.addoutput(shares) 

1348 def shares_output(self, results): 

1349 """ 

1350 Print the output of 'shares' 

1351 """ 

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

1353 

1354 @CLIUtil.addcommand(mono=True) 

1355 def use(self, share): 

1356 """ 

1357 Open a share 

1358 """ 

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

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

1361 self.ls_cache.clear() 

1362 

1363 @CLIUtil.addcomplete(use) 

1364 def use_complete(self, share): 

1365 """ 

1366 Auto-complete 'use' 

1367 """ 

1368 return [ 

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

1370 ] 

1371 

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

1373 """ 

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

1375 """ 

1376 # Find parent directory if it exists 

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

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

1379 eltname = elt.name 

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

1381 eltpar = elt 

1382 eltname = "" 

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

1384 eltpar = elt.parent 

1385 return eltpar, eltname 

1386 

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

1388 """ 

1389 Return a listing of the remote files for completion purposes 

1390 """ 

1391 if cond is None: 

1392 cond = lambda _: True 

1393 eltpar, eltname = self._parsepath(arg) 

1394 # ls in that directory 

1395 try: 

1396 files = self.ls(parent=eltpar) 

1397 except ValueError: 

1398 return [] 

1399 return [ 

1400 str(eltpar / x[0]) 

1401 for x in files 

1402 if ( 

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

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

1405 and cond(x[1]) 

1406 ) 

1407 ] 

1408 

1409 def _dir_complete(self, arg): 

1410 """ 

1411 Return a directories of remote files for completion purposes 

1412 """ 

1413 results = self._fs_complete( 

1414 arg, 

1415 cond=lambda x: x.FILE_ATTRIBUTE_DIRECTORY, 

1416 ) 

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

1418 # skip through folders 

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

1420 return results 

1421 

1422 @CLIUtil.addcommand(mono=True) 

1423 def ls(self, parent=None): 

1424 """ 

1425 List the files in the remote directory 

1426 -t: sort by timestamp 

1427 -S: sort by size 

1428 -r: reverse while sorting 

1429 """ 

1430 if self._require_share(): 

1431 return 

1432 # Get pwd of the ls 

1433 pwd = self.pwd 

1434 if parent is not None: 

1435 pwd /= parent 

1436 pwd = self.normalize_path(pwd) 

1437 # Poll the cache 

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

1439 return self.ls_cache[pwd] 

1440 self.smbsock.set_TID(self.current_tree) 

1441 # Open folder 

1442 fileId = self.smbsock.create_request( 

1443 pwd, 

1444 type="folder", 

1445 extra_create_options=self.extra_create_options, 

1446 ) 

1447 # Query the folder 

1448 files = self.smbsock.query_directory(fileId) 

1449 # Close the folder 

1450 self.smbsock.close_request(fileId) 

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

1452 return files 

1453 

1454 @CLIUtil.addoutput(ls) 

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

1456 """ 

1457 Print the output of 'ls' 

1458 """ 

1459 fld = UTCTimeField( 

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

1461 ) 

1462 if t: 

1463 # Sort by time 

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

1465 if S: 

1466 # Sort by size 

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

1468 if r: 

1469 # Reverse sort 

1470 results = results[::-1] 

1471 results = [ 

1472 ( 

1473 x[0], 

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

1475 human_size(x[2]), 

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

1477 ) 

1478 for x in results 

1479 ] 

1480 print( 

1481 pretty_list( 

1482 results, 

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

1484 sortBy=None, 

1485 ) 

1486 ) 

1487 

1488 @CLIUtil.addcomplete(ls) 

1489 def ls_complete(self, folder): 

1490 """ 

1491 Auto-complete ls 

1492 """ 

1493 if self._require_share(silent=True): 

1494 return [] 

1495 return self._dir_complete(folder) 

1496 

1497 @CLIUtil.addcommand(mono=True) 

1498 def cd(self, folder): 

1499 """ 

1500 Change the remote current directory 

1501 """ 

1502 if self._require_share(): 

1503 return 

1504 if not folder: 

1505 # show mode 

1506 return str(self.pwd) 

1507 self.pwd /= folder 

1508 self.pwd = self.collapse_path(self.pwd) 

1509 self.ls_cache.clear() 

1510 

1511 @CLIUtil.addcomplete(cd) 

1512 def cd_complete(self, folder): 

1513 """ 

1514 Auto-complete cd 

1515 """ 

1516 if self._require_share(silent=True): 

1517 return [] 

1518 return self._dir_complete(folder) 

1519 

1520 def _lfs_complete(self, arg, cond): 

1521 """ 

1522 Return a listing of local files for completion purposes 

1523 """ 

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

1525 eltpar = self.localpwd / eltpar 

1526 return [ 

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

1528 str(eltpar / x.name) 

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

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

1531 ] 

1532 

1533 @CLIUtil.addoutput(cd) 

1534 def cd_output(self, result): 

1535 """ 

1536 Print the output of 'cd' 

1537 """ 

1538 if result: 

1539 print(result) 

1540 

1541 @CLIUtil.addcommand() 

1542 def lls(self): 

1543 """ 

1544 List the files in the local directory 

1545 """ 

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

1547 

1548 @CLIUtil.addoutput(lls) 

1549 def lls_output(self, results): 

1550 """ 

1551 Print the output of 'lls' 

1552 """ 

1553 results = [ 

1554 ( 

1555 x.name, 

1556 human_size(stat.st_size), 

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

1558 ) 

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

1560 ] 

1561 print( 

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

1563 ) 

1564 

1565 @CLIUtil.addcommand(mono=True) 

1566 def lcd(self, folder): 

1567 """ 

1568 Change the local current directory 

1569 """ 

1570 if not folder: 

1571 # show mode 

1572 return str(self.localpwd) 

1573 self.localpwd /= folder 

1574 self.localpwd = self.localpwd.resolve() 

1575 

1576 @CLIUtil.addcomplete(lcd) 

1577 def lcd_complete(self, folder): 

1578 """ 

1579 Auto-complete lcd 

1580 """ 

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

1582 

1583 @CLIUtil.addoutput(lcd) 

1584 def lcd_output(self, result): 

1585 """ 

1586 Print the output of 'lcd' 

1587 """ 

1588 if result: 

1589 print(result) 

1590 

1591 def _get_file(self, file, fd): 

1592 """ 

1593 Gets the file bytes from a remote host 

1594 """ 

1595 # Get pwd of the ls 

1596 fpath = self.pwd / file 

1597 self.smbsock.set_TID(self.current_tree) 

1598 

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 

1609 # Get the file size 

1610 info = FileAllInformation( 

1611 self.smbsock.query_info( 

1612 FileId=fileId, 

1613 InfoType="SMB2_0_INFO_FILE", 

1614 FileInfoClass="FileAllInformation", 

1615 ) 

1616 ) 

1617 length = info.StandardInformation.EndOfFile 

1618 offset = 0 

1619 

1620 # Read the file 

1621 while length: 

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

1623 fd.write( 

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

1625 ) 

1626 offset += lengthRead 

1627 length -= lengthRead 

1628 

1629 # Close the file 

1630 self.smbsock.close_request(fileId) 

1631 return offset 

1632 

1633 def _send_file(self, fname, fd): 

1634 """ 

1635 Send the file bytes to a remote host 

1636 """ 

1637 # Get destination file 

1638 fpath = self.pwd / fname 

1639 self.smbsock.set_TID(self.current_tree) 

1640 # Open file 

1641 fileId = self.smbsock.create_request( 

1642 self.normalize_path(fpath), 

1643 type="file", 

1644 mode="w", 

1645 extra_create_options=self.extra_create_options, 

1646 ) 

1647 # Send the file 

1648 offset = 0 

1649 while True: 

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

1651 if not data: 

1652 # end of file 

1653 break 

1654 offset += self.smbsock.write_request( 

1655 Data=data, 

1656 FileId=fileId, 

1657 Offset=offset, 

1658 ) 

1659 # Close the file 

1660 self.smbsock.close_request(fileId) 

1661 return offset 

1662 

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

1664 """ 

1665 Internal recursive function to get a directory 

1666 

1667 :param directory: the remote directory to get 

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

1669 """ 

1670 size = 0 

1671 if not _root.exists(): 

1672 _root.mkdir() 

1673 # ls the directory 

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

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

1676 # Discard . and .. 

1677 continue 

1678 remote = directory / x[0] 

1679 local = _root / x[0] 

1680 try: 

1681 if x[1].FILE_ATTRIBUTE_DIRECTORY: 

1682 # Sub-directory 

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

1684 else: 

1685 # Sub-file 

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

1687 if _verb: 

1688 print(remote) 

1689 except ValueError as ex: 

1690 if _verb: 

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

1692 return size 

1693 

1694 @CLIUtil.addcommand(mono=True, globsupport=True) 

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

1696 """ 

1697 Retrieve a file 

1698 -r: recursively download a directory 

1699 """ 

1700 if self._require_share(): 

1701 return 

1702 if r: 

1703 dirpar, dirname = self._parsepath(file) 

1704 return file, self._getr( 

1705 dirpar / dirname, # Remotely 

1706 _root=self.localpwd / dirname, # Locally 

1707 _verb=_verb, 

1708 ) 

1709 else: 

1710 fname = pathlib.PureWindowsPath(file).name 

1711 # Write the buffer 

1712 if _dest is None: 

1713 _dest = self.localpwd / fname 

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

1715 size = self._get_file(file, fd) 

1716 return fname, size 

1717 

1718 @CLIUtil.addoutput(get) 

1719 def get_output(self, info): 

1720 """ 

1721 Print the output of 'get' 

1722 """ 

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

1724 

1725 @CLIUtil.addcomplete(get) 

1726 def get_complete(self, file): 

1727 """ 

1728 Auto-complete get 

1729 """ 

1730 if self._require_share(silent=True): 

1731 return [] 

1732 return self._fs_complete(file) 

1733 

1734 @CLIUtil.addcommand(mono=True, globsupport=True) 

1735 def cat(self, file): 

1736 """ 

1737 Print a file 

1738 """ 

1739 if self._require_share(): 

1740 return 

1741 # Write the buffer to buffer 

1742 buf = io.BytesIO() 

1743 self._get_file(file, buf) 

1744 return buf.getvalue() 

1745 

1746 @CLIUtil.addoutput(cat) 

1747 def cat_output(self, result): 

1748 """ 

1749 Print the output of 'cat' 

1750 """ 

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

1752 

1753 @CLIUtil.addcomplete(cat) 

1754 def cat_complete(self, file): 

1755 """ 

1756 Auto-complete cat 

1757 """ 

1758 if self._require_share(silent=True): 

1759 return [] 

1760 return self._fs_complete(file) 

1761 

1762 @CLIUtil.addcommand(mono=True, globsupport=True) 

1763 def put(self, file): 

1764 """ 

1765 Upload a file 

1766 """ 

1767 if self._require_share(): 

1768 return 

1769 local_file = self.localpwd / file 

1770 if local_file.is_dir(): 

1771 # Directory 

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

1773 else: 

1774 fname = pathlib.Path(file).name 

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

1776 size = self._send_file(fname, fd) 

1777 self.ls_cache.clear() 

1778 return fname, size 

1779 

1780 @CLIUtil.addcomplete(put) 

1781 def put_complete(self, folder): 

1782 """ 

1783 Auto-complete put 

1784 """ 

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

1786 

1787 @CLIUtil.addcommand(mono=True) 

1788 def rm(self, file): 

1789 """ 

1790 Delete a file 

1791 """ 

1792 if self._require_share(): 

1793 return 

1794 # Get pwd of the ls 

1795 fpath = self.pwd / file 

1796 self.smbsock.set_TID(self.current_tree) 

1797 # Open file 

1798 fileId = self.smbsock.create_request( 

1799 self.normalize_path(fpath), 

1800 type="file", 

1801 mode="d", 

1802 extra_create_options=self.extra_create_options, 

1803 ) 

1804 # Close the file 

1805 self.smbsock.close_request(fileId) 

1806 self.ls_cache.clear() 

1807 return fpath.name 

1808 

1809 @CLIUtil.addcomplete(rm) 

1810 def rm_complete(self, file): 

1811 """ 

1812 Auto-complete rm 

1813 """ 

1814 if self._require_share(silent=True): 

1815 return [] 

1816 return self._fs_complete(file) 

1817 

1818 @CLIUtil.addcommand() 

1819 def backup(self): 

1820 """ 

1821 Turn on or off backup intent 

1822 """ 

1823 if "FILE_OPEN_FOR_BACKUP_INTENT" in self.extra_create_options: 

1824 print("Backup Intent: Off") 

1825 self.extra_create_options.remove("FILE_OPEN_FOR_BACKUP_INTENT") 

1826 else: 

1827 print("Backup Intent: On") 

1828 self.extra_create_options.append("FILE_OPEN_FOR_BACKUP_INTENT") 

1829 

1830 @CLIUtil.addcommand(mono=True) 

1831 def watch(self, folder): 

1832 """ 

1833 Watch file changes in folder (recursively) 

1834 """ 

1835 if self._require_share(): 

1836 return 

1837 # Get pwd of the ls 

1838 fpath = self.pwd / folder 

1839 self.smbsock.set_TID(self.current_tree) 

1840 # Open file 

1841 fileId = self.smbsock.create_request( 

1842 self.normalize_path(fpath), 

1843 type="folder", 

1844 extra_create_options=self.extra_create_options, 

1845 ) 

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

1847 # Watch for changes 

1848 try: 

1849 while True: 

1850 changes = self.smbsock.changenotify(fileId) 

1851 for chg in changes: 

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

1853 except KeyboardInterrupt: 

1854 pass 

1855 print("Cancelled.") 

1856 

1857 @CLIUtil.addcommand(mono=True) 

1858 def getsd(self, file): 

1859 """ 

1860 Get the Security Descriptor 

1861 """ 

1862 if self._require_share(): 

1863 return 

1864 fpath = self.pwd / file 

1865 self.smbsock.set_TID(self.current_tree) 

1866 # Open file 

1867 fileId = self.smbsock.create_request( 

1868 self.normalize_path(fpath), 

1869 type="", 

1870 mode="", 

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

1872 ) 

1873 # Get the file size 

1874 info = self.smbsock.query_info( 

1875 FileId=fileId, 

1876 InfoType="SMB2_0_INFO_SECURITY", 

1877 FileInfoClass=0, 

1878 AdditionalInformation=( 

1879 0x00000001 

1880 | 0x00000002 

1881 | 0x00000004 

1882 | 0x00000008 

1883 | 0x00000010 

1884 | 0x00000020 

1885 | 0x00000040 

1886 | 0x00010000 

1887 ), 

1888 ) 

1889 self.smbsock.close_request(fileId) 

1890 return info 

1891 

1892 @CLIUtil.addcomplete(getsd) 

1893 def getsd_complete(self, file): 

1894 """ 

1895 Auto-complete getsd 

1896 """ 

1897 if self._require_share(silent=True): 

1898 return [] 

1899 return self._fs_complete(file) 

1900 

1901 @CLIUtil.addoutput(getsd) 

1902 def getsd_output(self, results): 

1903 """ 

1904 Print the output of 'getsd' 

1905 """ 

1906 sd = SECURITY_DESCRIPTOR(results) 

1907 sd.show_print() 

1908 

1909 

1910if __name__ == "__main__": 

1911 from scapy.utils import AutoArgparse 

1912 

1913 AutoArgparse(smbclient)