Coverage for /root/GitHubProjects/impacket/impacket/smbserver.py: 6%

# Coverage for /root/GitHubProjects/impacket/impacket/smbserver.py : 6%

## 3124 statements

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1# Impacket - Collection of Python classes for working with network protocols.

2#

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

4#

5# This software is provided under a slightly modified version

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

7# for more information.

8#

9# Author:

10# Alberto Solino (@agsolino)

11#

12# TODO:

13# [-] Functions should return NT error codes

14# [-] Handling errors in all situations, right now it's just raising exceptions.

15# [*] Standard authentication support

16# [ ] Organize the connectionData stuff

17# [*] Add capability to send a bad user ID if the user is not authenticated,

18# right now you can ask for any command without actually being authenticated

19# [ ] PATH TRAVERSALS EVERYWHERE.. BE WARNED!

20# [ ] Check error situation (now many places assume the right data is coming)

21# [ ] Implement IPC to the main process so the connectionData is on a single place

22# [ ] Hence.. implement locking

23# estamos en la B

24

25 import calendar

26 import socket

27 import time

28 import datetime

29 import struct

30 import threading

31 import logging

32 import logging.config

33 import ntpath

34 import os

35 import fnmatch

36 import errno

37 import sys

38 import random

39 import shutil

40 import string

41 import hashlib

42 import hmac

43

44 from binascii import unhexlify, hexlify, a2b_hex

45 from six import PY2, b, text_type

46 from six.moves import configparser, socketserver

47

48# For signing

49 from impacket import smb, nmb, ntlm, uuid

50 from impacket import smb3structs as smb2

51 from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, MechTypes, SPNEGO_NegTokenResp, ASN1_AID, \

52 ASN1_SUPPORTED_MECH

53 from impacket.nt_errors import STATUS_NO_MORE_FILES, STATUS_NETWORK_NAME_DELETED, STATUS_INVALID_PARAMETER, \

54 STATUS_FILE_CLOSED, STATUS_MORE_PROCESSING_REQUIRED, STATUS_OBJECT_PATH_NOT_FOUND, STATUS_DIRECTORY_NOT_EMPTY, \

55 STATUS_FILE_IS_A_DIRECTORY, STATUS_NOT_IMPLEMENTED, STATUS_INVALID_HANDLE, STATUS_OBJECT_NAME_COLLISION, \

56 STATUS_NO_SUCH_FILE, STATUS_CANCELLED, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_SUCCESS, STATUS_ACCESS_DENIED, \

57 STATUS_NOT_SUPPORTED, STATUS_INVALID_DEVICE_REQUEST, STATUS_FS_DRIVER_REQUIRED, STATUS_INVALID_INFO_CLASS, \

58 STATUS_LOGON_FAILURE, STATUS_OBJECT_PATH_SYNTAX_BAD

59

60# Setting LOG to current's module name

61 LOG = logging.getLogger(__name__)

62

63# These ones not defined in nt_errors

64 STATUS_SMB_BAD_UID = 0x005B0002

65 STATUS_SMB_BAD_TID = 0x00050002

66

67

68# Utility functions

69# and general functions.

70# There are some common functions that can be accessed from more than one SMB

71# command (or either TRANSACTION). That's why I'm putting them here

72# TODO: Return NT ERROR Codes

73

74 def computeNTLMv2(identity, lmhash, nthash, serverChallenge, authenticateMessage, ntlmChallenge, type1):

75 # Let's calculate the NTLMv2 Response

76

77 responseKeyNT = ntlm.NTOWFv2(identity, '', authenticateMessage['domain_name'].decode('utf-16le'), nthash)

78 responseKeyLM = ntlm.LMOWFv2(identity, '', authenticateMessage['domain_name'].decode('utf-16le'), lmhash)

79

80 ntProofStr = authenticateMessage['ntlm'][:16]

81 temp = authenticateMessage['ntlm'][16:]

82 ntProofStr2 = ntlm.hmac_md5(responseKeyNT, serverChallenge + temp)

83 lmChallengeResponse = authenticateMessage['lanman']

84 sessionBaseKey = ntlm.hmac_md5(responseKeyNT, ntProofStr)

85

86 responseFlags = type1['flags']

87

88 # Let's check the return flags

89 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) == 0:

90 # No extended session security, taking it out

91 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY

92 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_128) == 0:

93 # No support for 128 key len, taking it out

94 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_128

95 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH) == 0:

96 # No key exchange supported, taking it out

97 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH

98 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SEAL) == 0:

99 # No sign available, taking it out

100 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SEAL

101 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SIGN) == 0:

102 # No sign available, taking it out

103 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SIGN

104 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN) == 0:

105 # No sign available, taking it out

106 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN

107

108 keyExchangeKey = ntlm.KXKEY(ntlmChallenge['flags'], sessionBaseKey, lmChallengeResponse,

109 ntlmChallenge['challenge'], '',

110 lmhash, nthash, True)

111

112 # If we set up key exchange, let's fill the right variables

113 if ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:

114 exportedSessionKey = authenticateMessage['session_key']

115 exportedSessionKey = ntlm.generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey)

116 else:

117 encryptedRandomSessionKey = None

118 # [MS-NLMP] page 46

119 exportedSessionKey = keyExchangeKey

120

121 # Do they match?

122 if ntProofStr == ntProofStr2:

123 # Yes!, process login

124 return STATUS_SUCCESS, exportedSessionKey

125 else:

126 return STATUS_LOGON_FAILURE, exportedSessionKey

127

128

129 def outputToJohnFormat(challenge, username, domain, lmresponse, ntresponse):

130 # We don't want to add a possible failure here, since this is an

131 # extra bonus. We try, if it fails, returns nothing

132 # ToDo: Document the parameter's types (bytes / string) and check all the places where it's called

133 ret_value = ''

134 if type(challenge) is not bytes:

135 challenge = challenge.decode('latin-1')

136

137 try:

138 if len(ntresponse) > 24:

139 # Extended Security - NTLMv2

140 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (

141 username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(challenge).decode('latin-1'),

142 hexlify(ntresponse).decode('latin-1')[:32],

143 hexlify(ntresponse).decode()[32:]), 'hash_version': 'ntlmv2'}

144 else:

145 # NTLMv1

146 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (

147 username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(lmresponse).decode('latin-1'),

148 hexlify(ntresponse).decode('latin-1'),

149 hexlify(challenge).decode()), 'hash_version': 'ntlm'}

150 except:

151 # Let's try w/o decoding Unicode

152 try:

153 if len(ntresponse) > 24:

154 # Extended Security - NTLMv2

155 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (

156 username.decode('latin-1'), domain.decode('latin-1'), hexlify(challenge).decode('latin-1'),

157 hexlify(ntresponse)[:32].decode('latin-1'), hexlify(ntresponse)[32:].decode('latin-1')),

158 'hash_version': 'ntlmv2'}

159 else:

160 # NTLMv1

161 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (

162 username, domain, hexlify(lmresponse).decode('latin-1'), hexlify(ntresponse).decode('latin-1'),

163 hexlify(challenge).decode('latin-1')), 'hash_version': 'ntlm'}

164 except Exception as e:

165 import traceback

166 traceback.print_exc()

167 LOG.error("outputToJohnFormat: %s" % e)

168 pass

169

170 return ret_value

171

172

173 def writeJohnOutputToFile(hash_string, hash_version, file_name):

174 fn_data = os.path.splitext(file_name)

175 if hash_version == "ntlmv2":

176 output_filename = fn_data[0] + "_ntlmv2" + fn_data[1]

177 else:

178 output_filename = fn_data[0] + "_ntlm" + fn_data[1]

179

180 with open(output_filename, "a") as f:

181 f.write(hash_string)

182 f.write('\n')

183

184

185 def decodeSMBString(flags, text):

186 if flags & smb.SMB.FLAGS2_UNICODE:

187 return text.decode('utf-16le')

188 else:

189 return text

190

191

192 def encodeSMBString(flags, text):

193 if flags & smb.SMB.FLAGS2_UNICODE:

194 return (text).encode('utf-16le')

195 else:

196 return text.encode('ascii')

197

198

199 def getFileTime(t):

200 t *= 10000000

201 t += 116444736000000000

202 return t

203

204

205 def getUnixTime(t):

206 t -= 116444736000000000

207 t //= 10000000

208 return t

209

210

211 def getSMBDate(t):

212 # TODO: Fix this :P

213 d = datetime.date.fromtimestamp(t)

214 year = d.year - 1980

215 ret = (year << 8) + (d.month << 4) + d.day

216 return ret

217

218

219 def getSMBTime(t):

220 # TODO: Fix this :P

221 d = datetime.datetime.fromtimestamp(t)

222 return (d.hour << 8) + (d.minute << 4) + d.second

223

224

225 def getShares(connId, smbServer):

226 config = smbServer.getServerConfig()

227 sections = config.sections()

228 # Remove the global one

229 del (sections[sections.index('global')])

230 shares = {}

231 for i in sections:

232 shares[i] = dict(config.items(i))

233 return shares

234

235

236 def searchShare(connId, share, smbServer):

237 config = smbServer.getServerConfig()

238 if config.has_section(share):

239 return dict(config.items(share))

240 else:

241 return None

242

243

244 def normalize_path(file_name, path=None):

245 """Normalizes a path by replacing "\" with "/" and stripping potential

246 leading "/" chars. If a path is provided, only strip leading '/' when

247 the path is empty.

248

249 :param file_name: file name to normalize

250 :type file_name: string

251

252 :param path: path to normalize

253 :type path: string

254

255 :return normalized file name

256 :rtype string

257 """

258 file_name = os.path.normpath(file_name.replace('\\', '/'))

259 if len(file_name) > 0 and (file_name[0] == '/' or file_name[0] == '\\'):

260 if path is None or path != '':

261 # Strip leading "/"

262 file_name = file_name[1:]

263 return file_name

264

265

266 def isInFileJail(path, file_name):

267 """Validates if a provided file name path is inside a path. This function is used

268 to check for path traversals.

269

270 :param path: base path to check

271 :type path: string

272 :param file_name: file name to validate

273 :type file_name: string

274

275 :return whether the file name is inside the base path or not

276 :rtype bool

277 """

278 path_name = os.path.join(path, file_name)

279 share_real_path = os.path.realpath(path)

280 return os.path.commonprefix((os.path.realpath(path_name), share_real_path)) == share_real_path

281

282

283 def openFile(path, fileName, accessMode, fileAttributes, openMode):

284 fileName = normalize_path(fileName)

285 pathName = os.path.join(path, fileName)

286 errorCode = 0

287 mode = 0

288

289 if not isInFileJail(path, fileName):

290 LOG.error("Path not in current working directory")

291 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD

292 return 0, mode, pathName, errorCode

293

294 # Check the Open Mode

295 if openMode & 0x10:

296 # If the file does not exist, create it.

297 mode = os.O_CREAT

298 else:

299 # If file does not exist, return an error

300 if os.path.exists(pathName) is not True:

301 errorCode = STATUS_NO_SUCH_FILE

302 return 0, mode, pathName, errorCode

303

304 if os.path.isdir(pathName) and (fileAttributes & smb.ATTR_DIRECTORY) == 0:

305 # Request to open a normal file and this is actually a directory

306 errorCode = STATUS_FILE_IS_A_DIRECTORY

307 return 0, mode, pathName, errorCode

308 # Check the Access Mode

309 if accessMode & 0x7 == 1:

310 mode |= os.O_WRONLY

311 elif accessMode & 0x7 == 2:

312 mode |= os.O_RDWR

313 else:

314 mode = os.O_RDONLY

315

316 try:

317 if sys.platform == 'win32':

318 mode |= os.O_BINARY

319 fid = os.open(pathName, mode)

320 except Exception as e:

321 LOG.error("openFile: %s,%s" % (pathName, mode), e)

322 fid = 0

323 errorCode = STATUS_ACCESS_DENIED

324

325 return fid, mode, pathName, errorCode

326

327

328 def queryFsInformation(path, filename, level=None, pktFlags=smb.SMB.FLAGS2_UNICODE):

329 if pktFlags & smb.SMB.FLAGS2_UNICODE:

330 encoding = 'utf-16le'

331 else:

332 encoding = 'ascii'

333

334 fileName = normalize_path(filename)

335 pathName = os.path.join(path, fileName)

336 fileSize = os.path.getsize(pathName)

337 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)

338

339 if level is None:

340 lastWriteTime = mtime

341 attribs = 0

342 if os.path.isdir(pathName):

343 attribs |= smb.SMB_FILE_ATTRIBUTE_DIRECTORY

344 if os.path.isfile(pathName):

345 attribs |= smb.SMB_FILE_ATTRIBUTE_NORMAL

346 fileAttributes = attribs

347 return fileSize, lastWriteTime, fileAttributes

348

349 elif level == smb.SMB_QUERY_FS_ATTRIBUTE_INFO or level == smb2.SMB2_FILESYSTEM_ATTRIBUTE_INFO:

350 data = smb.SMBQueryFsAttributeInfo()

351 data['FileSystemAttributes'] = smb.FILE_CASE_SENSITIVE_SEARCH | smb.FILE_CASE_PRESERVED_NAMES

352 data['MaxFilenNameLengthInBytes'] = 255

353 data['LengthOfFileSystemName'] = len('XTFS') * 2

354 data['FileSystemName'] = 'XTFS'.encode('utf-16le')

355 return data.getData()

356 elif level == smb.SMB_INFO_VOLUME:

357 data = smb.SMBQueryFsInfoVolume(flags=pktFlags)

358 data['VolumeLabel'] = 'SHARE'.encode(encoding)

359 return data.getData()

360 elif level == smb.SMB_QUERY_FS_VOLUME_INFO or level == smb2.SMB2_FILESYSTEM_VOLUME_INFO:

361 data = smb.SMBQueryFsVolumeInfo()

362 data['VolumeLabel'] = ''

363 data['VolumeCreationTime'] = getFileTime(ctime)

364 return data.getData()

365 elif level == smb.SMB_QUERY_FS_SIZE_INFO:

366 data = smb.SMBQueryFsSizeInfo()

367 return data.getData()

368 elif level == smb.SMB_QUERY_FS_DEVICE_INFO or level == smb2.SMB2_FILESYSTEM_DEVICE_INFO:

369 data = smb.SMBQueryFsDeviceInfo()

370 data['DeviceType'] = smb.FILE_DEVICE_DISK

371 return data.getData()

372 elif level == smb.FILE_FS_FULL_SIZE_INFORMATION:

373 data = smb.SMBFileFsFullSizeInformation()

374 return data.getData()

375 elif level == smb.FILE_FS_SIZE_INFORMATION:

376 data = smb.FileFsSizeInformation()

377 return data.getData()

378 else:

379 return None

380

381

382 def findFirst2(path, fileName, level, searchAttributes, pktFlags=smb.SMB.FLAGS2_UNICODE, isSMB2=False):

383 # TODO: Depending on the level, this could be done much simpler

384

385 # Let's choose the right encoding depending on the request

386 if pktFlags & smb.SMB.FLAGS2_UNICODE:

387 encoding = 'utf-16le'

388 else:

389 encoding = 'ascii'

390

391 fileName = normalize_path(fileName)

392 pathName = os.path.join(path, fileName)

393

394 if not isInFileJail(path, fileName):

395 LOG.error("Path not in current working directory")

396 return [], 0, STATUS_OBJECT_PATH_SYNTAX_BAD

397

398 files = []

399

400 if pathName.find('*') == -1 and pathName.find('?') == -1:

401 # No search patterns

402 pattern = ''

403 else:

404 pattern = os.path.basename(pathName)

405 dirName = os.path.dirname(pathName)

406

407 # Always add . and .. Not that important for Windows, but Samba whines if

408 # not present (for * search only)

409 if pattern == '*':

410 files.append(os.path.join(dirName, '.'))

411 files.append(os.path.join(dirName, '..'))

412

413 if pattern != '':

414 for file in os.listdir(dirName):

415 if fnmatch.fnmatch(file.lower(), pattern.lower()):

416 entry = os.path.join(dirName, file)

417 if os.path.isdir(entry):

418 if searchAttributes & smb.ATTR_DIRECTORY:

419 files.append(entry)

420 else:

421 files.append(entry)

422 else:

423 if os.path.exists(pathName):

424 files.append(pathName)

425

426 searchResult = []

427 searchCount = len(files)

428 errorCode = STATUS_SUCCESS

429

430 for i in files:

431 if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO:

432 item = smb.SMBFindFileBothDirectoryInfo(flags=pktFlags)

433 elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO or level == smb2.SMB2_FILE_DIRECTORY_INFO:

434 item = smb.SMBFindFileDirectoryInfo(flags=pktFlags)

435 elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO:

436 item = smb.SMBFindFileFullDirectoryInfo(flags=pktFlags)

437 elif level == smb.SMB_FIND_INFO_STANDARD:

438 item = smb.SMBFindInfoStandard(flags=pktFlags)

439 elif level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO:

440 item = smb.SMBFindFileIdFullDirectoryInfo(flags=pktFlags)

441 elif level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO:

442 item = smb.SMBFindFileIdBothDirectoryInfo(flags=pktFlags)

443 elif level == smb.SMB_FIND_FILE_NAMES_INFO or level == smb2.SMB2_FILE_NAMES_INFO:

444 item = smb.SMBFindFileNamesInfo(flags=pktFlags)

445 else:

446 LOG.error("Wrong level %d!" % level)

447 return searchResult, searchCount, STATUS_NOT_SUPPORTED

448

449 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(i)

450 if os.path.isdir(i):

451 item['ExtFileAttributes'] = smb.ATTR_DIRECTORY

452 else:

453 item['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE

454

455 item['FileName'] = os.path.basename(i).encode(encoding)

456

457 if level in [smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO, smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO,

458 smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO]:

459 item['EaSize'] = 0

460 item['EndOfFile'] = size

461 item['AllocationSize'] = size

462 item['CreationTime'] = getFileTime(ctime)

463 item['LastAccessTime'] = getFileTime(atime)

464 item['LastWriteTime'] = getFileTime(mtime)

465 item['LastChangeTime'] = getFileTime(mtime)

466 item['ShortName'] = '\x00' * 24

467 item['FileName'] = os.path.basename(i).encode(encoding)

468 padLen = (8 - (len(item) % 8)) % 8

469 item['NextEntryOffset'] = len(item) + padLen

470 elif level in [smb.SMB_FIND_FILE_DIRECTORY_INFO, smb2.SMB2_FILE_DIRECTORY_INFO]:

471 item['EndOfFile'] = size

472 item['AllocationSize'] = size

473 item['CreationTime'] = getFileTime(ctime)

474 item['LastAccessTime'] = getFileTime(atime)

475 item['LastWriteTime'] = getFileTime(mtime)

476 item['LastChangeTime'] = getFileTime(mtime)

477 item['FileName'] = os.path.basename(i).encode(encoding)

478 padLen = (8 - (len(item) % 8)) % 8

479 item['NextEntryOffset'] = len(item) + padLen

480 elif level in [smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO, smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO,

481 smb2.SMB2_FULL_DIRECTORY_INFO, smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO]:

482 item['EaSize'] = 0

483 item['EndOfFile'] = size

484 item['AllocationSize'] = size

485 item['CreationTime'] = getFileTime(ctime)

486 item['LastAccessTime'] = getFileTime(atime)

487 item['LastWriteTime'] = getFileTime(mtime)

488 item['LastChangeTime'] = getFileTime(mtime)

489 padLen = (8 - (len(item) % 8)) % 8

490 item['NextEntryOffset'] = len(item) + padLen

491 elif level == smb.SMB_FIND_INFO_STANDARD:

492 item['EaSize'] = size

493 item['CreationDate'] = getSMBDate(ctime)

494 item['CreationTime'] = getSMBTime(ctime)

495 item['LastAccessDate'] = getSMBDate(atime)

496 item['LastAccessTime'] = getSMBTime(atime)

497 item['LastWriteDate'] = getSMBDate(mtime)

498 item['LastWriteTime'] = getSMBTime(mtime)

499 searchResult.append(item)

500

501 # No more files

502 if (level >= smb.SMB_FIND_FILE_DIRECTORY_INFO or isSMB2 is True) and searchCount > 0:

503 searchResult[-1]['NextEntryOffset'] = 0

504

505 return searchResult, searchCount, errorCode

506

507

508 def queryFileInformation(path, filename, level):

509 # print "queryFileInfo path: %s, filename: %s, level:0x%x" % (path,filename,level)

510 return queryPathInformation(path, filename, level)

511

512

513 def queryPathInformation(path, filename, level):

514 # TODO: Depending on the level, this could be done much simpler

515 try:

516 errorCode = 0

517 fileName = normalize_path(filename, path)

518 pathName = os.path.join(path, fileName)

519

520 if not isInFileJail(path, fileName):

521 LOG.error("Path not in current working directory")

522 return None, STATUS_OBJECT_PATH_SYNTAX_BAD

523

524 if os.path.exists(pathName):

525 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)

526 if level == smb.SMB_QUERY_FILE_BASIC_INFO:

527 infoRecord = smb.SMBQueryFileBasicInfo()

528 infoRecord['CreationTime'] = getFileTime(ctime)

529 infoRecord['LastAccessTime'] = getFileTime(atime)

530 infoRecord['LastWriteTime'] = getFileTime(mtime)

531 infoRecord['LastChangeTime'] = getFileTime(mtime)

532 if os.path.isdir(pathName):

533 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY

534 else:

535 infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE

536 elif level == smb.SMB_QUERY_FILE_STANDARD_INFO or level == smb2.SMB2_FILE_STANDARD_INFO:

537 infoRecord = smb.SMBQueryFileStandardInfo()

538 infoRecord['AllocationSize'] = size

539 infoRecord['EndOfFile'] = size

540 if os.path.isdir(pathName):

541 infoRecord['Directory'] = 1

542 else:

543 infoRecord['Directory'] = 0

544 elif level == smb.SMB_QUERY_FILE_ALL_INFO:

545 infoRecord = smb.SMBQueryFileAllInfo()

546 infoRecord['CreationTime'] = getFileTime(ctime)

547 infoRecord['LastAccessTime'] = getFileTime(atime)

548 infoRecord['LastWriteTime'] = getFileTime(mtime)

549 infoRecord['LastChangeTime'] = getFileTime(mtime)

550 if os.path.isdir(pathName):

551 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY

552 else:

553 infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE

554 infoRecord['AllocationSize'] = size

555 infoRecord['EndOfFile'] = size

556 if os.path.isdir(pathName):

557 infoRecord['Directory'] = 1

558 else:

559 infoRecord['Directory'] = 0

560 infoRecord['FileName'] = filename.encode('utf-16le')

561 elif level == smb2.SMB2_FILE_ALL_INFO:

562 infoRecord = smb2.FILE_ALL_INFORMATION()

563 infoRecord['BasicInformation'] = smb2.FILE_BASIC_INFORMATION()

564 infoRecord['StandardInformation'] = smb2.FILE_STANDARD_INFORMATION()

565 infoRecord['InternalInformation'] = smb2.FILE_INTERNAL_INFORMATION()

566 infoRecord['EaInformation'] = smb2.FILE_EA_INFORMATION()

567 infoRecord['AccessInformation'] = smb2.FILE_ACCESS_INFORMATION()

568 infoRecord['PositionInformation'] = smb2.FILE_POSITION_INFORMATION()

569 infoRecord['ModeInformation'] = smb2.FILE_MODE_INFORMATION()

570 infoRecord['AlignmentInformation'] = smb2.FILE_ALIGNMENT_INFORMATION()

571 infoRecord['NameInformation'] = smb2.FILE_NAME_INFORMATION()

572 infoRecord['BasicInformation']['CreationTime'] = getFileTime(ctime)

573 infoRecord['BasicInformation']['LastAccessTime'] = getFileTime(atime)

574 infoRecord['BasicInformation']['LastWriteTime'] = getFileTime(mtime)

575 infoRecord['BasicInformation']['ChangeTime'] = getFileTime(mtime)

576 if os.path.isdir(pathName):

577 infoRecord['BasicInformation']['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY

578 infoRecord['StandardInformation']['Directory'] = 1

579 infoRecord['EaInformation']['EaSize'] = smb.ATTR_DIRECTORY

580 else:

581 infoRecord['BasicInformation']['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_NORMAL | smb.SMB_FILE_ATTRIBUTE_ARCHIVE

582 infoRecord['StandardInformation']['Directory'] = 0

583 infoRecord['EaInformation']['EaSize'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE

584 infoRecord['StandardInformation']['AllocationSize'] = size

585 infoRecord['StandardInformation']['EndOfFile'] = size

586 infoRecord['StandardInformation']['NumberOfLinks'] = nlink

587 infoRecord['StandardInformation']['DeletePending'] = 0

588 infoRecord['InternalInformation']['IndexNumber'] = ino

589 infoRecord['AccessInformation']['AccessFlags'] = 0 #

590 infoRecord['PositionInformation']['CurrentByteOffset'] = 0 #

591 infoRecord['ModeInformation']['mode'] = mode

592 infoRecord['AlignmentInformation']['AlignmentRequirement'] = 0 #

593 infoRecord['NameInformation']['FileName'] = fileName

594 infoRecord['NameInformation']['FileNameLength'] = len(fileName)

595 elif level == smb2.SMB2_FILE_NETWORK_OPEN_INFO:

596 infoRecord = smb.SMBFileNetworkOpenInfo()

597 infoRecord['CreationTime'] = getFileTime(ctime)

598 infoRecord['LastAccessTime'] = getFileTime(atime)

599 infoRecord['LastWriteTime'] = getFileTime(mtime)

600 infoRecord['ChangeTime'] = getFileTime(mtime)

601 infoRecord['AllocationSize'] = size

602 infoRecord['EndOfFile'] = size

603 if os.path.isdir(pathName):

604 infoRecord['FileAttributes'] = smb.ATTR_DIRECTORY

605 else:

606 infoRecord['FileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE

607 elif level == smb.SMB_QUERY_FILE_EA_INFO or level == smb2.SMB2_FILE_EA_INFO:

608 infoRecord = smb.SMBQueryFileEaInfo()

609 elif level == smb2.SMB2_FILE_STREAM_INFO:

610 infoRecord = smb.SMBFileStreamInformation()

611 else:

612 LOG.error('Unknown level for query path info! 0x%x' % level)

613 # UNSUPPORTED

614 return None, STATUS_NOT_SUPPORTED

615

616 return infoRecord, errorCode

617 else:

618 # NOT FOUND

619 return None, STATUS_OBJECT_NAME_NOT_FOUND

620 except Exception as e:

621 LOG.error('queryPathInfo: %s' % e)

622 raise

623

624

625 def queryDiskInformation(path):

626 # TODO: Do something useful here :)

627 # For now we just return fake values

628 totalUnits = 65535

629 freeUnits = 65535

630 return totalUnits, freeUnits

631

632

633# Here we implement the NT transaction handlers

634 class NTTRANSCommands:

635 def default(self, connId, smbServer, recvPacket, parameters, data, maxDataCount=0):

636 pass

637

638

639# Here we implement the NT transaction handlers

640 class TRANSCommands:

641 @staticmethod

642 def lanMan(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):

643 # Minimal [MS-RAP] implementation, just to return the shares

644 connData = smbServer.getConnectionData(connId)

645

646 respSetup = b''

647 respParameters = b''

648 respData = b''

649 errorCode = STATUS_SUCCESS

650 if struct.unpack('<H', parameters[:2])[0] == 0:

651 # NetShareEnum Request

652 netShareEnum = smb.SMBNetShareEnum(parameters)

653 if netShareEnum['InfoLevel'] == 1:

654 shares = getShares(connId, smbServer)

655 respParameters = smb.SMBNetShareEnumResponse()

656 respParameters['EntriesReturned'] = len(shares)

657 respParameters['EntriesAvailable'] = len(shares)

658 tailData = ''

659 for i in shares:

660 # NetShareInfo1 len == 20

661 entry = smb.NetShareInfo1()

662 entry['NetworkName'] = i + '\x00' * (13 - len(i))

663 entry['Type'] = int(shares[i]['share type'])

664 # (beto) If offset == 0 it crashes explorer.exe on windows 7

665 entry['RemarkOffsetLow'] = 20 * len(shares) + len(tailData)

666 respData += entry.getData()

667 if 'comment' in shares[i]:

668 tailData += shares[i]['comment'] + '\x00'

669 else:

670 tailData += '\x00'

671 respData += tailData

672 else:

673 # We don't support other info levels

674 errorCode = STATUS_NOT_SUPPORTED

675 elif struct.unpack('<H', parameters[:2])[0] == 13:

676 # NetrServerGetInfo Request

677 respParameters = smb.SMBNetServerGetInfoResponse()

678 netServerInfo = smb.SMBNetServerInfo1()

679 netServerInfo['ServerName'] = smbServer.getServerName()

680 respData = netServerInfo.getData()

681 respParameters['TotalBytesAvailable'] = len(respData)

682 elif struct.unpack('<H', parameters[:2])[0] == 1:

683 # NetrShareGetInfo Request

684 request = smb.SMBNetShareGetInfo(parameters)

685 respParameters = smb.SMBNetShareGetInfoResponse()

686 shares = getShares(connId, smbServer)

687 share = shares[request['ShareName'].upper()]

688 shareInfo = smb.NetShareInfo1()

689 shareInfo['NetworkName'] = request['ShareName'].upper() + '\x00'

690 shareInfo['Type'] = int(share['share type'])

691 respData = shareInfo.getData()

692 if 'comment' in share:

693 shareInfo['RemarkOffsetLow'] = len(respData)

694 respData += share['comment'] + '\x00'

695 respParameters['TotalBytesAvailable'] = len(respData)

696

697 else:

698 # We don't know how to handle anything else

699 errorCode = STATUS_NOT_SUPPORTED

700

701 smbServer.setConnectionData(connId, connData)

702

703 return respSetup, respParameters, respData, errorCode

704

705 @staticmethod

706 def transactNamedPipe(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):

707 connData = smbServer.getConnectionData(connId)

708

709 respSetup = b''

710 respParameters = b''

711 respData = b''

712 errorCode = STATUS_SUCCESS

713 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])

714 transParameters = smb.SMBTransaction_Parameters(SMBCommand['Parameters'])

715

716 # Extract the FID

717 fid = struct.unpack('<H', transParameters['Setup'][2:])[0]

718

719 if fid in connData['OpenedFiles']:

720 fileHandle = connData['OpenedFiles'][fid]['FileHandle']

721 if fileHandle != PIPE_FILE_DESCRIPTOR:

722 os.write(fileHandle, data)

723 respData = os.read(fileHandle, data)

724 else:

725 sock = connData['OpenedFiles'][fid]['Socket']

726 sock.send(data)

727 respData = sock.recv(maxDataCount)

728 else:

729 errorCode = STATUS_INVALID_HANDLE

730

731 smbServer.setConnectionData(connId, connData)

732

733 return respSetup, respParameters, respData, errorCode

734

735

736# Here we implement the transaction2 handlers

737 class TRANS2Commands:

738 # All these commands return setup, parameters, data, errorCode

739 @staticmethod

740 def setPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):

741 connData = smbServer.getConnectionData(connId)

742

743 respSetup = b''

744 respParameters = b''

745 respData = b''

746 errorCode = STATUS_SUCCESS

747 setPathInfoParameters = smb.SMBSetPathInformation_Parameters(flags=recvPacket['Flags2'], data=parameters)

748 if recvPacket['Tid'] in connData['ConnectedShares']:

749 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

750 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], setPathInfoParameters['FileName']), path)

751 pathName = os.path.join(path, fileName)

752

753 if isInFileJail(path, fileName):

754 smbServer.log("Path not in current working directory")

755 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD

756

757 elif os.path.exists(pathName):

758 informationLevel = setPathInfoParameters['InformationLevel']

759 if informationLevel == smb.SMB_SET_FILE_BASIC_INFO:

760 infoRecord = smb.SMBSetFileBasicInfo(data)

761 # Creation time won't be set, the other ones we play with.

762 atime = infoRecord['LastAccessTime']

763 if atime == 0:

764 atime = -1

765 else:

766 atime = getUnixTime(atime)

767 mtime = infoRecord['LastWriteTime']

768 if mtime == 0:

769 mtime = -1

770 else:

771 mtime = getUnixTime(mtime)

772 if mtime != -1 or atime != -1:

773 os.utime(pathName, (atime, mtime))

774 else:

775 smbServer.log('Unknown level for set path info! 0x%x' % setPathInfoParameters['InformationLevel'],

776 logging.ERROR)

777 # UNSUPPORTED

778 errorCode = STATUS_NOT_SUPPORTED

779 else:

780 errorCode = STATUS_OBJECT_NAME_NOT_FOUND

781

782 if errorCode == STATUS_SUCCESS:

783 respParameters = smb.SMBSetPathInformationResponse_Parameters()

784

785 else:

786 errorCode = STATUS_SMB_BAD_TID

787

788 smbServer.setConnectionData(connId, connData)

789

790 return respSetup, respParameters, respData, errorCode

791

792 @staticmethod

793 def setFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):

794 connData = smbServer.getConnectionData(connId)

795

796 respSetup = b''

797 respParameters = b''

798 respData = b''

799 errorCode = STATUS_SUCCESS

800 setFileInfoParameters = smb.SMBSetFileInformation_Parameters(parameters)

801

802 if recvPacket['Tid'] in connData['ConnectedShares']:

803 if setFileInfoParameters['FID'] in connData['OpenedFiles']:

804 fileName = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileName']

805 informationLevel = setFileInfoParameters['InformationLevel']

806 if informationLevel == smb.SMB_SET_FILE_DISPOSITION_INFO:

807 infoRecord = smb.SMBSetFileDispositionInfo(parameters)

808 if infoRecord['DeletePending'] > 0:

809 # Mark this file for removal after closed

810 connData['OpenedFiles'][setFileInfoParameters['FID']]['DeleteOnClose'] = True

811 respParameters = smb.SMBSetFileInformationResponse_Parameters()

812 elif informationLevel == smb.SMB_SET_FILE_BASIC_INFO:

813 infoRecord = smb.SMBSetFileBasicInfo(data)

814 # Creation time won't be set, the other ones we play with.

815 atime = infoRecord['LastAccessTime']

816 if atime == 0:

817 atime = -1

818 else:

819 atime = getUnixTime(atime)

820 mtime = infoRecord['LastWriteTime']

821 if mtime == 0:

822 mtime = -1

823 else:

824 mtime = getUnixTime(mtime)

825 os.utime(fileName, (atime, mtime))

826 elif informationLevel == smb.SMB_SET_FILE_END_OF_FILE_INFO:

827 fileHandle = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileHandle']

828 infoRecord = smb.SMBSetFileEndOfFileInfo(data)

829 if infoRecord['EndOfFile'] > 0:

830 os.lseek(fileHandle, infoRecord['EndOfFile'] - 1, 0)

831 os.write(fileHandle, b'\x00')

832 else:

833 smbServer.log('Unknown level for set file info! 0x%x' % setFileInfoParameters['InformationLevel'],

834 logging.ERROR)

835 # UNSUPPORTED

836 errorCode = STATUS_NOT_SUPPORTED

837 else:

838 errorCode = STATUS_NO_SUCH_FILE

839

840 if errorCode == STATUS_SUCCESS:

841 respParameters = smb.SMBSetFileInformationResponse_Parameters()

842 else:

843 errorCode = STATUS_SMB_BAD_TID

844

845 smbServer.setConnectionData(connId, connData)

846

847 return respSetup, respParameters, respData, errorCode

848

849 @staticmethod

850 def queryFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):

851 connData = smbServer.getConnectionData(connId)

852

853 respSetup = b''

854 respParameters = b''

855 respData = b''

856

857 queryFileInfoParameters = smb.SMBQueryFileInformation_Parameters(parameters)

858

859 if recvPacket['Tid'] in connData['ConnectedShares']:

860 if queryFileInfoParameters['FID'] in connData['OpenedFiles']:

861 pathName = connData['OpenedFiles'][queryFileInfoParameters['FID']]['FileName']

862

863 infoRecord, errorCode = queryFileInformation(os.path.dirname(pathName), os.path.basename(pathName),

864 queryFileInfoParameters['InformationLevel'])

865

866 if infoRecord is not None:

867 respParameters = smb.SMBQueryFileInformationResponse_Parameters()

868 respData = infoRecord

869 else:

870 errorCode = STATUS_INVALID_HANDLE

871 else:

872 errorCode = STATUS_SMB_BAD_TID

873

874 smbServer.setConnectionData(connId, connData)

875

876 return respSetup, respParameters, respData, errorCode

877

878 @staticmethod

879 def queryPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):

880 connData = smbServer.getConnectionData(connId)

881

882 respSetup = b''

883 respParameters = b''

884 respData = b''

885 errorCode = 0

886

887 queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags=recvPacket['Flags2'], data=parameters)

888

889 if recvPacket['Tid'] in connData['ConnectedShares']:

890 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

891 try:

892 infoRecord, errorCode = queryPathInformation(path, decodeSMBString(recvPacket['Flags2'],

893 queryPathInfoParameters['FileName']),

894 queryPathInfoParameters['InformationLevel'])

895 except Exception as e:

896 smbServer.log("queryPathInformation: %s" % e, logging.ERROR)

897

898 if infoRecord is not None:

899 respParameters = smb.SMBQueryPathInformationResponse_Parameters()

900 respData = infoRecord

901 else:

902 errorCode = STATUS_SMB_BAD_TID

903

904 smbServer.setConnectionData(connId, connData)

905

906 return respSetup, respParameters, respData, errorCode

907

908 @staticmethod

909 def queryFsInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):

910 connData = smbServer.getConnectionData(connId)

911 errorCode = 0

912 # Get the Tid associated

913 if recvPacket['Tid'] in connData['ConnectedShares']:

914 data = queryFsInformation(connData['ConnectedShares'][recvPacket['Tid']]['path'], '',

915 struct.unpack('<H', parameters)[0], pktFlags=recvPacket['Flags2'])

916

917 smbServer.setConnectionData(connId, connData)

918

919 return b'', b'', data, errorCode

920

921 @staticmethod

922 def findNext2(connId, smbServer, recvPacket, parameters, data, maxDataCount):

923 connData = smbServer.getConnectionData(connId)

924

925 respSetup = b''

926 respParameters = b''

927 respData = b''

928 errorCode = STATUS_SUCCESS

929 findNext2Parameters = smb.SMBFindNext2_Parameters(flags=recvPacket['Flags2'], data=parameters)

930

931 sid = findNext2Parameters['SID']

932 if recvPacket['Tid'] in connData['ConnectedShares']:

933 if sid in connData['SIDs']:

934 searchResult = connData['SIDs'][sid]

935 respParameters = smb.SMBFindNext2Response_Parameters()

936 endOfSearch = 1

937 searchCount = 1

938 totalData = 0

939 for i in enumerate(searchResult):

940 data = i[1].getData()

941 lenData = len(data)

942 if (totalData + lenData) >= maxDataCount or (i[0] + 1) >= findNext2Parameters['SearchCount']:

943 # We gotta stop here and continue on a find_next2

944 endOfSearch = 0

945 connData['SIDs'][sid] = searchResult[i[0]:]

946 respParameters['LastNameOffset'] = totalData

947 break

948 else:

949 searchCount += 1

950 respData += data

951 totalData += lenData

952

953 # Have we reached the end of the search or still stuff to send?

954 if endOfSearch > 0:

955 # Let's remove the SID from our ConnData

956 del (connData['SIDs'][sid])

957

958 respParameters['EndOfSearch'] = endOfSearch

959 respParameters['SearchCount'] = searchCount

960 else:

961 errorCode = STATUS_INVALID_HANDLE

962 else:

963 errorCode = STATUS_SMB_BAD_TID

964

965 smbServer.setConnectionData(connId, connData)

966

967 return respSetup, respParameters, respData, errorCode

968

969 @staticmethod

970 def findFirst2(connId, smbServer, recvPacket, parameters, data, maxDataCount):

971 connData = smbServer.getConnectionData(connId)

972

973 respSetup = b''

974 respParameters = b''

975 respData = b''

976 findFirst2Parameters = smb.SMBFindFirst2_Parameters(recvPacket['Flags2'], data=parameters)

977

978 if recvPacket['Tid'] in connData['ConnectedShares']:

979 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

980

981 searchResult, searchCount, errorCode = findFirst2(path,

982 decodeSMBString(recvPacket['Flags2'],

983 findFirst2Parameters['FileName']),

984 findFirst2Parameters['InformationLevel'],

985 findFirst2Parameters['SearchAttributes'],

986 pktFlags=recvPacket['Flags2'])

987

988 if searchCount > 0:

989 respParameters = smb.SMBFindFirst2Response_Parameters()

990 endOfSearch = 1

991 sid = 0x80 # default SID

992 searchCount = 0

993 totalData = 0

994 for i in enumerate(searchResult):

995 # i[1].dump()

996 data = i[1].getData()

997 lenData = len(data)

998 if (totalData + lenData) >= maxDataCount or (i[0] + 1) > findFirst2Parameters['SearchCount']:

999 # We gotta stop here and continue on a find_next2

1000 endOfSearch = 0

1001 # Simple way to generate a fid

1002 if len(connData['SIDs']) == 0:

1003 sid = 1

1004 else:

1005 sid = list(connData['SIDs'].keys())[-1] + 1

1006 # Store the remaining search results in the ConnData SID

1007 connData['SIDs'][sid] = searchResult[i[0]:]

1008 respParameters['LastNameOffset'] = totalData

1009 break

1010 else:

1011 searchCount += 1

1012 respData += data

1013

1014 padLen = (8 - (lenData % 8)) % 8

1015 respData += b'\xaa' * padLen

1016 totalData += lenData + padLen

1017

1018 respParameters['SID'] = sid

1019 respParameters['EndOfSearch'] = endOfSearch

1020 respParameters['SearchCount'] = searchCount

1021

1022 # If we've empty files and errorCode was not already set, we return NO_SUCH_FILE

1023 elif errorCode == 0:

1024 errorCode = STATUS_NO_SUCH_FILE

1025 else:

1026 errorCode = STATUS_SMB_BAD_TID

1027

1028 smbServer.setConnectionData(connId, connData)

1029

1030 return respSetup, respParameters, respData, errorCode

1031

1032

1033# Here we implement the commands handlers

1034 class SMBCommands:

1035

1036 @staticmethod

1037 def smbTransaction(connId, smbServer, SMBCommand, recvPacket, transCommands):

1038 connData = smbServer.getConnectionData(connId)

1039

1040 respSMBCommand = smb.SMBCommand(recvPacket['Command'])

1041

1042 transParameters = smb.SMBTransaction_Parameters(SMBCommand['Parameters'])

1043

1044 # Do the stuff

1045 if transParameters['ParameterCount'] != transParameters['TotalParameterCount']:

1046 # TODO: Handle partial parameters

1047 raise Exception("Unsupported partial parameters in TRANSACT2!")

1048 else:

1049 transData = smb.SMBTransaction_SData(flags=recvPacket['Flags2'])

1050 # Standard says servers shouldn't trust Parameters and Data comes

1051 # in order, so we have to parse the offsets, ugly

1052

1053 paramCount = transParameters['ParameterCount']

1054 transData['Trans_ParametersLength'] = paramCount

1055 dataCount = transParameters['DataCount']

1056 transData['Trans_DataLength'] = dataCount

1057 transData.fromString(SMBCommand['Data'])

1058 if transParameters['ParameterOffset'] > 0:

1059 paramOffset = transParameters['ParameterOffset'] - 63 - transParameters['SetupLength']

1060 transData['Trans_Parameters'] = SMBCommand['Data'][paramOffset: paramOffset + paramCount]

1061 else:

1062 transData['Trans_Parameters'] = b''

1063

1064 if transParameters['DataOffset'] > 0:

1065 dataOffset = transParameters['DataOffset'] - 63 - transParameters['SetupLength']

1066 transData['Trans_Data'] = SMBCommand['Data'][dataOffset: dataOffset + dataCount]

1067 else:

1068 transData['Trans_Data'] = b''

1069

1070 # Call the handler for this TRANSACTION

1071 if transParameters['SetupCount'] == 0:

1072 # No subcommand, let's play with the Name

1073 command = decodeSMBString(recvPacket['Flags2'], transData['Name'])

1074 else:

1075 command = struct.unpack('<H', transParameters['Setup'][:2])[0]

1076

1077 if command in transCommands:

1078 # Call the TRANS subcommand

1079 setup = b''

1080 parameters = b''

1081 data = b''

1082 try:

1083 setup, parameters, data, errorCode = transCommands[command](connId,

1084 smbServer,

1085 recvPacket,

1086 transData['Trans_Parameters'],

1087 transData['Trans_Data'],

1088 transParameters['MaxDataCount'])

1089 except Exception as e:

1090 # print 'Transaction: %s' % e,e

1091 smbServer.log('Transaction: (%r,%s)' % (command, e), logging.ERROR)

1092 errorCode = STATUS_ACCESS_DENIED

1093 # raise

1094

1095 if setup == b'' and parameters == b'' and data == b'':

1096 # Something wen't wrong

1097 respParameters = b''

1098 respData = b''

1099 else:

1100 # Build the answer

1101 if hasattr(data, 'getData'):

1102 data = data.getData()

1103 remainingData = len(data)

1104 if hasattr(parameters, 'getData'):

1105 parameters = parameters.getData()

1106 remainingParameters = len(parameters)

1107 commands = []

1108 dataDisplacement = 0

1109 while remainingData > 0 or remainingParameters > 0:

1110 respSMBCommand = smb.SMBCommand(recvPacket['Command'])

1111 respParameters = smb.SMBTransactionResponse_Parameters()

1112 respData = smb.SMBTransaction2Response_Data()

1113

1114 respParameters['TotalParameterCount'] = len(parameters)

1115 respParameters['ParameterCount'] = len(parameters)

1116 respData['Trans_ParametersLength'] = len(parameters)

1117 respParameters['TotalDataCount'] = len(data)

1118 respParameters['DataDisplacement'] = dataDisplacement

1119

1120 # TODO: Do the same for parameters

1121 if len(data) > transParameters['MaxDataCount']:

1122 # Answer doesn't fit in this packet

1123 LOG.debug("Lowering answer from %d to %d" % (len(data), transParameters['MaxDataCount']))

1124 respParameters['DataCount'] = transParameters['MaxDataCount']

1125 else:

1126 respParameters['DataCount'] = len(data)

1127

1128 respData['Trans_DataLength'] = respParameters['DataCount']

1129 respParameters['SetupCount'] = len(setup)

1130 respParameters['Setup'] = setup

1131 # TODO: Make sure we're calculating the pad right

1132 if len(parameters) > 0:

1133 # padLen = 4 - (55 + len(setup)) % 4

1134 padLen = (4 - (55 + len(setup)) % 4) % 4

1135 padBytes = b'\xFF' * padLen

1136 respData['Pad1'] = padBytes

1137 respParameters['ParameterOffset'] = 55 + len(setup) + padLen

1138 else:

1139 padLen = 0

1140 respParameters['ParameterOffset'] = 0

1141 respData['Pad1'] = b''

1142

1143 if len(data) > 0:

1144 # pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4

1145 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4

1146 respData['Pad2'] = b'\xFF' * pad2Len

1147 respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len

1148 else:

1149 respParameters['DataOffset'] = 0

1150 respData['Pad2'] = b''

1151

1152 respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']]

1153 respData['Trans_Data'] = data[:respParameters['DataCount']]

1154 respSMBCommand['Parameters'] = respParameters

1155 respSMBCommand['Data'] = respData

1156

1157 data = data[respParameters['DataCount']:]

1158 remainingData -= respParameters['DataCount']

1159 dataDisplacement += respParameters['DataCount'] + 1

1160

1161 parameters = parameters[respParameters['ParameterCount']:]

1162 remainingParameters -= respParameters['ParameterCount']

1163 commands.append(respSMBCommand)

1164

1165 smbServer.setConnectionData(connId, connData)

1166 return commands, None, errorCode

1167

1168 else:

1169 smbServer.log("Unsupported Transact command %r" % command, logging.ERROR)

1170 respParameters = b''

1171 respData = b''

1172 errorCode = STATUS_NOT_IMPLEMENTED

1173

1174 respSMBCommand['Parameters'] = respParameters

1175 respSMBCommand['Data'] = respData

1176 smbServer.setConnectionData(connId, connData)

1177

1178 return [respSMBCommand], None, errorCode

1179

1180 @staticmethod

1181 def smbNTTransact(connId, smbServer, SMBCommand, recvPacket, transCommands):

1182 connData = smbServer.getConnectionData(connId)

1183

1184 respSMBCommand = smb.SMBCommand(recvPacket['Command'])

1185

1186 NTTransParameters = smb.SMBNTTransaction_Parameters(SMBCommand['Parameters'])

1187 # Do the stuff

1188 if NTTransParameters['ParameterCount'] != NTTransParameters['TotalParameterCount']:

1189 # TODO: Handle partial parameters

1190 raise Exception("Unsupported partial parameters in NTTrans!")

1191 else:

1192 NTTransData = smb.SMBNTTransaction_Data()

1193 # Standard says servers shouldn't trust Parameters and Data comes

1194 # in order, so we have to parse the offsets, ugly

1195

1196 paramCount = NTTransParameters['ParameterCount']

1197 NTTransData['NT_Trans_ParametersLength'] = paramCount

1198 dataCount = NTTransParameters['DataCount']

1199 NTTransData['NT_Trans_DataLength'] = dataCount

1200

1201 if NTTransParameters['ParameterOffset'] > 0:

1202 paramOffset = NTTransParameters['ParameterOffset'] - 73 - NTTransParameters['SetupLength']

1203 NTTransData['NT_Trans_Parameters'] = SMBCommand['Data'][paramOffset: paramOffset + paramCount]

1204 else:

1205 NTTransData['NT_Trans_Parameters'] = b''

1206

1207 if NTTransParameters['DataOffset'] > 0:

1208 dataOffset = NTTransParameters['DataOffset'] - 73 - NTTransParameters['SetupLength']

1209 NTTransData['NT_Trans_Data'] = SMBCommand['Data'][dataOffset: dataOffset + dataCount]

1210 else:

1211 NTTransData['NT_Trans_Data'] = b''

1212

1213 # Call the handler for this TRANSACTION

1214 command = NTTransParameters['Function']

1215 if command in transCommands:

1216 # Call the NT TRANS subcommand

1217 setup = b''

1218 parameters = b''

1219 data = b''

1220 try:

1221 setup, parameters, data, errorCode = transCommands[command](connId,

1222 smbServer,

1223 recvPacket,

1224 NTTransData['NT_Trans_Parameters'],

1225 NTTransData['NT_Trans_Data'],

1226 NTTransParameters['MaxDataCount'])

1227 except Exception as e:

1228 smbServer.log('NTTransaction: (0x%x,%s)' % (command, e), logging.ERROR)

1229 errorCode = STATUS_ACCESS_DENIED

1230 # raise

1231

1232 if setup == b'' and parameters == b'' and data == b'':

1233 # Something wen't wrong

1234 respParameters = b''

1235 respData = b''

1236 if errorCode == STATUS_SUCCESS:

1237 errorCode = STATUS_ACCESS_DENIED

1238 else:

1239 # Build the answer

1240 if hasattr(data, 'getData'):

1241 data = data.getData()

1242 remainingData = len(data)

1243 if hasattr(parameters, 'getData'):

1244 parameters = parameters.getData()

1245 remainingParameters = len(parameters)

1246 commands = []

1247 dataDisplacement = 0

1248 while remainingData > 0 or remainingParameters > 0:

1249 respSMBCommand = smb.SMBCommand(recvPacket['Command'])

1250 respParameters = smb.SMBNTTransactionResponse_Parameters()

1251 respData = smb.SMBNTTransactionResponse_Data()

1252

1253 respParameters['TotalParameterCount'] = len(parameters)

1254 respParameters['ParameterCount'] = len(parameters)

1255 respData['Trans_ParametersLength'] = len(parameters)

1256 respParameters['TotalDataCount'] = len(data)

1257 respParameters['DataDisplacement'] = dataDisplacement

1258 # TODO: Do the same for parameters

1259 if len(data) > NTTransParameters['MaxDataCount']:

1260 # Answer doesn't fit in this packet

1261 LOG.debug("Lowering answer from %d to %d" % (len(data), NTTransParameters['MaxDataCount']))

1262 respParameters['DataCount'] = NTTransParameters['MaxDataCount']

1263 else:

1264 respParameters['DataCount'] = len(data)

1265

1266 respData['NT_Trans_DataLength'] = respParameters['DataCount']

1267 respParameters['SetupCount'] = len(setup)

1268 respParameters['Setup'] = setup

1269 # TODO: Make sure we're calculating the pad right

1270 if len(parameters) > 0:

1271 # padLen = 4 - (71 + len(setup)) % 4

1272 padLen = (4 - (73 + len(setup)) % 4) % 4

1273 padBytes = b'\xFF' * padLen

1274 respData['Pad1'] = padBytes

1275 respParameters['ParameterOffset'] = 73 + len(setup) + padLen

1276 else:

1277 padLen = 0

1278 respParameters['ParameterOffset'] = 0

1279 respData['Pad1'] = b''

1280

1281 if len(data) > 0:

1282 # pad2Len = 4 - (71 + len(setup) + padLen + len(parameters)) % 4

1283 pad2Len = (4 - (73 + len(setup) + padLen + len(parameters)) % 4) % 4

1284 respData['Pad2'] = b'\xFF' * pad2Len

1285 respParameters['DataOffset'] = 73 + len(setup) + padLen + len(parameters) + pad2Len

1286 else:

1287 respParameters['DataOffset'] = 0

1288 respData['Pad2'] = b''

1289

1290 respData['NT_Trans_Parameters'] = parameters[:respParameters['ParameterCount']]

1291 respData['NT_Trans_Data'] = data[:respParameters['DataCount']]

1292 respSMBCommand['Parameters'] = respParameters

1293 respSMBCommand['Data'] = respData

1294

1295 data = data[respParameters['DataCount']:]

1296 remainingData -= respParameters['DataCount']

1297 dataDisplacement += respParameters['DataCount'] + 1

1298

1299 parameters = parameters[respParameters['ParameterCount']:]

1300 remainingParameters -= respParameters['ParameterCount']

1301 commands.append(respSMBCommand)

1302

1303 smbServer.setConnectionData(connId, connData)

1304 return commands, None, errorCode

1305

1306 else:

1307 # smbServer.log("Unsupported NTTransact command 0x%x" % command, logging.ERROR)

1308 respParameters = b''

1309 respData = b''

1310 errorCode = STATUS_NOT_IMPLEMENTED

1311

1312 respSMBCommand['Parameters'] = respParameters

1313 respSMBCommand['Data'] = respData

1314

1315 smbServer.setConnectionData(connId, connData)

1316 return [respSMBCommand], None, errorCode

1317

1318 @staticmethod

1319 def smbTransaction2(connId, smbServer, SMBCommand, recvPacket, transCommands):

1320 connData = smbServer.getConnectionData(connId)

1321

1322 respSMBCommand = smb.SMBCommand(recvPacket['Command'])

1323

1324 trans2Parameters = smb.SMBTransaction2_Parameters(SMBCommand['Parameters'])

1325

1326 # Do the stuff

1327 if trans2Parameters['ParameterCount'] != trans2Parameters['TotalParameterCount']:

1328 # TODO: Handle partial parameters

1329 # print "Unsupported partial parameters in TRANSACT2!"

1330 raise Exception("Unsupported partial parameters in TRANSACT2!")

1331 else:

1332 trans2Data = smb.SMBTransaction2_Data()

1333 # Standard says servers shouldn't trust Parameters and Data comes

1334 # in order, so we have to parse the offsets, ugly

1335

1336 paramCount = trans2Parameters['ParameterCount']

1337 trans2Data['Trans_ParametersLength'] = paramCount

1338 dataCount = trans2Parameters['DataCount']

1339 trans2Data['Trans_DataLength'] = dataCount

1340

1341 if trans2Parameters['ParameterOffset'] > 0:

1342 paramOffset = trans2Parameters['ParameterOffset'] - 63 - trans2Parameters['SetupLength']

1343 trans2Data['Trans_Parameters'] = SMBCommand['Data'][paramOffset: paramOffset + paramCount]

1344 else:

1345 trans2Data['Trans_Parameters'] = b''

1346

1347 if trans2Parameters['DataOffset'] > 0:

1348 dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength']

1349 trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset: dataOffset + dataCount]

1350 else:

1351 trans2Data['Trans_Data'] = b''

1352

1353 # Call the handler for this TRANSACTION

1354 command = struct.unpack('<H', trans2Parameters['Setup'])[0]

1355 if command in transCommands:

1356 # Call the TRANS2 subcommand

1357 try:

1358 setup, parameters, data, errorCode = transCommands[command](connId,

1359 smbServer,

1360 recvPacket,

1361 trans2Data['Trans_Parameters'],

1362 trans2Data['Trans_Data'],

1363 trans2Parameters['MaxDataCount'])

1364 except Exception as e:

1365 smbServer.log('Transaction2: (0x%x,%s)' % (command, e), logging.ERROR)

1366 # import traceback

1367 # traceback.print_exc()

1368 raise

1369

1370 if setup == b'' and parameters == b'' and data == b'':

1371 # Something wen't wrong

1372 respParameters = b''

1373 respData = b''

1374 else:

1375 # Build the answer

1376 if hasattr(data, 'getData'):

1377 data = data.getData()

1378 remainingData = len(data)

1379 if hasattr(parameters, 'getData'):

1380 parameters = parameters.getData()

1381 remainingParameters = len(parameters)

1382 commands = []

1383 dataDisplacement = 0

1384 while remainingData > 0 or remainingParameters > 0:

1385 respSMBCommand = smb.SMBCommand(recvPacket['Command'])

1386 respParameters = smb.SMBTransaction2Response_Parameters()

1387 respData = smb.SMBTransaction2Response_Data()

1388

1389 respParameters['TotalParameterCount'] = len(parameters)

1390 respParameters['ParameterCount'] = len(parameters)

1391 respData['Trans_ParametersLength'] = len(parameters)

1392 respParameters['TotalDataCount'] = len(data)

1393 respParameters['DataDisplacement'] = dataDisplacement

1394 # TODO: Do the same for parameters

1395 if len(data) > trans2Parameters['MaxDataCount']:

1396 # Answer doesn't fit in this packet

1397 LOG.debug("Lowering answer from %d to %d" % (len(data), trans2Parameters['MaxDataCount']))

1398 respParameters['DataCount'] = trans2Parameters['MaxDataCount']

1399 else:

1400 respParameters['DataCount'] = len(data)

1401

1402 respData['Trans_DataLength'] = respParameters['DataCount']

1403 respParameters['SetupCount'] = len(setup)

1404 respParameters['Setup'] = setup

1405 # TODO: Make sure we're calculating the pad right

1406 if len(parameters) > 0:

1407 # padLen = 4 - (55 + len(setup)) % 4

1408 padLen = (4 - (55 + len(setup)) % 4) % 4

1409 padBytes = b'\xFF' * padLen

1410 respData['Pad1'] = padBytes

1411 respParameters['ParameterOffset'] = 55 + len(setup) + padLen

1412 else:

1413 padLen = 0

1414 respParameters['ParameterOffset'] = 0

1415 respData['Pad1'] = b''

1416

1417 if len(data) > 0:

1418 # pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4

1419 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4

1420 respData['Pad2'] = b'\xFF' * pad2Len

1421 respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len

1422 else:

1423 respParameters['DataOffset'] = 0

1424 respData['Pad2'] = b''

1425

1426 respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']]

1427 respData['Trans_Data'] = data[:respParameters['DataCount']]

1428 respSMBCommand['Parameters'] = respParameters

1429 respSMBCommand['Data'] = respData

1430

1431 data = data[respParameters['DataCount']:]

1432 remainingData -= respParameters['DataCount']

1433 dataDisplacement += respParameters['DataCount'] + 1

1434

1435 parameters = parameters[respParameters['ParameterCount']:]

1436 remainingParameters -= respParameters['ParameterCount']

1437 commands.append(respSMBCommand)

1438

1439 smbServer.setConnectionData(connId, connData)

1440 return commands, None, errorCode

1441

1442 else:

1443 smbServer.log("Unsupported Transact/2 command 0x%x" % command, logging.ERROR)

1444 respParameters = b''

1445 respData = b''

1446 errorCode = STATUS_NOT_IMPLEMENTED

1447

1448 respSMBCommand['Parameters'] = respParameters

1449 respSMBCommand['Data'] = respData

1450

1451 smbServer.setConnectionData(connId, connData)

1452 return [respSMBCommand], None, errorCode

1453

1454 @staticmethod

1455 def smbComLockingAndX(connId, smbServer, SMBCommand, recvPacket):

1456 connData = smbServer.getConnectionData(connId)

1457

1458 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOCKING_ANDX)

1459 respParameters = b''

1460 respData = b''

1461

1462 # I'm actually doing nothing.. just make MacOS happy ;)

1463 errorCode = STATUS_SUCCESS

1464

1465 respSMBCommand['Parameters'] = respParameters

1466 respSMBCommand['Data'] = respData

1467 smbServer.setConnectionData(connId, connData)

1468

1469 return [respSMBCommand], None, errorCode

1470

1471 @staticmethod

1472 def smbComClose(connId, smbServer, SMBCommand, recvPacket):

1473 connData = smbServer.getConnectionData(connId)

1474

1475 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CLOSE)

1476 respParameters = b''

1477 respData = b''

1478

1479 comClose = smb.SMBClose_Parameters(SMBCommand['Parameters'])

1480

1481 # Get the Tid associated

1482 if recvPacket['Tid'] in connData['ConnectedShares']:

1483 if comClose['FID'] in connData['OpenedFiles']:

1484 errorCode = STATUS_SUCCESS

1485 fileHandle = connData['OpenedFiles'][comClose['FID']]['FileHandle']

1486 try:

1487 if fileHandle == PIPE_FILE_DESCRIPTOR:

1488 connData['OpenedFiles'][comClose['FID']]['Socket'].close()

1489 elif fileHandle != VOID_FILE_DESCRIPTOR:

1490 os.close(fileHandle)

1491 except Exception as e:

1492 smbServer.log("comClose %s" % e, logging.ERROR)

1493 errorCode = STATUS_ACCESS_DENIED

1494 else:

1495 # Check if the file was marked for removal

1496 if connData['OpenedFiles'][comClose['FID']]['DeleteOnClose'] is True:

1497 try:

1498 os.remove(connData['OpenedFiles'][comClose['FID']]['FileName'])

1499 except Exception as e:

1500 smbServer.log("comClose %s" % e, logging.ERROR)

1501 errorCode = STATUS_ACCESS_DENIED

1502 del (connData['OpenedFiles'][comClose['FID']])

1503 else:

1504 errorCode = STATUS_INVALID_HANDLE

1505 else:

1506 errorCode = STATUS_SMB_BAD_TID

1507

1508 if errorCode > 0:

1509 respParameters = b''

1510 respData = b''

1511

1512 respSMBCommand['Parameters'] = respParameters

1513 respSMBCommand['Data'] = respData

1514 smbServer.setConnectionData(connId, connData)

1515

1516 return [respSMBCommand], None, errorCode

1517

1518 @staticmethod

1519 def smbComWrite(connId, smbServer, SMBCommand, recvPacket):

1520 connData = smbServer.getConnectionData(connId)

1521

1522 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE)

1523 respParameters = smb.SMBWriteResponse_Parameters()

1524 respData = b''

1525

1526 comWriteParameters = smb.SMBWrite_Parameters(SMBCommand['Parameters'])

1527 comWriteData = smb.SMBWrite_Data(SMBCommand['Data'])

1528

1529 # Get the Tid associated

1530 if recvPacket['Tid'] in connData['ConnectedShares']:

1531 if comWriteParameters['Fid'] in connData['OpenedFiles']:

1532 fileHandle = connData['OpenedFiles'][comWriteParameters['Fid']]['FileHandle']

1533 errorCode = STATUS_SUCCESS

1534 try:

1535 if fileHandle != PIPE_FILE_DESCRIPTOR:

1536 # TODO: Handle big size files

1537 # If we're trying to write past the file end we just skip the write call (Vista does this)

1538 if os.lseek(fileHandle, 0, 2) >= comWriteParameters['Offset']:

1539 os.lseek(fileHandle, comWriteParameters['Offset'], 0)

1540 os.write(fileHandle, comWriteData['Data'])

1541 else:

1542 sock = connData['OpenedFiles'][comWriteParameters['Fid']]['Socket']

1543 sock.send(comWriteData['Data'])

1544 respParameters['Count'] = comWriteParameters['Count']

1545 except Exception as e:

1546 smbServer.log('smbComWrite: %s' % e, logging.ERROR)

1547 errorCode = STATUS_ACCESS_DENIED

1548 else:

1549 errorCode = STATUS_INVALID_HANDLE

1550 else:

1551 errorCode = STATUS_SMB_BAD_TID

1552

1553 if errorCode > 0:

1554 respParameters = b''

1555 respData = b''

1556

1557 respSMBCommand['Parameters'] = respParameters

1558 respSMBCommand['Data'] = respData

1559 smbServer.setConnectionData(connId, connData)

1560

1561 return [respSMBCommand], None, errorCode

1562

1563 @staticmethod

1564 def smbComFlush(connId, smbServer, SMBCommand, recvPacket):

1565 connData = smbServer.getConnectionData(connId)

1566

1567 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_FLUSH)

1568 respParameters = b''

1569 respData = b''

1570

1571 comFlush = smb.SMBFlush_Parameters(SMBCommand['Parameters'])

1572

1573 # Get the Tid associated

1574 if recvPacket['Tid'] in connData['ConnectedShares']:

1575 if comFlush['FID'] in connData['OpenedFiles']:

1576 errorCode = STATUS_SUCCESS

1577 fileHandle = connData['OpenedFiles'][comFlush['FID']]['FileHandle']

1578 try:

1579 os.fsync(fileHandle)

1580 except Exception as e:

1581 smbServer.log("comFlush %s" % e, logging.ERROR)

1582 errorCode = STATUS_ACCESS_DENIED

1583 else:

1584 errorCode = STATUS_INVALID_HANDLE

1585 else:

1586 errorCode = STATUS_SMB_BAD_TID

1587

1588 if errorCode > 0:

1589 respParameters = b''

1590 respData = b''

1591

1592 respSMBCommand['Parameters'] = respParameters

1593 respSMBCommand['Data'] = respData

1594 smbServer.setConnectionData(connId, connData)

1595

1596 return [respSMBCommand], None, errorCode

1597

1598 @staticmethod

1599 def smbComCreateDirectory(connId, smbServer, SMBCommand, recvPacket):

1600 connData = smbServer.getConnectionData(connId)

1601

1602 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY)

1603 respParameters = b''

1604 respData = b''

1605

1606 comCreateDirectoryData = smb.SMBCreateDirectory_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])

1607

1608 # Get the Tid associated

1609 if recvPacket['Tid'] in connData['ConnectedShares']:

1610 errorCode = STATUS_SUCCESS

1611 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

1612 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comCreateDirectoryData['DirectoryName']))

1613 pathName = os.path.join(path, fileName)

1614

1615 if not isInFileJail(path, fileName):

1616 smbServer.log("Path not in current working directory", logging.ERROR)

1617 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD

1618

1619 elif os.path.exists(pathName):

1620 errorCode = STATUS_OBJECT_NAME_COLLISION

1621

1622 else:

1623 try:

1624 os.mkdir(pathName)

1625 except Exception as e:

1626 smbServer.log("smbComCreateDirectory: %s" % e, logging.ERROR)

1627 errorCode = STATUS_ACCESS_DENIED

1628 else:

1629 errorCode = STATUS_SMB_BAD_TID

1630

1631 if errorCode > 0:

1632 respParameters = b''

1633 respData = b''

1634

1635 respSMBCommand['Parameters'] = respParameters

1636 respSMBCommand['Data'] = respData

1637 smbServer.setConnectionData(connId, connData)

1638

1639 return [respSMBCommand], None, errorCode

1640

1641 @staticmethod

1642 def smbComRename(connId, smbServer, SMBCommand, recvPacket):

1643 connData = smbServer.getConnectionData(connId)

1644

1645 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_RENAME)

1646 respParameters = b''

1647 respData = b''

1648

1649 comRenameData = smb.SMBRename_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])

1650

1651 # Get the Tid associated

1652 if recvPacket['Tid'] in connData['ConnectedShares']:

1653 errorCode = STATUS_SUCCESS

1654 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

1655 oldFileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comRenameData['OldFileName']))

1656 oldPathName = os.path.join(path, oldFileName)

1657 newFileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comRenameData['NewFileName']))

1658 newPathName = os.path.join(path, newFileName)

1659

1660 if not isInFileJail(path, oldFileName) or not isInFileJail(path, newFileName):

1661 smbServer.log("Path not in current working directory", logging.ERROR)

1662 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD

1663

1664 elif not os.path.exists(oldPathName):

1665 errorCode = STATUS_NO_SUCH_FILE

1666

1667 else:

1668 try:

1669 os.rename(oldPathName, newPathName)

1670 except OSError as e:

1671 smbServer.log("smbComRename: %s" % e, logging.ERROR)

1672 errorCode = STATUS_ACCESS_DENIED

1673 else:

1674 errorCode = STATUS_SMB_BAD_TID

1675

1676 if errorCode > 0:

1677 respParameters = b''

1678 respData = b''

1679

1680 respSMBCommand['Parameters'] = respParameters

1681 respSMBCommand['Data'] = respData

1682 smbServer.setConnectionData(connId, connData)

1683

1684 return [respSMBCommand], None, errorCode

1685

1686 @staticmethod

1687 def smbComDelete(connId, smbServer, SMBCommand, recvPacket):

1688 connData = smbServer.getConnectionData(connId)

1689

1690 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE)

1691 respParameters = b''

1692 respData = b''

1693

1694 comDeleteData = smb.SMBDelete_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])

1695

1696 # Get the Tid associated

1697 if recvPacket['Tid'] in connData['ConnectedShares']:

1698 errorCode = STATUS_SUCCESS

1699 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

1700 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comDeleteData['FileName']))

1701 pathName = os.path.join(path, fileName)

1702

1703 if not isInFileJail(path, fileName):

1704 smbServer.log("Path not in current working directory", logging.ERROR)

1705 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD

1706

1707 elif not os.path.exists(pathName):

1708 errorCode = STATUS_NO_SUCH_FILE

1709

1710 else:

1711 try:

1712 os.remove(pathName)

1713 except OSError as e:

1714 smbServer.log("smbComDelete: %s" % e, logging.ERROR)

1715 errorCode = STATUS_ACCESS_DENIED

1716 else:

1717 errorCode = STATUS_SMB_BAD_TID

1718

1719 if errorCode > 0:

1720 respParameters = b''

1721 respData = b''

1722

1723 respSMBCommand['Parameters'] = respParameters

1724 respSMBCommand['Data'] = respData

1725 smbServer.setConnectionData(connId, connData)

1726

1727 return [respSMBCommand], None, errorCode

1728

1729 @staticmethod

1730 def smbComDeleteDirectory(connId, smbServer, SMBCommand, recvPacket):

1731 connData = smbServer.getConnectionData(connId)

1732

1733 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY)

1734 respParameters = b''

1735 respData = b''

1736

1737 comDeleteDirectoryData = smb.SMBDeleteDirectory_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])

1738

1739 # Get the Tid associated

1740 if recvPacket['Tid'] in connData['ConnectedShares']:

1741 errorCode = STATUS_SUCCESS

1742 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

1743 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comDeleteDirectoryData['DirectoryName']))

1744 pathName = os.path.join(path, fileName)

1745

1746 if not isInFileJail(path, fileName):

1747 smbServer.log("Path not in current working directory", logging.ERROR)

1748 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD

1749

1750 if os.path.exists(pathName) is not True:

1751 errorCode = STATUS_NO_SUCH_FILE

1752

1753 else:

1754 try:

1755 os.rmdir(pathName)

1756 except OSError as e:

1757 smbServer.log("smbComDeleteDirectory: %s" % e, logging.ERROR)

1758 if e.errno == errno.ENOTEMPTY:

1759 errorCode = STATUS_DIRECTORY_NOT_EMPTY

1760 else:

1761 errorCode = STATUS_ACCESS_DENIED

1762 else:

1763 errorCode = STATUS_SMB_BAD_TID

1764

1765 if errorCode > 0:

1766 respParameters = b''

1767 respData = b''

1768

1769 respSMBCommand['Parameters'] = respParameters

1770 respSMBCommand['Data'] = respData

1771 smbServer.setConnectionData(connId, connData)

1772

1773 return [respSMBCommand], None, errorCode

1774

1775 @staticmethod

1776 def smbComWriteAndX(connId, smbServer, SMBCommand, recvPacket):

1777 connData = smbServer.getConnectionData(connId)

1778

1779 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX)

1780 respParameters = smb.SMBWriteAndXResponse_Parameters()

1781 respData = b''

1782

1783 if SMBCommand['WordCount'] == 0x0C:

1784 writeAndX = smb.SMBWriteAndX_Parameters_Short(SMBCommand['Parameters'])

1785 writeAndXData = smb.SMBWriteAndX_Data_Short()

1786 else:

1787 writeAndX = smb.SMBWriteAndX_Parameters(SMBCommand['Parameters'])

1788 writeAndXData = smb.SMBWriteAndX_Data()

1789 writeAndXData['DataLength'] = writeAndX['DataLength']

1790 writeAndXData['DataOffset'] = writeAndX['DataOffset']

1791 writeAndXData.fromString(SMBCommand['Data'])

1792

1793 # Get the Tid associated

1794 if recvPacket['Tid'] in connData['ConnectedShares']:

1795 if writeAndX['Fid'] in connData['OpenedFiles']:

1796 fileHandle = connData['OpenedFiles'][writeAndX['Fid']]['FileHandle']

1797 errorCode = STATUS_SUCCESS

1798 try:

1799 if fileHandle != PIPE_FILE_DESCRIPTOR:

1800 offset = writeAndX['Offset']

1801 if 'HighOffset' in writeAndX.fields:

1802 offset += (writeAndX['HighOffset'] << 32)

1803 # If we're trying to write past the file end we just skip the write call (Vista does this)

1804 if os.lseek(fileHandle, 0, 2) >= offset:

1805 os.lseek(fileHandle, offset, 0)

1806 os.write(fileHandle, writeAndXData['Data'])

1807 else:

1808 sock = connData['OpenedFiles'][writeAndX['Fid']]['Socket']

1809 sock.send(writeAndXData['Data'])

1810

1811 respParameters['Count'] = writeAndX['DataLength']

1812 respParameters['Available'] = 0xff

1813 except Exception as e:

1814 smbServer.log('smbComWriteAndx: %s' % e, logging.ERROR)

1815 errorCode = STATUS_ACCESS_DENIED

1816 else:

1817 errorCode = STATUS_INVALID_HANDLE

1818 else:

1819 errorCode = STATUS_SMB_BAD_TID

1820

1821 if errorCode > 0:

1822 respParameters = b''

1823 respData = b''

1824

1825 respSMBCommand['Parameters'] = respParameters

1826 respSMBCommand['Data'] = respData

1827 smbServer.setConnectionData(connId, connData)

1828

1829 return [respSMBCommand], None, errorCode

1830

1831 @staticmethod

1832 def smbComRead(connId, smbServer, SMBCommand, recvPacket):

1833 connData = smbServer.getConnectionData(connId)

1834

1835 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ)

1836 respParameters = smb.SMBReadResponse_Parameters()

1837 respData = smb.SMBReadResponse_Data()

1838

1839 comReadParameters = smb.SMBRead_Parameters(SMBCommand['Parameters'])

1840

1841 # Get the Tid associated

1842 if recvPacket['Tid'] in connData['ConnectedShares']:

1843 if comReadParameters['Fid'] in connData['OpenedFiles']:

1844 fileHandle = connData['OpenedFiles'][comReadParameters['Fid']]['FileHandle']

1845 errorCode = STATUS_SUCCESS

1846 try:

1847 if fileHandle != PIPE_FILE_DESCRIPTOR:

1848 # TODO: Handle big size files

1849 os.lseek(fileHandle, comReadParameters['Offset'], 0)

1850 content = os.read(fileHandle, comReadParameters['Count'])

1851 else:

1852 sock = connData['OpenedFiles'][comReadParameters['Fid']]['Socket']

1853 content = sock.recv(comReadParameters['Count'])

1854 respParameters['Count'] = len(content)

1855 respData['DataLength'] = len(content)

1856 respData['Data'] = content

1857 except Exception as e:

1858 smbServer.log('smbComRead: %s ' % e, logging.ERROR)

1859 errorCode = STATUS_ACCESS_DENIED

1860 else:

1861 errorCode = STATUS_INVALID_HANDLE

1862 else:

1863 errorCode = STATUS_SMB_BAD_TID

1864

1865 if errorCode > 0:

1866 respParameters = b''

1867 respData = b''

1868

1869 respSMBCommand['Parameters'] = respParameters

1870 respSMBCommand['Data'] = respData

1871 smbServer.setConnectionData(connId, connData)

1872

1873 return [respSMBCommand], None, errorCode

1874

1875 @staticmethod

1876 def smbComReadAndX(connId, smbServer, SMBCommand, recvPacket):

1877 connData = smbServer.getConnectionData(connId)

1878

1879 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ_ANDX)

1880 respParameters = smb.SMBReadAndXResponse_Parameters()

1881 respData = b''

1882

1883 if SMBCommand['WordCount'] == 0x0A:

1884 readAndX = smb.SMBReadAndX_Parameters2(SMBCommand['Parameters'])

1885 else:

1886 readAndX = smb.SMBReadAndX_Parameters(SMBCommand['Parameters'])

1887

1888 # Get the Tid associated

1889 if recvPacket['Tid'] in connData['ConnectedShares']:

1890 if readAndX['Fid'] in connData['OpenedFiles']:

1891 fileHandle = connData['OpenedFiles'][readAndX['Fid']]['FileHandle']

1892 errorCode = 0

1893 try:

1894 if fileHandle != PIPE_FILE_DESCRIPTOR:

1895 offset = readAndX['Offset']

1896 if 'HighOffset' in readAndX.fields:

1897 offset += (readAndX['HighOffset'] << 32)

1898 os.lseek(fileHandle, offset, 0)

1899 content = os.read(fileHandle, readAndX['MaxCount'])

1900 else:

1901 sock = connData['OpenedFiles'][readAndX['Fid']]['Socket']

1902 content = sock.recv(readAndX['MaxCount'])

1903 respParameters['Remaining'] = 0xffff

1904 respParameters['DataCount'] = len(content)

1905 respParameters['DataOffset'] = 59

1906 respParameters['DataCount_Hi'] = 0

1907 respData = content

1908 except Exception as e:

1909 smbServer.log('smbComReadAndX: %s ' % e, logging.ERROR)

1910 errorCode = STATUS_ACCESS_DENIED

1911 else:

1912 errorCode = STATUS_INVALID_HANDLE

1913 else:

1914 errorCode = STATUS_SMB_BAD_TID

1915

1916 if errorCode > 0:

1917 respParameters = b''

1918 respData = b''

1919

1920 respSMBCommand['Parameters'] = respParameters

1921 respSMBCommand['Data'] = respData

1922 smbServer.setConnectionData(connId, connData)

1923

1924 return [respSMBCommand], None, errorCode

1925

1926 @staticmethod

1927 def smbQueryInformation(connId, smbServer, SMBCommand, recvPacket):

1928 connData = smbServer.getConnectionData(connId)

1929

1930 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION)

1931 respParameters = smb.SMBQueryInformationResponse_Parameters()

1932 respData = b''

1933

1934 queryInformation = smb.SMBQueryInformation_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])

1935

1936 # Get the Tid associated

1937 if recvPacket['Tid'] in connData['ConnectedShares']:

1938 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

1939 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], queryInformation['FileName']))

1940 if not isInFileJail(path, fileName):

1941 smbServer.log("Path not in current working directory", logging.ERROR)

1942 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD

1943

1944 else:

1945 fileSize, lastWriteTime, fileAttributes = queryFsInformation(path, fileName, pktFlags=recvPacket['Flags2'])

1946

1947 respParameters['FileSize'] = fileSize

1948 respParameters['LastWriteTime'] = lastWriteTime

1949 respParameters['FileAttributes'] = fileAttributes

1950 errorCode = STATUS_SUCCESS

1951 else:

1952 errorCode = STATUS_SMB_BAD_TID

1953

1954 if errorCode > 0:

1955 respParameters = b''

1956 respData = b''

1957

1958 respSMBCommand['Parameters'] = respParameters

1959 respSMBCommand['Data'] = respData

1960

1961 smbServer.setConnectionData(connId, connData)

1962 return [respSMBCommand], None, errorCode

1963

1964 @staticmethod

1965 def smbQueryInformationDisk(connId, smbServer, SMBCommand, recvPacket):

1966 connData = smbServer.getConnectionData(connId)

1967

1968 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION_DISK)

1969 respParameters = smb.SMBQueryInformationDiskResponse_Parameters()

1970 respData = b''

1971

1972 # Get the Tid associated

1973 if recvPacket['Tid'] in connData['ConnectedShares']:

1974 totalUnits, freeUnits = queryDiskInformation(

1975 connData['ConnectedShares'][recvPacket['Tid']]['path'])

1976

1977 respParameters['TotalUnits'] = totalUnits

1978 respParameters['BlocksPerUnit'] = 1

1979 respParameters['BlockSize'] = 1

1980 respParameters['FreeUnits'] = freeUnits

1981 errorCode = STATUS_SUCCESS

1982 else:

1983 errorCode = STATUS_SMB_BAD_TID

1984

1985 if errorCode > 0:

1986 respData = b''

1987 respParameters = b''

1988

1989 respSMBCommand['Parameters'] = respParameters

1990 respSMBCommand['Data'] = respData

1991

1992 smbServer.setConnectionData(connId, connData)

1993 return [respSMBCommand], None, errorCode

1994

1995 @staticmethod

1996 def smbComEcho(connId, smbServer, SMBCommand, recvPacket):

1997 connData = smbServer.getConnectionData(connId)

1998

1999 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)

2000 respParameters = smb.SMBEchoResponse_Parameters()

2001 respData = smb.SMBEchoResponse_Data()

2002

2003 echoData = smb.SMBEcho_Data(SMBCommand['Data'])

2004

2005 respParameters['SequenceNumber'] = 1

2006 respData['Data'] = echoData['Data']

2007

2008 respSMBCommand['Parameters'] = respParameters

2009 respSMBCommand['Data'] = respData

2010

2011 errorCode = STATUS_SUCCESS

2012 smbServer.setConnectionData(connId, connData)

2013 return [respSMBCommand], None, errorCode

2014

2015 @staticmethod

2016 def smbComTreeDisconnect(connId, smbServer, SMBCommand, recvPacket):

2017 connData = smbServer.getConnectionData(connId)

2018

2019 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_DISCONNECT)

2020

2021 # Check if the Tid matches the Tid trying to disconnect

2022 respParameters = b''

2023 respData = b''

2024

2025 if recvPacket['Tid'] in connData['ConnectedShares']:

2026 smbServer.log("Disconnecting Share(%d:%s)" % (

2027 recvPacket['Tid'], connData['ConnectedShares'][recvPacket['Tid']]['shareName']))

2028 del (connData['ConnectedShares'][recvPacket['Tid']])

2029 errorCode = STATUS_SUCCESS

2030 else:

2031 errorCode = STATUS_SMB_BAD_TID

2032

2033 respSMBCommand['Parameters'] = respParameters

2034 respSMBCommand['Data'] = respData

2035

2036 smbServer.setConnectionData(connId, connData)

2037 return [respSMBCommand], None, errorCode

2038

2039 @staticmethod

2040 def smbComLogOffAndX(connId, smbServer, SMBCommand, recvPacket):

2041 connData = smbServer.getConnectionData(connId)

2042

2043 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOGOFF_ANDX)

2044

2045 # Check if the Uid matches the user trying to logoff

2046 respParameters = b''

2047 respData = b''

2048 if recvPacket['Uid'] != connData['Uid']:

2049 errorCode = STATUS_SMB_BAD_UID

2050 else:

2051 errorCode = STATUS_SUCCESS

2052

2053 respSMBCommand['Parameters'] = respParameters

2054 respSMBCommand['Data'] = respData

2055 connData['Uid'] = 0

2056 connData['Authenticated'] = False

2057

2058 smbServer.setConnectionData(connId, connData)

2059

2060 return [respSMBCommand], None, errorCode

2061

2062 @staticmethod

2063 def smbComQueryInformation2(connId, smbServer, SMBCommand, recvPacket):

2064 connData = smbServer.getConnectionData(connId)

2065

2066 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION2)

2067 respParameters = smb.SMBQueryInformation2Response_Parameters()

2068 respData = b''

2069

2070 queryInformation2 = smb.SMBQueryInformation2_Parameters(SMBCommand['Parameters'])

2071 errorCode = 0xFF

2072

2073 # Get the Tid associated

2074 if recvPacket['Tid'] in connData['ConnectedShares']:

2075 if queryInformation2['Fid'] in connData['OpenedFiles']:

2076 errorCode = STATUS_SUCCESS

2077 pathName = connData['OpenedFiles'][queryInformation2['Fid']]['FileName']

2078 try:

2079 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)

2080 respParameters['CreateDate'] = getSMBDate(ctime)

2081 respParameters['CreationTime'] = getSMBTime(ctime)

2082 respParameters['LastAccessDate'] = getSMBDate(atime)

2083 respParameters['LastAccessTime'] = getSMBTime(atime)

2084 respParameters['LastWriteDate'] = getSMBDate(mtime)

2085 respParameters['LastWriteTime'] = getSMBTime(mtime)

2086 respParameters['FileDataSize'] = size

2087 respParameters['FileAllocationSize'] = size

2088 attribs = 0

2089 if os.path.isdir(pathName):

2090 attribs = smb.SMB_FILE_ATTRIBUTE_DIRECTORY

2091 if os.path.isfile(pathName):

2092 attribs = smb.SMB_FILE_ATTRIBUTE_NORMAL

2093 respParameters['FileAttributes'] = attribs

2094 except Exception as e:

2095 smbServer.log('smbComQueryInformation2 %s' % e, logging.ERROR)

2096 errorCode = STATUS_ACCESS_DENIED

2097 else:

2098 errorCode = STATUS_SMB_BAD_TID

2099

2100 if errorCode > 0:

2101 respParameters = b''

2102 respData = b''

2103

2104 respSMBCommand['Parameters'] = respParameters

2105 respSMBCommand['Data'] = respData

2106 smbServer.setConnectionData(connId, connData)

2107

2108 return [respSMBCommand], None, errorCode

2109

2110 @staticmethod

2111 def smbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket):

2112 # TODO: Fully implement this

2113 connData = smbServer.getConnectionData(connId)

2114

2115 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX)

2116 respParameters = smb.SMBNtCreateAndXResponse_Parameters()

2117 respData = b''

2118

2119 ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters'])

2120 ntCreateAndXData = smb.SMBNtCreateAndX_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])

2121

2122 # if ntCreateAndXParameters['CreateFlags'] & 0x10: # NT_CREATE_REQUEST_EXTENDED_RESPONSE

2123 # respParameters = smb.SMBNtCreateAndXExtendedResponse_Parameters()

2124 # respParameters['VolumeGUID'] = '\x00'

2125

2126 # Get the Tid associated

2127 if recvPacket['Tid'] in connData['ConnectedShares']:

2128 # If we have a rootFid, the path is relative to that fid

2129 errorCode = STATUS_SUCCESS

2130 if ntCreateAndXParameters['RootFid'] > 0:

2131 path = connData['OpenedFiles'][ntCreateAndXParameters['RootFid']]['FileName']

2132 LOG.debug("RootFid present %s!" % path)

2133 else:

2134 if 'path' in connData['ConnectedShares'][recvPacket['Tid']]:

2135 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

2136 else:

2137 path = 'NONE'

2138 errorCode = STATUS_ACCESS_DENIED

2139

2140 deleteOnClose = False

2141

2142 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], ntCreateAndXData['FileName']))

2143 if not isInFileJail(path, fileName):

2144 LOG.error("Path not in current working directory")

2145 respSMBCommand['Parameters'] = b''

2146 respSMBCommand['Data'] = b''

2147 return [respSMBCommand], None, STATUS_OBJECT_PATH_SYNTAX_BAD

2148

2149 pathName = os.path.join(path, fileName)

2150 createDisposition = ntCreateAndXParameters['Disposition']

2151 mode = 0

2152

2153 if createDisposition == smb.FILE_SUPERSEDE:

2154 mode |= os.O_TRUNC | os.O_CREAT

2155 elif createDisposition & smb.FILE_OVERWRITE_IF == smb.FILE_OVERWRITE_IF:

2156 mode |= os.O_TRUNC | os.O_CREAT

2157 elif createDisposition & smb.FILE_OVERWRITE == smb.FILE_OVERWRITE:

2158 if os.path.exists(pathName) is True:

2159 mode |= os.O_TRUNC

2160 else:

2161 errorCode = STATUS_NO_SUCH_FILE

2162 elif createDisposition & smb.FILE_OPEN_IF == smb.FILE_OPEN_IF:

2163 if os.path.exists(pathName) is True:

2164 mode |= os.O_TRUNC

2165 else:

2166 mode |= os.O_TRUNC | os.O_CREAT

2167 elif createDisposition & smb.FILE_CREATE == smb.FILE_CREATE:

2168 if os.path.exists(pathName) is True:

2169 errorCode = STATUS_OBJECT_NAME_COLLISION

2170 else:

2171 mode |= os.O_CREAT

2172 elif createDisposition & smb.FILE_OPEN == smb.FILE_OPEN:

2173 if os.path.exists(pathName) is not True and (

2174 str(pathName) in smbServer.getRegisteredNamedPipes()) is not True:

2175 errorCode = STATUS_NO_SUCH_FILE

2176

2177 if errorCode == STATUS_SUCCESS:

2178 desiredAccess = ntCreateAndXParameters['AccessMask']

2179 if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ):

2180 mode |= os.O_RDONLY

2181 if (desiredAccess & smb.FILE_WRITE_DATA) or (desiredAccess & smb.GENERIC_WRITE):

2182 if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ):

2183 mode |= os.O_RDWR # | os.O_APPEND

2184 else:

2185 mode |= os.O_WRONLY # | os.O_APPEND

2186 if desiredAccess & smb.GENERIC_ALL:

2187 mode |= os.O_RDWR # | os.O_APPEND

2188

2189 createOptions = ntCreateAndXParameters['CreateOptions']

2190 if mode & os.O_CREAT == os.O_CREAT:

2191 if createOptions & smb.FILE_DIRECTORY_FILE == smb.FILE_DIRECTORY_FILE:

2192 try:

2193 # Let's create the directory

2194 os.mkdir(pathName)

2195 mode = os.O_RDONLY

2196 except Exception as e:

2197 smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName, mode, e), logging.ERROR)

2198 errorCode = STATUS_ACCESS_DENIED

2199 if createOptions & smb.FILE_NON_DIRECTORY_FILE == smb.FILE_NON_DIRECTORY_FILE:

2200 # If the file being opened is a directory, the server MUST fail the request with

2201 # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server

2202 # response.

2203 if os.path.isdir(pathName) is True:

2204 errorCode = STATUS_FILE_IS_A_DIRECTORY

2205

2206 if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE:

2207 deleteOnClose = True

2208

2209 if errorCode == STATUS_SUCCESS:

2210 try:

2211 if os.path.isdir(pathName) and sys.platform == 'win32':

2212 fid = VOID_FILE_DESCRIPTOR

2213 else:

2214 if sys.platform == 'win32':

2215 mode |= os.O_BINARY

2216 if str(pathName) in smbServer.getRegisteredNamedPipes():

2217 fid = PIPE_FILE_DESCRIPTOR

2218 sock = socket.socket()

2219 sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)])

2220 else:

2221 fid = os.open(pathName, mode)

2222 except Exception as e:

2223 smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName, mode, e), logging.ERROR)

2224 # print e

2225 fid = 0

2226 errorCode = STATUS_ACCESS_DENIED

2227 else:

2228 errorCode = STATUS_SMB_BAD_TID

2229

2230 if errorCode == STATUS_SUCCESS:

2231 # Simple way to generate a fid

2232 if len(connData['OpenedFiles']) == 0:

2233 fakefid = 1

2234 else:

2235 fakefid = list(connData['OpenedFiles'].keys())[-1] + 1

2236 respParameters['Fid'] = fakefid

2237 respParameters['CreateAction'] = createDisposition

2238 if fid == PIPE_FILE_DESCRIPTOR:

2239 respParameters['FileAttributes'] = 0x80

2240 respParameters['IsDirectory'] = 0

2241 respParameters['CreateTime'] = 0

2242 respParameters['LastAccessTime'] = 0

2243 respParameters['LastWriteTime'] = 0

2244 respParameters['LastChangeTime'] = 0

2245 respParameters['AllocationSize'] = 4096

2246 respParameters['EndOfFile'] = 0

2247 respParameters['FileType'] = 2

2248 respParameters['IPCState'] = 0x5ff

2249 else:

2250 if os.path.isdir(pathName):

2251 respParameters['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY

2252 respParameters['IsDirectory'] = 1

2253 else:

2254 respParameters['IsDirectory'] = 0

2255 respParameters['FileAttributes'] = ntCreateAndXParameters['FileAttributes']

2256 # Let's get this file's information

2257 respInfo, errorCode = queryPathInformation(path, fileName, level=smb.SMB_QUERY_FILE_ALL_INFO)

2258 if errorCode == STATUS_SUCCESS:

2259 respParameters['CreateTime'] = respInfo['CreationTime']

2260 respParameters['LastAccessTime'] = respInfo['LastAccessTime']

2261 respParameters['LastWriteTime'] = respInfo['LastWriteTime']

2262 respParameters['LastChangeTime'] = respInfo['LastChangeTime']

2263 respParameters['FileAttributes'] = respInfo['ExtFileAttributes']

2264 respParameters['AllocationSize'] = respInfo['AllocationSize']

2265 respParameters['EndOfFile'] = respInfo['EndOfFile']

2266 else:

2267 respParameters = b''

2268 respData = b''

2269

2270 if errorCode == STATUS_SUCCESS:

2271 # Let's store the fid for the connection

2272 # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode))

2273 connData['OpenedFiles'][fakefid] = {}

2274 connData['OpenedFiles'][fakefid]['FileHandle'] = fid

2275 connData['OpenedFiles'][fakefid]['FileName'] = pathName

2276 connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose

2277 if fid == PIPE_FILE_DESCRIPTOR:

2278 connData['OpenedFiles'][fakefid]['Socket'] = sock

2279 else:

2280 respParameters = b''

2281 respData = b''

2282

2283 respSMBCommand['Parameters'] = respParameters

2284 respSMBCommand['Data'] = respData

2285 smbServer.setConnectionData(connId, connData)

2286

2287 return [respSMBCommand], None, errorCode

2288

2289 @staticmethod

2290 def smbComOpenAndX(connId, smbServer, SMBCommand, recvPacket):

2291 connData = smbServer.getConnectionData(connId)

2292

2293 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_OPEN_ANDX)

2294 respParameters = smb.SMBOpenAndXResponse_Parameters()

2295 respData = b''

2296

2297 openAndXParameters = smb.SMBOpenAndX_Parameters(SMBCommand['Parameters'])

2298 openAndXData = smb.SMBOpenAndX_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])

2299

2300 # Get the Tid associated

2301 if recvPacket['Tid'] in connData['ConnectedShares']:

2302 path = connData['ConnectedShares'][recvPacket['Tid']]['path']

2303 openedFile, mode, pathName, errorCode = openFile(path,

2304 decodeSMBString(recvPacket['Flags2'],

2305 openAndXData['FileName']),

2306 openAndXParameters['DesiredAccess'],

2307 openAndXParameters['FileAttributes'],

2308 openAndXParameters['OpenMode'])

2309 else:

2310 errorCode = STATUS_SMB_BAD_TID

2311

2312 if errorCode == STATUS_SUCCESS:

2313 # Simple way to generate a fid

2314 fid = len(connData['OpenedFiles']) + 1

2315 if len(connData['OpenedFiles']) == 0:

2316 fid = 1

2317 else:

2318 fid = list(connData['OpenedFiles'].keys())[-1] + 1

2319 respParameters['Fid'] = fid

2320 if mode & os.O_CREAT:

2321 # File did not exist and was created

2322 respParameters['Action'] = 0x2

2323 elif mode & os.O_RDONLY:

2324 # File existed and was opened

2325 respParameters['Action'] = 0x1

2326 elif mode & os.O_APPEND:

2327 # File existed and was opened

2328 respParameters['Action'] = 0x1

2329 else:

2330 # File existed and was truncated

2331 respParameters['Action'] = 0x3

2332

2333 # Let's store the fid for the connection

2334 # smbServer.log('Opening file %s' % pathName)

2335 connData['OpenedFiles'][fid] = {}

2336 connData['OpenedFiles'][fid]['FileHandle'] = openedFile

2337 connData['OpenedFiles'][fid]['FileName'] = pathName

2338 connData['OpenedFiles'][fid]['DeleteOnClose'] = False

2339 else:

2340 respParameters = b''

2341 respData = b''

2342

2343 respSMBCommand['Parameters'] = respParameters

2344 respSMBCommand['Data'] = respData

2345 smbServer.setConnectionData(connId, connData)

2346

2347 return [respSMBCommand], None, errorCode

2348

2349 @staticmethod

2350 def smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket):

2351 connData = smbServer.getConnectionData(connId)

2352

2353 resp = smb.NewSMBPacket()

2354 resp['Flags1'] = smb.SMB.FLAGS1_REPLY

2355 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \

2356 recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE

2357

2358 resp['Tid'] = recvPacket['Tid']

2359 resp['Mid'] = recvPacket['Mid']

2360 resp['Pid'] = connData['Pid']

2361

2362 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX)

2363 respParameters = smb.SMBTreeConnectAndXResponse_Parameters()

2364 respData = smb.SMBTreeConnectAndXResponse_Data()

2365

2366 treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])

2367

2368 if treeConnectAndXParameters['Flags'] & 0x8:

2369 respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()

2370

2371 treeConnectAndXData = smb.SMBTreeConnectAndX_Data(flags=recvPacket['Flags2'])

2372 treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']

2373 treeConnectAndXData.fromString(SMBCommand['Data'])

2374

2375 errorCode = STATUS_SUCCESS

2376

2377 ## Process here the request, does the share exist?

2378 UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])

2379

2380 # Is this a UNC?

2381 if ntpath.ismount(UNCOrShare):

2382 path = UNCOrShare.split('\\')[3]

2383 else:

2384 path = ntpath.basename(UNCOrShare)

2385

2386 share = searchShare(connId, path, smbServer)

2387 if share is not None:

2388 # Simple way to generate a Tid

2389 if len(connData['ConnectedShares']) == 0:

2390 tid = 1

2391 else:

2392 tid = list(connData['ConnectedShares'].keys())[-1] + 1

2393 connData['ConnectedShares'][tid] = share

2394 connData['ConnectedShares'][tid]['shareName'] = path

2395 resp['Tid'] = tid

2396 # smbServer.log("Connecting Share(%d:%s)" % (tid,path))

2397 else:

2398 smbServer.log("TreeConnectAndX not found %s" % path, logging.ERROR)

2399 errorCode = STATUS_OBJECT_PATH_NOT_FOUND

2400 resp['ErrorCode'] = errorCode >> 16

2401 resp['ErrorClass'] = errorCode & 0xff

2402 ##

2403 respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS

2404

2405 if path == 'IPC$':

2406 respData['Service'] = 'IPC'

2407 else:

2408 respData['Service'] = path

2409 respData['PadLen'] = 0

2410 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS').decode()

2411

2412 respSMBCommand['Parameters'] = respParameters

2413 respSMBCommand['Data'] = respData

2414

2415 resp['Uid'] = connData['Uid']

2416 resp.addCommand(respSMBCommand)

2417

2418 # Sign the packet if needed

2419 if connData['SignatureEnabled']:

2420 smbServer.signSMBv1(connData, resp, connData['SigningSessionKey'], connData['SigningChallengeResponse'])

2421 smbServer.setConnectionData(connId, connData)

2422

2423 return None, [resp], errorCode

2424

2425 @staticmethod

2426 def smbComSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket):

2427 connData = smbServer.getConnectionData(connId, checkStatus=False)

2428

2429 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)

2430

2431 # From [MS-SMB]

2432 # When extended security is being used (see section 3.2.4.2.4), the

2433 # request MUST take the following form

2434 # [..]

2435 # WordCount (1 byte): The value of this field MUST be 0x0C.

2436 if SMBCommand['WordCount'] == 12:

2437 # Extended security. Here we deal with all SPNEGO stuff

2438 respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters()

2439 respData = smb.SMBSessionSetupAndX_Extended_Response_Data(flags=recvPacket['Flags2'])

2440 sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters'])

2441 sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()

2442 sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']

2443 sessionSetupData.fromString(SMBCommand['Data'])

2444 connData['Capabilities'] = sessionSetupParameters['Capabilities']

2445

2446 rawNTLM = False

2447 if struct.unpack('B', sessionSetupData['SecurityBlob'][0:1])[0] == ASN1_AID:

2448 # NEGOTIATE packet

2449 blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob'])

2450 token = blob['MechToken']

2451 if len(blob['MechTypes'][0]) > 0:

2452 # Is this GSSAPI NTLM or something else we don't support?

2453 mechType = blob['MechTypes'][0]

2454 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']:

2455 # Nope, do we know it?

2456 if mechType in MechTypes:

2457 mechStr = MechTypes[mechType]

2458 else:

2459 mechStr = hexlify(mechType)

2460 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)

2461 # We don't know the token, we answer back again saying

2462 # we just support NTLM.

2463 # ToDo: Build this into a SPNEGO_NegTokenResp()

2464 respToken = b'\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a'

2465 respParameters['SecurityBlobLength'] = len(respToken)

2466 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']

2467 respData['SecurityBlob'] = respToken

2468 respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())

2469 respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())

2470 respSMBCommand['Parameters'] = respParameters

2471 respSMBCommand['Data'] = respData

2472 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED

2473

2474 elif struct.unpack('B', sessionSetupData['SecurityBlob'][0:1])[0] == ASN1_SUPPORTED_MECH:

2475 # AUTH packet

2476 blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob'])

2477 token = blob['ResponseToken']

2478 else:

2479 # No GSSAPI stuff, raw NTLMSSP

2480 rawNTLM = True

2481 token = sessionSetupData['SecurityBlob']

2482

2483 # Here we only handle NTLMSSP, depending on what stage of the

2484 # authentication we are, we act on it

2485 messageType = struct.unpack('<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0]

2486

2487 if messageType == 0x01:

2488 # NEGOTIATE_MESSAGE

2489 negotiateMessage = ntlm.NTLMAuthNegotiate()

2490 negotiateMessage.fromString(token)

2491 # Let's store it in the connection data

2492 connData['NEGOTIATE_MESSAGE'] = negotiateMessage

2493 # Let's build the answer flags

2494 # TODO: Parse all the flags. With this we're leaving some clients out

2495

2496 ansFlags = 0

2497

2498 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:

2499 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56

2500 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:

2501 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128

2502 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:

2503 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH

2504 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:

2505 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY

2506 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:

2507 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE

2508 if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:

2509 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM

2510

2511 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET

2512

2513 # Generate the AV_PAIRS

2514 av_pairs = ntlm.AV_PAIRS()

2515 # TODO: Put the proper data from SMBSERVER config

2516 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[

2517 ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')

2518 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[

2519 ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')

2520 av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (

2521 116444736000000000 + calendar.timegm(time.gmtime()) * 10000000))

2522

2523 challengeMessage = ntlm.NTLMAuthChallenge()

2524 challengeMessage['flags'] = ansFlags

2525 challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le'))

2526 challengeMessage['domain_max_len'] = challengeMessage['domain_len']

2527 challengeMessage['domain_offset'] = 40 + 16

2528 challengeMessage['challenge'] = smbServer.getSMBChallenge()

2529 challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le')

2530 challengeMessage['TargetInfoFields_len'] = len(av_pairs)

2531 challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)

2532 challengeMessage['TargetInfoFields'] = av_pairs

2533 challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name'])

2534 challengeMessage['Version'] = b'\xff' * 8

2535 challengeMessage['VersionLen'] = 8

2536

2537 if rawNTLM is False:

2538 respToken = SPNEGO_NegTokenResp()

2539 # accept-incomplete. We want more data

2540 respToken['NegState'] = b'\x01'

2541 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']

2542

2543 respToken['ResponseToken'] = challengeMessage.getData()

2544 else:

2545 respToken = challengeMessage

2546

2547 # Setting the packet to STATUS_MORE_PROCESSING

2548 errorCode = STATUS_MORE_PROCESSING_REQUIRED

2549 # Let's set up an UID for this connection and store it

2550 # in the connection's data

2551 # Picking a fixed value

2552 # TODO: Manage more UIDs for the same session

2553 connData['Uid'] = 10

2554 # Let's store it in the connection data

2555 connData['CHALLENGE_MESSAGE'] = challengeMessage

2556

2557 elif messageType == 0x02:

2558 # CHALLENGE_MESSAGE

2559 raise Exception('Challenge Message raise, not implemented!')

2560 elif messageType == 0x03:

2561 # AUTHENTICATE_MESSAGE, here we deal with authentication

2562 authenticateMessage = ntlm.NTLMAuthChallengeResponse()

2563 authenticateMessage.fromString(token)

2564 smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (

2565 authenticateMessage['domain_name'].decode('utf-16le'),

2566 authenticateMessage['user_name'].decode('utf-16le'),

2567 authenticateMessage['host_name'].decode('utf-16le')))

2568 # Do we have credentials to check?

2569 if len(smbServer.getCredentials()) > 0:

2570 identity = authenticateMessage['user_name'].decode('utf-16le').lower()

2571 # Do we have this user's credentials?

2572 if identity in smbServer.getCredentials():

2573 # Process data:

2574 # Let's parse some data and keep it to ourselves in case it is asked

2575 uid, lmhash, nthash = smbServer.getCredentials()[identity]

2576

2577 errorCode, sessionKey = computeNTLMv2(identity, lmhash, nthash, smbServer.getSMBChallenge(),

2578 authenticateMessage, connData['CHALLENGE_MESSAGE'],

2579 connData['NEGOTIATE_MESSAGE'])

2580

2581 if sessionKey is not None:

2582 connData['SignatureEnabled'] = False

2583 connData['SigningSessionKey'] = sessionKey

2584 connData['SignSequenceNumber'] = 1

2585 else:

2586 errorCode = STATUS_LOGON_FAILURE

2587 else:

2588 # No credentials provided, let's grant access

2589 errorCode = STATUS_SUCCESS

2590

2591 if errorCode == STATUS_SUCCESS:

2592 connData['Authenticated'] = True

2593 respToken = SPNEGO_NegTokenResp()

2594 # accept-completed

2595 respToken['NegState'] = b'\x00'

2596

2597 smbServer.log(

2598 'User %s\\%s authenticated successfully' % (authenticateMessage['host_name'].decode('utf-16le'),

2599 authenticateMessage['user_name'].decode(

2600 'utf-16le')))

2601 # Let's store it in the connection data

2602 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage

2603 try:

2604 jtr_dump_path = smbServer.getJTRdumpPath()

2605 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],

2606 authenticateMessage['user_name'],

2607 authenticateMessage['domain_name'],

2608 authenticateMessage['lanman'], authenticateMessage['ntlm'])

2609 smbServer.log(ntlm_hash_data['hash_string'])

2610 if jtr_dump_path != '':

2611 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],

2612 jtr_dump_path)

2613 except:

2614 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)

2615 else:

2616 respToken = SPNEGO_NegTokenResp()

2617 respToken['NegState'] = b'\x02'

2618 smbServer.log("Could not authenticate user!")

2619 else:

2620 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)

2621

2622 respParameters['SecurityBlobLength'] = len(respToken)

2623 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']

2624 respData['SecurityBlob'] = respToken.getData()

2625

2626 else:

2627 # Process Standard Security

2628 respParameters = smb.SMBSessionSetupAndXResponse_Parameters()

2629 respData = smb.SMBSessionSetupAndXResponse_Data()

2630 sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters'])

2631 sessionSetupData = smb.SMBSessionSetupAndX_Data()

2632 sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']

2633 sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']

2634 sessionSetupData.fromString(SMBCommand['Data'])

2635 connData['Capabilities'] = sessionSetupParameters['Capabilities']

2636 # Do the verification here, for just now we grant access

2637 # TODO: Manage more UIDs for the same session

2638 errorCode = STATUS_SUCCESS

2639 connData['Uid'] = 10

2640 connData['Authenticated'] = True

2641 respParameters['Action'] = 0

2642 smbServer.log('User %s\\%s authenticated successfully (basic)' % (

2643 sessionSetupData['PrimaryDomain'], sessionSetupData['Account']))

2644 try:

2645 jtr_dump_path = smbServer.getJTRdumpPath()

2646 ntlm_hash_data = outputToJohnFormat(b'', b(sessionSetupData['Account']),

2647 b(sessionSetupData['PrimaryDomain']), sessionSetupData['AnsiPwd'],

2648 sessionSetupData['UnicodePwd'])

2649 smbServer.log(ntlm_hash_data['hash_string'])

2650 if jtr_dump_path != '':

2651 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path)

2652 except:

2653 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)

2654

2655 respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())

2656 respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())

2657 respSMBCommand['Parameters'] = respParameters

2658 respSMBCommand['Data'] = respData

2659

2660 # From now on, the client can ask for other commands

2661 connData['Authenticated'] = True

2662 # For now, just switching to nobody

2663 # os.setregid(65534,65534)

2664 # os.setreuid(65534,65534)

2665 smbServer.setConnectionData(connId, connData)

2666

2667 return [respSMBCommand], None, errorCode

2668

2669 @staticmethod

2670 def smbComNegotiate(connId, smbServer, SMBCommand, recvPacket):

2671 connData = smbServer.getConnectionData(connId, checkStatus=False)

2672 connData['Pid'] = recvPacket['Pid']

2673

2674 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])

2675 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE)

2676

2677 resp = smb.NewSMBPacket()

2678 resp['Flags1'] = smb.SMB.FLAGS1_REPLY

2679 resp['Pid'] = connData['Pid']

2680 resp['Tid'] = recvPacket['Tid']

2681 resp['Mid'] = recvPacket['Mid']

2682

2683 # TODO: We support more dialects, and parse them accordingly

2684 dialects = SMBCommand['Data'].split(b'\x02')

2685 try:

2686 index = dialects.index(b'NT LM 0.12\x00') - 1

2687 # Let's fill the data for NTLM

2688 if recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY:

2689 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE

2690 # resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS

2691 _dialects_data = smb.SMBExtended_Security_Data()

2692 _dialects_data['ServerGUID'] = b'A' * 16

2693 blob = SPNEGO_NegTokenInit()

2694 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]

2695 _dialects_data['SecurityBlob'] = blob.getData()

2696

2697 _dialects_parameters = smb.SMBExtended_Security_Parameters()

2698 _dialects_parameters[

2699 'Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS | smb.SMB.CAP_UNICODE

2700 _dialects_parameters['ChallengeLength'] = 0

2701

2702 else:

2703 resp['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE

2704 _dialects_parameters = smb.SMBNTLMDialect_Parameters()

2705 _dialects_data = smb.SMBNTLMDialect_Data()

2706 _dialects_data['Payload'] = ''

2707 if 'EncryptionKey' in connData:

2708 _dialects_data['Challenge'] = connData['EncryptionKey']

2709 _dialects_parameters['ChallengeLength'] = len(_dialects_data.getData())

2710 else:

2711 # TODO: Handle random challenges, now one that can be used with rainbow tables

2712 _dialects_data['Challenge'] = b'\x11\x22\x33\x44\x55\x66\x77\x88'

2713 _dialects_parameters['ChallengeLength'] = 8

2714 _dialects_parameters['Capabilities'] = smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS

2715

2716 # Let's see if we need to support RPC_REMOTE_APIS

2717 config = smbServer.getServerConfig()

2718 if config.has_option('global', 'rpc_apis'):

2719 if config.getboolean('global', 'rpc_apis') is True:

2720 _dialects_parameters['Capabilities'] |= smb.SMB.CAP_RPC_REMOTE_APIS

2721

2722 _dialects_parameters['DialectIndex'] = index

2723 # _dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER | smb.SMB.SECURITY_SIGNATURES_REQUIRED

2724 _dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER

2725 _dialects_parameters['MaxMpxCount'] = 1

2726 _dialects_parameters['MaxNumberVcs'] = 1

2727 _dialects_parameters['MaxBufferSize'] = 64000

2728 _dialects_parameters['MaxRawSize'] = 65536

2729 _dialects_parameters['SessionKey'] = 0

2730 _dialects_parameters['LowDateTime'] = 0

2731 _dialects_parameters['HighDateTime'] = 0

2732 _dialects_parameters['ServerTimeZone'] = 0

2733

2734 respSMBCommand['Data'] = _dialects_data

2735 respSMBCommand['Parameters'] = _dialects_parameters

2736 connData['_dialects_data'] = _dialects_data

2737 connData['_dialects_parameters'] = _dialects_parameters

2738

2739 except Exception as e:

2740 # No NTLM throw an error

2741 smbServer.log('smbComNegotiate: %s' % e, logging.ERROR)

2742 respSMBCommand['Data'] = struct.pack('<H', 0xffff)

2743

2744 smbServer.setConnectionData(connId, connData)

2745

2746 resp.addCommand(respSMBCommand)

2747

2748 return None, [resp], STATUS_SUCCESS

2749

2750 @staticmethod

2751 def default(connId, smbServer, SMBCommand, recvPacket):

2752 # By default we return an SMB Packet with error not implemented

2753 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'], logging.DEBUG)

2754 packet = smb.NewSMBPacket()

2755 packet['Flags1'] = smb.SMB.FLAGS1_REPLY

2756 packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS

2757 packet['Command'] = recvPacket['Command']

2758 packet['Pid'] = recvPacket['Pid']

2759 packet['Tid'] = recvPacket['Tid']

2760 packet['Mid'] = recvPacket['Mid']

2761 packet['Uid'] = recvPacket['Uid']

2762 packet['Data'] = b'\x00\x00\x00'

2763 errorCode = STATUS_NOT_IMPLEMENTED

2764 packet['ErrorCode'] = errorCode >> 16

2765 packet['ErrorClass'] = errorCode & 0xff

2766

2767 return None, [packet], errorCode

2768

2769

2770 class SMB2Commands:

2771 @staticmethod

2772 def smb2Negotiate(connId, smbServer, recvPacket, isSMB1=False):

2773 connData = smbServer.getConnectionData(connId, checkStatus=False)

2774

2775 respPacket = smb2.SMB2Packet()

2776 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR

2777 respPacket['Status'] = STATUS_SUCCESS

2778 respPacket['CreditRequestResponse'] = 1

2779 respPacket['Command'] = smb2.SMB2_NEGOTIATE

2780 respPacket['SessionID'] = 0

2781 if isSMB1 is False:

2782 respPacket['MessageID'] = recvPacket['MessageID']

2783 else:

2784 respPacket['MessageID'] = 0

2785 respPacket['TreeID'] = 0

2786

2787 respSMBCommand = smb2.SMB2Negotiate_Response()

2788

2789 respSMBCommand['SecurityMode'] = 1

2790 if isSMB1 is True:

2791 # Let's first parse the packet to see if the client supports SMB2

2792 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])

2793

2794 dialects = SMBCommand['Data'].split(b'\x02')

2795 if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects:

2796 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002

2797 else:

2798 # Client does not support SMB2 fallbacking

2799 raise Exception('SMB2 not supported, fallbacking')

2800 else:

2801 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002

2802 respSMBCommand['ServerGuid'] = b'A' * 16

2803 respSMBCommand['Capabilities'] = 0

2804 respSMBCommand['MaxTransactSize'] = 65536

2805 respSMBCommand['MaxReadSize'] = 65536

2806 respSMBCommand['MaxWriteSize'] = 65536

2807 respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime()))

2808 respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime()))

2809 respSMBCommand['SecurityBufferOffset'] = 0x80

2810

2811 blob = SPNEGO_NegTokenInit()

2812 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]

2813

2814 respSMBCommand['Buffer'] = blob.getData()

2815 respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer'])

2816

2817 respPacket['Data'] = respSMBCommand

2818

2819 smbServer.setConnectionData(connId, connData)

2820

2821 return None, [respPacket], STATUS_SUCCESS

2822

2823 @staticmethod

2824 def smb2SessionSetup(connId, smbServer, recvPacket):

2825 connData = smbServer.getConnectionData(connId, checkStatus=False)

2826

2827 respSMBCommand = smb2.SMB2SessionSetup_Response()

2828

2829 sessionSetupData = smb2.SMB2SessionSetup(recvPacket['Data'])

2830

2831 connData['Capabilities'] = sessionSetupData['Capabilities']

2832

2833 securityBlob = sessionSetupData['Buffer']

2834

2835 rawNTLM = False

2836 if struct.unpack('B', securityBlob[0:1])[0] == ASN1_AID:

2837 # NEGOTIATE packet

2838 blob = SPNEGO_NegTokenInit(securityBlob)

2839 token = blob['MechToken']

2840 if len(blob['MechTypes'][0]) > 0:

2841 # Is this GSSAPI NTLM or something else we don't support?

2842 mechType = blob['MechTypes'][0]

2843 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']:

2844 # Nope, do we know it?

2845 if mechType in MechTypes:

2846 mechStr = MechTypes[mechType]

2847 else:

2848 mechStr = hexlify(mechType)

2849 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)

2850 # We don't know the token, we answer back again saying

2851 # we just support NTLM.

2852 # ToDo: Build this into a SPNEGO_NegTokenResp()

2853 respToken = b'\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a'

2854 respSMBCommand['SecurityBufferOffset'] = 0x48

2855 respSMBCommand['SecurityBufferLength'] = len(respToken)

2856 respSMBCommand['Buffer'] = respToken

2857

2858 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED

2859 elif struct.unpack('B', securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH:

2860 # AUTH packet

2861 blob = SPNEGO_NegTokenResp(securityBlob)

2862 token = blob['ResponseToken']

2863 else:

2864 # No GSSAPI stuff, raw NTLMSSP

2865 rawNTLM = True

2866 token = securityBlob

2867

2868 # Here we only handle NTLMSSP, depending on what stage of the

2869 # authentication we are, we act on it

2870 messageType = struct.unpack('<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0]

2871

2872 if messageType == 0x01:

2873 # NEGOTIATE_MESSAGE

2874 negotiateMessage = ntlm.NTLMAuthNegotiate()

2875 negotiateMessage.fromString(token)

2876 # Let's store it in the connection data

2877 connData['NEGOTIATE_MESSAGE'] = negotiateMessage

2878 # Let's build the answer flags

2879 # TODO: Parse all the flags. With this we're leaving some clients out

2880

2881 ansFlags = 0

2882

2883 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:

2884 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56

2885 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:

2886 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128

2887 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:

2888 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH

2889 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:

2890 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY

2891 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:

2892 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE

2893 if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:

2894 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM

2895

2896 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET

2897

2898 # Generate the AV_PAIRS

2899 av_pairs = ntlm.AV_PAIRS()

2900 # TODO: Put the proper data from SMBSERVER config

2901 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[

2902 ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')

2903 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[

2904 ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')

2905 av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (

2906 116444736000000000 + calendar.timegm(time.gmtime()) * 10000000))

2907

2908 challengeMessage = ntlm.NTLMAuthChallenge()

2909 challengeMessage['flags'] = ansFlags

2910 challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le'))

2911 challengeMessage['domain_max_len'] = challengeMessage['domain_len']

2912 challengeMessage['domain_offset'] = 40 + 16

2913 challengeMessage['challenge'] = smbServer.getSMBChallenge()

2914 challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le')

2915 challengeMessage['TargetInfoFields_len'] = len(av_pairs)

2916 challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)

2917 challengeMessage['TargetInfoFields'] = av_pairs

2918 challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name'])

2919 challengeMessage['Version'] = b'\xff' * 8

2920 challengeMessage['VersionLen'] = 8

2921

2922 if rawNTLM is False:

2923 respToken = SPNEGO_NegTokenResp()

2924 # accept-incomplete. We want more data

2925 respToken['NegState'] = b'\x01'

2926 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']

2927

2928 respToken['ResponseToken'] = challengeMessage.getData()

2929 else:

2930 respToken = challengeMessage

2931

2932 # Setting the packet to STATUS_MORE_PROCESSING

2933 errorCode = STATUS_MORE_PROCESSING_REQUIRED

2934 # Let's set up an UID for this connection and store it

2935 # in the connection's data

2936 # Picking a fixed value

2937 # TODO: Manage more UIDs for the same session

2938 connData['Uid'] = random.randint(1, 0xffffffff)

2939 # Let's store it in the connection data

2940 connData['CHALLENGE_MESSAGE'] = challengeMessage

2941

2942 elif messageType == 0x02:

2943 # CHALLENGE_MESSAGE

2944 raise Exception('Challenge Message raise, not implemented!')

2945 elif messageType == 0x03:

2946 # AUTHENTICATE_MESSAGE, here we deal with authentication

2947 authenticateMessage = ntlm.NTLMAuthChallengeResponse()

2948 authenticateMessage.fromString(token)

2949 smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (

2950 authenticateMessage['domain_name'].decode('utf-16le'),

2951 authenticateMessage['user_name'].decode('utf-16le'),

2952 authenticateMessage['host_name'].decode('utf-16le')))

2953

2954 isGuest = False

2955 isAnonymus = False

2956

2957 # TODO: Check the credentials! Now granting permissions

2958 # Do we have credentials to check?

2959 if len(smbServer.getCredentials()) > 0:

2960 identity = authenticateMessage['user_name'].decode('utf-16le').lower()

2961 # Do we have this user's credentials?

2962 if identity in smbServer.getCredentials():

2963 # Process data:

2964 # Let's parse some data and keep it to ourselves in case it is asked

2965 uid, lmhash, nthash = smbServer.getCredentials()[identity]

2966

2967 errorCode, sessionKey = computeNTLMv2(identity, lmhash, nthash, smbServer.getSMBChallenge(),

2968 authenticateMessage, connData['CHALLENGE_MESSAGE'],

2969 connData['NEGOTIATE_MESSAGE'])

2970

2971 if sessionKey is not None:

2972 connData['SignatureEnabled'] = True

2973 connData['SigningSessionKey'] = sessionKey

2974 connData['SignSequenceNumber'] = 1

2975 else:

2976 errorCode = STATUS_LOGON_FAILURE

2977 else:

2978 # No credentials provided, let's grant access

2979 if authenticateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_ANONYMOUS:

2980 isAnonymus = True

2981 if smbServer._SMBSERVER__anonymousLogon == False:

2982 errorCode = STATUS_ACCESS_DENIED

2983 else:

2984 errorCode = STATUS_SUCCESS

2985 else:

2986 isGuest = True

2987 errorCode = STATUS_SUCCESS

2988

2989 if errorCode == STATUS_SUCCESS:

2990 connData['Authenticated'] = True

2991 respToken = SPNEGO_NegTokenResp()

2992 # accept-completed

2993 respToken['NegState'] = b'\x00'

2994 smbServer.log('User %s\\%s authenticated successfully' % (

2995 authenticateMessage['host_name'].decode('utf-16le'),

2996 authenticateMessage['user_name'].decode('utf-16le')))

2997 # Let's store it in the connection data

2998 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage

2999 try:

3000 jtr_dump_path = smbServer.getJTRdumpPath()

3001 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],

3002 authenticateMessage['user_name'],

3003 authenticateMessage['domain_name'],

3004 authenticateMessage['lanman'], authenticateMessage['ntlm'])

3005 smbServer.log(ntlm_hash_data['hash_string'])

3006 if jtr_dump_path != '':

3007 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],

3008 jtr_dump_path)

3009 except:

3010 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)

3011

3012 if isGuest:

3013 respSMBCommand['SessionFlags'] = 1

3014 elif isAnonymus:

3015 respSMBCommand['SessionFlags'] = 2

3016

3017 else:

3018 respToken = SPNEGO_NegTokenResp()

3019 respToken['NegState'] = b'\x02'

3020 smbServer.log("Could not authenticate user!")

3021 else:

3022 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)

3023

3024 respSMBCommand['SecurityBufferOffset'] = 0x48

3025 respSMBCommand['SecurityBufferLength'] = len(respToken)

3026 respSMBCommand['Buffer'] = respToken.getData()

3027

3028 # From now on, the client can ask for other commands

3029 connData['Authenticated'] = True

3030 # For now, just switching to nobody

3031 # os.setregid(65534,65534)

3032 # os.setreuid(65534,65534)

3033 smbServer.setConnectionData(connId, connData)

3034

3035 return [respSMBCommand], None, errorCode

3036

3037 @staticmethod

3038 def smb2TreeConnect(connId, smbServer, recvPacket):

3039 connData = smbServer.getConnectionData(connId)

3040

3041 respPacket = smb2.SMB2Packet()

3042 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR

3043 respPacket['Status'] = STATUS_SUCCESS

3044 respPacket['CreditRequestResponse'] = 1

3045 respPacket['Command'] = recvPacket['Command']

3046 respPacket['SessionID'] = connData['Uid']

3047 respPacket['Reserved'] = recvPacket['Reserved']

3048 respPacket['MessageID'] = recvPacket['MessageID']

3049 respPacket['TreeID'] = recvPacket['TreeID']

3050

3051 respSMBCommand = smb2.SMB2TreeConnect_Response()

3052

3053 treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data'])

3054

3055 errorCode = STATUS_SUCCESS

3056

3057 ## Process here the request, does the share exist?

3058 path = recvPacket.getData()[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']]

3059 UNCOrShare = path.decode('utf-16le')

3060

3061 # Is this a UNC?

3062 if ntpath.ismount(UNCOrShare):

3063 path = UNCOrShare.split('\\')[3]

3064 else:

3065 path = ntpath.basename(UNCOrShare)

3066

3067 share = searchShare(connId, path.upper(), smbServer)

3068 if share is not None:

3069 # Simple way to generate a Tid

3070 if len(connData['ConnectedShares']) == 0:

3071 tid = 1

3072 else:

3073 tid = list(connData['ConnectedShares'].keys())[-1] + 1

3074 connData['ConnectedShares'][tid] = share

3075 connData['ConnectedShares'][tid]['shareName'] = path

3076 respPacket['TreeID'] = tid

3077 smbServer.log("Connecting Share(%d:%s)" % (tid, path))

3078 else:

3079 smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR)

3080 errorCode = STATUS_OBJECT_PATH_NOT_FOUND

3081 respPacket['Status'] = errorCode

3082 ##

3083

3084 if path.upper() == 'IPC$':

3085 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE

3086 respSMBCommand['ShareFlags'] = 0x30

3087 else:

3088 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK

3089 respSMBCommand['ShareFlags'] = 0x0

3090

3091 respSMBCommand['Capabilities'] = 0

3092 respSMBCommand['MaximalAccess'] = 0x000f01ff

3093

3094 respPacket['Data'] = respSMBCommand

3095

3096 # Sign the packet if needed

3097 if connData['SignatureEnabled']:

3098 smbServer.signSMBv2(respPacket, connData['SigningSessionKey'])

3099 smbServer.setConnectionData(connId, connData)

3100

3101 return None, [respPacket], errorCode

3102

3103 @staticmethod

3104 def smb2Create(connId, smbServer, recvPacket):

3105 connData = smbServer.getConnectionData(connId)

3106

3107 respSMBCommand = smb2.SMB2Create_Response()

3108

3109 ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])

3110

3111 respSMBCommand['Buffer'] = b'\x00'

3112 # Get the Tid associated

3113 if recvPacket['TreeID'] in connData['ConnectedShares']:

3114 # If we have a rootFid, the path is relative to that fid

3115 errorCode = STATUS_SUCCESS

3116 if 'path' in connData['ConnectedShares'][recvPacket['TreeID']]:

3117 path = connData['ConnectedShares'][recvPacket['TreeID']]['path']

3118 else:

3119 path = 'NONE'

3120 errorCode = STATUS_ACCESS_DENIED

3121

3122 deleteOnClose = False

3123

3124 fileName = normalize_path(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le'))

3125

3126 if not isInFileJail(path, fileName):

3127 LOG.error("Path not in current working directory")

3128 return [smb2.SMB2Error()], None, STATUS_OBJECT_PATH_SYNTAX_BAD

3129

3130 pathName = os.path.join(path, fileName)

3131 createDisposition = ntCreateRequest['CreateDisposition']

3132 mode = 0

3133

3134 if createDisposition == smb2.FILE_SUPERSEDE:

3135 mode |= os.O_TRUNC | os.O_CREAT

3136 elif createDisposition & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:

3137 mode |= os.O_TRUNC | os.O_CREAT

3138 elif createDisposition & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:

3139 if os.path.exists(pathName) is True:

3140 mode |= os.O_TRUNC

3141 else:

3142 errorCode = STATUS_NO_SUCH_FILE

3143 elif createDisposition & smb2.FILE_OPEN_IF == smb2.FILE_OPEN_IF:

3144 if os.path.exists(pathName) is True:

3145 mode |= os.O_TRUNC

3146 else:

3147 mode |= os.O_TRUNC | os.O_CREAT

3148 elif createDisposition & smb2.FILE_CREATE == smb2.FILE_CREATE:

3149 if os.path.exists(pathName) is True:

3150 errorCode = STATUS_OBJECT_NAME_COLLISION

3151 else:

3152 mode |= os.O_CREAT

3153 elif createDisposition & smb2.FILE_OPEN == smb2.FILE_OPEN:

3154 if os.path.exists(pathName) is not True and (

3155 str(pathName) in smbServer.getRegisteredNamedPipes()) is not True:

3156 errorCode = STATUS_NO_SUCH_FILE

3157

3158 if errorCode == STATUS_SUCCESS:

3159 desiredAccess = ntCreateRequest['DesiredAccess']

3160 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):

3161 mode |= os.O_RDONLY

3162 if (desiredAccess & smb2.FILE_WRITE_DATA) or (desiredAccess & smb2.GENERIC_WRITE):

3163 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):

3164 mode |= os.O_RDWR # | os.O_APPEND

3165 else:

3166 mode |= os.O_WRONLY # | os.O_APPEND

3167 if desiredAccess & smb2.GENERIC_ALL:

3168 mode |= os.O_RDWR # | os.O_APPEND

3169

3170 createOptions = ntCreateRequest['CreateOptions']

3171 if mode & os.O_CREAT == os.O_CREAT:

3172 if createOptions & smb2.FILE_DIRECTORY_FILE == smb2.FILE_DIRECTORY_FILE:

3173 try:

3174 # Let's create the directory

3175 os.mkdir(pathName)

3176 mode = os.O_RDONLY

3177 except Exception as e:

3178 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName, mode, e), logging.ERROR)

3179 errorCode = STATUS_ACCESS_DENIED

3180 if createOptions & smb2.FILE_NON_DIRECTORY_FILE == smb2.FILE_NON_DIRECTORY_FILE:

3181 # If the file being opened is a directory, the server MUST fail the request with

3182 # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server

3183 # response.

3184 if os.path.isdir(pathName) is True:

3185 errorCode = STATUS_FILE_IS_A_DIRECTORY

3186

3187 if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:

3188 deleteOnClose = True

3189

3190 if errorCode == STATUS_SUCCESS:

3191 try:

3192 if os.path.isdir(pathName) and sys.platform == 'win32':

3193 fid = VOID_FILE_DESCRIPTOR

3194 else:

3195 if sys.platform == 'win32':

3196 mode |= os.O_BINARY

3197 if str(pathName) in smbServer.getRegisteredNamedPipes():

3198 fid = PIPE_FILE_DESCRIPTOR

3199 sock = socket.socket()

3200 sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)])

3201 else:

3202 fid = os.open(pathName, mode)

3203 except Exception as e:

3204 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName, mode, e), logging.ERROR)

3205 # print e

3206 fid = 0

3207 errorCode = STATUS_ACCESS_DENIED

3208 else:

3209 errorCode = STATUS_SMB_BAD_TID

3210

3211 if errorCode == STATUS_SUCCESS:

3212 # Simple way to generate a fid

3213 fakefid = uuid.generate()

3214

3215 respSMBCommand['FileID'] = fakefid

3216 respSMBCommand['CreateAction'] = createDisposition

3217

3218 if fid == PIPE_FILE_DESCRIPTOR:

3219 respSMBCommand['CreationTime'] = 0

3220 respSMBCommand['LastAccessTime'] = 0

3221 respSMBCommand['LastWriteTime'] = 0

3222 respSMBCommand['ChangeTime'] = 0

3223 respSMBCommand['AllocationSize'] = 4096

3224 respSMBCommand['EndOfFile'] = 0

3225 respSMBCommand['FileAttributes'] = 0x80

3226

3227 else:

3228 if os.path.isdir(pathName):

3229 respSMBCommand['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY

3230 else:

3231 respSMBCommand['FileAttributes'] = ntCreateRequest['FileAttributes']

3232 # Let's get this file's information

3233 respInfo, errorCode = queryPathInformation(path, fileName, level=smb.SMB_QUERY_FILE_ALL_INFO)

3234 if errorCode == STATUS_SUCCESS:

3235 respSMBCommand['CreationTime'] = respInfo['CreationTime']

3236 respSMBCommand['LastAccessTime'] = respInfo['LastAccessTime']

3237 respSMBCommand['LastWriteTime'] = respInfo['LastWriteTime']

3238 respSMBCommand['LastChangeTime'] = respInfo['LastChangeTime']

3239 respSMBCommand['FileAttributes'] = respInfo['ExtFileAttributes']

3240 respSMBCommand['AllocationSize'] = respInfo['AllocationSize']

3241 respSMBCommand['EndOfFile'] = respInfo['EndOfFile']

3242

3243 if errorCode == STATUS_SUCCESS:

3244 # Let's store the fid for the connection

3245 # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode))

3246 connData['OpenedFiles'][fakefid] = {}

3247 connData['OpenedFiles'][fakefid]['FileHandle'] = fid

3248 connData['OpenedFiles'][fakefid]['FileName'] = pathName

3249 connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose

3250 connData['OpenedFiles'][fakefid]['Open'] = {}

3251 connData['OpenedFiles'][fakefid]['Open']['EnumerationLocation'] = 0

3252 connData['OpenedFiles'][fakefid]['Open']['EnumerationSearchPattern'] = ''

3253 if fid == PIPE_FILE_DESCRIPTOR:

3254 connData['OpenedFiles'][fakefid]['Socket'] = sock

3255 else:

3256 respSMBCommand = smb2.SMB2Error()

3257

3258 if errorCode == STATUS_SUCCESS:

3259 connData['LastRequest']['SMB2_CREATE'] = respSMBCommand

3260 smbServer.setConnectionData(connId, connData)

3261

3262 return [respSMBCommand], None, errorCode

3263

3264 @staticmethod

3265 def smb2Close(connId, smbServer, recvPacket):

3266 connData = smbServer.getConnectionData(connId)

3267

3268 respSMBCommand = smb2.SMB2Close_Response()

3269

3270 closeRequest = smb2.SMB2Close(recvPacket['Data'])

3271

3272 if closeRequest['FileID'].getData() == b'\xff' * 16:

3273 # Let's take the data from the lastRequest

3274 if 'SMB2_CREATE' in connData['LastRequest']:

3275 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3276 else:

3277 fileID = closeRequest['FileID'].getData()

3278 else:

3279 fileID = closeRequest['FileID'].getData()

3280

3281 # Get the Tid associated

3282 if recvPacket['TreeID'] in connData['ConnectedShares']:

3283 if fileID in connData['OpenedFiles']:

3284 errorCode = STATUS_SUCCESS

3285 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']

3286 pathName = connData['OpenedFiles'][fileID]['FileName']

3287 infoRecord = None

3288 try:

3289 if fileHandle == PIPE_FILE_DESCRIPTOR:

3290 connData['OpenedFiles'][fileID]['Socket'].close()

3291 elif fileHandle != VOID_FILE_DESCRIPTOR:

3292 os.close(fileHandle)

3293 infoRecord, errorCode = queryFileInformation(os.path.dirname(pathName), os.path.basename(pathName),

3294 smb2.SMB2_FILE_NETWORK_OPEN_INFO)

3295 except Exception as e:

3296 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)

3297 errorCode = STATUS_INVALID_HANDLE

3298 else:

3299 # Check if the file was marked for removal

3300 if connData['OpenedFiles'][fileID]['DeleteOnClose'] is True:

3301 try:

3302 if os.path.isdir(pathName):

3303 shutil.rmtree(connData['OpenedFiles'][fileID]['FileName'])

3304 else:

3305 os.remove(connData['OpenedFiles'][fileID]['FileName'])

3306 except Exception as e:

3307 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)

3308 errorCode = STATUS_ACCESS_DENIED

3309

3310 # Now fill out the response

3311 if infoRecord is not None:

3312 respSMBCommand['CreationTime'] = infoRecord['CreationTime']

3313 respSMBCommand['LastAccessTime'] = infoRecord['LastAccessTime']

3314 respSMBCommand['LastWriteTime'] = infoRecord['LastWriteTime']

3315 respSMBCommand['ChangeTime'] = infoRecord['ChangeTime']

3316 respSMBCommand['AllocationSize'] = infoRecord['AllocationSize']

3317 respSMBCommand['EndofFile'] = infoRecord['EndOfFile']

3318 respSMBCommand['FileAttributes'] = infoRecord['FileAttributes']

3319 if errorCode == STATUS_SUCCESS:

3320 del (connData['OpenedFiles'][fileID])

3321 else:

3322 errorCode = STATUS_INVALID_HANDLE

3323 else:

3324 errorCode = STATUS_SMB_BAD_TID

3325

3326 smbServer.setConnectionData(connId, connData)

3327 return [respSMBCommand], None, errorCode

3328

3329 @staticmethod

3330 def smb2QueryInfo(connId, smbServer, recvPacket):

3331 connData = smbServer.getConnectionData(connId)

3332

3333 respSMBCommand = smb2.SMB2QueryInfo_Response()

3334

3335 queryInfo = smb2.SMB2QueryInfo(recvPacket['Data'])

3336

3337 errorCode = STATUS_SUCCESS

3338

3339 respSMBCommand['OutputBufferOffset'] = 0x48

3340 respSMBCommand['Buffer'] = b'\x00'

3341

3342 if queryInfo['FileID'].getData() == b'\xff' * 16:

3343 # Let's take the data from the lastRequest

3344 if 'SMB2_CREATE' in connData['LastRequest']:

3345 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3346 else:

3347 fileID = queryInfo['FileID'].getData()

3348 else:

3349 fileID = queryInfo['FileID'].getData()

3350

3351 # Get the Tid associated

3352 if recvPacket['TreeID'] in connData['ConnectedShares']:

3353 if fileID in connData['OpenedFiles']:

3354 fileName = connData['OpenedFiles'][fileID]['FileName']

3355

3356 if queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:

3357 if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_INTERNAL_INFO:

3358 # No need to call queryFileInformation, we have the data here

3359 infoRecord = smb2.FILE_INTERNAL_INFORMATION()

3360 infoRecord['IndexNumber'] = fileID

3361 else:

3362 infoRecord, errorCode = queryFileInformation(os.path.dirname(fileName),

3363 os.path.basename(fileName),

3364 queryInfo['FileInfoClass'])

3365 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:

3366 if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_EA_INFO:

3367 infoRecord = b'\x00' * 4

3368 else:

3369 infoRecord = queryFsInformation(os.path.dirname(fileName), os.path.basename(fileName),

3370 queryInfo['FileInfoClass'])

3371 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:

3372 # Failing for now, until we support it

3373 infoRecord = None

3374 errorCode = STATUS_ACCESS_DENIED

3375 else:

3376 smbServer.log("queryInfo not supported (%x)" % queryInfo['InfoType'], logging.ERROR)

3377

3378 if infoRecord is not None:

3379 respSMBCommand['OutputBufferLength'] = len(infoRecord)

3380 respSMBCommand['Buffer'] = infoRecord

3381 else:

3382 errorCode = STATUS_INVALID_HANDLE

3383 else:

3384 errorCode = STATUS_SMB_BAD_TID

3385

3386 smbServer.setConnectionData(connId, connData)

3387 return [respSMBCommand], None, errorCode

3388

3389 @staticmethod

3390 def smb2SetInfo(connId, smbServer, recvPacket):

3391 connData = smbServer.getConnectionData(connId)

3392

3393 respSMBCommand = smb2.SMB2SetInfo_Response()

3394

3395 setInfo = smb2.SMB2SetInfo(recvPacket['Data'])

3396

3397 errorCode = STATUS_SUCCESS

3398

3399 if setInfo['FileID'].getData() == b'\xff' * 16:

3400 # Let's take the data from the lastRequest

3401 if 'SMB2_CREATE' in connData['LastRequest']:

3402 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3403 else:

3404 fileID = setInfo['FileID'].getData()

3405 else:

3406 fileID = setInfo['FileID'].getData()

3407

3408 # Get the Tid associated

3409 if recvPacket['TreeID'] in connData['ConnectedShares']:

3410 path = connData['ConnectedShares'][recvPacket['TreeID']]['path']

3411 if fileID in connData['OpenedFiles']:

3412 pathName = connData['OpenedFiles'][fileID]['FileName']

3413

3414 if setInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:

3415 # The file information is being set

3416 informationLevel = setInfo['FileInfoClass']

3417 if informationLevel == smb2.SMB2_FILE_DISPOSITION_INFO:

3418 infoRecord = smb.SMBSetFileDispositionInfo(setInfo['Buffer'])

3419 if infoRecord['DeletePending'] > 0:

3420 # Mark this file for removal after closed

3421 connData['OpenedFiles'][fileID]['DeleteOnClose'] = True

3422 elif informationLevel == smb2.SMB2_FILE_BASIC_INFO:

3423 infoRecord = smb.SMBSetFileBasicInfo(setInfo['Buffer'])

3424 # Creation time won't be set, the other ones we play with.

3425 atime = infoRecord['LastWriteTime']

3426 if atime == 0:

3427 atime = -1

3428 else:

3429 atime = getUnixTime(atime)

3430 mtime = infoRecord['ChangeTime']

3431 if mtime == 0:

3432 mtime = -1

3433 else:

3434 mtime = getUnixTime(mtime)

3435 if atime > 0 and mtime > 0:

3436 os.utime(pathName, (atime, mtime))

3437 elif informationLevel == smb2.SMB2_FILE_END_OF_FILE_INFO:

3438 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']

3439 infoRecord = smb.SMBSetFileEndOfFileInfo(setInfo['Buffer'])

3440 if infoRecord['EndOfFile'] > 0:

3441 os.lseek(fileHandle, infoRecord['EndOfFile'] - 1, 0)

3442 os.write(fileHandle, b'\x00')

3443 elif informationLevel == smb2.SMB2_FILE_RENAME_INFO:

3444 renameInfo = smb2.FILE_RENAME_INFORMATION_TYPE_2(setInfo['Buffer'])

3445 newFileName = normalize_path(renameInfo['FileName'].decode('utf-16le'))

3446 newPathName = os.path.join(path, newFileName)

3447 if not isInFileJail(path, newFileName):

3448 smbServer.log("Path not in current working directory", logging.ERROR)

3449 return [smb2.SMB2Error()], None, STATUS_OBJECT_PATH_SYNTAX_BAD

3450

3451 if renameInfo['ReplaceIfExists'] == 0 and os.path.exists(newPathName):

3452 return [smb2.SMB2Error()], None, STATUS_OBJECT_NAME_COLLISION

3453 try:

3454 os.rename(pathName, newPathName)

3455 connData['OpenedFiles'][fileID]['FileName'] = newPathName

3456 except Exception as e:

3457 smbServer.log("smb2SetInfo: %s" % e, logging.ERROR)

3458 errorCode = STATUS_ACCESS_DENIED

3459 else:

3460 smbServer.log('Unknown level for set file info! 0x%x' % informationLevel, logging.ERROR)

3461 # UNSUPPORTED

3462 errorCode = STATUS_NOT_SUPPORTED

3463 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:

3464 # # The underlying object store information is being set.

3465 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])

3466 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:

3467 # # The security information is being set.

3468 # # Failing for now, until we support it

3469 # infoRecord = None

3470 # errorCode = STATUS_ACCESS_DENIED

3471 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_QUOTA:

3472 # # The underlying object store quota information is being set.

3473 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])

3474 else:

3475 smbServer.log("setInfo not supported (%x)" % setInfo['InfoType'], logging.ERROR)

3476

3477 else:

3478 errorCode = STATUS_INVALID_HANDLE

3479 else:

3480 errorCode = STATUS_SMB_BAD_TID

3481

3482 smbServer.setConnectionData(connId, connData)

3483 return [respSMBCommand], None, errorCode

3484

3485 @staticmethod

3486 def smb2Write(connId, smbServer, recvPacket):

3487 connData = smbServer.getConnectionData(connId)

3488

3489 respSMBCommand = smb2.SMB2Write_Response()

3490 writeRequest = smb2.SMB2Write(recvPacket['Data'])

3491

3492 respSMBCommand['Buffer'] = b'\x00'

3493

3494 if writeRequest['FileID'].getData() == b'\xff' * 16:

3495 # Let's take the data from the lastRequest

3496 if 'SMB2_CREATE' in connData['LastRequest']:

3497 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3498 else:

3499 fileID = writeRequest['FileID'].getData()

3500 else:

3501 fileID = writeRequest['FileID'].getData()

3502

3503 # Get the Tid associated

3504 if recvPacket['TreeID'] in connData['ConnectedShares']:

3505 if fileID in connData['OpenedFiles']:

3506 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']

3507 errorCode = STATUS_SUCCESS

3508 try:

3509 if fileHandle != PIPE_FILE_DESCRIPTOR:

3510 offset = writeRequest['Offset']

3511 # If we're trying to write past the file end we just skip the write call (Vista does this)

3512 if os.lseek(fileHandle, 0, 2) >= offset:

3513 os.lseek(fileHandle, offset, 0)

3514 os.write(fileHandle, writeRequest['Buffer'])

3515 else:

3516 sock = connData['OpenedFiles'][fileID]['Socket']

3517 sock.send(writeRequest['Buffer'])

3518

3519 respSMBCommand['Count'] = writeRequest['Length']

3520 respSMBCommand['Remaining'] = 0xff

3521 except Exception as e:

3522 smbServer.log('SMB2_WRITE: %s' % e, logging.ERROR)

3523 errorCode = STATUS_ACCESS_DENIED

3524 else:

3525 errorCode = STATUS_INVALID_HANDLE

3526 else:

3527 errorCode = STATUS_SMB_BAD_TID

3528

3529 smbServer.setConnectionData(connId, connData)

3530 return [respSMBCommand], None, errorCode

3531

3532 @staticmethod

3533 def smb2Read(connId, smbServer, recvPacket):

3534 connData = smbServer.getConnectionData(connId)

3535

3536 respSMBCommand = smb2.SMB2Read_Response()

3537 readRequest = smb2.SMB2Read(recvPacket['Data'])

3538

3539 respSMBCommand['Buffer'] = b'\x00'

3540

3541 if readRequest['FileID'].getData() == b'\xff' * 16:

3542 # Let's take the data from the lastRequest

3543 if 'SMB2_CREATE' in connData['LastRequest']:

3544 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3545 else:

3546 fileID = readRequest['FileID'].getData()

3547 else:

3548 fileID = readRequest['FileID'].getData()

3549

3550 # Get the Tid associated

3551 if recvPacket['TreeID'] in connData['ConnectedShares']:

3552 if fileID in connData['OpenedFiles']:

3553 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']

3554 errorCode = 0

3555 try:

3556 if fileHandle != PIPE_FILE_DESCRIPTOR:

3557 offset = readRequest['Offset']

3558 os.lseek(fileHandle, offset, 0)

3559 content = os.read(fileHandle, readRequest['Length'])

3560 else:

3561 sock = connData['OpenedFiles'][fileID]['Socket']

3562 content = sock.recv(readRequest['Length'])

3563

3564 respSMBCommand['DataOffset'] = 0x50

3565 respSMBCommand['DataLength'] = len(content)

3566 respSMBCommand['DataRemaining'] = 0

3567 respSMBCommand['Buffer'] = content

3568 except Exception as e:

3569 smbServer.log('SMB2_READ: %s ' % e, logging.ERROR)

3570 errorCode = STATUS_ACCESS_DENIED

3571 else:

3572 errorCode = STATUS_INVALID_HANDLE

3573 else:

3574 errorCode = STATUS_SMB_BAD_TID

3575

3576 smbServer.setConnectionData(connId, connData)

3577 return [respSMBCommand], None, errorCode

3578

3579 @staticmethod

3580 def smb2Flush(connId, smbServer, recvPacket):

3581 connData = smbServer.getConnectionData(connId)

3582

3583 respSMBCommand = smb2.SMB2Flush_Response()

3584 flushRequest = smb2.SMB2Flush(recvPacket['Data'])

3585

3586 # Get the Tid associated

3587 if recvPacket['TreeID'] in connData['ConnectedShares']:

3588 if flushRequest['FileID'].getData() in connData['OpenedFiles']:

3589 fileHandle = connData['OpenedFiles'][flushRequest['FileID'].getData()]['FileHandle']

3590 errorCode = STATUS_SUCCESS

3591 try:

3592 os.fsync(fileHandle)

3593 except Exception as e:

3594 smbServer.log("SMB2_FLUSH %s" % e, logging.ERROR)

3595 errorCode = STATUS_ACCESS_DENIED

3596 else:

3597 errorCode = STATUS_INVALID_HANDLE

3598 else:

3599 errorCode = STATUS_SMB_BAD_TID

3600

3601 smbServer.setConnectionData(connId, connData)

3602 return [respSMBCommand], None, errorCode

3603

3604 @staticmethod

3605 def smb2QueryDirectory(connId, smbServer, recvPacket):

3606 connData = smbServer.getConnectionData(connId)

3607 respSMBCommand = smb2.SMB2QueryDirectory_Response()

3608 queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data'])

3609

3610 respSMBCommand['Buffer'] = b'\x00'

3611

3612 # The server MUST locate the tree connection, as specified in section 3.3.5.2.11.

3613 if (recvPacket['TreeID'] in connData['ConnectedShares']) is False:

3614 return [smb2.SMB2Error()], None, STATUS_NETWORK_NAME_DELETED

3615

3616 # Next, the server MUST locate the open for the directory to be queried

3617 # If no open is found, the server MUST fail the request with STATUS_FILE_CLOSED

3618 if queryDirectoryRequest['FileID'].getData() == b'\xff' * 16:

3619 # Let's take the data from the lastRequest

3620 if 'SMB2_CREATE' in connData['LastRequest']:

3621 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3622 else:

3623 fileID = queryDirectoryRequest['FileID'].getData()

3624 else:

3625 fileID = queryDirectoryRequest['FileID'].getData()

3626

3627 if (fileID in connData['OpenedFiles']) is False:

3628 return [smb2.SMB2Error()], None, STATUS_FILE_CLOSED

3629

3630 # If the open is not an open to a directory, the request MUST be failed

3631 # with STATUS_INVALID_PARAMETER.

3632 if os.path.isdir(connData['OpenedFiles'][fileID]['FileName']) is False:

3633 return [smb2.SMB2Error()], None, STATUS_INVALID_PARAMETER

3634

3635 # If any other information class is specified in the FileInformationClass

3636 # field of the SMB2 QUERY_DIRECTORY Request, the server MUST fail the

3637 # operation with STATUS_INVALID_INFO_CLASS.

3638 if queryDirectoryRequest['FileInformationClass'] not in (

3639 smb2.FILE_DIRECTORY_INFORMATION, smb2.FILE_FULL_DIRECTORY_INFORMATION,

3640 smb2.FILEID_FULL_DIRECTORY_INFORMATION,

3641 smb2.FILE_BOTH_DIRECTORY_INFORMATION, smb2.FILEID_BOTH_DIRECTORY_INFORMATION,

3642 smb2.FILENAMES_INFORMATION):

3643 return [smb2.SMB2Error()], None, STATUS_INVALID_INFO_CLASS

3644

3645 # If SMB2_REOPEN is set in the Flags field of the SMB2 QUERY_DIRECTORY

3646 # Request, the server SHOULD<326> set Open.EnumerationLocation to 0

3647 # and Open.EnumerationSearchPattern to an empty string.

3648 if queryDirectoryRequest['Flags'] & smb2.SMB2_REOPEN:

3649 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0

3650 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = ''

3651

3652 # If SMB2_RESTART_SCANS is set in the Flags field of the SMB2

3653 # QUERY_DIRECTORY Request, the server MUST set

3654 # Open.EnumerationLocation to 0.

3655 if queryDirectoryRequest['Flags'] & smb2.SMB2_RESTART_SCANS:

3656 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0

3657

3658 # If Open.EnumerationLocation is 0 and Open.EnumerationSearchPattern

3659 # is an empty string, then Open.EnumerationSearchPattern MUST be set

3660 # to the search pattern specified in the SMB2 QUERY_DIRECTORY by

3661 # FileNameOffset and FileNameLength. If FileNameLength is 0, the server

3662 # SHOULD<327> set Open.EnumerationSearchPattern as "*" to search all entries.

3663

3664 pattern = queryDirectoryRequest['Buffer'].decode('utf-16le')

3665 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0 and \

3666 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] == '':

3667 if pattern == '':

3668 pattern = '*'

3669 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern

3670

3671 # If SMB2_INDEX_SPECIFIED is set and FileNameLength is not zero,

3672 # the server MUST set Open.EnumerationSearchPattern to the search pattern

3673 # specified in the request by FileNameOffset and FileNameLength.

3674 if queryDirectoryRequest['Flags'] & smb2.SMB2_INDEX_SPECIFIED and \

3675 queryDirectoryRequest['FileNameLength'] > 0:

3676 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern

3677

3678 pathName = os.path.join(os.path.normpath(connData['OpenedFiles'][fileID]['FileName']), pattern)

3679 searchResult, searchCount, errorCode = findFirst2(os.path.dirname(pathName),

3680 os.path.basename(pathName),

3681 queryDirectoryRequest['FileInformationClass'],

3682 smb.ATTR_DIRECTORY, isSMB2=True)

3683

3684 if errorCode != STATUS_SUCCESS:

3685 return [smb2.SMB2Error()], None, errorCode

3686

3687 if searchCount > 2 and pattern == '*':

3688 # strip . and ..

3689 searchCount -= 2

3690 searchResult = searchResult[2:]

3691

3692 if searchCount == 0 and connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0:

3693 return [smb2.SMB2Error()], None, STATUS_NO_SUCH_FILE

3694

3695 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] < 0:

3696 return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES

3697

3698 totalData = 0

3699 respData = b''

3700 for nItem in range(connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'], searchCount):

3701 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] += 1

3702 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:

3703 # If single entry is requested we must clear the NextEntryOffset

3704 searchResult[nItem]['NextEntryOffset'] = 0

3705 data = searchResult[nItem].getData()

3706 lenData = len(data)

3707 padLen = (8 - (lenData % 8)) % 8

3708

3709 if (totalData + lenData) >= queryDirectoryRequest['OutputBufferLength']:

3710 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] -= 1

3711 break

3712 else:

3713 respData += data + b'\x00' * padLen

3714 totalData += lenData + padLen

3715

3716 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:

3717 break

3718

3719 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] >= searchCount:

3720 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = -1

3721

3722 respSMBCommand['OutputBufferOffset'] = 0x48

3723 respSMBCommand['OutputBufferLength'] = totalData

3724 respSMBCommand['Buffer'] = respData

3725

3726 smbServer.setConnectionData(connId, connData)

3727 return [respSMBCommand], None, errorCode

3728

3729 @staticmethod

3730 def smb2ChangeNotify(connId, smbServer, recvPacket):

3731

3732 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED

3733

3734 @staticmethod

3735 def smb2Echo(connId, smbServer, recvPacket):

3736

3737 respSMBCommand = smb2.SMB2Echo_Response()

3738

3739 return [respSMBCommand], None, STATUS_SUCCESS

3740

3741 @staticmethod

3742 def smb2TreeDisconnect(connId, smbServer, recvPacket):

3743 connData = smbServer.getConnectionData(connId)

3744

3745 respSMBCommand = smb2.SMB2TreeDisconnect_Response()

3746

3747 # Get the Tid associated

3748 if recvPacket['TreeID'] in connData['ConnectedShares']:

3749 smbServer.log("Disconnecting Share(%d:%s)" % (

3750 recvPacket['TreeID'], connData['ConnectedShares'][recvPacket['TreeID']]['shareName']))

3751 del (connData['ConnectedShares'][recvPacket['TreeID']])

3752 errorCode = STATUS_SUCCESS

3753 else:

3754 errorCode = STATUS_SMB_BAD_TID

3755

3756 smbServer.setConnectionData(connId, connData)

3757 return [respSMBCommand], None, errorCode

3758

3759 @staticmethod

3760 def smb2Logoff(connId, smbServer, recvPacket):

3761 connData = smbServer.getConnectionData(connId)

3762

3763 respSMBCommand = smb2.SMB2Logoff_Response()

3764

3765 if recvPacket['SessionID'] != connData['Uid']:

3766 # STATUS_SMB_BAD_UID

3767 errorCode = STATUS_SMB_BAD_UID

3768 else:

3769 errorCode = STATUS_SUCCESS

3770

3771 connData['Uid'] = 0

3772 connData['Authenticated'] = False

3773

3774 smbServer.setConnectionData(connId, connData)

3775 return [respSMBCommand], None, errorCode

3776

3777 @staticmethod

3778 def smb2Ioctl(connId, smbServer, recvPacket):

3779 connData = smbServer.getConnectionData(connId)

3780

3781 respSMBCommand = smb2.SMB2Ioctl_Response()

3782 ioctlRequest = smb2.SMB2Ioctl(recvPacket['Data'])

3783

3784 ioctls = smbServer.getIoctls()

3785 if ioctlRequest['CtlCode'] in ioctls:

3786 outputData, errorCode = ioctls[ioctlRequest['CtlCode']](connId, smbServer, ioctlRequest)

3787 if errorCode == STATUS_SUCCESS:

3788 respSMBCommand['CtlCode'] = ioctlRequest['CtlCode']

3789 respSMBCommand['FileID'] = ioctlRequest['FileID']

3790 respSMBCommand['InputOffset'] = 0

3791 respSMBCommand['InputCount'] = 0

3792 respSMBCommand['OutputOffset'] = 0x70

3793 respSMBCommand['OutputCount'] = len(outputData)

3794 respSMBCommand['Flags'] = 0

3795 respSMBCommand['Buffer'] = outputData

3796 else:

3797 respSMBCommand = outputData

3798 else:

3799 smbServer.log("Ioctl not implemented command: 0x%x" % ioctlRequest['CtlCode'], logging.DEBUG)

3800 errorCode = STATUS_INVALID_DEVICE_REQUEST

3801 respSMBCommand = smb2.SMB2Error()

3802

3803 smbServer.setConnectionData(connId, connData)

3804 return [respSMBCommand], None, errorCode

3805

3806 @staticmethod

3807 def smb2Lock(connId, smbServer, recvPacket):

3808 connData = smbServer.getConnectionData(connId)

3809

3810 respSMBCommand = smb2.SMB2Lock_Response()

3811

3812 # I'm actually doing nothing.. just make MacOS happy ;)

3813 errorCode = STATUS_SUCCESS

3814

3815 smbServer.setConnectionData(connId, connData)

3816 return [respSMBCommand], None, errorCode

3817

3818 @staticmethod

3819 def smb2Cancel(connId, smbServer, recvPacket):

3820 # I'm actually doing nothing

3821 return [smb2.SMB2Error()], None, STATUS_CANCELLED

3822

3823 @staticmethod

3824 def default(connId, smbServer, recvPacket):

3825 # By default we return an SMB Packet with error not implemented

3826 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'], logging.DEBUG)

3827 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED

3828

3829

3830 class Ioctls:

3831 @staticmethod

3832 def fsctlDfsGetReferrals(connId, smbServer, ioctlRequest):

3833 return smb2.SMB2Error(), STATUS_FS_DRIVER_REQUIRED

3834

3835 @staticmethod

3836 def fsctlPipeTransceive(connId, smbServer, ioctlRequest):

3837 connData = smbServer.getConnectionData(connId)

3838

3839 ioctlResponse = ''

3840

3841 if ioctlRequest['FileID'].getData() in connData['OpenedFiles']:

3842 fileHandle = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['FileHandle']

3843 errorCode = STATUS_SUCCESS

3844 try:

3845 if fileHandle != PIPE_FILE_DESCRIPTOR:

3846 errorCode = STATUS_INVALID_DEVICE_REQUEST

3847 else:

3848 sock = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['Socket']

3849 sock.sendall(ioctlRequest['Buffer'])

3850 ioctlResponse = sock.recv(ioctlRequest['MaxOutputResponse'])

3851 except Exception as e:

3852 smbServer.log('fsctlPipeTransceive: %s ' % e, logging.ERROR)

3853 errorCode = STATUS_ACCESS_DENIED

3854 else:

3855 errorCode = STATUS_INVALID_DEVICE_REQUEST

3856

3857 smbServer.setConnectionData(connId, connData)

3858 return ioctlResponse, errorCode

3859

3860 @staticmethod

3861 def fsctlValidateNegotiateInfo(connId, smbServer, ioctlRequest):

3862 connData = smbServer.getConnectionData(connId)

3863

3864 errorCode = STATUS_SUCCESS

3865

3866 validateNegotiateInfo = smb2.VALIDATE_NEGOTIATE_INFO(ioctlRequest['Buffer'])

3867 validateNegotiateInfoResponse = smb2.VALIDATE_NEGOTIATE_INFO_RESPONSE()

3868 validateNegotiateInfoResponse['Capabilities'] = 0

3869 validateNegotiateInfoResponse['Guid'] = b'A' * 16

3870 validateNegotiateInfoResponse['SecurityMode'] = 1

3871 validateNegotiateInfoResponse['Dialect'] = smb2.SMB2_DIALECT_002

3872

3873 smbServer.setConnectionData(connId, connData)

3874 return validateNegotiateInfoResponse.getData(), errorCode

3875

3876

3877 class SMBSERVERHandler(socketserver.BaseRequestHandler):

3878 def __init__(self, request, client_address, server, select_poll=False):

3879 self.__SMB = server

3880 # In case of AF_INET6 the client_address contains 4 items, ignore the last 2

3881 self.__ip, self.__port = client_address[:2]

3882 self.__request = request

3883 self.__connId = threading.current_thread().name

3884 self.__timeOut = 60 * 5

3885 self.__select_poll = select_poll

3886 # self.__connId = os.getpid()

3887 socketserver.BaseRequestHandler.__init__(self, request, client_address, server)

3888

3889 def handle(self):

3890 self.__SMB.log("Incoming connection (%s,%d)" % (self.__ip, self.__port))

3891 self.__SMB.addConnection(self.__connId, self.__ip, self.__port)

3892 while True:

3893 try:

3894 # First of all let's get the NETBIOS packet

3895 session = nmb.NetBIOSTCPSession(self.__SMB.getServerName(), 'HOST', self.__ip, sess_port=self.__port,

3896 sock=self.__request, select_poll=self.__select_poll)

3897 try:

3898 p = session.recv_packet(self.__timeOut)

3899 except nmb.NetBIOSTimeout:

3900 raise

3901 except nmb.NetBIOSError:

3902 break

3903

3904 if p.get_type() == nmb.NETBIOS_SESSION_REQUEST:

3905 # Someone is requesting a session, we're gonna accept them all :)

3906 _, rn, my = p.get_trailer().split(b' ')

3907 remote_name = nmb.decode_name(b'\x20' + rn)

3908 myname = nmb.decode_name(b'\x20' + my)

3909 self.__SMB.log(

3910 "NetBIOS Session request (%s,%s,%s)" % (self.__ip, remote_name[1].strip(), myname[1]))

3911 r = nmb.NetBIOSSessionPacket()

3912 r.set_type(nmb.NETBIOS_SESSION_POSITIVE_RESPONSE)

3913 r.set_trailer(p.get_trailer())

3914 self.__request.send(r.rawData())

3915 else:

3916 resp = self.__SMB.processRequest(self.__connId, p.get_trailer())

3917 # Send all the packets received. Except for big transactions this should be

3918 # a single packet

3919 for i in resp:

3920 if hasattr(i, 'getData'):

3921 session.send_packet(i.getData())

3922 else:

3923 session.send_packet(i)

3924 except Exception as e:

3925 self.__SMB.log("Handle: %s" % e)

3926 # import traceback

3927 # traceback.print_exc()

3928 break

3929

3930 def finish(self):

3931 # Thread/process is dying, we should tell the main SMB thread to remove all this thread data

3932 self.__SMB.log("Closing down connection (%s,%d)" % (self.__ip, self.__port))

3933 self.__SMB.removeConnection(self.__connId)

3934 return socketserver.BaseRequestHandler.finish(self)

3935

3936

3937 class SMBSERVER(socketserver.ThreadingMixIn, socketserver.TCPServer):

3938 # class SMBSERVER(socketserver.ForkingMixIn, socketserver.TCPServer):

3939 def __init__(self, server_address, handler_class=SMBSERVERHandler, config_parser=None):

3940 socketserver.TCPServer.allow_reuse_address = True

3941 socketserver.TCPServer.__init__(self, server_address, handler_class)

3942

3943 # Server name and OS to be presented whenever is necessary

3944 self.__serverName = ''

3945 self.__serverOS = ''

3946 self.__serverDomain = ''

3947 self.__challenge = ''

3948 self.__log = None

3949

3950 # Our ConfigParser data

3951 self.__serverConfig = config_parser

3952

3953 # Our credentials to be used during the server's lifetime

3954 self.__credentials = {}

3955

3956 # Our log file

3957 self.__logFile = ''

3958

3959 # Registered Named Pipes, format is PipeName,Socket

3960 self.__registeredNamedPipes = {}

3961

3962 # JTR dump path

3963 self.__jtr_dump_path = ''

3964

3965 # SMB2 Support flag = default not active

3966 self.__SMB2Support = False

3967

3968 # Allow anonymous logon

3969 self.__anonymousLogon = True

3970

3971 # Our list of commands we will answer, by default the NOT IMPLEMENTED one

3972 self.__smbCommandsHandler = SMBCommands()

3973 self.__smbTrans2Handler = TRANS2Commands()

3974 self.__smbTransHandler = TRANSCommands()

3975 self.__smbNTTransHandler = NTTRANSCommands()

3976 self.__smb2CommandsHandler = SMB2Commands()

3977 self.__IoctlHandler = Ioctls()

3978

3979 self.__smbNTTransCommands = {

3980 # NT IOCTL, can't find doc for this

3981 0xff: self.__smbNTTransHandler.default

3982 }

3983

3984 self.__smbTransCommands = {

3985 '\\PIPE\\LANMAN': self.__smbTransHandler.lanMan,

3986 smb.SMB.TRANS_TRANSACT_NMPIPE: self.__smbTransHandler.transactNamedPipe,

3987 }

3988 self.__smbTrans2Commands = {

3989 smb.SMB.TRANS2_FIND_FIRST2: self.__smbTrans2Handler.findFirst2,

3990 smb.SMB.TRANS2_FIND_NEXT2: self.__smbTrans2Handler.findNext2,

3991 smb.SMB.TRANS2_QUERY_FS_INFORMATION: self.__smbTrans2Handler.queryFsInformation,

3992 smb.SMB.TRANS2_QUERY_PATH_INFORMATION: self.__smbTrans2Handler.queryPathInformation,

3993 smb.SMB.TRANS2_QUERY_FILE_INFORMATION: self.__smbTrans2Handler.queryFileInformation,

3994 smb.SMB.TRANS2_SET_FILE_INFORMATION: self.__smbTrans2Handler.setFileInformation,

3995 smb.SMB.TRANS2_SET_PATH_INFORMATION: self.__smbTrans2Handler.setPathInformation

3996 }

3997

3998 self.__smbCommands = {

3999 smb.SMB.SMB_COM_FLUSH: self.__smbCommandsHandler.smbComFlush,

4000 smb.SMB.SMB_COM_CREATE_DIRECTORY: self.__smbCommandsHandler.smbComCreateDirectory,

4001 smb.SMB.SMB_COM_DELETE_DIRECTORY: self.__smbCommandsHandler.smbComDeleteDirectory,

4002 smb.SMB.SMB_COM_RENAME: self.__smbCommandsHandler.smbComRename,

4003 smb.SMB.SMB_COM_DELETE: self.__smbCommandsHandler.smbComDelete,

4004 smb.SMB.SMB_COM_NEGOTIATE: self.__smbCommandsHandler.smbComNegotiate,

4005 smb.SMB.SMB_COM_SESSION_SETUP_ANDX: self.__smbCommandsHandler.smbComSessionSetupAndX,

4006 smb.SMB.SMB_COM_LOGOFF_ANDX: self.__smbCommandsHandler.smbComLogOffAndX,

4007 smb.SMB.SMB_COM_TREE_CONNECT_ANDX: self.__smbCommandsHandler.smbComTreeConnectAndX,

4008 smb.SMB.SMB_COM_TREE_DISCONNECT: self.__smbCommandsHandler.smbComTreeDisconnect,

4009 smb.SMB.SMB_COM_ECHO: self.__smbCommandsHandler.smbComEcho,

4010 smb.SMB.SMB_COM_QUERY_INFORMATION: self.__smbCommandsHandler.smbQueryInformation,

4011 smb.SMB.SMB_COM_TRANSACTION2: self.__smbCommandsHandler.smbTransaction2,

4012 smb.SMB.SMB_COM_TRANSACTION: self.__smbCommandsHandler.smbTransaction,

4013 # Not needed for now

4014 smb.SMB.SMB_COM_NT_TRANSACT: self.__smbCommandsHandler.smbNTTransact,

4015 smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: self.__smbCommandsHandler.smbQueryInformationDisk,

4016 smb.SMB.SMB_COM_OPEN_ANDX: self.__smbCommandsHandler.smbComOpenAndX,

4017 smb.SMB.SMB_COM_QUERY_INFORMATION2: self.__smbCommandsHandler.smbComQueryInformation2,

4018 smb.SMB.SMB_COM_READ_ANDX: self.__smbCommandsHandler.smbComReadAndX,

4019 smb.SMB.SMB_COM_READ: self.__smbCommandsHandler.smbComRead,

4020 smb.SMB.SMB_COM_WRITE_ANDX: self.__smbCommandsHandler.smbComWriteAndX,

4021 smb.SMB.SMB_COM_WRITE: self.__smbCommandsHandler.smbComWrite,

4022 smb.SMB.SMB_COM_CLOSE: self.__smbCommandsHandler.smbComClose,

4023 smb.SMB.SMB_COM_LOCKING_ANDX: self.__smbCommandsHandler.smbComLockingAndX,

4024 smb.SMB.SMB_COM_NT_CREATE_ANDX: self.__smbCommandsHandler.smbComNtCreateAndX,

4025 0xFF: self.__smbCommandsHandler.default

4026 }

4027

4028 self.__smb2Ioctls = {

4029 smb2.FSCTL_DFS_GET_REFERRALS: self.__IoctlHandler.fsctlDfsGetReferrals,

4030 # smb2.FSCTL_PIPE_PEEK: self.__IoctlHandler.fsctlPipePeek,

4031 # smb2.FSCTL_PIPE_WAIT: self.__IoctlHandler.fsctlPipeWait,

4032 smb2.FSCTL_PIPE_TRANSCEIVE: self.__IoctlHandler.fsctlPipeTransceive,

4033 # smb2.FSCTL_SRV_COPYCHUNK: self.__IoctlHandler.fsctlSrvCopyChunk,

4034 # smb2.FSCTL_SRV_ENUMERATE_SNAPSHOTS: self.__IoctlHandler.fsctlSrvEnumerateSnapshots,

4035 # smb2.FSCTL_SRV_REQUEST_RESUME_KEY: self.__IoctlHandler.fsctlSrvRequestResumeKey,

4036 # smb2.FSCTL_SRV_READ_HASH: self.__IoctlHandler.fsctlSrvReadHash,

4037 # smb2.FSCTL_SRV_COPYCHUNK_WRITE: self.__IoctlHandler.fsctlSrvCopyChunkWrite,

4038 # smb2.FSCTL_LMR_REQUEST_RESILIENCY: self.__IoctlHandler.fsctlLmrRequestResiliency,

4039 # smb2.FSCTL_QUERY_NETWORK_INTERFACE_INFO: self.__IoctlHandler.fsctlQueryNetworkInterfaceInfo,

4040 # smb2.FSCTL_SET_REPARSE_POINT: self.__IoctlHandler.fsctlSetReparsePoint,

4041 # smb2.FSCTL_DFS_GET_REFERRALS_EX: self.__IoctlHandler.fsctlDfsGetReferralsEx,

4042 # smb2.FSCTL_FILE_LEVEL_TRIM: self.__IoctlHandler.fsctlFileLevelTrim,

4043 smb2.FSCTL_VALIDATE_NEGOTIATE_INFO: self.__IoctlHandler.fsctlValidateNegotiateInfo,

4044 }

4045

4046 self.__smb2Commands = {

4047 smb2.SMB2_NEGOTIATE: self.__smb2CommandsHandler.smb2Negotiate,

4048 smb2.SMB2_SESSION_SETUP: self.__smb2CommandsHandler.smb2SessionSetup,

4049 smb2.SMB2_LOGOFF: self.__smb2CommandsHandler.smb2Logoff,

4050 smb2.SMB2_TREE_CONNECT: self.__smb2CommandsHandler.smb2TreeConnect,

4051 smb2.SMB2_TREE_DISCONNECT: self.__smb2CommandsHandler.smb2TreeDisconnect,

4052 smb2.SMB2_CREATE: self.__smb2CommandsHandler.smb2Create,

4053 smb2.SMB2_CLOSE: self.__smb2CommandsHandler.smb2Close,

4054 smb2.SMB2_FLUSH: self.__smb2CommandsHandler.smb2Flush,

4055 smb2.SMB2_READ: self.__smb2CommandsHandler.smb2Read,

4056 smb2.SMB2_WRITE: self.__smb2CommandsHandler.smb2Write,

4057 smb2.SMB2_LOCK: self.__smb2CommandsHandler.smb2Lock,

4058 smb2.SMB2_IOCTL: self.__smb2CommandsHandler.smb2Ioctl,

4059 smb2.SMB2_CANCEL: self.__smb2CommandsHandler.smb2Cancel,

4060 smb2.SMB2_ECHO: self.__smb2CommandsHandler.smb2Echo,

4061 smb2.SMB2_QUERY_DIRECTORY: self.__smb2CommandsHandler.smb2QueryDirectory,

4062 smb2.SMB2_CHANGE_NOTIFY: self.__smb2CommandsHandler.smb2ChangeNotify,

4063 smb2.SMB2_QUERY_INFO: self.__smb2CommandsHandler.smb2QueryInfo,

4064 smb2.SMB2_SET_INFO: self.__smb2CommandsHandler.smb2SetInfo,

4065 # smb2.SMB2_OPLOCK_BREAK: self.__smb2CommandsHandler.smb2SessionSetup,

4066 0xFF: self.__smb2CommandsHandler.default

4067 }

4068

4069 # List of active connections

4070 self.__activeConnections = {}

4071

4072 def getIoctls(self):

4073 return self.__smb2Ioctls

4074

4075 def getCredentials(self):

4076 return self.__credentials

4077

4078 def removeConnection(self, name):

4079 try:

4080 del (self.__activeConnections[name])

4081 except:

4082 pass

4083 self.log("Remaining connections %s" % list(self.__activeConnections.keys()))

4084

4085 def addConnection(self, name, ip, port):

4086 self.__activeConnections[name] = {}

4087 # Let's init with some know stuff we will need to have

4088 # TODO: Document what's in there

4089 # print "Current Connections", self.__activeConnections.keys()

4090 self.__activeConnections[name]['PacketNum'] = 0

4091 self.__activeConnections[name]['ClientIP'] = ip

4092 self.__activeConnections[name]['ClientPort'] = port

4093 self.__activeConnections[name]['Uid'] = 0

4094 self.__activeConnections[name]['ConnectedShares'] = {}

4095 self.__activeConnections[name]['OpenedFiles'] = {}

4096 # SID results for findfirst2

4097 self.__activeConnections[name]['SIDs'] = {}

4098 self.__activeConnections[name]['LastRequest'] = {}

4099 self.__activeConnections[name]['SignatureEnabled'] = False

4100 self.__activeConnections[name]['SigningChallengeResponse'] = ''

4101 self.__activeConnections[name]['SigningSessionKey'] = b''

4102 self.__activeConnections[name]['Authenticated'] = False

4103

4104 def getActiveConnections(self):

4105 return self.__activeConnections

4106

4107 def setConnectionData(self, connId, data):

4108 self.__activeConnections[connId] = data

4109 # print "setConnectionData"

4110 # print self.__activeConnections

4111

4112 def getConnectionData(self, connId, checkStatus=True):

4113 conn = self.__activeConnections[connId]

4114 if checkStatus is True:

4115 if ('Authenticated' in conn) is not True:

4116 # Can't keep going further

4117 raise Exception("User not Authenticated!")

4118 return conn

4119

4120 def getRegisteredNamedPipes(self):

4121 return self.__registeredNamedPipes

4122

4123 def registerNamedPipe(self, pipeName, address):

4124 self.__registeredNamedPipes[str(pipeName)] = address

4125 return True

4126

4127 def unregisterNamedPipe(self, pipeName):

4128 if pipeName in self.__registeredNamedPipes:

4129 del (self.__registeredNamedPipes[str(pipeName)])

4130 return True

4131 return False

4132

4133 def unregisterTransaction(self, transCommand):

4134 if transCommand in self.__smbTransCommands:

4135 del (self.__smbTransCommands[transCommand])

4136

4137 def hookTransaction(self, transCommand, callback):

4138 # If you call this function, callback will replace

4139 # the current Transaction sub command.

4140 # (don't get confused with the Transaction smbCommand)

4141 # If the transaction sub command doesn't not exist, it is added

4142 # If the transaction sub command exists, it returns the original function # replaced

4143 #

4144 # callback MUST be declared as:

4145 # callback(connId, smbServer, recvPacket, parameters, data, maxDataCount=0)

4146 #

4147 # WHERE:

4148 #

4149 # connId : the connection Id, used to grab/update information about

4150 # the current connection

4151 # smbServer : the SMBServer instance available for you to ask

4152 # configuration data

4153 # recvPacket : the full SMBPacket that triggered this command

4154 # parameters : the transaction parameters

4155 # data : the transaction data

4156 # maxDataCount: the max amount of data that can be transferred agreed

4157 # with the client

4158 #

4159 # and MUST return:

4160 # respSetup, respParameters, respData, errorCode

4161 #

4162 # WHERE:

4163 #

4164 # respSetup: the setup response of the transaction

4165 # respParameters: the parameters response of the transaction

4166 # respData: the data response of the transaction

4167 # errorCode: the NT error code

4168

4169 if transCommand in self.__smbTransCommands:

4170 originalCommand = self.__smbTransCommands[transCommand]

4171 else:

4172 originalCommand = None

4173

4174 self.__smbTransCommands[transCommand] = callback

4175 return originalCommand

4176

4177 def unregisterTransaction2(self, transCommand):

4178 if transCommand in self.__smbTrans2Commands:

4179 del (self.__smbTrans2Commands[transCommand])

4180

4181 def hookTransaction2(self, transCommand, callback):

4182 # Here we should add to __smbTrans2Commands

4183 # Same description as Transaction

4184 if transCommand in self.__smbTrans2Commands:

4185 originalCommand = self.__smbTrans2Commands[transCommand]

4186 else:

4187 originalCommand = None

4188

4189 self.__smbTrans2Commands[transCommand] = callback

4190 return originalCommand

4191

4192 def unregisterNTTransaction(self, transCommand):

4193 if transCommand in self.__smbNTTransCommands:

4194 del (self.__smbNTTransCommands[transCommand])

4195

4196 def hookNTTransaction(self, transCommand, callback):

4197 # Here we should add to __smbNTTransCommands

4198 # Same description as Transaction

4199 if transCommand in self.__smbNTTransCommands:

4200 originalCommand = self.__smbNTTransCommands[transCommand]

4201 else:

4202 originalCommand = None

4203

4204 self.__smbNTTransCommands[transCommand] = callback

4205 return originalCommand

4206

4207 def unregisterSmbCommand(self, smbCommand):

4208 if smbCommand in self.__smbCommands:

4209 del (self.__smbCommands[smbCommand])

4210

4211 def hookSmbCommand(self, smbCommand, callback):

4212 # Here we should add to self.__smbCommands

4213 # If you call this function, callback will replace

4214 # the current smbCommand.

4215 # If smbCommand doesn't not exist, it is added

4216 # If SMB command exists, it returns the original function replaced

4217 #

4218 # callback MUST be declared as:

4219 # callback(connId, smbServer, SMBCommand, recvPacket)

4220 #

4221 # WHERE:

4222 #

4223 # connId : the connection Id, used to grab/update information about

4224 # the current connection

4225 # smbServer : the SMBServer instance available for you to ask

4226 # configuration data

4227 # SMBCommand: the SMBCommand itself, with its data and parameters.

4228 # Check smb.py:SMBCommand() for a reference

4229 # recvPacket: the full SMBPacket that triggered this command

4230 #

4231 # and MUST return:

4232 # <list of respSMBCommands>, <list of packets>, errorCode

4233 # <list of packets> has higher preference over commands, in case you

4234 # want to change the whole packet

4235 # errorCode: the NT error code

4236 #

4237 # For SMB_COM_TRANSACTION2, SMB_COM_TRANSACTION and SMB_COM_NT_TRANSACT

4238 # the callback function is slightly different:

4239 #

4240 # callback(connId, smbServer, SMBCommand, recvPacket, transCommands)

4241 #

4242 # WHERE:

4243 #

4244 # transCommands: a list of transaction subcommands already registered

4245 #

4246

4247 if smbCommand in self.__smbCommands:

4248 originalCommand = self.__smbCommands[smbCommand]

4249 else:

4250 originalCommand = None

4251

4252 self.__smbCommands[smbCommand] = callback

4253 return originalCommand

4254

4255 def unregisterSmb2Command(self, smb2Command):

4256 if smb2Command in self.__smb2Commands:

4257 del (self.__smb2Commands[smb2Command])

4258

4259 def hookSmb2Command(self, smb2Command, callback):

4260 if smb2Command in self.__smb2Commands:

4261 originalCommand = self.__smb2Commands[smb2Command]

4262 else:

4263 originalCommand = None

4264

4265 self.__smb2Commands[smb2Command] = callback

4266 return originalCommand

4267

4268 def log(self, msg, level=logging.INFO):

4269 self.__log.log(level, msg)

4270

4271 def getServerName(self):

4272 return self.__serverName

4273

4274 def getServerOS(self):

4275 return self.__serverOS

4276

4277 def getServerDomain(self):

4278 return self.__serverDomain

4279

4280 def getSMBChallenge(self):

4281 return self.__challenge

4282

4283 def getServerConfig(self):

4284 return self.__serverConfig

4285

4286 def setServerConfig(self, config):

4287 self.__serverConfig = config

4288

4289 def getJTRdumpPath(self):

4290 return self.__jtr_dump_path

4291

4292 def verify_request(self, request, client_address):

4293 # TODO: Control here the max amount of processes we want to launch

4294 # returning False, closes the connection

4295 return True

4296

4297 def signSMBv1(self, connData, packet, signingSessionKey, signingChallengeResponse):

4298 # This logic MUST be applied for messages sent in response to any of the higher-layer actions and in

4299 # compliance with the message sequencing rules.

4300 # * The client or server that sends the message MUST provide the 32-bit sequence number for this

4301 # message, as specified in sections 3.2.4.1 and 3.3.4.1.

4302 # * The SMB_FLAGS2_SMB_SECURITY_SIGNATURE flag in the header MUST be set.

4303 # * To generate the signature, a 32-bit sequence number is copied into the

4304 # least significant 32 bits of the SecuritySignature field and the remaining

4305 # 4 bytes are set to 0x00.

4306 # * The MD5 algorithm, as specified in [RFC1321], MUST be used to generate a hash of the SMB

4307 # message from the start of the SMB Header, which is defined as follows.

4308 # CALL MD5Init( md5context )

4309 # CALL MD5Update( md5context, Connection.SigningSessionKey )

4310 # CALL MD5Update( md5context, Connection.SigningChallengeResponse )

4311 # CALL MD5Update( md5context, SMB message )

4312 # CALL MD5Final( digest, md5context )

4313 # SET signature TO the first 8 bytes of the digest

4314 # The resulting 8-byte signature MUST be copied into the SecuritySignature field of the SMB Header,

4315 # after which the message can be transmitted.

4316

4317 # print "seq(%d) signingSessionKey %r, signingChallengeResponse %r" % (connData['SignSequenceNumber'], signingSessionKey, signingChallengeResponse)

4318 packet['SecurityFeatures'] = struct.pack('<q', connData['SignSequenceNumber'])

4319 # Sign with the sequence

4320 m = hashlib.md5()

4321 m.update(signingSessionKey)

4322 m.update(signingChallengeResponse)

4323 if hasattr(packet, 'getData'):

4324 m.update(packet.getData())

4325 else:

4326 m.update(packet)

4327 # Replace sequence with acual hash

4328 packet['SecurityFeatures'] = m.digest()[:8]

4329 connData['SignSequenceNumber'] += 2

4330

4331 def signSMBv2(self, packet, signingSessionKey):

4332 packet['Signature'] = b'\x00' * 16

4333 packet['Flags'] |= smb2.SMB2_FLAGS_SIGNED

4334 signature = hmac.new(signingSessionKey, packet.getData(), hashlib.sha256).digest()

4335 packet['Signature'] = signature[:16]

4336 # print "%s" % packet['Signature'].encode('hex')

4337

4338 def processRequest(self, connId, data):

4339

4340 # TODO: Process batched commands.

4341 isSMB2 = False

4342 SMBCommand = None

4343 try:

4344 packet = smb.NewSMBPacket(data=data)

4345 SMBCommand = smb.SMBCommand(packet['Data'][0])

4346 except:

4347 # Maybe a SMB2 packet?

4348 packet = smb2.SMB2Packet(data=data)

4349 connData = self.getConnectionData(connId, False)

4350 self.signSMBv2(packet, connData['SigningSessionKey'])

4351 isSMB2 = True

4352

4353 connData = self.getConnectionData(connId, False)

4354

4355 # We might have compound requests

4356 compoundedPacketsResponse = []

4357 compoundedPackets = []

4358 try:

4359 # Search out list of implemented commands

4360 # We provide them with:

4361 # connId : representing the data for this specific connection

4362 # self : the SMBSERVER if they want to ask data to it

4363 # SMBCommand : the SMBCommand they are expecting to process

4364 # packet : the received packet itself, in case they need more data than the actual command

4365 # Only for Transactions

4366 # transCommand: a list of transaction subcommands

4367 # We expect to get:

4368 # respCommands: a list of answers for the commands processed

4369 # respPacket : if the commands chose to directly craft packet/s, we use this and not the previous

4370 # this MUST be a list

4371 # errorCode : self explanatory

4372 if isSMB2 is False:

4373 # Is the client authenticated already?

4374 if connData['Authenticated'] is False and packet['Command'] not in (

4375 smb.SMB.SMB_COM_NEGOTIATE, smb.SMB.SMB_COM_SESSION_SETUP_ANDX):

4376 # Nope.. in that case he should only ask for a few commands, if not throw him out.

4377 errorCode = STATUS_ACCESS_DENIED

4378 respPackets = None

4379 respCommands = [smb.SMBCommand(packet['Command'])]

4380 else:

4381 if packet['Command'] == smb.SMB.SMB_COM_TRANSACTION2:

4382 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4383 connId,

4384 self,

4385 SMBCommand,

4386 packet,

4387 self.__smbTrans2Commands)

4388 elif packet['Command'] == smb.SMB.SMB_COM_NT_TRANSACT:

4389 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4390 connId,

4391 self,

4392 SMBCommand,

4393 packet,

4394 self.__smbNTTransCommands)

4395 elif packet['Command'] == smb.SMB.SMB_COM_TRANSACTION:

4396 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4397 connId,

4398 self,

4399 SMBCommand,

4400 packet,

4401 self.__smbTransCommands)

4402 else:

4403 if packet['Command'] in self.__smbCommands:

4404 if self.__SMB2Support is True:

4405 if packet['Command'] == smb.SMB.SMB_COM_NEGOTIATE:

4406 try:

4407 respCommands, respPackets, errorCode = self.__smb2Commands[smb2.SMB2_NEGOTIATE](

4408 connId, self, packet, True)

4409 isSMB2 = True

4410 except Exception as e:

4411 import traceback

4412 traceback.print_exc()

4413 self.log('SMB2_NEGOTIATE: %s' % e, logging.ERROR)

4414 # If something went wrong, let's fallback to SMB1

4415 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4416 connId,

4417 self,

4418 SMBCommand,

4419 packet)

4420 # self.__SMB2Support = False

4421 pass

4422 else:

4423 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4424 connId,

4425 self,

4426 SMBCommand,

4427 packet)

4428 else:

4429 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4430 connId,

4431 self,

4432 SMBCommand,

4433 packet)

4434 else:

4435 respCommands, respPackets, errorCode = self.__smbCommands[255](connId, self, SMBCommand,

4436 packet)

4437

4438 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))

4439 compoundedPackets.append(packet)

4440

4441 else:

4442 # Is the client authenticated already?

4443 if connData['Authenticated'] is False and packet['Command'] not in (

4444 smb2.SMB2_NEGOTIATE, smb2.SMB2_SESSION_SETUP):

4445 # Nope.. in that case he should only ask for a few commands, if not throw him out.

4446 errorCode = STATUS_ACCESS_DENIED

4447 respPackets = None

4448 respCommands = ['']

4449 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))

4450 compoundedPackets.append(packet)

4451 else:

4452 done = False

4453 while not done:

4454 if packet['Command'] in self.__smb2Commands:

4455 if self.__SMB2Support is True:

4456 respCommands, respPackets, errorCode = self.__smb2Commands[packet['Command']](

4457 connId,

4458 self,

4459 packet)

4460 else:

4461 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)

4462 else:

4463 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)

4464 # Let's store the result for this compounded packet

4465 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))

4466 compoundedPackets.append(packet)

4467 if packet['NextCommand'] != 0:

4468 data = data[packet['NextCommand']:]

4469 packet = smb2.SMB2Packet(data=data)

4470 else:

4471 done = True

4472

4473 except Exception as e:

4474 # import traceback

4475 # traceback.print_exc()

4476 # Something wen't wrong, defaulting to Bad user ID

4477 self.log('processRequest (0x%x,%s)' % (packet['Command'], e), logging.ERROR)

4478 raise

4479

4480 # We prepare the response packet to commands don't need to bother about that.

4481 connData = self.getConnectionData(connId, False)

4482

4483 # Force reconnection loop.. This is just a test.. client will send me back credentials :)

4484 # connData['PacketNum'] += 1

4485 # if connData['PacketNum'] == 15:

4486 # connData['PacketNum'] = 0

4487 # # Something wen't wrong, defaulting to Bad user ID

4488 # self.log('Sending BAD USER ID!', logging.ERROR)

4489 # #raise

4490 # packet['Flags1'] |= smb.SMB.FLAGS1_REPLY

4491 # packet['Flags2'] = 0

4492 # errorCode = STATUS_SMB_BAD_UID

4493 # packet['ErrorCode'] = errorCode >> 16

4494 # packet['ErrorClass'] = errorCode & 0xff

4495 # return [packet]

4496

4497 self.setConnectionData(connId, connData)

4498

4499 packetsToSend = []

4500 for packetNum in range(len(compoundedPacketsResponse)):

4501 respCommands, respPackets, errorCode = compoundedPacketsResponse[packetNum]

4502 packet = compoundedPackets[packetNum]

4503 if respPackets is None:

4504 for respCommand in respCommands:

4505 if isSMB2 is False:

4506 respPacket = smb.NewSMBPacket()

4507 respPacket['Flags1'] = smb.SMB.FLAGS1_REPLY

4508

4509 # TODO this should come from a per session configuration

4510 respPacket[

4511 'Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \

4512 packet['Flags2'] & smb.SMB.FLAGS2_UNICODE

4513 # respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES

4514 # respPacket['Flags1'] = 0x98

4515 # respPacket['Flags2'] = 0xc807

4516

4517 respPacket['Tid'] = packet['Tid']

4518 respPacket['Mid'] = packet['Mid']

4519 respPacket['Pid'] = packet['Pid']

4520 respPacket['Uid'] = connData['Uid']

4521

4522 respPacket['ErrorCode'] = errorCode >> 16

4523 respPacket['_reserved'] = errorCode >> 8 & 0xff

4524 respPacket['ErrorClass'] = errorCode & 0xff

4525 respPacket.addCommand(respCommand)

4526

4527 if connData['SignatureEnabled']:

4528 respPacket['Flags2'] |= smb.SMB.FLAGS2_SMB_SECURITY_SIGNATURE

4529 self.signSMBv1(connData, respPacket, connData['SigningSessionKey'],

4530 connData['SigningChallengeResponse'])

4531

4532 packetsToSend.append(respPacket)

4533 else:

4534 respPacket = smb2.SMB2Packet()

4535 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR

4536 if packetNum > 0:

4537 respPacket['Flags'] |= smb2.SMB2_FLAGS_RELATED_OPERATIONS

4538 respPacket['Status'] = errorCode

4539 respPacket['CreditRequestResponse'] = packet['CreditRequestResponse']

4540 respPacket['Command'] = packet['Command']

4541 respPacket['CreditCharge'] = packet['CreditCharge']

4542 # respPacket['CreditCharge'] = 0

4543 respPacket['Reserved'] = packet['Reserved']

4544 respPacket['SessionID'] = connData['Uid']

4545 respPacket['MessageID'] = packet['MessageID']

4546 respPacket['TreeID'] = packet['TreeID']

4547 if hasattr(respCommand, 'getData'):

4548 respPacket['Data'] = respCommand.getData()

4549 else:

4550 respPacket['Data'] = str(respCommand)

4551

4552 if connData['SignatureEnabled']:

4553 self.signSMBv2(respPacket, connData['SigningSessionKey'])

4554

4555 packetsToSend.append(respPacket)

4556 else:

4557 # The SMBCommand took care of building the packet

4558 packetsToSend = respPackets

4559

4560 if isSMB2 is True:

4561 # Let's build a compound answer

4562 finalData = b''

4563 i = 0

4564 for i in range(len(packetsToSend) - 1):

4565 packet = packetsToSend[i]

4566 # Align to 8-bytes

4567 padLen = (8 - (len(packet) % 8)) % 8

4568 packet['NextCommand'] = len(packet) + padLen

4569 if hasattr(packet, 'getData'):

4570 finalData += packet.getData() + padLen * b'\x00'

4571 else:

4572 finalData += packet + padLen * b'\x00'

4573

4574 # Last one

4575 if hasattr(packetsToSend[len(packetsToSend) - 1], 'getData'):

4576 finalData += packetsToSend[len(packetsToSend) - 1].getData()

4577 else:

4578 finalData += packetsToSend[len(packetsToSend) - 1]

4579 packetsToSend = [finalData]

4580

4581 # We clear the compound requests

4582 connData['LastRequest'] = {}

4583

4584 return packetsToSend

4585

4586 def processConfigFile(self, configFile=None):

4587 # TODO: Do a real config parser

4588 if self.__serverConfig is None:

4589 if configFile is None:

4590 configFile = 'smb.conf'

4591 self.__serverConfig = configparser.ConfigParser()

4592 self.__serverConfig.read(configFile)

4593

4594 self.__serverName = self.__serverConfig.get('global', 'server_name')

4595 self.__serverOS = self.__serverConfig.get('global', 'server_os')

4596 self.__serverDomain = self.__serverConfig.get('global', 'server_domain')

4597 self.__logFile = self.__serverConfig.get('global', 'log_file')

4598 if self.__serverConfig.has_option('global', 'challenge'):

4599 self.__challenge = unhexlify(self.__serverConfig.get('global', 'challenge'))

4600 else:

4601 self.__challenge = b'A' * 16

4602

4603 if self.__serverConfig.has_option("global", "jtr_dump_path"):

4604 self.__jtr_dump_path = self.__serverConfig.get("global", "jtr_dump_path")

4605

4606 if self.__serverConfig.has_option("global", "SMB2Support"):

4607 self.__SMB2Support = self.__serverConfig.getboolean("global", "SMB2Support")

4608 else:

4609 self.__SMB2Support = False

4610

4611

4612 if self.__serverConfig.has_option("global", "anonymous_logon"):

4613 self.__anonymousLogon = self.__serverConfig.getboolean("global", "anonymous_logon")

4614 else:

4615 self.__anonymousLogon = True

4616

4617 if self.__logFile != 'None':

4618 logging.basicConfig(filename=self.__logFile,

4619 level=logging.DEBUG,

4620 format="%(asctime)s: %(levelname)s: %(message)s",

4621 datefmt='%m/%d/%Y %I:%M:%S %p')

4622 self.__log = LOG

4623

4624 # Process the credentials

4625 credentials_fname = self.__serverConfig.get('global', 'credentials_file')

4626 if credentials_fname != "":

4627 cred = open(credentials_fname)

4628 line = cred.readline()

4629 while line:

4630 name, uid, lmhash, nthash = line.split(':')

4631 self.__credentials[name.lower()] = (uid, lmhash, nthash.strip('\r\n'))

4632 line = cred.readline()

4633 cred.close()

4634 self.log('Config file parsed')

4635

4636 def addCredential(self, name, uid, lmhash, nthash):

4637 # If we have hashes, normalize them

4638 if lmhash != '' or nthash != '':

4639 if len(lmhash) % 2:

4640 lmhash = '0%s' % lmhash

4641 if len(nthash) % 2:

4642 nthash = '0%s' % nthash

4643 try: # just in case they were converted already

4644 lmhash = a2b_hex(lmhash)

4645 nthash = a2b_hex(nthash)

4646 except:

4647 pass

4648 self.__credentials[name.lower()] = (uid, lmhash, nthash)

4649

4650

4651# For windows platforms, opening a directory is not an option, so we set a void FD

4652 VOID_FILE_DESCRIPTOR = -1

4653 PIPE_FILE_DESCRIPTOR = -2

4654

4655######################################################################

4656# HELPER CLASSES

4657######################################################################

4658

4659 from impacket.dcerpc.v5.rpcrt import DCERPCServer

4660 from impacket.dcerpc.v5.dtypes import NULL

4661 from impacket.dcerpc.v5.srvs import NetrShareEnum, NetrShareEnumResponse, SHARE_INFO_1, NetrServerGetInfo, \

4662 NetrServerGetInfoResponse, NetrShareGetInfo, NetrShareGetInfoResponse

4663 from impacket.dcerpc.v5.wkst import NetrWkstaGetInfo, NetrWkstaGetInfoResponse

4664 from impacket.system_errors import ERROR_INVALID_LEVEL

4665

4666

4667 class WKSTServer(DCERPCServer):

4668 def __init__(self):

4669 DCERPCServer.__init__(self)

4670 self.wkssvcCallBacks = {

4671 0: self.NetrWkstaGetInfo,

4672 }

4673 self.addCallbacks(('6BFFD098-A112-3610-9833-46C3F87E345A', '1.0'), '\\PIPE\\wkssvc', self.wkssvcCallBacks)

4674

4675 def NetrWkstaGetInfo(self, data):

4676 request = NetrWkstaGetInfo(data)

4677 self.log("NetrWkstaGetInfo Level: %d" % request['Level'])

4678

4679 answer = NetrWkstaGetInfoResponse()

4680

4681 if request['Level'] not in (100, 101):

4682 answer['ErrorCode'] = ERROR_INVALID_LEVEL

4683 return answer

4684

4685 answer['WkstaInfo']['tag'] = request['Level']

4686

4687 if request['Level'] == 100:

4688 # Windows. Decimal value 500.

4689 answer['WkstaInfo']['WkstaInfo100']['wki100_platform_id'] = 0x000001F4

4690 answer['WkstaInfo']['WkstaInfo100']['wki100_computername'] = NULL

4691 answer['WkstaInfo']['WkstaInfo100']['wki100_langroup'] = NULL

4692 answer['WkstaInfo']['WkstaInfo100']['wki100_ver_major'] = 5

4693 answer['WkstaInfo']['WkstaInfo100']['wki100_ver_minor'] = 0

4694 else:

4695 # Windows. Decimal value 500.

4696 answer['WkstaInfo']['WkstaInfo101']['wki101_platform_id'] = 0x000001F4

4697 answer['WkstaInfo']['WkstaInfo101']['wki101_computername'] = NULL

4698 answer['WkstaInfo']['WkstaInfo101']['wki101_langroup'] = NULL

4699 answer['WkstaInfo']['WkstaInfo101']['wki101_ver_major'] = 5

4700 answer['WkstaInfo']['WkstaInfo101']['wki101_ver_minor'] = 0

4701 answer['WkstaInfo']['WkstaInfo101']['wki101_lanroot'] = NULL

4702

4703 return answer

4704

4705

4706 class SRVSServer(DCERPCServer):

4707 def __init__(self):

4708 DCERPCServer.__init__(self)

4709

4710 self._shares = {}

4711 self.__serverConfig = None

4712 self.__logFile = None

4713

4714 self.srvsvcCallBacks = {

4715 15: self.NetrShareEnum,

4716 16: self.NetrShareGetInfo,

4717 21: self.NetrServerGetInfo,

4718 }

4719

4720 self.addCallbacks(('4B324FC8-1670-01D3-1278-5A47BF6EE188', '3.0'), '\\PIPE\\srvsvc', self.srvsvcCallBacks)

4721

4722 def setServerConfig(self, config):

4723 self.__serverConfig = config

4724

4725 def processConfigFile(self, configFile=None):

4726 if configFile is not None:

4727 self.__serverConfig = configparser.ConfigParser()

4728 self.__serverConfig.read(configFile)

4729 sections = self.__serverConfig.sections()

4730 # Let's check the log file

4731 self.__logFile = self.__serverConfig.get('global', 'log_file')

4732 if self.__logFile != 'None':

4733 logging.basicConfig(filename=self.__logFile,

4734 level=logging.DEBUG,

4735 format="%(asctime)s: %(levelname)s: %(message)s",

4736 datefmt='%m/%d/%Y %I:%M:%S %p')

4737

4738 # Remove the global one

4739 del (sections[sections.index('global')])

4740 self._shares = {}

4741 for i in sections:

4742 self._shares[i] = dict(self.__serverConfig.items(i))

4743

4744 def NetrShareGetInfo(self, data):

4745 request = NetrShareGetInfo(data)

4746 self.log("NetrGetShareInfo Level: %d" % request['Level'])

4747

4748 s = request['NetName'][:-1].upper()

4749 answer = NetrShareGetInfoResponse()

4750 if s in self._shares:

4751 share = self._shares[s]

4752

4753 answer['InfoStruct']['tag'] = 1

4754 answer['InfoStruct']['ShareInfo1']['shi1_netname'] = s + '\x00'

4755 answer['InfoStruct']['ShareInfo1']['shi1_type'] = share['share type']

4756 answer['InfoStruct']['ShareInfo1']['shi1_remark'] = share['comment'] + '\x00'

4757 answer['ErrorCode'] = 0

4758 else:

4759 answer['InfoStruct']['tag'] = 1

4760 answer['InfoStruct']['ShareInfo1'] = NULL

4761 answer['ErrorCode'] = 0x0906 # WERR_NET_NAME_NOT_FOUND

4762

4763 return answer

4764

4765 def NetrServerGetInfo(self, data):

4766 request = NetrServerGetInfo(data)

4767 self.log("NetrServerGetInfo Level: %d" % request['Level'])

4768 answer = NetrServerGetInfoResponse()

4769 answer['InfoStruct']['tag'] = 101

4770 # PLATFORM_ID_NT = 500

4771 answer['InfoStruct']['ServerInfo101']['sv101_platform_id'] = 500

4772 answer['InfoStruct']['ServerInfo101']['sv101_name'] = request['ServerName']

4773 # Windows 7 = 6.1

4774 answer['InfoStruct']['ServerInfo101']['sv101_version_major'] = 6

4775 answer['InfoStruct']['ServerInfo101']['sv101_version_minor'] = 1

4776 # Workstation = 1

4777 answer['InfoStruct']['ServerInfo101']['sv101_type'] = 1

4778 answer['InfoStruct']['ServerInfo101']['sv101_comment'] = NULL

4779 answer['ErrorCode'] = 0

4780 return answer

4781

4782 def NetrShareEnum(self, data):

4783 request = NetrShareEnum(data)

4784 self.log("NetrShareEnum Level: %d" % request['InfoStruct']['Level'])

4785 shareEnum = NetrShareEnumResponse()

4786 shareEnum['InfoStruct']['Level'] = 1

4787 shareEnum['InfoStruct']['ShareInfo']['tag'] = 1

4788 shareEnum['TotalEntries'] = len(self._shares)

4789 shareEnum['InfoStruct']['ShareInfo']['Level1']['EntriesRead'] = len(self._shares)

4790 shareEnum['ErrorCode'] = 0

4791

4792 for i in self._shares:

4793 shareInfo = SHARE_INFO_1()

4794 shareInfo['shi1_netname'] = i + '\x00'

4795 shareInfo['shi1_type'] = self._shares[i]['share type']

4796 shareInfo['shi1_remark'] = self._shares[i]['comment'] + '\x00'

4797 shareEnum['InfoStruct']['ShareInfo']['Level1']['Buffer'].append(shareInfo)

4798

4799 return shareEnum

4800

4801

4802 class SimpleSMBServer:

4803 """

4804 SimpleSMBServer class - Implements a simple, customizable SMB Server

4805

4806 :param string listenAddress: the address you want the server to listen on

4807 :param integer listenPort: the port number you want the server to listen on

4808 :param string configFile: a file with all the servers' configuration. If no file specified, this class will create the basic parameters needed to run. You will need to add your shares manually tho. See addShare() method

4809 """

4810

4811 def __init__(self, listenAddress='0.0.0.0', listenPort=445, configFile=''):

4812 if configFile != '':

4813 self.__server = SMBSERVER((listenAddress, listenPort))

4814 self.__server.processConfigFile(configFile)

4815 self.__smbConfig = None

4816 else:

4817 # Here we write a mini config for the server

4818 self.__smbConfig = configparser.ConfigParser()

4819 self.__smbConfig.add_section('global')

4820 self.__smbConfig.set('global', 'server_name',

4821 ''.join([random.choice(string.ascii_letters) for _ in range(8)]))

4822 self.__smbConfig.set('global', 'server_os', ''.join([random.choice(string.ascii_letters) for _ in range(8)])

4823 )

4824 self.__smbConfig.set('global', 'server_domain',

4825 ''.join([random.choice(string.ascii_letters) for _ in range(8)])

4826 )

4827 self.__smbConfig.set('global', 'log_file', 'None')

4828 self.__smbConfig.set('global', 'rpc_apis', 'yes')

4829 self.__smbConfig.set('global', 'credentials_file', '')

4830 self.__smbConfig.set('global', 'challenge', "A" * 16)

4831

4832 # IPC always needed

4833 self.__smbConfig.add_section('IPC$')

4834 self.__smbConfig.set('IPC$', 'comment', '')

4835 self.__smbConfig.set('IPC$', 'read only', 'yes')

4836 self.__smbConfig.set('IPC$', 'share type', '3')

4837 self.__smbConfig.set('IPC$', 'path', '')

4838 self.__server = SMBSERVER((listenAddress, listenPort), config_parser=self.__smbConfig)

4839 self.__server.processConfigFile()

4840

4841 # Now we have to register the MS-SRVS server. This specially important for

4842 # Windows 7+ and Mavericks clients since they WON'T (specially OSX)

4843 # ask for shares using MS-RAP.

4844

4845 self.__srvsServer = SRVSServer()

4846 self.__srvsServer.daemon = True

4847 self.__wkstServer = WKSTServer()

4848 self.__wkstServer.daemon = True

4849 self.__server.registerNamedPipe('srvsvc', ('127.0.0.1', self.__srvsServer.getListenPort()))

4850 self.__server.registerNamedPipe('wkssvc', ('127.0.0.1', self.__wkstServer.getListenPort()))

4851

4852 def start(self):

4853 self.__srvsServer.start()

4854 self.__wkstServer.start()

4855 self.__server.serve_forever()

4856

4857 def stop(self):

4858 self.__server.server_close()

4859

4860 def registerNamedPipe(self, pipeName, address):

4861 return self.__server.registerNamedPipe(pipeName, address)

4862

4863 def unregisterNamedPipe(self, pipeName):

4864 return self.__server.unregisterNamedPipe(pipeName)

4865

4866 def getRegisteredNamedPipes(self):

4867 return self.__server.getRegisteredNamedPipes()

4868

4869 def addShare(self, shareName, sharePath, shareComment='', shareType='0', readOnly='no'):

4870 share = shareName.upper()

4871 self.__smbConfig.add_section(share)

4872 self.__smbConfig.set(share, 'comment', shareComment)

4873 self.__smbConfig.set(share, 'read only', readOnly)

4874 self.__smbConfig.set(share, 'share type', shareType)

4875 self.__smbConfig.set(share, 'path', sharePath)

4876 self.__server.setServerConfig(self.__smbConfig)

4877 self.__srvsServer.setServerConfig(self.__smbConfig)

4878 self.__server.processConfigFile()

4879 self.__srvsServer.processConfigFile()

4880

4881 def removeShare(self, shareName):

4882 self.__smbConfig.remove_section(shareName.upper())

4883 self.__server.setServerConfig(self.__smbConfig)

4884 self.__srvsServer.setServerConfig(self.__smbConfig)

4885 self.__server.processConfigFile()

4886 self.__srvsServer.processConfigFile()

4887

4888 def setSMBChallenge(self, challenge):

4889 if challenge != '':

4890 self.__smbConfig.set('global', 'challenge', challenge)

4891 self.__server.setServerConfig(self.__smbConfig)

4892 self.__server.processConfigFile()

4893

4894 def setLogFile(self, logFile):

4895 self.__smbConfig.set('global', 'log_file', logFile)

4896 self.__server.setServerConfig(self.__smbConfig)

4897 self.__server.processConfigFile()

4898

4899 def setCredentialsFile(self, logFile):

4900 self.__smbConfig.set('global', 'credentials_file', logFile)

4901 self.__server.setServerConfig(self.__smbConfig)

4902 self.__server.processConfigFile()

4903

4904 def addCredential(self, name, uid, lmhash, nthash):

4905 self.__server.addCredential(name, uid, lmhash, nthash)

4906

4907 def setSMB2Support(self, value):

4908 if value is True:

4909 self.__smbConfig.set("global", "SMB2Support", "True")

4910 else:

4911 self.__smbConfig.set("global", "SMB2Support", "False")

4912 self.__server.setServerConfig(self.__smbConfig)

4913 self.__server.processConfigFile()