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

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

758 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 2 Server Automaton 

8 

9This provides a [MS-SMB2] server that can: 

10- serve files 

11- host a DCE/RPC server 

12 

13This is a Scapy Automaton that is supposedly easily extendable. 

14 

15.. note:: 

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

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

18""" 

19 

20import hashlib 

21import pathlib 

22import socket 

23import struct 

24import time 

25 

26from scapy.arch import get_if_addr 

27from scapy.automaton import ATMT, Automaton 

28from scapy.config import conf 

29from scapy.consts import WINDOWS 

30from scapy.error import log_runtime, log_interactive 

31from scapy.volatile import RandUUID 

32 

33from scapy.layers.dcerpc import ( 

34 DCERPC_Transport, 

35 NDRUnion, 

36) 

37from scapy.layers.gssapi import ( 

38 GSS_S_COMPLETE, 

39 GSS_S_CONTINUE_NEEDED, 

40 GSS_S_CREDENTIALS_EXPIRED, 

41) 

42from scapy.layers.msrpce.rpcserver import DCERPC_Server 

43from scapy.layers.ntlm import ( 

44 NTLMSSP, 

45) 

46from scapy.layers.smb import ( 

47 SMBNegotiate_Request, 

48 SMBNegotiate_Response_Extended_Security, 

49 SMBNegotiate_Response_Security, 

50 SMBSession_Null, 

51 SMBSession_Setup_AndX_Request, 

52 SMBSession_Setup_AndX_Request_Extended_Security, 

53 SMBSession_Setup_AndX_Response, 

54 SMBSession_Setup_AndX_Response_Extended_Security, 

55 SMBTree_Connect_AndX, 

56 SMB_Header, 

57) 

58from scapy.layers.smb2 import ( 

59 DFS_REFERRAL_ENTRY1, 

60 DFS_REFERRAL_V3, 

61 DirectTCP, 

62 FILE_BOTH_DIR_INFORMATION, 

63 FILE_FULL_DIR_INFORMATION, 

64 FILE_ID_BOTH_DIR_INFORMATION, 

65 FILE_NAME_INFORMATION, 

66 FileAllInformation, 

67 FileAlternateNameInformation, 

68 FileBasicInformation, 

69 FileEaInformation, 

70 FileFsAttributeInformation, 

71 FileFsSizeInformation, 

72 FileFsVolumeInformation, 

73 FileIdBothDirectoryInformation, 

74 FileInternalInformation, 

75 FileNetworkOpenInformation, 

76 FileStandardInformation, 

77 FileStreamInformation, 

78 NETWORK_INTERFACE_INFO, 

79 SECURITY_DESCRIPTOR, 

80 SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2, 

81 SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE, 

82 SMB2_CREATE_QUERY_ON_DISK_ID, 

83 SMB2_Cancel_Request, 

84 SMB2_Change_Notify_Request, 

85 SMB2_Change_Notify_Response, 

86 SMB2_Close_Request, 

87 SMB2_Close_Response, 

88 SMB2_Create_Context, 

89 SMB2_Create_Request, 

90 SMB2_Create_Response, 

91 SMB2_ENCRYPTION_CIPHERS, 

92 SMB2_Echo_Request, 

93 SMB2_Echo_Response, 

94 SMB2_Encryption_Capabilities, 

95 SMB2_Error_Response, 

96 SMB2_FILEID, 

97 SMB2_Header, 

98 SMB2_IOCTL_Network_Interface_Info, 

99 SMB2_IOCTL_RESP_GET_DFS_Referral, 

100 SMB2_IOCTL_Request, 

101 SMB2_IOCTL_Response, 

102 SMB2_IOCTL_Validate_Negotiate_Info_Response, 

103 SMB2_Negotiate_Context, 

104 SMB2_Negotiate_Protocol_Request, 

105 SMB2_Negotiate_Protocol_Response, 

106 SMB2_Preauth_Integrity_Capabilities, 

107 SMB2_Query_Directory_Request, 

108 SMB2_Query_Directory_Response, 

109 SMB2_Query_Info_Request, 

110 SMB2_Query_Info_Response, 

111 SMB2_Read_Request, 

112 SMB2_Read_Response, 

113 SMB2_SIGNING_ALGORITHMS, 

114 SMB2_Session_Logoff_Request, 

115 SMB2_Session_Logoff_Response, 

116 SMB2_Session_Setup_Request, 

117 SMB2_Session_Setup_Response, 

118 SMB2_Set_Info_Request, 

119 SMB2_Set_Info_Response, 

120 SMB2_Signing_Capabilities, 

121 SMB2_Tree_Connect_Request, 

122 SMB2_Tree_Connect_Response, 

123 SMB2_Tree_Disconnect_Request, 

124 SMB2_Tree_Disconnect_Response, 

125 SMB2_Write_Request, 

126 SMB2_Write_Response, 

127 SMBStreamSocket, 

128 SOCKADDR_STORAGE, 

129 SRVSVC_SHARE_TYPES, 

130) 

131from scapy.layers.spnego import SPNEGOSSP 

132 

133# Import DCE/RPC 

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

135 LPSERVER_INFO_101, 

136 LPSHARE_ENUM_STRUCT, 

137 LPSHARE_INFO_1, 

138 NetrServerGetInfo_Request, 

139 NetrServerGetInfo_Response, 

140 NetrShareEnum_Request, 

141 NetrShareEnum_Response, 

142 NetrShareGetInfo_Request, 

143 NetrShareGetInfo_Response, 

144 SHARE_INFO_1_CONTAINER, 

145) 

146from scapy.layers.msrpce.raw.ms_wkst import ( 

147 LPWKSTA_INFO_100, 

148 NetrWkstaGetInfo_Request, 

149 NetrWkstaGetInfo_Response, 

150) 

151 

152 

153class SMBShare: 

154 """ 

155 A class used to define a share, used by SMB_Server 

156 

157 :param name: the share name 

158 :param path: the path the the folder hosted by the share 

159 :param type: (optional) share type per [MS-SRVS] sect 2.2.2.4 

160 :param remark: (optional) a description of the share 

161 :param encryptdata: (optional) whether encryption should be used for this 

162 share. This only applies to SMB 3.1.1. 

163 """ 

164 

165 def __init__(self, name, path=".", type=None, remark="", encryptdata=False): 

166 # Set the default type 

167 if type is None: 

168 type = 0 # DISKTREE 

169 if name.endswith("$"): 

170 type &= 0x80000000 # SPECIAL 

171 # Lower case the name for resolution 

172 self._name = name.lower() 

173 # Resolve path 

174 self.path = pathlib.Path(path).resolve() 

175 # props 

176 self.name = name 

177 self.type = type 

178 self.remark = remark 

179 self.encryptdata = encryptdata 

180 

181 def __repr__(self): 

182 type = SRVSVC_SHARE_TYPES[self.type & 0x0FFFFFFF] 

183 if self.type & 0x80000000: 

184 type = "SPECIAL+" + type 

185 if self.type & 0x40000000: 

186 type = "TEMPORARY+" + type 

187 return "<SMBShare %s [%s]%s = %s>" % ( 

188 self.name, 

189 type, 

190 self.remark and (" '%s'" % self.remark) or "", 

191 str(self.path), 

192 ) 

193 

194 

195# The SMB Automaton 

196 

197 

198class SMB_Server(Automaton): 

199 """ 

200 SMB server automaton 

201 

202 :param shares: the shares to serve. By default, share nothing. 

203 Note that IPC$ is appended. 

204 :param ssp: the SSP to use 

205 

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

207 

208 :param ANONYMOUS_LOGIN: mark the clients as anonymous 

209 :param GUEST_LOGIN: mark the clients as guest 

210 :param REQUIRE_SIGNATURE: set 'Require Signature' 

211 :param REQUIRE_ENCRYPTION: globally require encryption. 

212 You could also make it share-specific on 3.1.1. 

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

214 :param TREE_SHARE_FLAGS: flags to announce on Tree_Connect_Response 

215 :param TREE_CAPABILITIES: capabilities to announce on Tree_Connect_Response 

216 :param TREE_MAXIMAL_ACCESS: maximal access to announce on Tree_Connect_Response 

217 :param FILE_MAXIMAL_ACCESS: maximal access to announce in MxAc Create Context 

218 """ 

219 

220 pkt_cls = DirectTCP 

221 socketcls = SMBStreamSocket 

222 

223 def __init__(self, shares=[], ssp=None, verb=True, readonly=True, *args, **kwargs): 

224 self.verb = verb 

225 if "sock" not in kwargs: 

226 raise ValueError( 

227 "SMB_Server cannot be started directly ! Use SMB_Server.spawn" 

228 ) 

229 # Various SMB server arguments 

230 self.ANONYMOUS_LOGIN = kwargs.pop("ANONYMOUS_LOGIN", False) 

231 self.GUEST_LOGIN = kwargs.pop("GUEST_LOGIN", None) 

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

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

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

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

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

237 self.TREE_SHARE_FLAGS = kwargs.pop( 

238 "TREE_SHARE_FLAGS", "FORCE_LEVELII_OPLOCK+RESTRICT_EXCLUSIVE_OPENS" 

239 ) 

240 self.TREE_CAPABILITIES = kwargs.pop("TREE_CAPABILITIES", 0) 

241 self.TREE_MAXIMAL_ACCESS = kwargs.pop( 

242 "TREE_MAXIMAL_ACCESS", 

243 "+".join( 

244 [ 

245 "FILE_READ_DATA", 

246 "FILE_WRITE_DATA", 

247 "FILE_APPEND_DATA", 

248 "FILE_READ_EA", 

249 "FILE_WRITE_EA", 

250 "FILE_EXECUTE", 

251 "FILE_DELETE_CHILD", 

252 "FILE_READ_ATTRIBUTES", 

253 "FILE_WRITE_ATTRIBUTES", 

254 "DELETE", 

255 "READ_CONTROL", 

256 "WRITE_DAC", 

257 "WRITE_OWNER", 

258 "SYNCHRONIZE", 

259 ] 

260 ), 

261 ) 

262 self.FILE_MAXIMAL_ACCESS = kwargs.pop( 

263 # Read-only 

264 "FILE_MAXIMAL_ACCESS", 

265 "+".join( 

266 [ 

267 "FILE_READ_DATA", 

268 "FILE_READ_EA", 

269 "FILE_EXECUTE", 

270 "FILE_READ_ATTRIBUTES", 

271 "READ_CONTROL", 

272 "SYNCHRONIZE", 

273 ] 

274 ), 

275 ) 

276 self.LOCAL_IPS = kwargs.pop( 

277 "LOCAL_IPS", [get_if_addr(kwargs.get("iface", conf.iface) or conf.iface)] 

278 ) 

279 self.DOMAIN_REFERRALS = kwargs.pop("DOMAIN_REFERRALS", []) 

280 if self.USE_SMB1: 

281 log_runtime.warning("Serving SMB1 is not supported :/") 

282 self.readonly = readonly 

283 # We don't want to update the parent shares argument 

284 self.shares = shares.copy() 

285 # Append the IPC$ share 

286 self.shares.append( 

287 SMBShare( 

288 name="IPC$", 

289 type=0x80000003, # SPECIAL+IPC 

290 remark="Remote IPC", 

291 ) 

292 ) 

293 # Initialize the DCE/RPC server for SMB 

294 self.rpc_server = SMB_DCERPC_Server( 

295 DCERPC_Transport.NCACN_NP, 

296 shares=self.shares, 

297 verb=self.verb, 

298 ) 

299 # Extend it if another DCE/RPC server is provided 

300 if "DCERPC_SERVER_CLS" in kwargs: 

301 self.rpc_server.extend(kwargs.pop("DCERPC_SERVER_CLS")) 

302 # Internal Session information 

303 self.SMB2 = False 

304 self.NegotiateCapabilities = None 

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

306 self.NextForceSign = False 

307 self.NextForceEncrypt = False 

308 # Compounds are handled on receiving by the StreamSocket, 

309 # and on aggregated in a CompoundQueue to be sent in one go 

310 self.NextCompound = False 

311 self.CompoundedHandle = None 

312 # SSP provider 

313 if ssp is None: 

314 # No SSP => fallback on NTLM with guest 

315 ssp = SPNEGOSSP( 

316 [ 

317 NTLMSSP( 

318 USE_MIC=False, 

319 DO_NOT_CHECK_LOGIN=True, 

320 ), 

321 ] 

322 ) 

323 if self.GUEST_LOGIN is None: 

324 self.GUEST_LOGIN = True 

325 # Initialize 

326 Automaton.__init__(self, *args, **kwargs) 

327 # Set session options 

328 self.session.ssp = ssp 

329 self.session.SigningRequired = ( 

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

331 ) 

332 

333 @property 

334 def session(self): 

335 # session shorthand 

336 return self.sock.session 

337 

338 def vprint(self, s=""): 

339 """ 

340 Verbose print (if enabled) 

341 """ 

342 if self.verb: 

343 if conf.interactive: 

344 log_interactive.info("> %s", s) 

345 else: 

346 print("> %s" % s) 

347 

348 def send(self, pkt): 

349 ForceSign, ForceEncrypt = self.NextForceSign, self.NextForceEncrypt 

350 self.NextForceSign = self.NextForceEncrypt = False 

351 return super(SMB_Server, self).send( 

352 pkt, 

353 Compounded=self.NextCompound, 

354 ForceSign=ForceSign, 

355 ForceEncrypt=ForceEncrypt, 

356 ) 

357 

358 @ATMT.state(initial=1) 

359 def BEGIN(self): 

360 self.authenticated = False 

361 

362 @ATMT.receive_condition(BEGIN) 

363 def received_negotiate(self, pkt): 

364 if SMBNegotiate_Request in pkt: 

365 raise self.NEGOTIATED().action_parameters(pkt) 

366 

367 @ATMT.receive_condition(BEGIN) 

368 def received_negotiate_smb2_begin(self, pkt): 

369 if SMB2_Negotiate_Protocol_Request in pkt: 

370 self.SMB2 = True 

371 raise self.NEGOTIATED().action_parameters(pkt) 

372 

373 @ATMT.action(received_negotiate_smb2_begin) 

374 def on_negotiate_smb2_begin(self, pkt): 

375 self.on_negotiate(pkt) 

376 

377 @ATMT.action(received_negotiate) 

378 def on_negotiate(self, pkt): 

379 self.session.sspcontext, spnego_token = self.session.ssp.NegTokenInit2() 

380 # Build negotiate response 

381 DialectIndex = None 

382 DialectRevision = None 

383 if SMB2_Negotiate_Protocol_Request in pkt: 

384 # SMB2 

385 DialectRevisions = pkt[SMB2_Negotiate_Protocol_Request].Dialects 

386 DialectRevisions = [x for x in DialectRevisions if x <= self.MAX_DIALECT] 

387 DialectRevisions.sort(reverse=True) 

388 if DialectRevisions: 

389 DialectRevision = DialectRevisions[0] 

390 else: 

391 # SMB1 

392 DialectIndexes = [ 

393 x.DialectString for x in pkt[SMBNegotiate_Request].Dialects 

394 ] 

395 if self.USE_SMB1: 

396 # Enforce SMB1 

397 DialectIndex = DialectIndexes.index(b"NT LM 0.12") 

398 else: 

399 # Find a value matching SMB2, fallback to SMB1 

400 for key, rev in [(b"SMB 2.???", 0x02FF), (b"SMB 2.002", 0x0202)]: 

401 try: 

402 DialectIndex = DialectIndexes.index(key) 

403 DialectRevision = rev 

404 self.SMB2 = True 

405 break 

406 except ValueError: 

407 pass 

408 else: 

409 DialectIndex = DialectIndexes.index(b"NT LM 0.12") 

410 if DialectRevision and DialectRevision & 0xFF != 0xFF: 

411 # Version isn't SMB X.??? 

412 self.session.Dialect = DialectRevision 

413 cls = None 

414 if self.SMB2: 

415 # SMB2 

416 cls = SMB2_Negotiate_Protocol_Response 

417 self.smb_header = DirectTCP() / SMB2_Header( 

418 Flags="SMB2_FLAGS_SERVER_TO_REDIR", 

419 CreditRequest=1, 

420 CreditCharge=1, 

421 ) 

422 if SMB2_Negotiate_Protocol_Request in pkt: 

423 self.update_smbheader(pkt) 

424 else: 

425 # SMB1 

426 self.smb_header = DirectTCP() / SMB_Header( 

427 Flags="REPLY+CASE_INSENSITIVE+CANONICALIZED_PATHS", 

428 Flags2=( 

429 "LONG_NAMES+EAS+NT_STATUS+SMB_SECURITY_SIGNATURE+" 

430 "UNICODE+EXTENDED_SECURITY" 

431 ), 

432 TID=pkt.TID, 

433 MID=pkt.MID, 

434 UID=pkt.UID, 

435 PIDLow=pkt.PIDLow, 

436 ) 

437 if self.EXTENDED_SECURITY: 

438 cls = SMBNegotiate_Response_Extended_Security 

439 else: 

440 cls = SMBNegotiate_Response_Security 

441 if DialectRevision is None and DialectIndex is None: 

442 # No common dialect found. 

443 if self.SMB2: 

444 resp = self.smb_header.copy() / SMB2_Error_Response() 

445 resp.Command = "SMB2_NEGOTIATE" 

446 else: 

447 resp = self.smb_header.copy() / SMBSession_Null() 

448 resp.Command = "SMB_COM_NEGOTIATE" 

449 resp.Status = "STATUS_NOT_SUPPORTED" 

450 self.send(resp) 

451 return 

452 if self.SMB2: # SMB2 

453 # SecurityMode 

454 if SMB2_Header in pkt and pkt.SecurityMode.SIGNING_REQUIRED: 

455 self.session.SigningRequired = True 

456 # Capabilities: [MS-SMB2] 3.3.5.4 

457 self.NegotiateCapabilities = "+".join( 

458 [ 

459 "DFS", 

460 "LEASING", 

461 "LARGE_MTU", 

462 ] 

463 ) 

464 if DialectRevision >= 0x0300: 

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

466 # the server supports..." 

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

468 [ 

469 "MULTI_CHANNEL", 

470 "PERSISTENT_HANDLES", 

471 "DIRECTORY_LEASING", 

472 "ENCRYPTION", 

473 ] 

474 ) 

475 # Build response 

476 resp = self.smb_header.copy() / cls( 

477 DialectRevision=DialectRevision, 

478 SecurityMode=( 

479 "SIGNING_ENABLED+SIGNING_REQUIRED" 

480 if self.session.SigningRequired 

481 else "SIGNING_ENABLED" 

482 ), 

483 ServerTime=(time.time() + 11644473600) * 1e7, 

484 ServerStartTime=0, 

485 MaxTransactionSize=65536, 

486 MaxReadSize=65536, 

487 MaxWriteSize=65536, 

488 Capabilities=self.NegotiateCapabilities, 

489 ) 

490 # SMB >= 3.0.0 

491 if DialectRevision >= 0x0300: 

492 # [MS-SMB2] sect 3.3.5.3.1 note 253 

493 resp.MaxTransactionSize = 0x800000 

494 resp.MaxReadSize = 0x800000 

495 resp.MaxWriteSize = 0x800000 

496 # SMB 3.1.1 

497 if DialectRevision >= 0x0311 and pkt.NegotiateContextsCount: 

498 # Negotiate context-capabilities 

499 for ngctx in pkt.NegotiateContexts: 

500 if ngctx.ContextType == 0x0002: 

501 # SMB2_ENCRYPTION_CAPABILITIES 

502 for ciph in ngctx.Ciphers: 

503 tciph = SMB2_ENCRYPTION_CIPHERS.get(ciph, None) 

504 if tciph in self.session.SupportedCipherIds: 

505 # Common ! 

506 self.session.CipherId = tciph 

507 self.session.SupportsEncryption = True 

508 break 

509 elif ngctx.ContextType == 0x0008: 

510 # SMB2_SIGNING_CAPABILITIES 

511 for signalg in ngctx.SigningAlgorithms: 

512 tsignalg = SMB2_SIGNING_ALGORITHMS.get(signalg, None) 

513 if tsignalg in self.session.SupportedSigningAlgorithmIds: 

514 # Common ! 

515 self.session.SigningAlgorithmId = tsignalg 

516 break 

517 # Send back the negotiated algorithms 

518 resp.NegotiateContexts = [ 

519 # Preauth capabilities 

520 SMB2_Negotiate_Context() 

521 / SMB2_Preauth_Integrity_Capabilities( 

522 # SHA-512 by default 

523 HashAlgorithms=[self.session.PreauthIntegrityHashId], 

524 Salt=self.session.Salt, 

525 ), 

526 # Encryption capabilities 

527 SMB2_Negotiate_Context() 

528 / SMB2_Encryption_Capabilities( 

529 # AES-128-CCM by default 

530 Ciphers=[self.session.CipherId], 

531 ), 

532 # Signing capabilities 

533 SMB2_Negotiate_Context() 

534 / SMB2_Signing_Capabilities( 

535 # AES-128-CCM by default 

536 SigningAlgorithms=[self.session.SigningAlgorithmId], 

537 ), 

538 ] 

539 else: 

540 # SMB1 

541 resp = self.smb_header.copy() / cls( 

542 DialectIndex=DialectIndex, 

543 ServerCapabilities=( 

544 "UNICODE+LARGE_FILES+NT_SMBS+RPC_REMOTE_APIS+STATUS32+" 

545 "LEVEL_II_OPLOCKS+LOCK_AND_READ+NT_FIND+" 

546 "LWIO+INFOLEVEL_PASSTHRU+LARGE_READX+LARGE_WRITEX" 

547 ), 

548 SecurityMode=( 

549 "SIGNING_ENABLED+SIGNING_REQUIRED" 

550 if self.session.SigningRequired 

551 else "SIGNING_ENABLED" 

552 ), 

553 ServerTime=(time.time() + 11644473600) * 1e7, 

554 ServerTimeZone=0x3C, 

555 ) 

556 if self.EXTENDED_SECURITY: 

557 resp.ServerCapabilities += "EXTENDED_SECURITY" 

558 if self.EXTENDED_SECURITY or self.SMB2: 

559 # Extended SMB1 / SMB2 

560 resp.GUID = self.GUID 

561 # Add security blob 

562 resp.SecurityBlob = spnego_token 

563 else: 

564 # Non-extended SMB1 

565 # FIXME never tested. 

566 resp.SecurityBlob = spnego_token 

567 resp.Flags2 -= "EXTENDED_SECURITY" 

568 if not self.SMB2: 

569 resp[SMB_Header].Flags2 = ( 

570 resp[SMB_Header].Flags2 

571 - "SMB_SECURITY_SIGNATURE" 

572 + "SMB_SECURITY_SIGNATURE_REQUIRED+IS_LONG_NAME" 

573 ) 

574 if SMB2_Header in pkt: 

575 # If required, compute sessions 

576 self.session.computeSMBConnectionPreauth( 

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

578 bytes(resp[SMB2_Header]), # nego response 

579 ) 

580 self.send(resp) 

581 

582 @ATMT.state(final=1) 

583 def NEGO_FAILED(self): 

584 self.vprint("SMB Negotiate failed: encryption was not negotiated.") 

585 self.end() 

586 

587 @ATMT.state() 

588 def NEGOTIATED(self): 

589 pass 

590 

591 def update_smbheader(self, pkt): 

592 """ 

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

594 """ 

595 # [MS-SMB2] sect 3.2.5.1.4 - always grant client its credits 

596 self.smb_header.CreditRequest = pkt.CreditRequest 

597 # [MS-SMB2] sect 3.3.4.1 

598 # "the server SHOULD set the CreditCharge field in the SMB2 header 

599 # of the response to the CreditCharge value in the SMB2 header of the request." 

600 self.smb_header.CreditCharge = pkt.CreditCharge 

601 # If the packet has a NextCommand, set NextCompound to True 

602 self.NextCompound = bool(pkt.NextCommand) 

603 # [MS-SMB2] sect 3.3.4.1.1 - "If the request was signed by the client..." 

604 # If the packet was signed, note we must answer with a signed packet. 

605 if ( 

606 not self.session.SigningRequired 

607 and pkt.SecuritySignature != b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 

608 ): 

609 self.NextForceSign = True 

610 # [MS-SMB2] sect 3.3.4.1.4 - "If the message being sent is any response to a 

611 # client request for which Request.IsEncrypted is TRUE" 

612 if pkt[SMB2_Header]._decrypted: 

613 self.NextForceEncrypt = True 

614 # [MS-SMB2] sect 3.3.5.2.7.2 

615 # Add SMB2_FLAGS_RELATED_OPERATIONS to the response if present 

616 if pkt.Flags.SMB2_FLAGS_RELATED_OPERATIONS: 

617 self.smb_header.Flags += "SMB2_FLAGS_RELATED_OPERATIONS" 

618 else: 

619 self.smb_header.Flags -= "SMB2_FLAGS_RELATED_OPERATIONS" 

620 # [MS-SMB2] sect 2.2.1.2 - Priority 

621 if (self.session.Dialect or 0) >= 0x0311: 

622 self.smb_header.Flags &= 0xFF8F 

623 self.smb_header.Flags |= int(pkt.Flags) & 0x70 

624 # Update IDs 

625 self.smb_header.SessionId = pkt.SessionId 

626 self.smb_header.TID = pkt.TID 

627 self.smb_header.MID = pkt.MID 

628 self.smb_header.PID = pkt.PID 

629 

630 @ATMT.receive_condition(NEGOTIATED) 

631 def received_negotiate_smb2(self, pkt): 

632 if SMB2_Negotiate_Protocol_Request in pkt: 

633 raise self.NEGOTIATED().action_parameters(pkt) 

634 

635 @ATMT.action(received_negotiate_smb2) 

636 def on_negotiate_smb2(self, pkt): 

637 self.on_negotiate(pkt) 

638 

639 @ATMT.receive_condition(NEGOTIATED) 

640 def receive_setup_andx_request(self, pkt): 

641 if ( 

642 SMBSession_Setup_AndX_Request_Extended_Security in pkt 

643 or SMBSession_Setup_AndX_Request in pkt 

644 ): 

645 # SMB1 

646 if SMBSession_Setup_AndX_Request_Extended_Security in pkt: 

647 # Extended 

648 ssp_blob = pkt.SecurityBlob 

649 else: 

650 # Non-extended 

651 ssp_blob = pkt[SMBSession_Setup_AndX_Request].UnicodePassword 

652 raise self.RECEIVED_SETUP_ANDX_REQUEST().action_parameters(pkt, ssp_blob) 

653 elif SMB2_Session_Setup_Request in pkt: 

654 # SMB2 

655 ssp_blob = pkt.SecurityBlob 

656 raise self.RECEIVED_SETUP_ANDX_REQUEST().action_parameters(pkt, ssp_blob) 

657 

658 @ATMT.state() 

659 def RECEIVED_SETUP_ANDX_REQUEST(self): 

660 pass 

661 

662 @ATMT.action(receive_setup_andx_request) 

663 def on_setup_andx_request(self, pkt, ssp_blob): 

664 self.session.sspcontext, tok, status = self.session.ssp.GSS_Accept_sec_context( 

665 self.session.sspcontext, ssp_blob 

666 ) 

667 self.update_smbheader(pkt) 

668 if SMB2_Session_Setup_Request in pkt: 

669 # SMB2 

670 self.smb_header.SessionId = 0x0001000000000015 

671 if status not in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

672 # Error 

673 if SMB2_Session_Setup_Request in pkt: 

674 # SMB2 

675 resp = self.smb_header.copy() / SMB2_Session_Setup_Response() 

676 # Set security blob (if any) 

677 resp.SecurityBlob = tok 

678 else: 

679 # SMB1 

680 resp = self.smb_header.copy() / SMBSession_Null() 

681 # Map some GSS return codes to NTStatus 

682 if status == GSS_S_CREDENTIALS_EXPIRED: 

683 resp.Status = "STATUS_PASSWORD_EXPIRED" 

684 else: 

685 resp.Status = "STATUS_LOGON_FAILURE" 

686 # Reset Session preauth (SMB 3.1.1) 

687 self.session.SessionPreauthIntegrityHashValue = None 

688 else: 

689 # Negotiation 

690 if ( 

691 SMBSession_Setup_AndX_Request_Extended_Security in pkt 

692 or SMB2_Session_Setup_Request in pkt 

693 ): 

694 # SMB1 extended / SMB2 

695 if SMB2_Session_Setup_Request in pkt: 

696 resp = self.smb_header.copy() / SMB2_Session_Setup_Response() 

697 if self.GUEST_LOGIN: 

698 # "If the security subsystem indicates that the session 

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

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

701 resp.SessionFlags = "IS_GUEST" 

702 self.session.IsGuest = True 

703 self.session.SigningRequired = False 

704 if self.ANONYMOUS_LOGIN: 

705 resp.SessionFlags = "IS_NULL" 

706 # [MS-SMB2] sect 3.3.5.5.3 

707 if self.session.Dialect >= 0x0300 and self.REQUIRE_ENCRYPTION: 

708 resp.SessionFlags += "ENCRYPT_DATA" 

709 else: 

710 # SMB1 extended 

711 resp = ( 

712 self.smb_header.copy() 

713 / SMBSession_Setup_AndX_Response_Extended_Security( 

714 NativeOS="Windows 4.0", 

715 NativeLanMan="Windows 4.0", 

716 ) 

717 ) 

718 if self.GUEST_LOGIN: 

719 resp.Action = "SMB_SETUP_GUEST" 

720 # Set security blob 

721 resp.SecurityBlob = tok 

722 elif SMBSession_Setup_AndX_Request in pkt: 

723 # Non-extended 

724 resp = self.smb_header.copy() / SMBSession_Setup_AndX_Response( 

725 NativeOS="Windows 4.0", 

726 NativeLanMan="Windows 4.0", 

727 ) 

728 resp.Status = 0x0 if (status == GSS_S_COMPLETE) else 0xC0000016 

729 # We have a response. If required, compute sessions 

730 if status == GSS_S_CONTINUE_NEEDED: 

731 # the setup session response is used in hash 

732 self.session.computeSMBSessionPreauth( 

733 bytes(pkt[SMB2_Header]), # session setup request 

734 bytes(resp[SMB2_Header]), # session setup response 

735 ) 

736 else: 

737 # the setup session response is not used in hash 

738 self.session.computeSMBSessionPreauth( 

739 bytes(pkt[SMB2_Header]), # session setup request 

740 ) 

741 if status == GSS_S_COMPLETE: 

742 # Authentication was successful 

743 self.session.computeSMBSessionKeys(IsClient=False) 

744 self.authenticated = True 

745 # [MS-SMB2] Note: "Windows-based servers always sign the final session setup 

746 # response when the user is neither anonymous nor guest." 

747 # If not available, it will still be ignored. 

748 self.NextForceSign = True 

749 self.send(resp) 

750 # Check whether we must enable encryption from now on 

751 if ( 

752 self.authenticated 

753 and not self.session.IsGuest 

754 and self.session.Dialect >= 0x0300 

755 and self.REQUIRE_ENCRYPTION 

756 ): 

757 # [MS-SMB2] sect 3.3.5.5.3: from now on, turn encryption on ! 

758 self.session.EncryptData = True 

759 self.session.SigningRequired = False 

760 

761 @ATMT.condition(RECEIVED_SETUP_ANDX_REQUEST) 

762 def wait_for_next_request(self): 

763 if self.authenticated: 

764 self.vprint( 

765 "User authenticated %s!" % (self.GUEST_LOGIN and " as guest" or "") 

766 ) 

767 raise self.AUTHENTICATED() 

768 else: 

769 raise self.NEGOTIATED() 

770 

771 @ATMT.state() 

772 def AUTHENTICATED(self): 

773 """Dev: overload this""" 

774 pass 

775 

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

777 

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

779 def should_serve(self): 

780 # Serve files 

781 self.current_trees = {} 

782 self.current_handles = {} 

783 self.enumerate_index = {} # used for query directory enumeration 

784 self.tree_id = 0 

785 self.base_time_t = self.current_smb_time() 

786 raise self.SERVING() 

787 

788 def _ioctl_error(self, Status="STATUS_NOT_SUPPORTED"): 

789 pkt = self.smb_header.copy() / SMB2_Error_Response(ErrorData=b"\xff") 

790 pkt.Status = Status 

791 pkt.Command = "SMB2_IOCTL" 

792 self.send(pkt) 

793 

794 @ATMT.state(final=1) 

795 def END(self): 

796 self.end() 

797 

798 # SERVE FILES 

799 

800 def current_tree(self): 

801 """ 

802 Return the current tree name 

803 """ 

804 return self.current_trees[self.smb_header.TID] 

805 

806 def root_path(self): 

807 """ 

808 Return the root path of the current tree 

809 """ 

810 curtree = self.current_tree() 

811 try: 

812 share_path = next(x.path for x in self.shares if x._name == curtree.lower()) 

813 except StopIteration: 

814 return None 

815 return pathlib.Path(share_path).resolve() 

816 

817 @ATMT.state() 

818 def SERVING(self): 

819 """ 

820 Main state when serving files 

821 """ 

822 pass 

823 

824 @ATMT.receive_condition(SERVING) 

825 def receive_logoff_request(self, pkt): 

826 if SMB2_Session_Logoff_Request in pkt: 

827 raise self.NEGOTIATED().action_parameters(pkt) 

828 

829 @ATMT.action(receive_logoff_request) 

830 def send_logoff_response(self, pkt): 

831 self.update_smbheader(pkt) 

832 self.send(self.smb_header.copy() / SMB2_Session_Logoff_Response()) 

833 

834 @ATMT.receive_condition(SERVING) 

835 def receive_setup_andx_request_in_serving(self, pkt): 

836 self.receive_setup_andx_request(pkt) 

837 

838 @ATMT.receive_condition(SERVING) 

839 def is_smb1_tree(self, pkt): 

840 if SMBTree_Connect_AndX in pkt: 

841 # Unsupported 

842 log_runtime.warning("Tree request in SMB1: unimplemented. Quit") 

843 raise self.END() 

844 

845 @ATMT.receive_condition(SERVING) 

846 def receive_tree_connect(self, pkt): 

847 if SMB2_Tree_Connect_Request in pkt: 

848 tree_name = pkt[SMB2_Tree_Connect_Request].Path.split("\\")[-1] 

849 raise self.SERVING().action_parameters(pkt, tree_name) 

850 

851 @ATMT.action(receive_tree_connect) 

852 def send_tree_connect_response(self, pkt, tree_name): 

853 self.update_smbheader(pkt) 

854 # Check the tree name against the shares we're serving 

855 try: 

856 share = next(x for x in self.shares if x._name == tree_name.lower()) 

857 except StopIteration: 

858 # Unknown tree 

859 resp = self.smb_header.copy() / SMB2_Error_Response() 

860 resp.Command = "SMB2_TREE_CONNECT" 

861 resp.Status = "STATUS_BAD_NETWORK_NAME" 

862 self.send(resp) 

863 return 

864 # Add tree to current trees 

865 if tree_name not in self.current_trees: 

866 self.tree_id += 1 

867 self.smb_header.TID = self.tree_id 

868 self.current_trees[self.smb_header.TID] = tree_name 

869 

870 # Construct ShareFlags 

871 ShareFlags = ( 

872 "AUTO_CACHING+NO_CACHING" 

873 if self.current_tree() == "IPC$" 

874 else self.TREE_SHARE_FLAGS 

875 ) 

876 # [MS-SMB2] sect 3.3.5.7 

877 if ( 

878 self.session.Dialect >= 0x0311 

879 and not self.session.EncryptData 

880 and share.encryptdata 

881 ): 

882 if not self.session.SupportsEncryption: 

883 raise Exception("Peer asked for encryption but doesn't support it !") 

884 ShareFlags += "+ENCRYPT_DATA" 

885 

886 self.vprint("Tree Connect on: %s" % tree_name) 

887 self.send( 

888 self.smb_header.copy() 

889 / SMB2_Tree_Connect_Response( 

890 ShareType="PIPE" if self.current_tree() == "IPC$" else "DISK", 

891 ShareFlags=ShareFlags, 

892 Capabilities=( 

893 0 if self.current_tree() == "IPC$" else self.TREE_CAPABILITIES 

894 ), 

895 MaximalAccess=self.TREE_MAXIMAL_ACCESS, 

896 ) 

897 ) 

898 

899 @ATMT.receive_condition(SERVING) 

900 def receive_ioctl(self, pkt): 

901 if SMB2_IOCTL_Request in pkt: 

902 raise self.SERVING().action_parameters(pkt) 

903 

904 @ATMT.action(receive_ioctl) 

905 def send_ioctl_response(self, pkt): 

906 self.update_smbheader(pkt) 

907 if pkt.CtlCode == 0x11C017: 

908 # FSCTL_PIPE_TRANSCEIVE 

909 self.rpc_server.recv(pkt.Input.load) 

910 self.send( 

911 self.smb_header.copy() 

912 / SMB2_IOCTL_Response( 

913 CtlCode=0x11C017, 

914 FileId=pkt[SMB2_IOCTL_Request].FileId, 

915 Buffer=[("Output", self.rpc_server.get_response())], 

916 ) 

917 ) 

918 elif pkt.CtlCode == 0x00140204 and self.session.sspcontext.SessionKey: 

919 # FSCTL_VALIDATE_NEGOTIATE_INFO 

920 # This is a security measure asking the server to validate 

921 # what flags were negotiated during the SMBNegotiate exchange. 

922 # This packet is ALWAYS signed, and expects a signed response. 

923 

924 # https://docs.microsoft.com/en-us/archive/blogs/openspecification/smb3-secure-dialect-negotiation 

925 # > "Down-level servers (pre-Windows 2012) will return 

926 # > STATUS_NOT_SUPPORTED or STATUS_INVALID_DEVICE_REQUEST 

927 # > since they do not allow or implement 

928 # > FSCTL_VALIDATE_NEGOTIATE_INFO. 

929 # > The client should accept the 

930 # > response provided it's properly signed". 

931 

932 if (self.session.Dialect or 0) < 0x0300: 

933 # SMB < 3 isn't supposed to support FSCTL_VALIDATE_NEGOTIATE_INFO 

934 self._ioctl_error(Status="STATUS_FILE_CLOSED") 

935 return 

936 

937 # SMB3 

938 self.send( 

939 self.smb_header.copy() 

940 / SMB2_IOCTL_Response( 

941 CtlCode=0x00140204, 

942 FileId=pkt[SMB2_IOCTL_Request].FileId, 

943 Buffer=[ 

944 ( 

945 "Output", 

946 SMB2_IOCTL_Validate_Negotiate_Info_Response( 

947 GUID=self.GUID, 

948 DialectRevision=self.session.Dialect, 

949 SecurityMode=( 

950 "SIGNING_ENABLED+SIGNING_REQUIRED" 

951 if self.session.SigningRequired 

952 else "SIGNING_ENABLED" 

953 ), 

954 Capabilities=self.NegotiateCapabilities, 

955 ), 

956 ) 

957 ], 

958 ) 

959 ) 

960 elif pkt.CtlCode == 0x001401FC: 

961 # FSCTL_QUERY_NETWORK_INTERFACE_INFO 

962 self.send( 

963 self.smb_header.copy() 

964 / SMB2_IOCTL_Response( 

965 CtlCode=0x001401FC, 

966 FileId=pkt[SMB2_IOCTL_Request].FileId, 

967 Output=SMB2_IOCTL_Network_Interface_Info( 

968 interfaces=[ 

969 NETWORK_INTERFACE_INFO( 

970 SockAddr_Storage=SOCKADDR_STORAGE( 

971 Family=0x0002, 

972 IPv4Adddress=x, 

973 ) 

974 ) 

975 for x in self.LOCAL_IPS 

976 ] 

977 ), 

978 ) 

979 ) 

980 elif pkt.CtlCode == 0x00060194: 

981 # FSCTL_DFS_GET_REFERRALS 

982 if ( 

983 self.DOMAIN_REFERRALS 

984 and not pkt[SMB2_IOCTL_Request].Input.RequestFileName 

985 ): 

986 # Requesting domain referrals 

987 self.send( 

988 self.smb_header.copy() 

989 / SMB2_IOCTL_Response( 

990 CtlCode=0x00060194, 

991 FileId=pkt[SMB2_IOCTL_Request].FileId, 

992 Output=SMB2_IOCTL_RESP_GET_DFS_Referral( 

993 ReferralEntries=[ 

994 DFS_REFERRAL_V3( 

995 ReferralEntryFlags="NameListReferral", 

996 TimeToLive=600, 

997 ) 

998 for _ in self.DOMAIN_REFERRALS 

999 ], 

1000 ReferralBuffer=[ 

1001 DFS_REFERRAL_ENTRY1(SpecialName=name) 

1002 for name in self.DOMAIN_REFERRALS 

1003 ], 

1004 ), 

1005 ) 

1006 ) 

1007 return 

1008 resp = self.smb_header.copy() / SMB2_Error_Response() 

1009 resp.Command = "SMB2_IOCTL" 

1010 resp.Status = "STATUS_FS_DRIVER_REQUIRED" 

1011 self.send(resp) 

1012 else: 

1013 # Among other things, FSCTL_VALIDATE_NEGOTIATE_INFO 

1014 self._ioctl_error(Status="STATUS_NOT_SUPPORTED") 

1015 

1016 @ATMT.receive_condition(SERVING) 

1017 def receive_create_file(self, pkt): 

1018 if SMB2_Create_Request in pkt: 

1019 raise self.SERVING().action_parameters(pkt) 

1020 

1021 PIPES_TABLE = { 

1022 "srvsvc": SMB2_FILEID(Persistent=0x4000000012, Volatile=0x4000000001), 

1023 "wkssvc": SMB2_FILEID(Persistent=0x4000000013, Volatile=0x4000000002), 

1024 "NETLOGON": SMB2_FILEID(Persistent=0x4000000014, Volatile=0x4000000003), 

1025 } 

1026 

1027 # special handle in case of compounded requests ([MS-SMB2] 3.2.4.1.4) 

1028 # that points to the chained opened file handle 

1029 LAST_HANDLE = SMB2_FILEID( 

1030 Persistent=0xFFFFFFFFFFFFFFFF, Volatile=0xFFFFFFFFFFFFFFFF 

1031 ) 

1032 

1033 def current_smb_time(self): 

1034 return ( 

1035 FileNetworkOpenInformation().get_field("CreationTime").i2m(None, None) 

1036 - 864000000000 # one day ago 

1037 ) 

1038 

1039 def make_file_id(self, fname): 

1040 """ 

1041 Generate deterministic FileId based on the fname 

1042 """ 

1043 hash = hashlib.md5((fname or "").encode()).digest() 

1044 return 0x4000000000 | struct.unpack("<I", hash[:4])[0] 

1045 

1046 def lookup_file(self, fname, durable_handle=None, create=False, createOptions=None): 

1047 """ 

1048 Lookup the file and build it's SMB2_FILEID 

1049 """ 

1050 root = self.root_path() 

1051 if isinstance(fname, pathlib.Path): 

1052 path = fname 

1053 fname = path.name 

1054 else: 

1055 path = root / (fname or "").replace("\\", "/") 

1056 path = path.resolve() 

1057 # Word of caution: this check ONLY works because root and path have been 

1058 # resolve(). Be careful 

1059 # Note: symbolic links are currently unsupported. 

1060 if root not in path.parents and path != root: 

1061 raise FileNotFoundError 

1062 if WINDOWS and path.is_reserved(): 

1063 raise FileNotFoundError 

1064 if not path.exists(): 

1065 if create and createOptions: 

1066 if createOptions.FILE_DIRECTORY_FILE: 

1067 # Folder creation 

1068 path.mkdir() 

1069 self.vprint("Created folder:" + fname) 

1070 else: 

1071 # File creation 

1072 path.touch() 

1073 self.vprint("Created file:" + fname) 

1074 else: 

1075 raise FileNotFoundError 

1076 if durable_handle is None: 

1077 handle = SMB2_FILEID( 

1078 Persistent=self.make_file_id(fname) + self.smb_header.MID, 

1079 ) 

1080 else: 

1081 # We were given a durable handle. Use it 

1082 handle = durable_handle 

1083 attrs = { 

1084 "CreationTime": self.base_time_t, 

1085 "LastAccessTime": self.base_time_t, 

1086 "LastWriteTime": self.base_time_t, 

1087 "ChangeTime": self.base_time_t, 

1088 "EndOfFile": 0, 

1089 "AllocationSize": 0, 

1090 } 

1091 path_stat = path.stat() 

1092 attrs["EndOfFile"] = attrs["AllocationSize"] = path_stat.st_size 

1093 if fname is None: 

1094 # special case 

1095 attrs["FileAttributes"] = "+".join( 

1096 [ 

1097 "FILE_ATTRIBUTE_HIDDEN", 

1098 "FILE_ATTRIBUTE_SYSTEM", 

1099 "FILE_ATTRIBUTE_DIRECTORY", 

1100 ] 

1101 ) 

1102 elif path.is_dir(): 

1103 attrs["FileAttributes"] = "FILE_ATTRIBUTE_DIRECTORY" 

1104 else: 

1105 attrs["FileAttributes"] = "FILE_ATTRIBUTE_ARCHIVE" 

1106 self.current_handles[handle] = ( 

1107 path, # file path 

1108 attrs, # file attributes 

1109 ) 

1110 self.enumerate_index[handle] = 0 

1111 return handle 

1112 

1113 def set_compounded_handle(self, handle): 

1114 """ 

1115 Mark a handle as the current one being compounded. 

1116 """ 

1117 self.CompoundedHandle = handle 

1118 

1119 def get_file_id(self, pkt): 

1120 """ 

1121 Return the FileId attribute of pkt, accounting for compounded requests. 

1122 """ 

1123 fid = pkt.FileId 

1124 if fid == self.LAST_HANDLE: 

1125 return self.CompoundedHandle 

1126 return fid 

1127 

1128 def lookup_folder(self, handle, filter, offset, cls): 

1129 """ 

1130 Lookup a folder handle 

1131 """ 

1132 path = self.current_handles[handle][0] 

1133 self.vprint("Query directory: " + str(path)) 

1134 self.current_handles[handle][1]["LastAccessTime"] = self.current_smb_time() 

1135 if not path.is_dir(): 

1136 raise NotADirectoryError 

1137 return sorted( 

1138 [ 

1139 cls(FileName=x.name, **self.current_handles[self.lookup_file(x)][1]) 

1140 for x in path.glob(filter) 

1141 # Note: symbolic links are unsupported because it's hard to check 

1142 # for path traversal on them. 

1143 if not x.is_symlink() 

1144 ] 

1145 + [ 

1146 cls( 

1147 FileAttributes=("FILE_ATTRIBUTE_DIRECTORY"), 

1148 FileName=".", 

1149 ) 

1150 ] 

1151 + ( 

1152 [ 

1153 cls( 

1154 FileAttributes=("FILE_ATTRIBUTE_DIRECTORY"), 

1155 FileName="..", 

1156 ) 

1157 ] 

1158 if path.resolve() != self.root_path() 

1159 else [] 

1160 ), 

1161 key=lambda x: x.FileName, 

1162 )[offset:] 

1163 

1164 @ATMT.action(receive_create_file) 

1165 def send_create_file_response(self, pkt): 

1166 """ 

1167 Handle CreateFile request 

1168 

1169 See [MS-SMB2] 3.3.5.9 () 

1170 """ 

1171 self.update_smbheader(pkt) 

1172 if pkt[SMB2_Create_Request].NameLen: 

1173 fname = pkt[SMB2_Create_Request].Name 

1174 else: 

1175 fname = None 

1176 if fname: 

1177 self.vprint("Opened: " + fname) 

1178 if self.current_tree() == "IPC$": 

1179 # Special IPC$ case: opening a pipe 

1180 FILE_ID = self.PIPES_TABLE.get(fname, None) 

1181 if FILE_ID: 

1182 attrs = { 

1183 "CreationTime": 0, 

1184 "LastAccessTime": 0, 

1185 "LastWriteTime": 0, 

1186 "ChangeTime": 0, 

1187 "EndOfFile": 0, 

1188 "AllocationSize": 4096, 

1189 } 

1190 self.current_handles[FILE_ID] = ( 

1191 fname, 

1192 attrs, 

1193 ) 

1194 self.send( 

1195 self.smb_header.copy() 

1196 / SMB2_Create_Response( 

1197 OplockLevel=pkt.RequestedOplockLevel, 

1198 FileId=FILE_ID, 

1199 **attrs, 

1200 ) 

1201 ) 

1202 else: 

1203 # NOT_FOUND 

1204 resp = self.smb_header.copy() / SMB2_Error_Response() 

1205 resp.Command = "SMB2_CREATE" 

1206 resp.Status = "STATUS_OBJECT_NAME_NOT_FOUND" 

1207 self.send(resp) 

1208 return 

1209 else: 

1210 # Check if there is a Durable Handle Reconnect Request 

1211 durable_handle = None 

1212 if pkt[SMB2_Create_Request].CreateContextsLen: 

1213 try: 

1214 durable_handle = next( 

1215 x.Data.FileId 

1216 for x in pkt[SMB2_Create_Request].CreateContexts 

1217 if x.Name == b"DH2C" 

1218 ) 

1219 except StopIteration: 

1220 pass 

1221 # Lookup file handle 

1222 try: 

1223 handle = self.lookup_file(fname, durable_handle=durable_handle) 

1224 except FileNotFoundError: 

1225 # NOT_FOUND 

1226 if pkt[SMB2_Create_Request].CreateDisposition in [ 

1227 0x00000002, # FILE_CREATE 

1228 0x00000005, # FILE_OVERWRITE_IF 

1229 ]: 

1230 if self.readonly: 

1231 resp = self.smb_header.copy() / SMB2_Error_Response() 

1232 resp.Command = "SMB2_CREATE" 

1233 resp.Status = "STATUS_ACCESS_DENIED" 

1234 self.send(resp) 

1235 return 

1236 else: 

1237 # Create file 

1238 handle = self.lookup_file( 

1239 fname, 

1240 durable_handle=durable_handle, 

1241 create=True, 

1242 createOptions=pkt[SMB2_Create_Request].CreateOptions, 

1243 ) 

1244 else: 

1245 resp = self.smb_header.copy() / SMB2_Error_Response() 

1246 resp.Command = "SMB2_CREATE" 

1247 resp.Status = "STATUS_OBJECT_NAME_NOT_FOUND" 

1248 self.send(resp) 

1249 return 

1250 # Store compounded handle 

1251 self.set_compounded_handle(handle) 

1252 # Build response 

1253 attrs = self.current_handles[handle][1] 

1254 resp = self.smb_header.copy() / SMB2_Create_Response( 

1255 OplockLevel=pkt.RequestedOplockLevel, 

1256 FileId=handle, 

1257 **attrs, 

1258 ) 

1259 # Handle the various chain elements 

1260 if pkt[SMB2_Create_Request].CreateContextsLen: 

1261 CreateContexts = [] 

1262 # Note: failing to provide context elements when the client asks for 

1263 # them will make the windows implementation fall into a weird 

1264 # "the-server-is-dumb" mode. So provide them 'quoi qu'il en coûte'. 

1265 for elt in pkt[SMB2_Create_Request].CreateContexts: 

1266 if elt.Name == b"QFid": 

1267 # [MS-SMB2] sect 3.3.5.9.9 

1268 CreateContexts.append( 

1269 SMB2_Create_Context( 

1270 Name=b"QFid", 

1271 Data=SMB2_CREATE_QUERY_ON_DISK_ID( 

1272 DiskFileId=self.make_file_id(fname), 

1273 VolumeId=0xBA39CD11, 

1274 ), 

1275 ) 

1276 ) 

1277 elif elt.Name == b"MxAc": 

1278 # [MS-SMB2] sect 3.3.5.9.5 

1279 CreateContexts.append( 

1280 SMB2_Create_Context( 

1281 Name=b"MxAc", 

1282 Data=SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE( 

1283 QueryStatus=0, 

1284 MaximalAccess=self.FILE_MAXIMAL_ACCESS, 

1285 ), 

1286 ) 

1287 ) 

1288 elif elt.Name == b"DH2Q": 

1289 # [MS-SMB2] sect 3.3.5.9.10 

1290 if "FILE_ATTRIBUTE_DIRECTORY" in attrs["FileAttributes"]: 

1291 continue 

1292 CreateContexts.append( 

1293 SMB2_Create_Context( 

1294 Name=b"DH2Q", 

1295 Data=SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2( 

1296 Timeout=180000 

1297 ), 

1298 ) 

1299 ) 

1300 elif elt.Name == b"RqLs": 

1301 # [MS-SMB2] sect 3.3.5.9.11 

1302 # TODO: hmm, we are probably supposed to do something here 

1303 CreateContexts.append( 

1304 SMB2_Create_Context( 

1305 Name=b"RqLs", 

1306 Data=elt.Data, 

1307 ) 

1308 ) 

1309 resp.CreateContexts = CreateContexts 

1310 self.send(resp) 

1311 

1312 @ATMT.receive_condition(SERVING) 

1313 def receive_change_notify_info(self, pkt): 

1314 if SMB2_Change_Notify_Request in pkt: 

1315 raise self.SERVING().action_parameters(pkt) 

1316 

1317 @ATMT.action(receive_change_notify_info) 

1318 def send_change_notify_info_response(self, pkt): 

1319 # [MS-SMB2] sect 3.3.5.19 

1320 # "If the underlying object store does not support change notifications, the 

1321 # server MUST fail this request with STATUS_NOT_SUPPORTED." 

1322 self.update_smbheader(pkt) 

1323 resp = self.smb_header.copy() / SMB2_Error_Response() 

1324 resp.Command = "SMB2_CHANGE_NOTIFY" 

1325 # ScapyFS doesn't support notifications 

1326 resp.Status = "STATUS_NOT_SUPPORTED" 

1327 self.send(resp) 

1328 

1329 @ATMT.receive_condition(SERVING) 

1330 def receive_query_directory_info(self, pkt): 

1331 if SMB2_Query_Directory_Request in pkt: 

1332 raise self.SERVING().action_parameters(pkt) 

1333 

1334 @ATMT.action(receive_query_directory_info) 

1335 def send_query_directory_response(self, pkt): 

1336 self.update_smbheader(pkt) 

1337 if not pkt.FileNameLen: 

1338 # this is broken. 

1339 return 

1340 query = pkt.FileName 

1341 fid = self.get_file_id(pkt) 

1342 # Check for handled FileInformationClass 

1343 # 0x02: FileFullDirectoryInformation 

1344 # 0x03: FileBothDirectoryInformation 

1345 # 0x25: FileIdBothDirectoryInformation 

1346 if pkt.FileInformationClass not in [0x02, 0x03, 0x25]: 

1347 # Unknown FileInformationClass 

1348 resp = self.smb_header.copy() / SMB2_Error_Response() 

1349 resp.Command = "SMB2_QUERY_DIRECTORY" 

1350 resp.Status = "STATUS_INVALID_INFO_CLASS" 

1351 self.send(resp) 

1352 return 

1353 # Handle SMB2_RESTART_SCANS 

1354 if pkt[SMB2_Query_Directory_Request].Flags.SMB2_RESTART_SCANS: 

1355 self.enumerate_index[fid] = 0 

1356 # Lookup the files 

1357 try: 

1358 files = self.lookup_folder( 

1359 fid, 

1360 query, 

1361 self.enumerate_index[fid], 

1362 { 

1363 0x02: FILE_FULL_DIR_INFORMATION, 

1364 0x03: FILE_BOTH_DIR_INFORMATION, 

1365 0x25: FILE_ID_BOTH_DIR_INFORMATION, 

1366 }[pkt.FileInformationClass], 

1367 ) 

1368 except NotADirectoryError: 

1369 resp = self.smb_header.copy() / SMB2_Error_Response() 

1370 resp.Command = "SMB2_QUERY_DIRECTORY" 

1371 resp.Status = "STATUS_INVALID_PARAMETER" 

1372 self.send(resp) 

1373 return 

1374 if not files: 

1375 # No more files ! 

1376 self.enumerate_index[fid] = 0 

1377 resp = self.smb_header.copy() / SMB2_Error_Response() 

1378 resp.Command = "SMB2_QUERY_DIRECTORY" 

1379 resp.Status = "STATUS_NO_MORE_FILES" 

1380 self.send(resp) 

1381 return 

1382 # Handle SMB2_RETURN_SINGLE_ENTRY 

1383 if pkt[SMB2_Query_Directory_Request].Flags.SMB2_RETURN_SINGLE_ENTRY: 

1384 files = files[:1] 

1385 # Increment index 

1386 self.enumerate_index[fid] += len(files) 

1387 # Build response based on the FileInformationClass 

1388 fileinfo = FileIdBothDirectoryInformation( 

1389 files=files, 

1390 ) 

1391 self.send( 

1392 self.smb_header.copy() 

1393 / SMB2_Query_Directory_Response(Buffer=[("Output", fileinfo)]) 

1394 ) 

1395 

1396 @ATMT.receive_condition(SERVING) 

1397 def receive_query_info(self, pkt): 

1398 if SMB2_Query_Info_Request in pkt: 

1399 raise self.SERVING().action_parameters(pkt) 

1400 

1401 @ATMT.action(receive_query_info) 

1402 def send_query_info_response(self, pkt): 

1403 self.update_smbheader(pkt) 

1404 # [MS-FSCC] + [MS-SMB2] sect 2.2.37 / 3.3.5.20.1 

1405 fid = self.get_file_id(pkt) 

1406 if pkt.InfoType == 0x01: # SMB2_0_INFO_FILE 

1407 if pkt.FileInfoClass == 0x05: # FileStandardInformation 

1408 attrs = self.current_handles[fid][1] 

1409 fileinfo = FileStandardInformation( 

1410 EndOfFile=attrs["EndOfFile"], 

1411 AllocationSize=attrs["AllocationSize"], 

1412 ) 

1413 elif pkt.FileInfoClass == 0x06: # FileInternalInformation 

1414 pth = self.current_handles[fid][0] 

1415 fileinfo = FileInternalInformation( 

1416 IndexNumber=hash(pth) & 0xFFFFFFFFFFFFFFFF, 

1417 ) 

1418 elif pkt.FileInfoClass == 0x07: # FileEaInformation 

1419 fileinfo = FileEaInformation() 

1420 elif pkt.FileInfoClass == 0x12: # FileAllInformation 

1421 attrs = self.current_handles[fid][1] 

1422 fileinfo = FileAllInformation( 

1423 BasicInformation=FileBasicInformation( 

1424 CreationTime=attrs["CreationTime"], 

1425 LastAccessTime=attrs["LastAccessTime"], 

1426 LastWriteTime=attrs["LastWriteTime"], 

1427 ChangeTime=attrs["ChangeTime"], 

1428 FileAttributes=attrs["FileAttributes"], 

1429 ), 

1430 StandardInformation=FileStandardInformation( 

1431 EndOfFile=attrs["EndOfFile"], 

1432 AllocationSize=attrs["AllocationSize"], 

1433 ), 

1434 ) 

1435 elif pkt.FileInfoClass == 0x15: # FileAlternateNameInformation 

1436 pth = self.current_handles[fid][0] 

1437 fileinfo = FileAlternateNameInformation( 

1438 FileName=pth.name, 

1439 ) 

1440 elif pkt.FileInfoClass == 0x16: # FileStreamInformation 

1441 attrs = self.current_handles[fid][1] 

1442 fileinfo = FileStreamInformation( 

1443 StreamSize=attrs["EndOfFile"], 

1444 StreamAllocationSize=attrs["AllocationSize"], 

1445 ) 

1446 elif pkt.FileInfoClass == 0x22: # FileNetworkOpenInformation 

1447 attrs = self.current_handles[fid][1] 

1448 fileinfo = FileNetworkOpenInformation( 

1449 **attrs, 

1450 ) 

1451 elif pkt.FileInfoClass == 0x30: # FileNormalizedNameInformation 

1452 pth = self.current_handles[fid][0] 

1453 fileinfo = FILE_NAME_INFORMATION( 

1454 FileName=pth.name, 

1455 ) 

1456 else: 

1457 log_runtime.warning( 

1458 "Unimplemented: %s" 

1459 % pkt[SMB2_Query_Info_Request].sprintf("%InfoType% %FileInfoClass%") 

1460 ) 

1461 return 

1462 elif pkt.InfoType == 0x02: # SMB2_0_INFO_FILESYSTEM 

1463 # [MS-FSCC] sect 2.5 

1464 if pkt.FileInfoClass == 0x01: # FileFsVolumeInformation 

1465 fileinfo = FileFsVolumeInformation() 

1466 elif pkt.FileInfoClass == 0x03: # FileFsSizeInformation 

1467 fileinfo = FileFsSizeInformation() 

1468 elif pkt.FileInfoClass == 0x05: # FileFsAttributeInformation 

1469 fileinfo = FileFsAttributeInformation( 

1470 FileSystemAttributes=0x88000F, 

1471 ) 

1472 elif pkt.FileInfoClass == 0x07: # FileEaInformation 

1473 fileinfo = FileEaInformation() 

1474 else: 

1475 log_runtime.warning( 

1476 "Unimplemented: %s" 

1477 % pkt[SMB2_Query_Info_Request].sprintf("%InfoType% %FileInfoClass%") 

1478 ) 

1479 return 

1480 elif pkt.InfoType == 0x03: # SMB2_0_INFO_SECURITY 

1481 # [MS-FSCC] 2.4.6 

1482 fileinfo = SECURITY_DESCRIPTOR() 

1483 # TODO: fill it 

1484 if pkt.AdditionalInformation.OWNER_SECURITY_INFORMATION: 

1485 pass 

1486 if pkt.AdditionalInformation.GROUP_SECURITY_INFORMATION: 

1487 pass 

1488 if pkt.AdditionalInformation.DACL_SECURITY_INFORMATION: 

1489 pass 

1490 if pkt.AdditionalInformation.SACL_SECURITY_INFORMATION: 

1491 pass 

1492 # Observed: 

1493 if ( 

1494 pkt.AdditionalInformation.OWNER_SECURITY_INFORMATION 

1495 or pkt.AdditionalInformation.SACL_SECURITY_INFORMATION 

1496 or pkt.AdditionalInformation.GROUP_SECURITY_INFORMATION 

1497 or pkt.AdditionalInformation.DACL_SECURITY_INFORMATION 

1498 ): 

1499 pkt = self.smb_header.copy() / SMB2_Error_Response(ErrorData=b"\xff") 

1500 pkt.Status = "STATUS_ACCESS_DENIED" 

1501 pkt.Command = "SMB2_QUERY_INFO" 

1502 self.send(pkt) 

1503 return 

1504 if pkt.AdditionalInformation.ATTRIBUTE_SECURITY_INFORMATION: 

1505 fileinfo.Control = 0x8800 

1506 self.send( 

1507 self.smb_header.copy() 

1508 / SMB2_Query_Info_Response(Buffer=[("Output", fileinfo)]) 

1509 ) 

1510 

1511 @ATMT.receive_condition(SERVING) 

1512 def receive_set_info_request(self, pkt): 

1513 if SMB2_Set_Info_Request in pkt: 

1514 raise self.SERVING().action_parameters(pkt) 

1515 

1516 @ATMT.action(receive_set_info_request) 

1517 def send_set_info_response(self, pkt): 

1518 self.update_smbheader(pkt) 

1519 self.send(self.smb_header.copy() / SMB2_Set_Info_Response()) 

1520 

1521 @ATMT.receive_condition(SERVING) 

1522 def receive_write_request(self, pkt): 

1523 if SMB2_Write_Request in pkt: 

1524 raise self.SERVING().action_parameters(pkt) 

1525 

1526 @ATMT.action(receive_write_request) 

1527 def send_write_response(self, pkt): 

1528 self.update_smbheader(pkt) 

1529 resp = SMB2_Write_Response(Count=len(pkt.Data)) 

1530 fid = self.get_file_id(pkt) 

1531 if self.current_tree() == "IPC$": 

1532 if fid in self.PIPES_TABLE.values(): 

1533 # A pipe 

1534 self.rpc_server.recv(pkt.Data) 

1535 else: 

1536 if self.readonly: 

1537 # Read only ! 

1538 resp = SMB2_Error_Response() 

1539 resp.Command = "SMB2_WRITE" 

1540 resp.Status = "ERROR_FILE_READ_ONLY" 

1541 else: 

1542 # Write file 

1543 pth, _ = self.current_handles[fid] 

1544 length = pkt[SMB2_Write_Request].DataLen 

1545 off = pkt[SMB2_Write_Request].Offset 

1546 self.vprint("Writing %s bytes at %s" % (length, off)) 

1547 with open(pth, "r+b") as fd: 

1548 fd.seek(off) 

1549 resp.Count = fd.write(pkt[SMB2_Write_Request].Data) 

1550 self.send(self.smb_header.copy() / resp) 

1551 

1552 @ATMT.receive_condition(SERVING) 

1553 def receive_read_request(self, pkt): 

1554 if SMB2_Read_Request in pkt: 

1555 raise self.SERVING().action_parameters(pkt) 

1556 

1557 @ATMT.action(receive_read_request) 

1558 def send_read_response(self, pkt): 

1559 self.update_smbheader(pkt) 

1560 resp = SMB2_Read_Response() 

1561 fid = self.get_file_id(pkt) 

1562 if self.current_tree() == "IPC$": 

1563 # Read output from DCE/RPC server 

1564 r = self.rpc_server.get_response() 

1565 resp.Data = bytes(r) 

1566 else: 

1567 # Read file and send content 

1568 pth, _ = self.current_handles[fid] 

1569 length = pkt[SMB2_Read_Request].Length 

1570 off = pkt[SMB2_Read_Request].Offset 

1571 self.vprint("Reading %s bytes at %s" % (length, off)) 

1572 with open(pth, "rb") as fd: 

1573 fd.seek(off) 

1574 resp.Data = fd.read(length) 

1575 self.send(self.smb_header.copy() / resp) 

1576 

1577 @ATMT.receive_condition(SERVING) 

1578 def receive_close_request(self, pkt): 

1579 if SMB2_Close_Request in pkt: 

1580 raise self.SERVING().action_parameters(pkt) 

1581 

1582 @ATMT.action(receive_close_request) 

1583 def send_close_response(self, pkt): 

1584 self.update_smbheader(pkt) 

1585 if self.current_tree() != "IPC$": 

1586 fid = self.get_file_id(pkt) 

1587 pth, attrs = self.current_handles[fid] 

1588 if pth: 

1589 self.vprint("Closed: " + str(pth)) 

1590 del self.current_handles[fid] 

1591 del self.enumerate_index[fid] 

1592 self.send( 

1593 self.smb_header.copy() 

1594 / SMB2_Close_Response( 

1595 Flags=pkt[SMB2_Close_Request].Flags, 

1596 **attrs, 

1597 ) 

1598 ) 

1599 else: 

1600 self.send(self.smb_header.copy() / SMB2_Close_Response()) 

1601 

1602 @ATMT.receive_condition(SERVING) 

1603 def receive_tree_disconnect_request(self, pkt): 

1604 if SMB2_Tree_Disconnect_Request in pkt: 

1605 raise self.SERVING().action_parameters(pkt) 

1606 

1607 @ATMT.action(receive_tree_disconnect_request) 

1608 def send_tree_disconnect_response(self, pkt): 

1609 self.update_smbheader(pkt) 

1610 try: 

1611 del self.current_trees[self.smb_header.TID] # clear tree 

1612 resp = self.smb_header.copy() / SMB2_Tree_Disconnect_Response() 

1613 except KeyError: 

1614 resp = self.smb_header.copy() / SMB2_Error_Response() 

1615 resp.Command = "SMB2_TREE_DISCONNECT" 

1616 resp.Status = "STATUS_NETWORK_NAME_DELETED" 

1617 self.send(resp) 

1618 

1619 @ATMT.receive_condition(SERVING) 

1620 def receive_cancel_request(self, pkt): 

1621 if SMB2_Cancel_Request in pkt: 

1622 raise self.SERVING().action_parameters(pkt) 

1623 

1624 @ATMT.action(receive_cancel_request) 

1625 def send_notify_cancel_response(self, pkt): 

1626 self.update_smbheader(pkt) 

1627 resp = self.smb_header.copy() / SMB2_Change_Notify_Response() 

1628 resp.Status = "STATUS_CANCELLED" 

1629 self.send(resp) 

1630 

1631 @ATMT.receive_condition(SERVING) 

1632 def receive_echo_request(self, pkt): 

1633 if SMB2_Echo_Request in pkt: 

1634 raise self.SERVING().action_parameters(pkt) 

1635 

1636 @ATMT.action(receive_echo_request) 

1637 def send_echo_reply(self, pkt): 

1638 self.update_smbheader(pkt) 

1639 self.send(self.smb_header.copy() / SMB2_Echo_Response()) 

1640 

1641 

1642# DCE/RPC server for SMB 

1643 

1644 

1645class SMB_DCERPC_Server(DCERPC_Server): 

1646 """ 

1647 DCE/RPC server than handles the minimum RPCs for SMB to work: 

1648 """ 

1649 

1650 def __init__(self, *args, **kwargs): 

1651 self.shares = kwargs.pop("shares") 

1652 super(SMB_DCERPC_Server, self).__init__(*args, **kwargs) 

1653 

1654 @DCERPC_Server.answer(NetrShareEnum_Request) 

1655 def netr_share_enum(self, req): 

1656 """ 

1657 NetrShareEnum [MS-SRVS] 

1658 "retrieves information about each shared resource on a server." 

1659 """ 

1660 nbEntries = len(self.shares) 

1661 return NetrShareEnum_Response( 

1662 InfoStruct=LPSHARE_ENUM_STRUCT( 

1663 Level=1, 

1664 ShareInfo=NDRUnion( 

1665 tag=1, 

1666 value=SHARE_INFO_1_CONTAINER( 

1667 Buffer=[ 

1668 # Add shares 

1669 LPSHARE_INFO_1( 

1670 shi1_netname=x.name, 

1671 shi1_type=x.type, 

1672 shi1_remark=x.remark, 

1673 ) 

1674 for x in self.shares 

1675 ], 

1676 EntriesRead=nbEntries, 

1677 ), 

1678 ), 

1679 ), 

1680 TotalEntries=nbEntries, 

1681 ndr64=self.ndr64, 

1682 ) 

1683 

1684 @DCERPC_Server.answer(NetrWkstaGetInfo_Request) 

1685 def netr_wksta_getinfo(self, req): 

1686 """ 

1687 NetrWkstaGetInfo [MS-SRVS] 

1688 "returns information about the configuration of a workstation." 

1689 """ 

1690 return NetrWkstaGetInfo_Response( 

1691 WkstaInfo=NDRUnion( 

1692 tag=100, 

1693 value=LPWKSTA_INFO_100( 

1694 wki100_platform_id=500, # NT 

1695 wki100_ver_major=5, 

1696 ), 

1697 ), 

1698 ndr64=self.ndr64, 

1699 ) 

1700 

1701 @DCERPC_Server.answer(NetrServerGetInfo_Request) 

1702 def netr_server_getinfo(self, req): 

1703 """ 

1704 NetrServerGetInfo [MS-WKST] 

1705 "retrieves current configuration information for CIFS and 

1706 SMB Version 1.0 servers." 

1707 """ 

1708 return NetrServerGetInfo_Response( 

1709 ServerInfo=NDRUnion( 

1710 tag=101, 

1711 value=LPSERVER_INFO_101( 

1712 sv101_platform_id=500, # NT 

1713 sv101_name=req.ServerName.value.value[0].value, 

1714 sv101_version_major=6, 

1715 sv101_version_minor=1, 

1716 sv101_type=1, # Workstation 

1717 ), 

1718 ), 

1719 ndr64=self.ndr64, 

1720 ) 

1721 

1722 @DCERPC_Server.answer(NetrShareGetInfo_Request) 

1723 def netr_share_getinfo(self, req): 

1724 """ 

1725 NetrShareGetInfo [MS-SRVS] 

1726 "retrieves information about a particular shared resource on a server." 

1727 """ 

1728 return NetrShareGetInfo_Response( 

1729 ShareInfo=NDRUnion( 

1730 tag=1, 

1731 value=LPSHARE_INFO_1( 

1732 shi1_netname=req.NetName.value[0].value, 

1733 shi1_type=0, 

1734 shi1_remark=b"", 

1735 ), 

1736 ), 

1737 ndr64=self.ndr64, 

1738 ) 

1739 

1740 

1741# Util 

1742 

1743 

1744class smbserver: 

1745 r""" 

1746 Spawns a simple smbserver 

1747 

1748 smbserver parameters: 

1749 

1750 :param shares: the list of shares to announce. Note that IPC$ is appended. 

1751 By default, a 'Scapy' share on './' 

1752 :param port: (optional) the port to bind on, default 445 

1753 :param iface: (optional) the interface to bind on, default conf.iface 

1754 :param readonly: (optional) whether the server is read-only or not. default True 

1755 :param ssp: (optional) the SSP to use. See the examples below. 

1756 Default NTLM with guest 

1757 

1758 Many more SMB-specific parameters are available in help(SMB_Server) 

1759 """ 

1760 

1761 def __init__( 

1762 self, 

1763 shares=None, 

1764 iface: str = None, 

1765 port: int = 445, 

1766 verb: int = 2, 

1767 readonly: bool = True, 

1768 # SMB arguments 

1769 ssp=None, 

1770 **kwargs, 

1771 ): 

1772 # Default Share 

1773 if shares is None: 

1774 shares = [ 

1775 SMBShare( 

1776 name="Scapy", path=".", remark="Scapy's SMB server default share" 

1777 ) 

1778 ] 

1779 # Verb 

1780 if verb >= 2: 

1781 log_runtime.info("-- Scapy %s SMB Server --" % conf.version) 

1782 log_runtime.info( 

1783 "SSP: %s. Read-Only: %s. Serving %s shares:" 

1784 % ( 

1785 conf.color_theme.yellow(ssp or "NTLM (guest)"), 

1786 ( 

1787 conf.color_theme.yellow("YES") 

1788 if readonly 

1789 else conf.color_theme.format("NO", "bg_red+white") 

1790 ), 

1791 conf.color_theme.red(len(shares)), 

1792 ) 

1793 ) 

1794 for share in shares: 

1795 log_runtime.info(" * %s" % share) 

1796 # Start SMB Server 

1797 self.srv = SMB_Server.spawn( 

1798 # TCP server 

1799 port=port, 

1800 iface=iface or conf.loopback_name, 

1801 verb=verb, 

1802 # SMB server 

1803 ssp=ssp, 

1804 shares=shares, 

1805 readonly=readonly, 

1806 # SMB arguments 

1807 **kwargs, 

1808 ) 

1809 

1810 def close(self): 

1811 """ 

1812 Close the smbserver if started in background mode (bg=True) 

1813 """ 

1814 if self.srv: 

1815 try: 

1816 self.srv.shutdown(socket.SHUT_RDWR) 

1817 except OSError: 

1818 pass 

1819 self.srv.close() 

1820 

1821 

1822if __name__ == "__main__": 

1823 from scapy.utils import AutoArgparse 

1824 

1825 AutoArgparse(smbserver)