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

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

## 3033 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# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.

2#

3# This software is provided under under a slightly modified version

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

5# for more information.

6#

7# Author: Alberto Solino (@agsolino)

8#

9# TODO:

10# [-] Functions should return NT error codes

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

12# [*] Standard authentication support

13# [ ] Organize the connectionData stuff

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

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

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

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

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

19# [ ] Hence.. implement locking

20# estamos en la B

21

22

23 import calendar

24 import socket

25 import time

26 import datetime

27 import struct

28 import threading

29 import logging

30 import logging.config

31 import ntpath

32 import os

33 import fnmatch

34 import errno

35 import sys

36 import random

37 import shutil

38 import string

39 import hashlib

40 import hmac

41

42 from binascii import unhexlify, hexlify, a2b_hex

43 from six import PY2, b, text_type

44 from six.moves import configparser, socketserver

45

46# For signing

47 from impacket import smb, nmb, ntlm, uuid

48 from impacket import smb3structs as smb2

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

50 ASN1_SUPPORTED_MECH

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

52 STATUS_FILE_CLOSED, STATUS_MORE_PROCESSING_REQUIRED, STATUS_OBJECT_PATH_NOT_FOUND, STATUS_DIRECTORY_NOT_EMPTY, \

53 STATUS_FILE_IS_A_DIRECTORY, STATUS_NOT_IMPLEMENTED, STATUS_INVALID_HANDLE, STATUS_OBJECT_NAME_COLLISION, \

54 STATUS_NO_SUCH_FILE, STATUS_CANCELLED, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_SUCCESS, STATUS_ACCESS_DENIED, \

55 STATUS_NOT_SUPPORTED, STATUS_INVALID_DEVICE_REQUEST, STATUS_FS_DRIVER_REQUIRED, STATUS_INVALID_INFO_CLASS, \

56 STATUS_LOGON_FAILURE, STATUS_OBJECT_PATH_SYNTAX_BAD

57

58# Setting LOG to current's module name

59 LOG = logging.getLogger(__name__)

60

61# These ones not defined in nt_errors

62 STATUS_SMB_BAD_UID = 0x005B0002

63 STATUS_SMB_BAD_TID = 0x00050002

64

65

66# Utility functions

67# and general functions.

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

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

70# TODO: Return NT ERROR Codes

71

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

73 # Let's calculate the NTLMv2 Response

74

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

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

77

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

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

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

81 lmChallengeResponse = authenticateMessage['lanman']

82 sessionBaseKey = ntlm.hmac_md5(responseKeyNT, ntProofStr)

83

84 responseFlags = type1['flags']

85

86 # Let's check the return flags

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

88 # No extended session security, taking it out

89 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY

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

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

92 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_128

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

94 # No key exchange supported, taking it out

95 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH

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

97 # No sign available, taking it out

98 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SEAL

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

100 # No sign available, taking it out

101 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SIGN

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

103 # No sign available, taking it out

104 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN

105

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

107 ntlmChallenge['challenge'], '',

108 lmhash, nthash, True)

109

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

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

112 exportedSessionKey = authenticateMessage['session_key']

113 exportedSessionKey = ntlm.generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey)

114 else:

115 encryptedRandomSessionKey = None

116 # [MS-NLMP] page 46

117 exportedSessionKey = keyExchangeKey

118

119 # Do they match?

120 if ntProofStr == ntProofStr2:

121 # Yes!, process login

122 return STATUS_SUCCESS, exportedSessionKey

123 else:

124 return STATUS_LOGON_FAILURE, exportedSessionKey

125

126

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

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

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

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

131 ret_value = ''

132 if type(challenge) is not bytes:

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

134

135 try:

136 if len(ntresponse) > 24:

137 # Extended Security - NTLMv2

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

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

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

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

142 else:

143 # NTLMv1

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

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

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

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

148 except:

149 # Let's try w/o decoding Unicode

150 try:

151 if len(ntresponse) > 24:

152 # Extended Security - NTLMv2

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

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

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

156 'hash_version': 'ntlmv2'}

157 else:

158 # NTLMv1

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

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

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

162 except Exception as e:

163 import traceback

164 traceback.print_exc()

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

166 pass

167

168 return ret_value

169

170

171 def writeJohnOutputToFile(hash_string, hash_version, file_name):

172 fn_data = os.path.splitext(file_name)

173 if hash_version == "ntlmv2":

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

175 else:

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

177

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

179 f.write(hash_string)

180 f.write('\n')

181

182

183 def decodeSMBString(flags, text):

184 if flags & smb.SMB.FLAGS2_UNICODE:

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

186 else:

187 return text

188

189

190 def encodeSMBString(flags, text):

191 if flags & smb.SMB.FLAGS2_UNICODE:

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

193 else:

194 return text.encode('ascii')

195

196

197 def getFileTime(t):

198 t *= 10000000

199 t += 116444736000000000

200 return t

201

202

203 def getUnixTime(t):

204 t -= 116444736000000000

205 t //= 10000000

206 return t

207

208

209 def getSMBDate(t):

210 # TODO: Fix this :P

211 d = datetime.date.fromtimestamp(t)

212 year = d.year - 1980

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

214 return ret

215

216

217 def getSMBTime(t):

218 # TODO: Fix this :P

219 d = datetime.datetime.fromtimestamp(t)

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

221

222

223 def getShares(connId, smbServer):

224 config = smbServer.getServerConfig()

225 sections = config.sections()

226 # Remove the global one

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

228 shares = {}

229 for i in sections:

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

231 return shares

232

233

234 def searchShare(connId, share, smbServer):

235 config = smbServer.getServerConfig()

236 if config.has_section(share):

237 return dict(config.items(share))

238 else:

239 return None

240

241

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

243 fileName = os.path.normpath(fileName.replace('\\', '/'))

244 errorCode = 0

245 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):

246 # strip leading '/'

247 fileName = fileName[1:]

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

249 mode = 0

250 # Check the Open Mode

251 if openMode & 0x10:

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

253 mode = os.O_CREAT

254 else:

255 # If file does not exist, return an error

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

257 errorCode = STATUS_NO_SUCH_FILE

258 return 0, mode, pathName, errorCode

259

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

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

262 errorCode = STATUS_FILE_IS_A_DIRECTORY

263 return 0, mode, pathName, errorCode

264 # Check the Access Mode

265 if accessMode & 0x7 == 1:

266 mode |= os.O_WRONLY

267 elif accessMode & 0x7 == 2:

268 mode |= os.O_RDWR

269 else:

270 mode = os.O_RDONLY

271

272 try:

273 if sys.platform == 'win32':

274 mode |= os.O_BINARY

275 fid = os.open(pathName, mode)

276 except Exception as e:

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

278 fid = 0

279 errorCode = STATUS_ACCESS_DENIED

280

281 return fid, mode, pathName, errorCode

282

283

284 def queryFsInformation(path, filename, level=0, pktFlags=smb.SMB.FLAGS2_UNICODE):

285 if pktFlags & smb.SMB.FLAGS2_UNICODE:

286 encoding = 'utf-16le'

287 else:

288 encoding = 'ascii'

289

290 fileName = os.path.normpath(filename.replace('\\', '/'))

291 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):

292 # strip leading '/'

293 fileName = fileName[1:]

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

295 fileSize = os.path.getsize(pathName)

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

297 if level == smb.SMB_QUERY_FS_ATTRIBUTE_INFO or level == smb2.SMB2_FILESYSTEM_ATTRIBUTE_INFO:

298 data = smb.SMBQueryFsAttributeInfo()

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

300 data['MaxFilenNameLengthInBytes'] = 255

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

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

303 return data.getData()

304 elif level == smb.SMB_INFO_VOLUME:

305 data = smb.SMBQueryFsInfoVolume(flags=pktFlags)

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

307 return data.getData()

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

309 data = smb.SMBQueryFsVolumeInfo()

310 data['VolumeLabel'] = ''

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

312 return data.getData()

313 elif level == smb.SMB_QUERY_FS_SIZE_INFO:

314 data = smb.SMBQueryFsSizeInfo()

315 return data.getData()

316 elif level == smb.FILE_FS_FULL_SIZE_INFORMATION:

317 data = smb.SMBFileFsFullSizeInformation()

318 return data.getData()

319 elif level == smb.FILE_FS_SIZE_INFORMATION:

320 data = smb.FileFsSizeInformation()

321 return data.getData()

322 else:

323 lastWriteTime = mtime

324 attribs = 0

325 if os.path.isdir(pathName):

326 attribs |= smb.SMB_FILE_ATTRIBUTE_DIRECTORY

327 if os.path.isfile(pathName):

328 attribs |= smb.SMB_FILE_ATTRIBUTE_NORMAL

329 fileAttributes = attribs

330 return fileSize, lastWriteTime, fileAttributes

331

332

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

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

335

336 # print "FindFirs2 path:%s, filename:%s" % (path, fileName)

337 fileName = os.path.normpath(fileName.replace('\\', '/'))

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

339 if pktFlags & smb.SMB.FLAGS2_UNICODE:

340 encoding = 'utf-16le'

341 else:

342 encoding = 'ascii'

343

344 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):

345 # strip leading '/'

346 fileName = fileName[1:]

347

348 if not isInFileJail(path, fileName):

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

350 return [], 0, STATUS_OBJECT_PATH_SYNTAX_BAD

351

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

353 files = []

354

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

356 # No search patterns

357 pattern = ''

358 else:

359 pattern = os.path.basename(pathName)

360 dirName = os.path.dirname(pathName)

361

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

363 # not present (for * search only)

364 if pattern == '*':

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

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

367

368 if pattern != '':

369 for file in os.listdir(dirName):

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

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

372 if os.path.isdir(entry):

373 if searchAttributes & smb.ATTR_DIRECTORY:

374 files.append(entry)

375 else:

376 files.append(entry)

377 else:

378 if os.path.exists(pathName):

379 files.append(pathName)

380

381 searchResult = []

382 searchCount = len(files)

383 errorCode = STATUS_SUCCESS

384

385 for i in files:

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

387 item = smb.SMBFindFileBothDirectoryInfo(flags=pktFlags)

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

389 item = smb.SMBFindFileDirectoryInfo(flags=pktFlags)

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

391 item = smb.SMBFindFileFullDirectoryInfo(flags=pktFlags)

392 elif level == smb.SMB_FIND_INFO_STANDARD:

393 item = smb.SMBFindInfoStandard(flags=pktFlags)

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

395 item = smb.SMBFindFileIdFullDirectoryInfo(flags=pktFlags)

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

397 item = smb.SMBFindFileIdBothDirectoryInfo(flags=pktFlags)

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

399 item = smb.SMBFindFileNamesInfo(flags=pktFlags)

400 else:

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

402 return searchResult, searchCount, STATUS_NOT_SUPPORTED

403

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

405 if os.path.isdir(i):

406 item['ExtFileAttributes'] = smb.ATTR_DIRECTORY

407 else:

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

409

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

411

412 if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO:

413 item['EaSize'] = 0

414 item['EndOfFile'] = size

415 item['AllocationSize'] = size

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

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

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

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

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

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

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

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

424 elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO:

425 item['EndOfFile'] = size

426 item['AllocationSize'] = size

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

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

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

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

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

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

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

434 elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO:

435 item['EaSize'] = 0

436 item['EndOfFile'] = size

437 item['AllocationSize'] = size

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

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

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

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

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

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

444 elif level == smb.SMB_FIND_INFO_STANDARD:

445 item['EaSize'] = size

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

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

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

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

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

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

452 searchResult.append(item)

453

454 # No more files

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

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

457

458 return searchResult, searchCount, errorCode

459

460

461 def queryFileInformation(path, filename, level):

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

463 return queryPathInformation(path, filename, level)

464

465

466 def queryPathInformation(path, filename, level):

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

468 # print("queryPathInfo path: %s, filename: %s, level:0x%x" % (path,filename,level))

469 try:

470 errorCode = 0

471 fileName = os.path.normpath(filename.replace('\\', '/'))

472 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '':

473 # strip leading '/'

474 fileName = fileName[1:]

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

476 if os.path.exists(pathName):

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

478 if level == smb.SMB_QUERY_FILE_BASIC_INFO:

479 infoRecord = smb.SMBQueryFileBasicInfo()

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

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

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

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

484 if os.path.isdir(pathName):

485 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY

486 else:

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

488 elif level == smb.SMB_QUERY_FILE_STANDARD_INFO:

489 infoRecord = smb.SMBQueryFileStandardInfo()

490 infoRecord['AllocationSize'] = size

491 infoRecord['EndOfFile'] = size

492 if os.path.isdir(pathName):

493 infoRecord['Directory'] = 1

494 else:

495 infoRecord['Directory'] = 0

496 elif level == smb.SMB_QUERY_FILE_ALL_INFO or level == smb2.SMB2_FILE_ALL_INFO:

497 infoRecord = smb.SMBQueryFileAllInfo()

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

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

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

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

502 if os.path.isdir(pathName):

503 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY

504 else:

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

506 infoRecord['AllocationSize'] = size

507 infoRecord['EndOfFile'] = size

508 if os.path.isdir(pathName):

509 infoRecord['Directory'] = 1

510 else:

511 infoRecord['Directory'] = 0

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

513 elif level == smb2.SMB2_FILE_NETWORK_OPEN_INFO:

514 infoRecord = smb.SMBFileNetworkOpenInfo()

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

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

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

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

519 infoRecord['AllocationSize'] = size

520 infoRecord['EndOfFile'] = size

521 if os.path.isdir(pathName):

522 infoRecord['FileAttributes'] = smb.ATTR_DIRECTORY

523 else:

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

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

526 infoRecord = smb.SMBQueryFileEaInfo()

527 elif level == smb2.SMB2_FILE_STREAM_INFO:

528 infoRecord = smb.SMBFileStreamInformation()

529 else:

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

531 # UNSUPPORTED

532 return None, STATUS_NOT_SUPPORTED

533

534 return infoRecord, errorCode

535 else:

536 # NOT FOUND

537 return None, STATUS_OBJECT_NAME_NOT_FOUND

538 except Exception as e:

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

540 raise

541

542

543 def queryDiskInformation(path):

544 # TODO: Do something useful here :)

545 # For now we just return fake values

546 totalUnits = 65535

547 freeUnits = 65535

548 return totalUnits, freeUnits

549

550

551 def isInFileJail(path, fileName):

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

553 share_real_path = os.path.realpath(path)

554 return os.path.commonprefix((os.path.realpath(pathName), share_real_path)) == share_real_path

555

556

557# Here we implement the NT transaction handlers

558 class NTTRANSCommands:

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

560 pass

561

562

563# Here we implement the NT transaction handlers

564 class TRANSCommands:

565 @staticmethod

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

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

568 connData = smbServer.getConnectionData(connId)

569

570 respSetup = b''

571 respParameters = b''

572 respData = b''

573 errorCode = STATUS_SUCCESS

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

575 # NetShareEnum Request

576 netShareEnum = smb.SMBNetShareEnum(parameters)

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

578 shares = getShares(connId, smbServer)

579 respParameters = smb.SMBNetShareEnumResponse()

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

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

582 tailData = ''

583 for i in shares:

584 # NetShareInfo1 len == 20

585 entry = smb.NetShareInfo1()

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

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

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

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

590 respData += entry.getData()

591 if 'comment' in shares[i]:

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

593 else:

594 tailData += '\x00'

595 respData += tailData

596 else:

597 # We don't support other info levels

598 errorCode = STATUS_NOT_SUPPORTED

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

600 # NetrServerGetInfo Request

601 respParameters = smb.SMBNetServerGetInfoResponse()

602 netServerInfo = smb.SMBNetServerInfo1()

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

604 respData = netServerInfo.getData()

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

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

607 # NetrShareGetInfo Request

608 request = smb.SMBNetShareGetInfo(parameters)

609 respParameters = smb.SMBNetShareGetInfoResponse()

610 shares = getShares(connId, smbServer)

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

612 shareInfo = smb.NetShareInfo1()

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

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

615 respData = shareInfo.getData()

616 if 'comment' in share:

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

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

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

620

621 else:

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

623 errorCode = STATUS_NOT_SUPPORTED

624

625 smbServer.setConnectionData(connId, connData)

626

627 return respSetup, respParameters, respData, errorCode

628

629 @staticmethod

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

631 connData = smbServer.getConnectionData(connId)

632

633 respSetup = b''

634 respParameters = b''

635 respData = b''

636 errorCode = STATUS_SUCCESS

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

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

639

640 # Extract the FID

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

642

643 if fid in connData['OpenedFiles']:

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

645 if fileHandle != PIPE_FILE_DESCRIPTOR:

646 os.write(fileHandle, data)

647 respData = os.read(fileHandle, data)

648 else:

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

650 sock.send(data)

651 respData = sock.recv(maxDataCount)

652 else:

653 errorCode = STATUS_INVALID_HANDLE

654

655 smbServer.setConnectionData(connId, connData)

656

657 return respSetup, respParameters, respData, errorCode

658

659

660# Here we implement the transaction2 handlers

661 class TRANS2Commands:

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

663 @staticmethod

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

665 connData = smbServer.getConnectionData(connId)

666

667 respSetup = b''

668 respParameters = b''

669 respData = b''

670 errorCode = STATUS_SUCCESS

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

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

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

674 fileName = decodeSMBString(recvPacket['Flags2'], setPathInfoParameters['FileName'])

675 fileName = os.path.normpath(fileName.replace('\\', '/'))

676 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '':

677 # strip leading '/'

678 fileName = fileName[1:]

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

680 if os.path.exists(pathName):

681 informationLevel = setPathInfoParameters['InformationLevel']

682 if informationLevel == smb.SMB_SET_FILE_BASIC_INFO:

683 infoRecord = smb.SMBSetFileBasicInfo(data)

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

685 atime = infoRecord['LastAccessTime']

686 if atime == 0:

687 atime = -1

688 else:

689 atime = getUnixTime(atime)

690 mtime = infoRecord['LastWriteTime']

691 if mtime == 0:

692 mtime = -1

693 else:

694 mtime = getUnixTime(mtime)

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

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

697 else:

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

699 logging.ERROR)

700 # UNSUPPORTED

701 errorCode = STATUS_NOT_SUPPORTED

702 else:

703 errorCode = STATUS_OBJECT_NAME_NOT_FOUND

704

705 if errorCode == STATUS_SUCCESS:

706 respParameters = smb.SMBSetPathInformationResponse_Parameters()

707

708 else:

709 errorCode = STATUS_SMB_BAD_TID

710

711 smbServer.setConnectionData(connId, connData)

712

713 return respSetup, respParameters, respData, errorCode

714

715 @staticmethod

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

717 connData = smbServer.getConnectionData(connId)

718

719 respSetup = b''

720 respParameters = b''

721 respData = b''

722 errorCode = STATUS_SUCCESS

723 setFileInfoParameters = smb.SMBSetFileInformation_Parameters(parameters)

724

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

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

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

728 informationLevel = setFileInfoParameters['InformationLevel']

729 if informationLevel == smb.SMB_SET_FILE_DISPOSITION_INFO:

730 infoRecord = smb.SMBSetFileDispositionInfo(parameters)

731 if infoRecord['DeletePending'] > 0:

732 # Mark this file for removal after closed

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

734 respParameters = smb.SMBSetFileInformationResponse_Parameters()

735 elif informationLevel == smb.SMB_SET_FILE_BASIC_INFO:

736 infoRecord = smb.SMBSetFileBasicInfo(data)

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

738 atime = infoRecord['LastAccessTime']

739 if atime == 0:

740 atime = -1

741 else:

742 atime = getUnixTime(atime)

743 mtime = infoRecord['LastWriteTime']

744 if mtime == 0:

745 mtime = -1

746 else:

747 mtime = getUnixTime(mtime)

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

749 elif informationLevel == smb.SMB_SET_FILE_END_OF_FILE_INFO:

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

751 infoRecord = smb.SMBSetFileEndOfFileInfo(data)

752 if infoRecord['EndOfFile'] > 0:

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

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

755 else:

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

757 logging.ERROR)

758 # UNSUPPORTED

759 errorCode = STATUS_NOT_SUPPORTED

760 else:

761 errorCode = STATUS_NO_SUCH_FILE

762

763 if errorCode == STATUS_SUCCESS:

764 respParameters = smb.SMBSetFileInformationResponse_Parameters()

765 else:

766 errorCode = STATUS_SMB_BAD_TID

767

768 smbServer.setConnectionData(connId, connData)

769

770 return respSetup, respParameters, respData, errorCode

771

772 @staticmethod

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

774 connData = smbServer.getConnectionData(connId)

775

776 respSetup = b''

777 respParameters = b''

778 respData = b''

779

780 queryFileInfoParameters = smb.SMBQueryFileInformation_Parameters(parameters)

781

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

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

784 fileName = connData['OpenedFiles'][queryFileInfoParameters['FID']]['FileName']

785

786 infoRecord, errorCode = queryFileInformation('', fileName, queryFileInfoParameters['InformationLevel'])

787

788 if infoRecord is not None:

789 respParameters = smb.SMBQueryFileInformationResponse_Parameters()

790 respData = infoRecord

791 else:

792 errorCode = STATUS_INVALID_HANDLE

793 else:

794 errorCode = STATUS_SMB_BAD_TID

795

796 smbServer.setConnectionData(connId, connData)

797

798 return respSetup, respParameters, respData, errorCode

799

800 @staticmethod

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

802 connData = smbServer.getConnectionData(connId)

803

804 respSetup = b''

805 respParameters = b''

806 respData = b''

807 errorCode = 0

808

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

810

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

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

813 try:

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

815 queryPathInfoParameters['FileName']),

816 queryPathInfoParameters['InformationLevel'])

817 except Exception as e:

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

819

820 if infoRecord is not None:

821 respParameters = smb.SMBQueryPathInformationResponse_Parameters()

822 respData = infoRecord

823 else:

824 errorCode = STATUS_SMB_BAD_TID

825

826 smbServer.setConnectionData(connId, connData)

827

828 return respSetup, respParameters, respData, errorCode

829

830 @staticmethod

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

832 connData = smbServer.getConnectionData(connId)

833 errorCode = 0

834 # Get the Tid associated

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

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

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

838

839 smbServer.setConnectionData(connId, connData)

840

841 return b'', b'', data, errorCode

842

843 @staticmethod

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

845 connData = smbServer.getConnectionData(connId)

846

847 respSetup = b''

848 respParameters = b''

849 respData = b''

850 errorCode = STATUS_SUCCESS

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

852

853 sid = findNext2Parameters['SID']

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

855 if sid in connData['SIDs']:

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

857 respParameters = smb.SMBFindNext2Response_Parameters()

858 endOfSearch = 1

859 searchCount = 1

860 totalData = 0

861 for i in enumerate(searchResult):

862 data = i[1].getData()

863 lenData = len(data)

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

865 # We gotta stop here and continue on a find_next2

866 endOfSearch = 0

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

868 respParameters['LastNameOffset'] = totalData

869 break

870 else:

871 searchCount += 1

872 respData += data

873 totalData += lenData

874

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

876 if endOfSearch > 0:

877 # Let's remove the SID from our ConnData

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

879

880 respParameters['EndOfSearch'] = endOfSearch

881 respParameters['SearchCount'] = searchCount

882 else:

883 errorCode = STATUS_INVALID_HANDLE

884 else:

885 errorCode = STATUS_SMB_BAD_TID

886

887 smbServer.setConnectionData(connId, connData)

888

889 return respSetup, respParameters, respData, errorCode

890

891 @staticmethod

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

893 connData = smbServer.getConnectionData(connId)

894

895 respSetup = b''

896 respParameters = b''

897 respData = b''

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

899

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

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

902

903 searchResult, searchCount, errorCode = findFirst2(path,

904 decodeSMBString(recvPacket['Flags2'],

905 findFirst2Parameters['FileName']),

906 findFirst2Parameters['InformationLevel'],

907 findFirst2Parameters['SearchAttributes'],

908 pktFlags=recvPacket['Flags2'])

909

910 respParameters = smb.SMBFindFirst2Response_Parameters()

911 endOfSearch = 1

912 sid = 0x80 # default SID

913 searchCount = 0

914 totalData = 0

915 for i in enumerate(searchResult):

916 # i[1].dump()

917 data = i[1].getData()

918 lenData = len(data)

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

920 # We gotta stop here and continue on a find_next2

921 endOfSearch = 0

922 # Simple way to generate a fid

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

924 sid = 1

925 else:

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

927 # Store the remaining search results in the ConnData SID

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

929 respParameters['LastNameOffset'] = totalData

930 break

931 else:

932 searchCount += 1

933 respData += data

934

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

936 respData += b'\xaa' * padLen

937 totalData += lenData + padLen

938

939 respParameters['SID'] = sid

940 respParameters['EndOfSearch'] = endOfSearch

941 respParameters['SearchCount'] = searchCount

942 else:

943 errorCode = STATUS_SMB_BAD_TID

944

945 smbServer.setConnectionData(connId, connData)

946

947 return respSetup, respParameters, respData, errorCode

948

949

950# Here we implement the commands handlers

951 class SMBCommands:

952

953 @staticmethod

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

955 connData = smbServer.getConnectionData(connId)

956

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

958

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

960

961 # Do the stuff

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

963 # TODO: Handle partial parameters

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

965 else:

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

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

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

969

970 paramCount = transParameters['ParameterCount']

971 transData['Trans_ParametersLength'] = paramCount

972 dataCount = transParameters['DataCount']

973 transData['Trans_DataLength'] = dataCount

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

975 if transParameters['ParameterOffset'] > 0:

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

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

978 else:

979 transData['Trans_Parameters'] = b''

980

981 if transParameters['DataOffset'] > 0:

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

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

984 else:

985 transData['Trans_Data'] = b''

986

987 # Call the handler for this TRANSACTION

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

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

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

991 else:

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

993

994 if command in transCommands:

995 # Call the TRANS subcommand

996 setup = b''

997 parameters = b''

998 data = b''

999 try:

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

1001 smbServer,

1002 recvPacket,

1003 transData['Trans_Parameters'],

1004 transData['Trans_Data'],

1005 transParameters['MaxDataCount'])

1006 except Exception as e:

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

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

1009 errorCode = STATUS_ACCESS_DENIED

1010 # raise

1011

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

1013 # Something wen't wrong

1014 respParameters = b''

1015 respData = b''

1016 else:

1017 # Build the answer

1018 if hasattr(data, 'getData'):

1019 data = data.getData()

1020 remainingData = len(data)

1021 if hasattr(parameters, 'getData'):

1022 parameters = parameters.getData()

1023 remainingParameters = len(parameters)

1024 commands = []

1025 dataDisplacement = 0

1026 while remainingData > 0 or remainingParameters > 0:

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

1028 respParameters = smb.SMBTransactionResponse_Parameters()

1029 respData = smb.SMBTransaction2Response_Data()

1030

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

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

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

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

1035 respParameters['DataDisplacement'] = dataDisplacement

1036

1037 # TODO: Do the same for parameters

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

1039 # Answer doesn't fit in this packet

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

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

1042 else:

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

1044

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

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

1047 respParameters['Setup'] = setup

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

1049 if len(parameters) > 0:

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

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

1052 padBytes = b'\xFF' * padLen

1053 respData['Pad1'] = padBytes

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

1055 else:

1056 padLen = 0

1057 respParameters['ParameterOffset'] = 0

1058 respData['Pad1'] = b''

1059

1060 if len(data) > 0:

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

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

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

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

1065 else:

1066 respParameters['DataOffset'] = 0

1067 respData['Pad2'] = b''

1068

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

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

1071 respSMBCommand['Parameters'] = respParameters

1072 respSMBCommand['Data'] = respData

1073

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

1075 remainingData -= respParameters['DataCount']

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

1077

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

1079 remainingParameters -= respParameters['ParameterCount']

1080 commands.append(respSMBCommand)

1081

1082 smbServer.setConnectionData(connId, connData)

1083 return commands, None, errorCode

1084

1085 else:

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

1087 respParameters = b''

1088 respData = b''

1089 errorCode = STATUS_NOT_IMPLEMENTED

1090

1091 respSMBCommand['Parameters'] = respParameters

1092 respSMBCommand['Data'] = respData

1093 smbServer.setConnectionData(connId, connData)

1094

1095 return [respSMBCommand], None, errorCode

1096

1097 @staticmethod

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

1099 connData = smbServer.getConnectionData(connId)

1100

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

1102

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

1104 # Do the stuff

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

1106 # TODO: Handle partial parameters

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

1108 else:

1109 NTTransData = smb.SMBNTTransaction_Data()

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

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

1112

1113 paramCount = NTTransParameters['ParameterCount']

1114 NTTransData['NT_Trans_ParametersLength'] = paramCount

1115 dataCount = NTTransParameters['DataCount']

1116 NTTransData['NT_Trans_DataLength'] = dataCount

1117

1118 if NTTransParameters['ParameterOffset'] > 0:

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

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

1121 else:

1122 NTTransData['NT_Trans_Parameters'] = b''

1123

1124 if NTTransParameters['DataOffset'] > 0:

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

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

1127 else:

1128 NTTransData['NT_Trans_Data'] = b''

1129

1130 # Call the handler for this TRANSACTION

1131 command = NTTransParameters['Function']

1132 if command in transCommands:

1133 # Call the NT TRANS subcommand

1134 setup = b''

1135 parameters = b''

1136 data = b''

1137 try:

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

1139 smbServer,

1140 recvPacket,

1141 NTTransData['NT_Trans_Parameters'],

1142 NTTransData['NT_Trans_Data'],

1143 NTTransParameters['MaxDataCount'])

1144 except Exception as e:

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

1146 errorCode = STATUS_ACCESS_DENIED

1147 # raise

1148

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

1150 # Something wen't wrong

1151 respParameters = b''

1152 respData = b''

1153 if errorCode == STATUS_SUCCESS:

1154 errorCode = STATUS_ACCESS_DENIED

1155 else:

1156 # Build the answer

1157 if hasattr(data, 'getData'):

1158 data = data.getData()

1159 remainingData = len(data)

1160 if hasattr(parameters, 'getData'):

1161 parameters = parameters.getData()

1162 remainingParameters = len(parameters)

1163 commands = []

1164 dataDisplacement = 0

1165 while remainingData > 0 or remainingParameters > 0:

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

1167 respParameters = smb.SMBNTTransactionResponse_Parameters()

1168 respData = smb.SMBNTTransactionResponse_Data()

1169

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

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

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

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

1174 respParameters['DataDisplacement'] = dataDisplacement

1175 # TODO: Do the same for parameters

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

1177 # Answer doesn't fit in this packet

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

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

1180 else:

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

1182

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

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

1185 respParameters['Setup'] = setup

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

1187 if len(parameters) > 0:

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

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

1190 padBytes = b'\xFF' * padLen

1191 respData['Pad1'] = padBytes

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

1193 else:

1194 padLen = 0

1195 respParameters['ParameterOffset'] = 0

1196 respData['Pad1'] = b''

1197

1198 if len(data) > 0:

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

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

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

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

1203 else:

1204 respParameters['DataOffset'] = 0

1205 respData['Pad2'] = b''

1206

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

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

1209 respSMBCommand['Parameters'] = respParameters

1210 respSMBCommand['Data'] = respData

1211

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

1213 remainingData -= respParameters['DataCount']

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

1215

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

1217 remainingParameters -= respParameters['ParameterCount']

1218 commands.append(respSMBCommand)

1219

1220 smbServer.setConnectionData(connId, connData)

1221 return commands, None, errorCode

1222

1223 else:

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

1225 respParameters = b''

1226 respData = b''

1227 errorCode = STATUS_NOT_IMPLEMENTED

1228

1229 respSMBCommand['Parameters'] = respParameters

1230 respSMBCommand['Data'] = respData

1231

1232 smbServer.setConnectionData(connId, connData)

1233 return [respSMBCommand], None, errorCode

1234

1235 @staticmethod

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

1237 connData = smbServer.getConnectionData(connId)

1238

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

1240

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

1242

1243 # Do the stuff

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

1245 # TODO: Handle partial parameters

1246 # print "Unsupported partial parameters in TRANSACT2!"

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

1248 else:

1249 trans2Data = smb.SMBTransaction2_Data()

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

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

1252

1253 paramCount = trans2Parameters['ParameterCount']

1254 trans2Data['Trans_ParametersLength'] = paramCount

1255 dataCount = trans2Parameters['DataCount']

1256 trans2Data['Trans_DataLength'] = dataCount

1257

1258 if trans2Parameters['ParameterOffset'] > 0:

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

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

1261 else:

1262 trans2Data['Trans_Parameters'] = b''

1263

1264 if trans2Parameters['DataOffset'] > 0:

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

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

1267 else:

1268 trans2Data['Trans_Data'] = b''

1269

1270 # Call the handler for this TRANSACTION

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

1272 if command in transCommands:

1273 # Call the TRANS2 subcommand

1274 try:

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

1276 smbServer,

1277 recvPacket,

1278 trans2Data['Trans_Parameters'],

1279 trans2Data['Trans_Data'],

1280 trans2Parameters['MaxDataCount'])

1281 except Exception as e:

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

1283 # import traceback

1284 # traceback.print_exc()

1285 raise

1286

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

1288 # Something wen't wrong

1289 respParameters = b''

1290 respData = b''

1291 else:

1292 # Build the answer

1293 if hasattr(data, 'getData'):

1294 data = data.getData()

1295 remainingData = len(data)

1296 if hasattr(parameters, 'getData'):

1297 parameters = parameters.getData()

1298 remainingParameters = len(parameters)

1299 commands = []

1300 dataDisplacement = 0

1301 while remainingData > 0 or remainingParameters > 0:

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

1303 respParameters = smb.SMBTransaction2Response_Parameters()

1304 respData = smb.SMBTransaction2Response_Data()

1305

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

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

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

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

1310 respParameters['DataDisplacement'] = dataDisplacement

1311 # TODO: Do the same for parameters

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

1313 # Answer doesn't fit in this packet

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

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

1316 else:

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

1318

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

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

1321 respParameters['Setup'] = setup

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

1323 if len(parameters) > 0:

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

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

1326 padBytes = b'\xFF' * padLen

1327 respData['Pad1'] = padBytes

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

1329 else:

1330 padLen = 0

1331 respParameters['ParameterOffset'] = 0

1332 respData['Pad1'] = b''

1333

1334 if len(data) > 0:

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

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

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

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

1339 else:

1340 respParameters['DataOffset'] = 0

1341 respData['Pad2'] = b''

1342

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

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

1345 respSMBCommand['Parameters'] = respParameters

1346 respSMBCommand['Data'] = respData

1347

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

1349 remainingData -= respParameters['DataCount']

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

1351

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

1353 remainingParameters -= respParameters['ParameterCount']

1354 commands.append(respSMBCommand)

1355

1356 smbServer.setConnectionData(connId, connData)

1357 return commands, None, errorCode

1358

1359 else:

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

1361 respParameters = b''

1362 respData = b''

1363 errorCode = STATUS_NOT_IMPLEMENTED

1364

1365 respSMBCommand['Parameters'] = respParameters

1366 respSMBCommand['Data'] = respData

1367

1368 smbServer.setConnectionData(connId, connData)

1369 return [respSMBCommand], None, errorCode

1370

1371 @staticmethod

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

1373 connData = smbServer.getConnectionData(connId)

1374

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

1376 respParameters = b''

1377 respData = b''

1378

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

1380 errorCode = STATUS_SUCCESS

1381

1382 respSMBCommand['Parameters'] = respParameters

1383 respSMBCommand['Data'] = respData

1384 smbServer.setConnectionData(connId, connData)

1385

1386 return [respSMBCommand], None, errorCode

1387

1388 @staticmethod

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

1390 connData = smbServer.getConnectionData(connId)

1391

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

1393 respParameters = b''

1394 respData = b''

1395

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

1397

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

1399 errorCode = STATUS_SUCCESS

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

1401 try:

1402 if fileHandle == PIPE_FILE_DESCRIPTOR:

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

1404 elif fileHandle != VOID_FILE_DESCRIPTOR:

1405 os.close(fileHandle)

1406 except Exception as e:

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

1408 errorCode = STATUS_ACCESS_DENIED

1409 else:

1410 # Check if the file was marked for removal

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

1412 try:

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

1414 except Exception as e:

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

1416 errorCode = STATUS_ACCESS_DENIED

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

1418 else:

1419 errorCode = STATUS_INVALID_HANDLE

1420

1421 if errorCode > 0:

1422 respParameters = b''

1423 respData = b''

1424

1425 respSMBCommand['Parameters'] = respParameters

1426 respSMBCommand['Data'] = respData

1427 smbServer.setConnectionData(connId, connData)

1428

1429 return [respSMBCommand], None, errorCode

1430

1431 @staticmethod

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

1433 connData = smbServer.getConnectionData(connId)

1434

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

1436 respParameters = smb.SMBWriteResponse_Parameters()

1437 respData = b''

1438

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

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

1441

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

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

1444 errorCode = STATUS_SUCCESS

1445 try:

1446 if fileHandle != PIPE_FILE_DESCRIPTOR:

1447 # TODO: Handle big size files

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

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

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

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

1452 else:

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

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

1455 respParameters['Count'] = comWriteParameters['Count']

1456 except Exception as e:

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

1458 errorCode = STATUS_ACCESS_DENIED

1459 else:

1460 errorCode = STATUS_INVALID_HANDLE

1461

1462 if errorCode > 0:

1463 respParameters = b''

1464 respData = b''

1465

1466 respSMBCommand['Parameters'] = respParameters

1467 respSMBCommand['Data'] = respData

1468 smbServer.setConnectionData(connId, connData)

1469

1470 return [respSMBCommand], None, errorCode

1471

1472 @staticmethod

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

1474 connData = smbServer.getConnectionData(connId)

1475

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

1477 respParameters = b''

1478 respData = b''

1479

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

1481

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

1483 errorCode = STATUS_SUCCESS

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

1485 try:

1486 os.fsync(fileHandle)

1487 except Exception as e:

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

1489 errorCode = STATUS_ACCESS_DENIED

1490 else:

1491 errorCode = STATUS_INVALID_HANDLE

1492

1493 if errorCode > 0:

1494 respParameters = b''

1495 respData = b''

1496

1497 respSMBCommand['Parameters'] = respParameters

1498 respSMBCommand['Data'] = respData

1499 smbServer.setConnectionData(connId, connData)

1500

1501 return [respSMBCommand], None, errorCode

1502

1503 @staticmethod

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

1505 connData = smbServer.getConnectionData(connId)

1506

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

1508 respParameters = b''

1509 respData = b''

1510

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

1512

1513 # Get the Tid associated

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

1515 errorCode = STATUS_SUCCESS

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

1517 fileName = os.path.normpath(

1518 decodeSMBString(recvPacket['Flags2'], comCreateDirectoryData['DirectoryName']).replace('\\', '/'))

1519 if len(fileName) > 0:

1520 if fileName[0] == '/' or fileName[0] == '\\':

1521 # strip leading '/'

1522 fileName = fileName[1:]

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

1524 if os.path.exists(pathName):

1525 errorCode = STATUS_OBJECT_NAME_COLLISION

1526

1527 # TODO: More checks here in the future.. Specially when we support

1528 # user access

1529 else:

1530 try:

1531 os.mkdir(pathName)

1532 except Exception as e:

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

1534 errorCode = STATUS_ACCESS_DENIED

1535 else:

1536 errorCode = STATUS_SMB_BAD_TID

1537

1538 if errorCode > 0:

1539 respParameters = b''

1540 respData = b''

1541

1542 respSMBCommand['Parameters'] = respParameters

1543 respSMBCommand['Data'] = respData

1544 smbServer.setConnectionData(connId, connData)

1545

1546 return [respSMBCommand], None, errorCode

1547

1548 @staticmethod

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

1550 connData = smbServer.getConnectionData(connId)

1551

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

1553 respParameters = b''

1554 respData = b''

1555

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

1557 # Get the Tid associated

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

1559 errorCode = STATUS_SUCCESS

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

1561 oldFileName = os.path.normpath(

1562 decodeSMBString(recvPacket['Flags2'], comRenameData['OldFileName']).replace('\\', '/'))

1563 newFileName = os.path.normpath(

1564 decodeSMBString(recvPacket['Flags2'], comRenameData['NewFileName']).replace('\\', '/'))

1565 if len(oldFileName) > 0 and (oldFileName[0] == '/' or oldFileName[0] == '\\'):

1566 # strip leading '/'

1567 oldFileName = oldFileName[1:]

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

1569 if len(newFileName) > 0 and (newFileName[0] == '/' or newFileName[0] == '\\'):

1570 # strip leading '/'

1571 newFileName = newFileName[1:]

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

1573

1574 if os.path.exists(oldPathName) is not True:

1575 errorCode = STATUS_NO_SUCH_FILE

1576

1577 # TODO: More checks here in the future.. Specially when we support

1578 # user access

1579 else:

1580 try:

1581 os.rename(oldPathName, newPathName)

1582 except OSError as e:

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

1584 errorCode = STATUS_ACCESS_DENIED

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 smbComDelete(connId, smbServer, SMBCommand, recvPacket):

1600 connData = smbServer.getConnectionData(connId)

1601

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

1603 respParameters = b''

1604 respData = b''

1605

1606 comDeleteData = smb.SMBDelete_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 = os.path.normpath(

1613 decodeSMBString(recvPacket['Flags2'], comDeleteData['FileName']).replace('\\', '/'))

1614 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):

1615 # strip leading '/'

1616 fileName = fileName[1:]

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

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

1619 errorCode = STATUS_NO_SUCH_FILE

1620

1621 # TODO: More checks here in the future.. Specially when we support

1622 # user access

1623 else:

1624 try:

1625 os.remove(pathName)

1626 except OSError as e:

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

1628 errorCode = STATUS_ACCESS_DENIED

1629 else:

1630 errorCode = STATUS_SMB_BAD_TID

1631

1632 if errorCode > 0:

1633 respParameters = b''

1634 respData = b''

1635

1636 respSMBCommand['Parameters'] = respParameters

1637 respSMBCommand['Data'] = respData

1638 smbServer.setConnectionData(connId, connData)

1639

1640 return [respSMBCommand], None, errorCode

1641

1642 @staticmethod

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

1644 connData = smbServer.getConnectionData(connId)

1645

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

1647 respParameters = b''

1648 respData = b''

1649

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

1651

1652 # Get the Tid associated

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

1654 errorCode = STATUS_SUCCESS

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

1656 fileName = os.path.normpath(

1657 decodeSMBString(recvPacket['Flags2'], comDeleteDirectoryData['DirectoryName']).replace('\\', '/'))

1658 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):

1659 # strip leading '/'

1660 fileName = fileName[1:]

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

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

1663 errorCode = STATUS_NO_SUCH_FILE

1664

1665 # TODO: More checks here in the future.. Specially when we support

1666 # user access

1667 else:

1668 try:

1669 os.rmdir(pathName)

1670 except OSError as e:

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

1672 if e.errno == errno.ENOTEMPTY:

1673 errorCode = STATUS_DIRECTORY_NOT_EMPTY

1674 else:

1675 errorCode = STATUS_ACCESS_DENIED

1676 else:

1677 errorCode = STATUS_SMB_BAD_TID

1678

1679 if errorCode > 0:

1680 respParameters = b''

1681 respData = b''

1682

1683 respSMBCommand['Parameters'] = respParameters

1684 respSMBCommand['Data'] = respData

1685 smbServer.setConnectionData(connId, connData)

1686

1687 return [respSMBCommand], None, errorCode

1688

1689 @staticmethod

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

1691 connData = smbServer.getConnectionData(connId)

1692

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

1694 respParameters = smb.SMBWriteAndXResponse_Parameters()

1695 respData = b''

1696

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

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

1699 writeAndXData = smb.SMBWriteAndX_Data_Short()

1700 else:

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

1702 writeAndXData = smb.SMBWriteAndX_Data()

1703 writeAndXData['DataLength'] = writeAndX['DataLength']

1704 writeAndXData['DataOffset'] = writeAndX['DataOffset']

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

1706

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

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

1709 errorCode = STATUS_SUCCESS

1710 try:

1711 if fileHandle != PIPE_FILE_DESCRIPTOR:

1712 offset = writeAndX['Offset']

1713 if 'HighOffset' in writeAndX.fields:

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

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

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

1717 os.lseek(fileHandle, offset, 0)

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

1719 else:

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

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

1722

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

1724 respParameters['Available'] = 0xff

1725 except Exception as e:

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

1727 errorCode = STATUS_ACCESS_DENIED

1728 else:

1729 errorCode = STATUS_INVALID_HANDLE

1730

1731 if errorCode > 0:

1732 respParameters = b''

1733 respData = b''

1734

1735 respSMBCommand['Parameters'] = respParameters

1736 respSMBCommand['Data'] = respData

1737 smbServer.setConnectionData(connId, connData)

1738

1739 return [respSMBCommand], None, errorCode

1740

1741 @staticmethod

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

1743 connData = smbServer.getConnectionData(connId)

1744

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

1746 respParameters = smb.SMBReadResponse_Parameters()

1747 respData = smb.SMBReadResponse_Data()

1748

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

1750

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

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

1753 errorCode = STATUS_SUCCESS

1754 try:

1755 if fileHandle != PIPE_FILE_DESCRIPTOR:

1756 # TODO: Handle big size files

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

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

1759 else:

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

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

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

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

1764 respData['Data'] = content

1765 except Exception as e:

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

1767 errorCode = STATUS_ACCESS_DENIED

1768 else:

1769 errorCode = STATUS_INVALID_HANDLE

1770

1771 if errorCode > 0:

1772 respParameters = b''

1773 respData = b''

1774

1775 respSMBCommand['Parameters'] = respParameters

1776 respSMBCommand['Data'] = respData

1777 smbServer.setConnectionData(connId, connData)

1778

1779 return [respSMBCommand], None, errorCode

1780

1781 @staticmethod

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

1783 connData = smbServer.getConnectionData(connId)

1784

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

1786 respParameters = smb.SMBReadAndXResponse_Parameters()

1787 respData = b''

1788

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

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

1791 else:

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

1793

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

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

1796 errorCode = 0

1797 try:

1798 if fileHandle != PIPE_FILE_DESCRIPTOR:

1799 offset = readAndX['Offset']

1800 if 'HighOffset' in readAndX.fields:

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

1802 os.lseek(fileHandle, offset, 0)

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

1804 else:

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

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

1807 respParameters['Remaining'] = 0xffff

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

1809 respParameters['DataOffset'] = 59

1810 respParameters['DataCount_Hi'] = 0

1811 respData = content

1812 except Exception as e:

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

1814 errorCode = STATUS_ACCESS_DENIED

1815 else:

1816 errorCode = STATUS_INVALID_HANDLE

1817

1818 if errorCode > 0:

1819 respParameters = b''

1820 respData = b''

1821

1822 respSMBCommand['Parameters'] = respParameters

1823 respSMBCommand['Data'] = respData

1824 smbServer.setConnectionData(connId, connData)

1825

1826 return [respSMBCommand], None, errorCode

1827

1828 @staticmethod

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

1830 connData = smbServer.getConnectionData(connId)

1831

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

1833 respParameters = smb.SMBQueryInformationResponse_Parameters()

1834 respData = b''

1835

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

1837

1838 # Get the Tid associated

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

1840 fileSize, lastWriteTime, fileAttributes = queryFsInformation(

1841 connData['ConnectedShares'][recvPacket['Tid']]['path'],

1842 decodeSMBString(recvPacket['Flags2'], queryInformation['FileName']), pktFlags=recvPacket['Flags2'])

1843

1844 respParameters['FileSize'] = fileSize

1845 respParameters['LastWriteTime'] = lastWriteTime

1846 respParameters['FileAttributes'] = fileAttributes

1847 errorCode = STATUS_SUCCESS

1848 else:

1849 # STATUS_SMB_BAD_TID

1850 errorCode = STATUS_SMB_BAD_TID

1851 respParameters = b''

1852 respData = b''

1853

1854 respSMBCommand['Parameters'] = respParameters

1855 respSMBCommand['Data'] = respData

1856

1857 smbServer.setConnectionData(connId, connData)

1858 return [respSMBCommand], None, errorCode

1859

1860 @staticmethod

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

1862 connData = smbServer.getConnectionData(connId)

1863

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

1865 respParameters = smb.SMBQueryInformationDiskResponse_Parameters()

1866 respData = b''

1867

1868 # Get the Tid associated

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

1870 totalUnits, freeUnits = queryDiskInformation(

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

1872

1873 respParameters['TotalUnits'] = totalUnits

1874 respParameters['BlocksPerUnit'] = 1

1875 respParameters['BlockSize'] = 1

1876 respParameters['FreeUnits'] = freeUnits

1877 errorCode = STATUS_SUCCESS

1878 else:

1879 # STATUS_SMB_BAD_TID

1880 respData = b''

1881 respParameters = b''

1882 errorCode = STATUS_SMB_BAD_TID

1883

1884 respSMBCommand['Parameters'] = respParameters

1885 respSMBCommand['Data'] = respData

1886

1887 smbServer.setConnectionData(connId, connData)

1888 return [respSMBCommand], None, errorCode

1889

1890 @staticmethod

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

1892 connData = smbServer.getConnectionData(connId)

1893

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

1895 respParameters = smb.SMBEchoResponse_Parameters()

1896 respData = smb.SMBEchoResponse_Data()

1897

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

1899

1900 respParameters['SequenceNumber'] = 1

1901 respData['Data'] = echoData['Data']

1902

1903 respSMBCommand['Parameters'] = respParameters

1904 respSMBCommand['Data'] = respData

1905

1906 errorCode = STATUS_SUCCESS

1907 smbServer.setConnectionData(connId, connData)

1908 return [respSMBCommand], None, errorCode

1909

1910 @staticmethod

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

1912 connData = smbServer.getConnectionData(connId)

1913

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

1915

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

1917 respParameters = b''

1918 respData = b''

1919

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

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

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

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

1924 errorCode = STATUS_SUCCESS

1925 else:

1926 # STATUS_SMB_BAD_TID

1927 errorCode = STATUS_SMB_BAD_TID

1928

1929 respSMBCommand['Parameters'] = respParameters

1930 respSMBCommand['Data'] = respData

1931

1932 smbServer.setConnectionData(connId, connData)

1933 return [respSMBCommand], None, errorCode

1934

1935 @staticmethod

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

1937 connData = smbServer.getConnectionData(connId)

1938

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

1940

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

1942 respParameters = b''

1943 respData = b''

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

1945 # STATUS_SMB_BAD_UID

1946 errorCode = STATUS_SMB_BAD_UID

1947 else:

1948 errorCode = STATUS_SUCCESS

1949

1950 respSMBCommand['Parameters'] = respParameters

1951 respSMBCommand['Data'] = respData

1952 connData['Uid'] = 0

1953 connData['Authenticated'] = False

1954

1955 smbServer.setConnectionData(connId, connData)

1956

1957 return [respSMBCommand], None, errorCode

1958

1959 @staticmethod

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

1961 connData = smbServer.getConnectionData(connId)

1962

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

1964 respParameters = smb.SMBQueryInformation2Response_Parameters()

1965 respData = b''

1966

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

1968 errorCode = 0xFF

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

1970 errorCode = STATUS_SUCCESS

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

1972 try:

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

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

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

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

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

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

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

1980 respParameters['FileDataSize'] = size

1981 respParameters['FileAllocationSize'] = size

1982 attribs = 0

1983 if os.path.isdir(pathName):

1984 attribs = smb.SMB_FILE_ATTRIBUTE_DIRECTORY

1985 if os.path.isfile(pathName):

1986 attribs = smb.SMB_FILE_ATTRIBUTE_NORMAL

1987 respParameters['FileAttributes'] = attribs

1988 except Exception as e:

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

1990 errorCode = STATUS_ACCESS_DENIED

1991

1992 if errorCode > 0:

1993 respParameters = b''

1994 respData = b''

1995

1996 respSMBCommand['Parameters'] = respParameters

1997 respSMBCommand['Data'] = respData

1998 smbServer.setConnectionData(connId, connData)

1999

2000 return [respSMBCommand], None, errorCode

2001

2002 @staticmethod

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

2004 # TODO: Fully implement this

2005 connData = smbServer.getConnectionData(connId)

2006

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

2008 respParameters = smb.SMBNtCreateAndXResponse_Parameters()

2009 respData = b''

2010

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

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

2013

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

2015 # respParameters = smb.SMBNtCreateAndXExtendedResponse_Parameters()

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

2017

2018 # Get the Tid associated

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

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

2021 errorCode = STATUS_SUCCESS

2022 if ntCreateAndXParameters['RootFid'] > 0:

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

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

2025 else:

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

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

2028 else:

2029 path = 'NONE'

2030 errorCode = STATUS_ACCESS_DENIED

2031

2032 deleteOnClose = False

2033

2034 fileName = os.path.normpath(

2035 decodeSMBString(recvPacket['Flags2'], ntCreateAndXData['FileName']).replace('\\', '/'))

2036 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):

2037 # strip leading '/'

2038 fileName = fileName[1:]

2039

2040 if not isInFileJail(path, fileName):

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

2042 respSMBCommand['Parameters'] = b''

2043 respSMBCommand['Data'] = b''

2044 return [respSMBCommand], None, STATUS_OBJECT_PATH_SYNTAX_BAD

2045

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

2047 createDisposition = ntCreateAndXParameters['Disposition']

2048 mode = 0

2049

2050 if createDisposition == smb.FILE_SUPERSEDE:

2051 mode |= os.O_TRUNC | os.O_CREAT

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

2053 mode |= os.O_TRUNC | os.O_CREAT

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

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

2056 mode |= os.O_TRUNC

2057 else:

2058 errorCode = STATUS_NO_SUCH_FILE

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

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

2061 mode |= os.O_TRUNC

2062 else:

2063 mode |= os.O_TRUNC | os.O_CREAT

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

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

2066 errorCode = STATUS_OBJECT_NAME_COLLISION

2067 else:

2068 mode |= os.O_CREAT

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

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

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

2072 errorCode = STATUS_NO_SUCH_FILE

2073

2074 if errorCode == STATUS_SUCCESS:

2075 desiredAccess = ntCreateAndXParameters['AccessMask']

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

2077 mode |= os.O_RDONLY

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

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

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

2081 else:

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

2083 if desiredAccess & smb.GENERIC_ALL:

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

2085

2086 createOptions = ntCreateAndXParameters['CreateOptions']

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

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

2089 try:

2090 # Let's create the directory

2091 os.mkdir(pathName)

2092 mode = os.O_RDONLY

2093 except Exception as e:

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

2095 errorCode = STATUS_ACCESS_DENIED

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

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

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

2099 # response.

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

2101 errorCode = STATUS_FILE_IS_A_DIRECTORY

2102

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

2104 deleteOnClose = True

2105

2106 if errorCode == STATUS_SUCCESS:

2107 try:

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

2109 fid = VOID_FILE_DESCRIPTOR

2110 else:

2111 if sys.platform == 'win32':

2112 mode |= os.O_BINARY

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

2114 fid = PIPE_FILE_DESCRIPTOR

2115 sock = socket.socket()

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

2117 else:

2118 fid = os.open(pathName, mode)

2119 except Exception as e:

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

2121 # print e

2122 fid = 0

2123 errorCode = STATUS_ACCESS_DENIED

2124 else:

2125 errorCode = STATUS_SMB_BAD_TID

2126

2127 if errorCode == STATUS_SUCCESS:

2128 # Simple way to generate a fid

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

2130 fakefid = 1

2131 else:

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

2133 respParameters['Fid'] = fakefid

2134 respParameters['CreateAction'] = createDisposition

2135 if fid == PIPE_FILE_DESCRIPTOR:

2136 respParameters['FileAttributes'] = 0x80

2137 respParameters['IsDirectory'] = 0

2138 respParameters['CreateTime'] = 0

2139 respParameters['LastAccessTime'] = 0

2140 respParameters['LastWriteTime'] = 0

2141 respParameters['LastChangeTime'] = 0

2142 respParameters['AllocationSize'] = 4096

2143 respParameters['EndOfFile'] = 0

2144 respParameters['FileType'] = 2

2145 respParameters['IPCState'] = 0x5ff

2146 else:

2147 if os.path.isdir(pathName):

2148 respParameters['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY

2149 respParameters['IsDirectory'] = 1

2150 else:

2151 respParameters['IsDirectory'] = 0

2152 respParameters['FileAttributes'] = ntCreateAndXParameters['FileAttributes']

2153 # Let's get this file's information

2154 respInfo, errorCode = queryPathInformation('', pathName, level=smb.SMB_QUERY_FILE_ALL_INFO)

2155 if errorCode == STATUS_SUCCESS:

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

2157 respParameters['LastAccessTime'] = respInfo['LastAccessTime']

2158 respParameters['LastWriteTime'] = respInfo['LastWriteTime']

2159 respParameters['LastChangeTime'] = respInfo['LastChangeTime']

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

2161 respParameters['AllocationSize'] = respInfo['AllocationSize']

2162 respParameters['EndOfFile'] = respInfo['EndOfFile']

2163 else:

2164 respParameters = b''

2165 respData = b''

2166

2167 if errorCode == STATUS_SUCCESS:

2168 # Let's store the fid for the connection

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

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

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

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

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

2174 if fid == PIPE_FILE_DESCRIPTOR:

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

2176 else:

2177 respParameters = b''

2178 respData = b''

2179

2180 respSMBCommand['Parameters'] = respParameters

2181 respSMBCommand['Data'] = respData

2182 smbServer.setConnectionData(connId, connData)

2183

2184 return [respSMBCommand], None, errorCode

2185

2186 @staticmethod

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

2188 connData = smbServer.getConnectionData(connId)

2189

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

2191 respParameters = smb.SMBOpenAndXResponse_Parameters()

2192 respData = b''

2193

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

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

2196

2197 # Get the Tid associated

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

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

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

2201 decodeSMBString(recvPacket['Flags2'],

2202 openAndXData['FileName']),

2203 openAndXParameters['DesiredAccess'],

2204 openAndXParameters['FileAttributes'],

2205 openAndXParameters['OpenMode'])

2206 else:

2207 errorCode = STATUS_SMB_BAD_TID

2208

2209 if errorCode == STATUS_SUCCESS:

2210 # Simple way to generate a fid

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

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

2213 fid = 1

2214 else:

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

2216 respParameters['Fid'] = fid

2217 if mode & os.O_CREAT:

2218 # File did not exist and was created

2219 respParameters['Action'] = 0x2

2220 elif mode & os.O_RDONLY:

2221 # File existed and was opened

2222 respParameters['Action'] = 0x1

2223 elif mode & os.O_APPEND:

2224 # File existed and was opened

2225 respParameters['Action'] = 0x1

2226 else:

2227 # File existed and was truncated

2228 respParameters['Action'] = 0x3

2229

2230 # Let's store the fid for the connection

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

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

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

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

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

2236 else:

2237 respParameters = b''

2238 respData = b''

2239

2240 respSMBCommand['Parameters'] = respParameters

2241 respSMBCommand['Data'] = respData

2242 smbServer.setConnectionData(connId, connData)

2243

2244 return [respSMBCommand], None, errorCode

2245

2246 @staticmethod

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

2248 connData = smbServer.getConnectionData(connId)

2249

2250 resp = smb.NewSMBPacket()

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

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

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

2254

2255 resp['Tid'] = recvPacket['Tid']

2256 resp['Mid'] = recvPacket['Mid']

2257 resp['Pid'] = connData['Pid']

2258

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

2260 respParameters = smb.SMBTreeConnectAndXResponse_Parameters()

2261 respData = smb.SMBTreeConnectAndXResponse_Data()

2262

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

2264

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

2266 respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()

2267

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

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

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

2271

2272 errorCode = STATUS_SUCCESS

2273

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

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

2276

2277 # Is this a UNC?

2278 if ntpath.ismount(UNCOrShare):

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

2280 else:

2281 path = ntpath.basename(UNCOrShare)

2282

2283 share = searchShare(connId, path, smbServer)

2284 if share is not None:

2285 # Simple way to generate a Tid

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

2287 tid = 1

2288 else:

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

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

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

2292 resp['Tid'] = tid

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

2294 else:

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

2296 errorCode = STATUS_OBJECT_PATH_NOT_FOUND

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

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

2299 ##

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

2301

2302 if path == 'IPC$':

2303 respData['Service'] = 'IPC'

2304 else:

2305 respData['Service'] = path

2306 respData['PadLen'] = 0

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

2308

2309 respSMBCommand['Parameters'] = respParameters

2310 respSMBCommand['Data'] = respData

2311

2312 resp['Uid'] = connData['Uid']

2313 resp.addCommand(respSMBCommand)

2314

2315 # Sign the packet if needed

2316 if connData['SignatureEnabled']:

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

2318 smbServer.setConnectionData(connId, connData)

2319

2320 return None, [resp], errorCode

2321

2322 @staticmethod

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

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

2325

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

2327

2328 # From [MS-SMB]

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

2330 # request MUST take the following form

2331 # [..]

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

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

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

2335 respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters()

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

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

2338 sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()

2339 sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']

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

2341 connData['Capabilities'] = sessionSetupParameters['Capabilities']

2342

2343 rawNTLM = False

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

2345 # NEGOTIATE packet

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

2347 token = blob['MechToken']

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

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

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

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

2352 # Nope, do we know it?

2353 if mechType in MechTypes:

2354 mechStr = MechTypes[mechType]

2355 else:

2356 mechStr = hexlify(mechType)

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

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

2359 # we just support NTLM.

2360 # ToDo: Build this into a SPNEGO_NegTokenResp()

2361 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'

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

2363 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']

2364 respData['SecurityBlob'] = respToken

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

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

2367 respSMBCommand['Parameters'] = respParameters

2368 respSMBCommand['Data'] = respData

2369 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED

2370

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

2372 # AUTH packet

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

2374 token = blob['ResponseToken']

2375 else:

2376 # No GSSAPI stuff, raw NTLMSSP

2377 rawNTLM = True

2378 token = sessionSetupData['SecurityBlob']

2379

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

2381 # authentication we are, we act on it

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

2383

2384 if messageType == 0x01:

2385 # NEGOTIATE_MESSAGE

2386 negotiateMessage = ntlm.NTLMAuthNegotiate()

2387 negotiateMessage.fromString(token)

2388 # Let's store it in the connection data

2389 connData['NEGOTIATE_MESSAGE'] = negotiateMessage

2390 # Let's build the answer flags

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

2392

2393 ansFlags = 0

2394

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

2396 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56

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

2398 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128

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

2400 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH

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

2402 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY

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

2404 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE

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

2406 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM

2407

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

2409

2410 # Generate the AV_PAIRS

2411 av_pairs = ntlm.AV_PAIRS()

2412 # TODO: Put the proper data from SMBSERVER config

2413 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[

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

2415 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[

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

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

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

2419

2420 challengeMessage = ntlm.NTLMAuthChallenge()

2421 challengeMessage['flags'] = ansFlags

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

2423 challengeMessage['domain_max_len'] = challengeMessage['domain_len']

2424 challengeMessage['domain_offset'] = 40 + 16

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

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

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

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

2429 challengeMessage['TargetInfoFields'] = av_pairs

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

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

2432 challengeMessage['VersionLen'] = 8

2433

2434 if rawNTLM is False:

2435 respToken = SPNEGO_NegTokenResp()

2436 # accept-incomplete. We want more data

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

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

2439

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

2441 else:

2442 respToken = challengeMessage

2443

2444 # Setting the packet to STATUS_MORE_PROCESSING

2445 errorCode = STATUS_MORE_PROCESSING_REQUIRED

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

2447 # in the connection's data

2448 # Picking a fixed value

2449 # TODO: Manage more UIDs for the same session

2450 connData['Uid'] = 10

2451 # Let's store it in the connection data

2452 connData['CHALLENGE_MESSAGE'] = challengeMessage

2453

2454 elif messageType == 0x02:

2455 # CHALLENGE_MESSAGE

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

2457 elif messageType == 0x03:

2458 # AUTHENTICATE_MESSAGE, here we deal with authentication

2459 authenticateMessage = ntlm.NTLMAuthChallengeResponse()

2460 authenticateMessage.fromString(token)

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

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

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

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

2465 # Do we have credentials to check?

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

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

2468 # Do we have this user's credentials?

2469 if identity in smbServer.getCredentials():

2470 # Process data:

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

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

2473

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

2475 authenticateMessage, connData['CHALLENGE_MESSAGE'],

2476 connData['NEGOTIATE_MESSAGE'])

2477

2478 if sessionKey is not None:

2479 connData['SignatureEnabled'] = False

2480 connData['SigningSessionKey'] = sessionKey

2481 connData['SignSequenceNumber'] = 1

2482 else:

2483 errorCode = STATUS_LOGON_FAILURE

2484 else:

2485 # No credentials provided, let's grant access

2486 errorCode = STATUS_SUCCESS

2487

2488 if errorCode == STATUS_SUCCESS:

2489 connData['Authenticated'] = True

2490 respToken = SPNEGO_NegTokenResp()

2491 # accept-completed

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

2493

2494 smbServer.log(

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

2496 authenticateMessage['user_name'].decode(

2497 'utf-16le')))

2498 # Let's store it in the connection data

2499 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage

2500 try:

2501 jtr_dump_path = smbServer.getJTRdumpPath()

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

2503 authenticateMessage['user_name'],

2504 authenticateMessage['domain_name'],

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

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

2507 if jtr_dump_path != '':

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

2509 jtr_dump_path)

2510 except:

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

2512 else:

2513 respToken = SPNEGO_NegTokenResp()

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

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

2516 else:

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

2518

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

2520 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']

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

2522

2523 else:

2524 # Process Standard Security

2525 respParameters = smb.SMBSessionSetupAndXResponse_Parameters()

2526 respData = smb.SMBSessionSetupAndXResponse_Data()

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

2528 sessionSetupData = smb.SMBSessionSetupAndX_Data()

2529 sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']

2530 sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']

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

2532 connData['Capabilities'] = sessionSetupParameters['Capabilities']

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

2534 # TODO: Manage more UIDs for the same session

2535 errorCode = STATUS_SUCCESS

2536 connData['Uid'] = 10

2537 connData['Authenticated'] = True

2538 respParameters['Action'] = 0

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

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

2541 try:

2542 jtr_dump_path = smbServer.getJTRdumpPath()

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

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

2545 sessionSetupData['UnicodePwd'])

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

2547 if jtr_dump_path != '':

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

2549 except:

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

2551

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

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

2554 respSMBCommand['Parameters'] = respParameters

2555 respSMBCommand['Data'] = respData

2556

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

2558 connData['Authenticated'] = True

2559 # For now, just switching to nobody

2560 # os.setregid(65534,65534)

2561 # os.setreuid(65534,65534)

2562 smbServer.setConnectionData(connId, connData)

2563

2564 return [respSMBCommand], None, errorCode

2565

2566 @staticmethod

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

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

2569 connData['Pid'] = recvPacket['Pid']

2570

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

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

2573

2574 resp = smb.NewSMBPacket()

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

2576 resp['Pid'] = connData['Pid']

2577 resp['Tid'] = recvPacket['Tid']

2578 resp['Mid'] = recvPacket['Mid']

2579

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

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

2582 try:

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

2584 # Let's fill the data for NTLM

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

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

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

2588 _dialects_data = smb.SMBExtended_Security_Data()

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

2590 blob = SPNEGO_NegTokenInit()

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

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

2593

2594 _dialects_parameters = smb.SMBExtended_Security_Parameters()

2595 _dialects_parameters[

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

2597 _dialects_parameters['ChallengeLength'] = 0

2598

2599 else:

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

2601 _dialects_parameters = smb.SMBNTLMDialect_Parameters()

2602 _dialects_data = smb.SMBNTLMDialect_Data()

2603 _dialects_data['Payload'] = ''

2604 if 'EncryptionKey' in connData:

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

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

2607 else:

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

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

2610 _dialects_parameters['ChallengeLength'] = 8

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

2612

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

2614 config = smbServer.getServerConfig()

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

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

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

2618

2619 _dialects_parameters['DialectIndex'] = index

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

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

2622 _dialects_parameters['MaxMpxCount'] = 1

2623 _dialects_parameters['MaxNumberVcs'] = 1

2624 _dialects_parameters['MaxBufferSize'] = 64000

2625 _dialects_parameters['MaxRawSize'] = 65536

2626 _dialects_parameters['SessionKey'] = 0

2627 _dialects_parameters['LowDateTime'] = 0

2628 _dialects_parameters['HighDateTime'] = 0

2629 _dialects_parameters['ServerTimeZone'] = 0

2630

2631 respSMBCommand['Data'] = _dialects_data

2632 respSMBCommand['Parameters'] = _dialects_parameters

2633 connData['_dialects_data'] = _dialects_data

2634 connData['_dialects_parameters'] = _dialects_parameters

2635

2636 except Exception as e:

2637 # No NTLM throw an error

2638 smbServer.log('smbComNegotiate: %s' % e, logging.ERROR)

2639 respSMBCommand['Data'] = struct.pack('<H', 0xffff)

2640

2641 smbServer.setConnectionData(connId, connData)

2642

2643 resp.addCommand(respSMBCommand)

2644

2645 return None, [resp], STATUS_SUCCESS

2646

2647 @staticmethod

2648 def default(connId, smbServer, SMBCommand, recvPacket):

2649 # By default we return an SMB Packet with error not implemented

2650 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'], logging.DEBUG)

2651 packet = smb.NewSMBPacket()

2652 packet['Flags1'] = smb.SMB.FLAGS1_REPLY

2653 packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS

2654 packet['Command'] = recvPacket['Command']

2655 packet['Pid'] = recvPacket['Pid']

2656 packet['Tid'] = recvPacket['Tid']

2657 packet['Mid'] = recvPacket['Mid']

2658 packet['Uid'] = recvPacket['Uid']

2659 packet['Data'] = b'\x00\x00\x00'

2660 errorCode = STATUS_NOT_IMPLEMENTED

2661 packet['ErrorCode'] = errorCode >> 16

2662 packet['ErrorClass'] = errorCode & 0xff

2663

2664 return None, [packet], errorCode

2665

2666

2667 class SMB2Commands:

2668 @staticmethod

2669 def smb2Negotiate(connId, smbServer, recvPacket, isSMB1=False):

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

2671

2672 respPacket = smb2.SMB2Packet()

2673 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR

2674 respPacket['Status'] = STATUS_SUCCESS

2675 respPacket['CreditRequestResponse'] = 1

2676 respPacket['Command'] = smb2.SMB2_NEGOTIATE

2677 respPacket['SessionID'] = 0

2678 if isSMB1 is False:

2679 respPacket['MessageID'] = recvPacket['MessageID']

2680 else:

2681 respPacket['MessageID'] = 0

2682 respPacket['TreeID'] = 0

2683

2684 respSMBCommand = smb2.SMB2Negotiate_Response()

2685

2686 respSMBCommand['SecurityMode'] = 1

2687 if isSMB1 is True:

2688 # Let's first parse the packet to see if the client supports SMB2

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

2690

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

2692 if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects:

2693 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002

2694 else:

2695 # Client does not support SMB2 fallbacking

2696 raise Exception('SMB2 not supported, fallbacking')

2697 else:

2698 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002

2699 respSMBCommand['ServerGuid'] = b'A' * 16

2700 respSMBCommand['Capabilities'] = 0

2701 respSMBCommand['MaxTransactSize'] = 65536

2702 respSMBCommand['MaxReadSize'] = 65536

2703 respSMBCommand['MaxWriteSize'] = 65536

2704 respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime()))

2705 respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime()))

2706 respSMBCommand['SecurityBufferOffset'] = 0x80

2707

2708 blob = SPNEGO_NegTokenInit()

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

2710

2711 respSMBCommand['Buffer'] = blob.getData()

2712 respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer'])

2713

2714 respPacket['Data'] = respSMBCommand

2715

2716 smbServer.setConnectionData(connId, connData)

2717

2718 return None, [respPacket], STATUS_SUCCESS

2719

2720 @staticmethod

2721 def smb2SessionSetup(connId, smbServer, recvPacket):

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

2723

2724 respSMBCommand = smb2.SMB2SessionSetup_Response()

2725

2726 sessionSetupData = smb2.SMB2SessionSetup(recvPacket['Data'])

2727

2728 connData['Capabilities'] = sessionSetupData['Capabilities']

2729

2730 securityBlob = sessionSetupData['Buffer']

2731

2732 rawNTLM = False

2733 if struct.unpack('B', securityBlob[0:1])[0] == ASN1_AID:

2734 # NEGOTIATE packet

2735 blob = SPNEGO_NegTokenInit(securityBlob)

2736 token = blob['MechToken']

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

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

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

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

2741 # Nope, do we know it?

2742 if mechType in MechTypes:

2743 mechStr = MechTypes[mechType]

2744 else:

2745 mechStr = hexlify(mechType)

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

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

2748 # we just support NTLM.

2749 # ToDo: Build this into a SPNEGO_NegTokenResp()

2750 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'

2751 respSMBCommand['SecurityBufferOffset'] = 0x48

2752 respSMBCommand['SecurityBufferLength'] = len(respToken)

2753 respSMBCommand['Buffer'] = respToken

2754

2755 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED

2756 elif struct.unpack('B', securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH:

2757 # AUTH packet

2758 blob = SPNEGO_NegTokenResp(securityBlob)

2759 token = blob['ResponseToken']

2760 else:

2761 # No GSSAPI stuff, raw NTLMSSP

2762 rawNTLM = True

2763 token = securityBlob

2764

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

2766 # authentication we are, we act on it

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

2768

2769 if messageType == 0x01:

2770 # NEGOTIATE_MESSAGE

2771 negotiateMessage = ntlm.NTLMAuthNegotiate()

2772 negotiateMessage.fromString(token)

2773 # Let's store it in the connection data

2774 connData['NEGOTIATE_MESSAGE'] = negotiateMessage

2775 # Let's build the answer flags

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

2777

2778 ansFlags = 0

2779

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

2781 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56

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

2783 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128

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

2785 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH

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

2787 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY

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

2789 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE

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

2791 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM

2792

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

2794

2795 # Generate the AV_PAIRS

2796 av_pairs = ntlm.AV_PAIRS()

2797 # TODO: Put the proper data from SMBSERVER config

2798 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[

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

2800 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[

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

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

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

2804

2805 challengeMessage = ntlm.NTLMAuthChallenge()

2806 challengeMessage['flags'] = ansFlags

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

2808 challengeMessage['domain_max_len'] = challengeMessage['domain_len']

2809 challengeMessage['domain_offset'] = 40 + 16

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

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

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

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

2814 challengeMessage['TargetInfoFields'] = av_pairs

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

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

2817 challengeMessage['VersionLen'] = 8

2818

2819 if rawNTLM is False:

2820 respToken = SPNEGO_NegTokenResp()

2821 # accept-incomplete. We want more data

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

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

2824

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

2826 else:

2827 respToken = challengeMessage

2828

2829 # Setting the packet to STATUS_MORE_PROCESSING

2830 errorCode = STATUS_MORE_PROCESSING_REQUIRED

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

2832 # in the connection's data

2833 # Picking a fixed value

2834 # TODO: Manage more UIDs for the same session

2835 connData['Uid'] = random.randint(1, 0xffffffff)

2836 # Let's store it in the connection data

2837 connData['CHALLENGE_MESSAGE'] = challengeMessage

2838

2839 elif messageType == 0x02:

2840 # CHALLENGE_MESSAGE

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

2842 elif messageType == 0x03:

2843 # AUTHENTICATE_MESSAGE, here we deal with authentication

2844 authenticateMessage = ntlm.NTLMAuthChallengeResponse()

2845 authenticateMessage.fromString(token)

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

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

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

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

2850 # TODO: Check the credentials! Now granting permissions

2851 # Do we have credentials to check?

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

2853 isGuest = False

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

2855 # Do we have this user's credentials?

2856 if identity in smbServer.getCredentials():

2857 # Process data:

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

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

2860

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

2862 authenticateMessage, connData['CHALLENGE_MESSAGE'],

2863 connData['NEGOTIATE_MESSAGE'])

2864

2865 if sessionKey is not None:

2866 connData['SignatureEnabled'] = True

2867 connData['SigningSessionKey'] = sessionKey

2868 connData['SignSequenceNumber'] = 1

2869 else:

2870 errorCode = STATUS_LOGON_FAILURE

2871 else:

2872 # No credentials provided, let's grant access

2873 isGuest = True

2874 errorCode = STATUS_SUCCESS

2875

2876 if errorCode == STATUS_SUCCESS:

2877 connData['Authenticated'] = True

2878 respToken = SPNEGO_NegTokenResp()

2879 # accept-completed

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

2881 smbServer.log('User %s\\%s authenticated successfully' % (

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

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

2884 # Let's store it in the connection data

2885 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage

2886 try:

2887 jtr_dump_path = smbServer.getJTRdumpPath()

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

2889 authenticateMessage['user_name'],

2890 authenticateMessage['domain_name'],

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

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

2893 if jtr_dump_path != '':

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

2895 jtr_dump_path)

2896 except:

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

2898

2899 if isGuest:

2900 respSMBCommand['SessionFlags'] = 1

2901

2902 else:

2903 respToken = SPNEGO_NegTokenResp()

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

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

2906 else:

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

2908

2909 respSMBCommand['SecurityBufferOffset'] = 0x48

2910 respSMBCommand['SecurityBufferLength'] = len(respToken)

2911 respSMBCommand['Buffer'] = respToken.getData()

2912

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

2914 connData['Authenticated'] = True

2915 # For now, just switching to nobody

2916 # os.setregid(65534,65534)

2917 # os.setreuid(65534,65534)

2918 smbServer.setConnectionData(connId, connData)

2919

2920 return [respSMBCommand], None, errorCode

2921

2922 @staticmethod

2923 def smb2TreeConnect(connId, smbServer, recvPacket):

2924 connData = smbServer.getConnectionData(connId)

2925

2926 respPacket = smb2.SMB2Packet()

2927 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR

2928 respPacket['Status'] = STATUS_SUCCESS

2929 respPacket['CreditRequestResponse'] = 1

2930 respPacket['Command'] = recvPacket['Command']

2931 respPacket['SessionID'] = connData['Uid']

2932 respPacket['Reserved'] = recvPacket['Reserved']

2933 respPacket['MessageID'] = recvPacket['MessageID']

2934 respPacket['TreeID'] = recvPacket['TreeID']

2935

2936 respSMBCommand = smb2.SMB2TreeConnect_Response()

2937

2938 treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data'])

2939

2940 errorCode = STATUS_SUCCESS

2941

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

2943 path = recvPacket.getData()[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']]

2944 UNCOrShare = path.decode('utf-16le')

2945

2946 # Is this a UNC?

2947 if ntpath.ismount(UNCOrShare):

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

2949 else:

2950 path = ntpath.basename(UNCOrShare)

2951

2952 share = searchShare(connId, path.upper(), smbServer)

2953 if share is not None:

2954 # Simple way to generate a Tid

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

2956 tid = 1

2957 else:

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

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

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

2961 respPacket['TreeID'] = tid

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

2963 else:

2964 smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR)

2965 errorCode = STATUS_OBJECT_PATH_NOT_FOUND

2966 respPacket['Status'] = errorCode

2967 ##

2968

2969 if path.upper() == 'IPC$':

2970 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE

2971 respSMBCommand['ShareFlags'] = 0x30

2972 else:

2973 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK

2974 respSMBCommand['ShareFlags'] = 0x0

2975

2976 respSMBCommand['Capabilities'] = 0

2977 respSMBCommand['MaximalAccess'] = 0x000f01ff

2978

2979 respPacket['Data'] = respSMBCommand

2980

2981 # Sign the packet if needed

2982 if connData['SignatureEnabled']:

2983 smbServer.signSMBv2(respPacket, connData['SigningSessionKey'])

2984 smbServer.setConnectionData(connId, connData)

2985

2986 return None, [respPacket], errorCode

2987

2988 @staticmethod

2989 def smb2Create(connId, smbServer, recvPacket):

2990 connData = smbServer.getConnectionData(connId)

2991

2992 respSMBCommand = smb2.SMB2Create_Response()

2993

2994 ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])

2995

2996 respSMBCommand['Buffer'] = b'\x00'

2997 # Get the Tid associated

2998 if recvPacket['TreeID'] in connData['ConnectedShares']:

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

3000 errorCode = STATUS_SUCCESS

3001 if 'path' in connData['ConnectedShares'][recvPacket['TreeID']]:

3002 path = connData['ConnectedShares'][recvPacket['TreeID']]['path']

3003 else:

3004 path = 'NONE'

3005 errorCode = STATUS_ACCESS_DENIED

3006

3007 deleteOnClose = False

3008

3009 fileName = os.path.normpath(

3010 ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\', '/'))

3011 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):

3012 # strip leading '/'

3013 fileName = fileName[1:]

3014

3015 if not isInFileJail(path, fileName):

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

3017 return [smb2.SMB2Error()], None, STATUS_OBJECT_PATH_SYNTAX_BAD

3018

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

3020 createDisposition = ntCreateRequest['CreateDisposition']

3021 mode = 0

3022

3023 if createDisposition == smb2.FILE_SUPERSEDE:

3024 mode |= os.O_TRUNC | os.O_CREAT

3025 elif createDisposition & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:

3026 mode |= os.O_TRUNC | os.O_CREAT

3027 elif createDisposition & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:

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

3029 mode |= os.O_TRUNC

3030 else:

3031 errorCode = STATUS_NO_SUCH_FILE

3032 elif createDisposition & smb2.FILE_OPEN_IF == smb2.FILE_OPEN_IF:

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

3034 mode |= os.O_TRUNC

3035 else:

3036 mode |= os.O_TRUNC | os.O_CREAT

3037 elif createDisposition & smb2.FILE_CREATE == smb2.FILE_CREATE:

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

3039 errorCode = STATUS_OBJECT_NAME_COLLISION

3040 else:

3041 mode |= os.O_CREAT

3042 elif createDisposition & smb2.FILE_OPEN == smb2.FILE_OPEN:

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

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

3045 errorCode = STATUS_NO_SUCH_FILE

3046

3047 if errorCode == STATUS_SUCCESS:

3048 desiredAccess = ntCreateRequest['DesiredAccess']

3049 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):

3050 mode |= os.O_RDONLY

3051 if (desiredAccess & smb2.FILE_WRITE_DATA) or (desiredAccess & smb2.GENERIC_WRITE):

3052 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):

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

3054 else:

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

3056 if desiredAccess & smb2.GENERIC_ALL:

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

3058

3059 createOptions = ntCreateRequest['CreateOptions']

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

3061 if createOptions & smb2.FILE_DIRECTORY_FILE == smb2.FILE_DIRECTORY_FILE:

3062 try:

3063 # Let's create the directory

3064 os.mkdir(pathName)

3065 mode = os.O_RDONLY

3066 except Exception as e:

3067 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName, mode, e), logging.ERROR)

3068 errorCode = STATUS_ACCESS_DENIED

3069 if createOptions & smb2.FILE_NON_DIRECTORY_FILE == smb2.FILE_NON_DIRECTORY_FILE:

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

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

3072 # response.

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

3074 errorCode = STATUS_FILE_IS_A_DIRECTORY

3075

3076 if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:

3077 deleteOnClose = True

3078

3079 if errorCode == STATUS_SUCCESS:

3080 try:

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

3082 fid = VOID_FILE_DESCRIPTOR

3083 else:

3084 if sys.platform == 'win32':

3085 mode |= os.O_BINARY

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

3087 fid = PIPE_FILE_DESCRIPTOR

3088 sock = socket.socket()

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

3090 else:

3091 fid = os.open(pathName, mode)

3092 except Exception as e:

3093 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName, mode, e), logging.ERROR)

3094 # print e

3095 fid = 0

3096 errorCode = STATUS_ACCESS_DENIED

3097 else:

3098 errorCode = STATUS_SMB_BAD_TID

3099

3100 if errorCode == STATUS_SUCCESS:

3101 # Simple way to generate a fid

3102 fakefid = uuid.generate()

3103

3104 respSMBCommand['FileID'] = fakefid

3105 respSMBCommand['CreateAction'] = createDisposition

3106

3107 if fid == PIPE_FILE_DESCRIPTOR:

3108 respSMBCommand['CreationTime'] = 0

3109 respSMBCommand['LastAccessTime'] = 0

3110 respSMBCommand['LastWriteTime'] = 0

3111 respSMBCommand['ChangeTime'] = 0

3112 respSMBCommand['AllocationSize'] = 4096

3113 respSMBCommand['EndOfFile'] = 0

3114 respSMBCommand['FileAttributes'] = 0x80

3115

3116 else:

3117 if os.path.isdir(pathName):

3118 respSMBCommand['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY

3119 else:

3120 respSMBCommand['FileAttributes'] = ntCreateRequest['FileAttributes']

3121 # Let's get this file's information

3122 respInfo, errorCode = queryPathInformation('', pathName, level=smb.SMB_QUERY_FILE_ALL_INFO)

3123 if errorCode == STATUS_SUCCESS:

3124 respSMBCommand['CreationTime'] = respInfo['CreationTime']

3125 respSMBCommand['LastAccessTime'] = respInfo['LastAccessTime']

3126 respSMBCommand['LastWriteTime'] = respInfo['LastWriteTime']

3127 respSMBCommand['LastChangeTime'] = respInfo['LastChangeTime']

3128 respSMBCommand['FileAttributes'] = respInfo['ExtFileAttributes']

3129 respSMBCommand['AllocationSize'] = respInfo['AllocationSize']

3130 respSMBCommand['EndOfFile'] = respInfo['EndOfFile']

3131

3132 if errorCode == STATUS_SUCCESS:

3133 # Let's store the fid for the connection

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

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

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

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

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

3139 connData['OpenedFiles'][fakefid]['Open'] = {}

3140 connData['OpenedFiles'][fakefid]['Open']['EnumerationLocation'] = 0

3141 connData['OpenedFiles'][fakefid]['Open']['EnumerationSearchPattern'] = ''

3142 if fid == PIPE_FILE_DESCRIPTOR:

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

3144 else:

3145 respSMBCommand = smb2.SMB2Error()

3146

3147 if errorCode == STATUS_SUCCESS:

3148 connData['LastRequest']['SMB2_CREATE'] = respSMBCommand

3149 smbServer.setConnectionData(connId, connData)

3150

3151 return [respSMBCommand], None, errorCode

3152

3153 @staticmethod

3154 def smb2Close(connId, smbServer, recvPacket):

3155 connData = smbServer.getConnectionData(connId)

3156

3157 respSMBCommand = smb2.SMB2Close_Response()

3158

3159 closeRequest = smb2.SMB2Close(recvPacket['Data'])

3160

3161 if closeRequest['FileID'].getData() == b'\xff' * 16:

3162 # Let's take the data from the lastRequest

3163 if 'SMB2_CREATE' in connData['LastRequest']:

3164 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3165 else:

3166 fileID = closeRequest['FileID'].getData()

3167 else:

3168 fileID = closeRequest['FileID'].getData()

3169

3170 if fileID in connData['OpenedFiles']:

3171 errorCode = STATUS_SUCCESS

3172 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']

3173 pathName = connData['OpenedFiles'][fileID]['FileName']

3174 infoRecord = None

3175 try:

3176 if fileHandle == PIPE_FILE_DESCRIPTOR:

3177 connData['OpenedFiles'][fileID]['Socket'].close()

3178 elif fileHandle != VOID_FILE_DESCRIPTOR:

3179 os.close(fileHandle)

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

3181 smb2.SMB2_FILE_NETWORK_OPEN_INFO)

3182 except Exception as e:

3183 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)

3184 errorCode = STATUS_INVALID_HANDLE

3185 else:

3186 # Check if the file was marked for removal

3187 if connData['OpenedFiles'][fileID]['DeleteOnClose'] is True:

3188 try:

3189 if os.path.isdir(pathName):

3190 shutil.rmtree(connData['OpenedFiles'][fileID]['FileName'])

3191 else:

3192 os.remove(connData['OpenedFiles'][fileID]['FileName'])

3193 except Exception as e:

3194 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)

3195 errorCode = STATUS_ACCESS_DENIED

3196

3197 # Now fill out the response

3198 if infoRecord is not None:

3199 respSMBCommand['CreationTime'] = infoRecord['CreationTime']

3200 respSMBCommand['LastAccessTime'] = infoRecord['LastAccessTime']

3201 respSMBCommand['LastWriteTime'] = infoRecord['LastWriteTime']

3202 respSMBCommand['ChangeTime'] = infoRecord['ChangeTime']

3203 respSMBCommand['AllocationSize'] = infoRecord['AllocationSize']

3204 respSMBCommand['EndofFile'] = infoRecord['EndOfFile']

3205 respSMBCommand['FileAttributes'] = infoRecord['FileAttributes']

3206 if errorCode == STATUS_SUCCESS:

3207 del (connData['OpenedFiles'][fileID])

3208 else:

3209 errorCode = STATUS_INVALID_HANDLE

3210

3211 smbServer.setConnectionData(connId, connData)

3212 return [respSMBCommand], None, errorCode

3213

3214 @staticmethod

3215 def smb2QueryInfo(connId, smbServer, recvPacket):

3216 connData = smbServer.getConnectionData(connId)

3217

3218 respSMBCommand = smb2.SMB2QueryInfo_Response()

3219

3220 queryInfo = smb2.SMB2QueryInfo(recvPacket['Data'])

3221

3222 errorCode = STATUS_SUCCESS

3223

3224 respSMBCommand['OutputBufferOffset'] = 0x48

3225 respSMBCommand['Buffer'] = b'\x00'

3226

3227 if queryInfo['FileID'].getData() == b'\xff' * 16:

3228 # Let's take the data from the lastRequest

3229 if 'SMB2_CREATE' in connData['LastRequest']:

3230 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3231 else:

3232 fileID = queryInfo['FileID'].getData()

3233 else:

3234 fileID = queryInfo['FileID'].getData()

3235

3236 if recvPacket['TreeID'] in connData['ConnectedShares']:

3237 if fileID in connData['OpenedFiles']:

3238 fileName = connData['OpenedFiles'][fileID]['FileName']

3239

3240 if queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:

3241 if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_INTERNAL_INFO:

3242 # No need to call queryFileInformation, we have the data here

3243 infoRecord = smb2.FileInternalInformation()

3244 infoRecord['IndexNumber'] = fileID

3245 else:

3246 infoRecord, errorCode = queryFileInformation(os.path.dirname(fileName),

3247 os.path.basename(fileName),

3248 queryInfo['FileInfoClass'])

3249 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:

3250 if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_EA_INFO:

3251 infoRecord = b'\x00' * 4

3252 else:

3253 infoRecord = queryFsInformation(os.path.dirname(fileName), os.path.basename(fileName),

3254 queryInfo['FileInfoClass'])

3255 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:

3256 # Failing for now, until we support it

3257 infoRecord = None

3258 errorCode = STATUS_ACCESS_DENIED

3259 else:

3260 smbServer.log("queryInfo not supported (%x)" % queryInfo['InfoType'], logging.ERROR)

3261

3262 if infoRecord is not None:

3263 respSMBCommand['OutputBufferLength'] = len(infoRecord)

3264 respSMBCommand['Buffer'] = infoRecord

3265 else:

3266 errorCode = STATUS_INVALID_HANDLE

3267 else:

3268 errorCode = STATUS_SMB_BAD_TID

3269

3270 smbServer.setConnectionData(connId, connData)

3271 return [respSMBCommand], None, errorCode

3272

3273 @staticmethod

3274 def smb2SetInfo(connId, smbServer, recvPacket):

3275 connData = smbServer.getConnectionData(connId)

3276

3277 respSMBCommand = smb2.SMB2SetInfo_Response()

3278

3279 setInfo = smb2.SMB2SetInfo(recvPacket['Data'])

3280

3281 errorCode = STATUS_SUCCESS

3282

3283 if setInfo['FileID'].getData() == b'\xff' * 16:

3284 # Let's take the data from the lastRequest

3285 if 'SMB2_CREATE' in connData['LastRequest']:

3286 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3287 else:

3288 fileID = setInfo['FileID'].getData()

3289 else:

3290 fileID = setInfo['FileID'].getData()

3291

3292 if recvPacket['TreeID'] in connData['ConnectedShares']:

3293 path = connData['ConnectedShares'][recvPacket['TreeID']]['path']

3294 if fileID in connData['OpenedFiles']:

3295 pathName = connData['OpenedFiles'][fileID]['FileName']

3296

3297 if setInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:

3298 # The file information is being set

3299 informationLevel = setInfo['FileInfoClass']

3300 if informationLevel == smb2.SMB2_FILE_DISPOSITION_INFO:

3301 infoRecord = smb.SMBSetFileDispositionInfo(setInfo['Buffer'])

3302 if infoRecord['DeletePending'] > 0:

3303 # Mark this file for removal after closed

3304 connData['OpenedFiles'][fileID]['DeleteOnClose'] = True

3305 elif informationLevel == smb2.SMB2_FILE_BASIC_INFO:

3306 infoRecord = smb.SMBSetFileBasicInfo(setInfo['Buffer'])

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

3308 atime = infoRecord['LastWriteTime']

3309 if atime == 0:

3310 atime = -1

3311 else:

3312 atime = getUnixTime(atime)

3313 mtime = infoRecord['ChangeTime']

3314 if mtime == 0:

3315 mtime = -1

3316 else:

3317 mtime = getUnixTime(mtime)

3318 if atime > 0 and mtime > 0:

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

3320 elif informationLevel == smb2.SMB2_FILE_END_OF_FILE_INFO:

3321 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']

3322 infoRecord = smb.SMBSetFileEndOfFileInfo(setInfo['Buffer'])

3323 if infoRecord['EndOfFile'] > 0:

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

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

3326 elif informationLevel == smb2.SMB2_FILE_RENAME_INFO:

3327 renameInfo = smb2.FILE_RENAME_INFORMATION_TYPE_2(setInfo['Buffer'])

3328 newPathName = os.path.join(path, renameInfo['FileName'].decode('utf-16le').replace('\\', '/'))

3329 if renameInfo['ReplaceIfExists'] == 0 and os.path.exists(newPathName):

3330 return [smb2.SMB2Error()], None, STATUS_OBJECT_NAME_COLLISION

3331 try:

3332 os.rename(pathName, newPathName)

3333 connData['OpenedFiles'][fileID]['FileName'] = newPathName

3334 except Exception as e:

3335 smbServer.log("smb2SetInfo: %s" % e, logging.ERROR)

3336 errorCode = STATUS_ACCESS_DENIED

3337 else:

3338 smbServer.log('Unknown level for set file info! 0x%x' % informationLevel, logging.ERROR)

3339 # UNSUPPORTED

3340 errorCode = STATUS_NOT_SUPPORTED

3341 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:

3342 # # The underlying object store information is being set.

3343 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])

3344 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:

3345 # # The security information is being set.

3346 # # Failing for now, until we support it

3347 # infoRecord = None

3348 # errorCode = STATUS_ACCESS_DENIED

3349 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_QUOTA:

3350 # # The underlying object store quota information is being set.

3351 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])

3352 else:

3353 smbServer.log("setInfo not supported (%x)" % setInfo['InfoType'], logging.ERROR)

3354

3355 else:

3356 errorCode = STATUS_INVALID_HANDLE

3357 else:

3358 errorCode = STATUS_SMB_BAD_TID

3359

3360 smbServer.setConnectionData(connId, connData)

3361 return [respSMBCommand], None, errorCode

3362

3363 @staticmethod

3364 def smb2Write(connId, smbServer, recvPacket):

3365 connData = smbServer.getConnectionData(connId)

3366

3367 respSMBCommand = smb2.SMB2Write_Response()

3368 writeRequest = smb2.SMB2Write(recvPacket['Data'])

3369

3370 respSMBCommand['Buffer'] = b'\x00'

3371

3372 if writeRequest['FileID'].getData() == b'\xff' * 16:

3373 # Let's take the data from the lastRequest

3374 if 'SMB2_CREATE' in connData['LastRequest']:

3375 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3376 else:

3377 fileID = writeRequest['FileID'].getData()

3378 else:

3379 fileID = writeRequest['FileID'].getData()

3380

3381 if fileID in connData['OpenedFiles']:

3382 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']

3383 errorCode = STATUS_SUCCESS

3384 try:

3385 if fileHandle != PIPE_FILE_DESCRIPTOR:

3386 offset = writeRequest['Offset']

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

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

3389 os.lseek(fileHandle, offset, 0)

3390 os.write(fileHandle, writeRequest['Buffer'])

3391 else:

3392 sock = connData['OpenedFiles'][fileID]['Socket']

3393 sock.send(writeRequest['Buffer'])

3394

3395 respSMBCommand['Count'] = writeRequest['Length']

3396 respSMBCommand['Remaining'] = 0xff

3397 except Exception as e:

3398 smbServer.log('SMB2_WRITE: %s' % e, logging.ERROR)

3399 errorCode = STATUS_ACCESS_DENIED

3400 else:

3401 errorCode = STATUS_INVALID_HANDLE

3402

3403 smbServer.setConnectionData(connId, connData)

3404 return [respSMBCommand], None, errorCode

3405

3406 @staticmethod

3407 def smb2Read(connId, smbServer, recvPacket):

3408 connData = smbServer.getConnectionData(connId)

3409

3410 respSMBCommand = smb2.SMB2Read_Response()

3411 readRequest = smb2.SMB2Read(recvPacket['Data'])

3412

3413 respSMBCommand['Buffer'] = b'\x00'

3414

3415 if readRequest['FileID'].getData() == b'\xff' * 16:

3416 # Let's take the data from the lastRequest

3417 if 'SMB2_CREATE' in connData['LastRequest']:

3418 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3419 else:

3420 fileID = readRequest['FileID'].getData()

3421 else:

3422 fileID = readRequest['FileID'].getData()

3423

3424 if fileID in connData['OpenedFiles']:

3425 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']

3426 errorCode = 0

3427 try:

3428 if fileHandle != PIPE_FILE_DESCRIPTOR:

3429 offset = readRequest['Offset']

3430 os.lseek(fileHandle, offset, 0)

3431 content = os.read(fileHandle, readRequest['Length'])

3432 else:

3433 sock = connData['OpenedFiles'][fileID]['Socket']

3434 content = sock.recv(readRequest['Length'])

3435

3436 respSMBCommand['DataOffset'] = 0x50

3437 respSMBCommand['DataLength'] = len(content)

3438 respSMBCommand['DataRemaining'] = 0

3439 respSMBCommand['Buffer'] = content

3440 except Exception as e:

3441 smbServer.log('SMB2_READ: %s ' % e, logging.ERROR)

3442 errorCode = STATUS_ACCESS_DENIED

3443 else:

3444 errorCode = STATUS_INVALID_HANDLE

3445

3446 smbServer.setConnectionData(connId, connData)

3447 return [respSMBCommand], None, errorCode

3448

3449 @staticmethod

3450 def smb2Flush(connId, smbServer, recvPacket):

3451 connData = smbServer.getConnectionData(connId)

3452

3453 respSMBCommand = smb2.SMB2Flush_Response()

3454 flushRequest = smb2.SMB2Flush(recvPacket['Data'])

3455

3456 if flushRequest['FileID'].getData() in connData['OpenedFiles']:

3457 fileHandle = connData['OpenedFiles'][flushRequest['FileID'].getData()]['FileHandle']

3458 errorCode = STATUS_SUCCESS

3459 try:

3460 os.fsync(fileHandle)

3461 except Exception as e:

3462 smbServer.log("SMB2_FLUSH %s" % e, logging.ERROR)

3463 errorCode = STATUS_ACCESS_DENIED

3464 else:

3465 errorCode = STATUS_INVALID_HANDLE

3466

3467 smbServer.setConnectionData(connId, connData)

3468 return [respSMBCommand], None, errorCode

3469

3470 @staticmethod

3471 def smb2QueryDirectory(connId, smbServer, recvPacket):

3472 connData = smbServer.getConnectionData(connId)

3473 respSMBCommand = smb2.SMB2QueryDirectory_Response()

3474 queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data'])

3475

3476 respSMBCommand['Buffer'] = b'\x00'

3477

3478 # The server MUST locate the tree connection, as specified in section 3.3.5.2.11.

3479 if (recvPacket['TreeID'] in connData['ConnectedShares']) is False:

3480 return [smb2.SMB2Error()], None, STATUS_NETWORK_NAME_DELETED

3481

3482 # Next, the server MUST locate the open for the directory to be queried

3483 # If no open is found, the server MUST fail the request with STATUS_FILE_CLOSED

3484 if queryDirectoryRequest['FileID'].getData() == b'\xff' * 16:

3485 # Let's take the data from the lastRequest

3486 if 'SMB2_CREATE' in connData['LastRequest']:

3487 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']

3488 else:

3489 fileID = queryDirectoryRequest['FileID'].getData()

3490 else:

3491 fileID = queryDirectoryRequest['FileID'].getData()

3492

3493 if (fileID in connData['OpenedFiles']) is False:

3494 return [smb2.SMB2Error()], None, STATUS_FILE_CLOSED

3495

3496 # If the open is not an open to a directory, the request MUST be failed

3497 # with STATUS_INVALID_PARAMETER.

3498 if os.path.isdir(connData['OpenedFiles'][fileID]['FileName']) is False:

3499 return [smb2.SMB2Error()], None, STATUS_INVALID_PARAMETER

3500

3501 # If any other information class is specified in the FileInformationClass

3502 # field of the SMB2 QUERY_DIRECTORY Request, the server MUST fail the

3503 # operation with STATUS_INVALID_INFO_CLASS.

3504 if queryDirectoryRequest['FileInformationClass'] not in (

3505 smb2.FILE_DIRECTORY_INFORMATION, smb2.FILE_FULL_DIRECTORY_INFORMATION,

3506 smb2.FILEID_FULL_DIRECTORY_INFORMATION,

3507 smb2.FILE_BOTH_DIRECTORY_INFORMATION, smb2.FILEID_BOTH_DIRECTORY_INFORMATION,

3508 smb2.FILENAMES_INFORMATION):

3509 return [smb2.SMB2Error()], None, STATUS_INVALID_INFO_CLASS

3510

3511 # If SMB2_REOPEN is set in the Flags field of the SMB2 QUERY_DIRECTORY

3512 # Request, the server SHOULD<326> set Open.EnumerationLocation to 0

3513 # and Open.EnumerationSearchPattern to an empty string.

3514 if queryDirectoryRequest['Flags'] & smb2.SMB2_REOPEN:

3515 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0

3516 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = ''

3517

3518 # If SMB2_RESTART_SCANS is set in the Flags field of the SMB2

3519 # QUERY_DIRECTORY Request, the server MUST set

3520 # Open.EnumerationLocation to 0.

3521 if queryDirectoryRequest['Flags'] & smb2.SMB2_RESTART_SCANS:

3522 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0

3523

3524 # If Open.EnumerationLocation is 0 and Open.EnumerationSearchPattern

3525 # is an empty string, then Open.EnumerationSearchPattern MUST be set

3526 # to the search pattern specified in the SMB2 QUERY_DIRECTORY by

3527 # FileNameOffset and FileNameLength. If FileNameLength is 0, the server

3528 # SHOULD<327> set Open.EnumerationSearchPattern as "*" to search all entries.

3529

3530 pattern = queryDirectoryRequest['Buffer'].decode('utf-16le')

3531 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0 and \

3532 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] == '':

3533 if pattern == '':

3534 pattern = '*'

3535 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern

3536

3537 # If SMB2_INDEX_SPECIFIED is set and FileNameLength is not zero,

3538 # the server MUST set Open.EnumerationSearchPattern to the search pattern

3539 # specified in the request by FileNameOffset and FileNameLength.

3540 if queryDirectoryRequest['Flags'] & smb2.SMB2_INDEX_SPECIFIED and \

3541 queryDirectoryRequest['FileNameLength'] > 0:

3542 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern

3543

3544 pathName = os.path.join(os.path.normpath(connData['OpenedFiles'][fileID]['FileName']), pattern)

3545 searchResult, searchCount, errorCode = findFirst2(os.path.dirname(pathName),

3546 os.path.basename(pathName),

3547 queryDirectoryRequest['FileInformationClass'],

3548 smb.ATTR_DIRECTORY, isSMB2=True)

3549

3550 if errorCode != STATUS_SUCCESS:

3551 return [smb2.SMB2Error()], None, errorCode

3552

3553 if searchCount > 2 and pattern == '*':

3554 # strip . and ..

3555 searchCount -= 2

3556 searchResult = searchResult[2:]

3557

3558 if searchCount == 0 and connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0:

3559 return [smb2.SMB2Error()], None, STATUS_NO_SUCH_FILE

3560

3561 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] < 0:

3562 return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES

3563

3564 totalData = 0

3565 respData = b''

3566 for nItem in range(connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'], searchCount):

3567 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] += 1

3568 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:

3569 # If single entry is requested we must clear the NextEntryOffset

3570 searchResult[nItem]['NextEntryOffset'] = 0

3571 data = searchResult[nItem].getData()

3572 lenData = len(data)

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

3574

3575 if (totalData + lenData) >= queryDirectoryRequest['OutputBufferLength']:

3576 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] -= 1

3577 break

3578 else:

3579 respData += data + b'\x00' * padLen

3580 totalData += lenData + padLen

3581

3582 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:

3583 break

3584

3585 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] >= searchCount:

3586 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = -1

3587

3588 respSMBCommand['OutputBufferOffset'] = 0x48

3589 respSMBCommand['OutputBufferLength'] = totalData

3590 respSMBCommand['Buffer'] = respData

3591

3592 smbServer.setConnectionData(connId, connData)

3593 return [respSMBCommand], None, errorCode

3594

3595 @staticmethod

3596 def smb2ChangeNotify(connId, smbServer, recvPacket):

3597

3598 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED

3599

3600 @staticmethod

3601 def smb2Echo(connId, smbServer, recvPacket):

3602

3603 respSMBCommand = smb2.SMB2Echo_Response()

3604

3605 return [respSMBCommand], None, STATUS_SUCCESS

3606

3607 @staticmethod

3608 def smb2TreeDisconnect(connId, smbServer, recvPacket):

3609 connData = smbServer.getConnectionData(connId)

3610

3611 respSMBCommand = smb2.SMB2TreeDisconnect_Response()

3612

3613 if recvPacket['TreeID'] in connData['ConnectedShares']:

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

3615 recvPacket['TreeID'], connData['ConnectedShares'][recvPacket['TreeID']]['shareName']))

3616 del (connData['ConnectedShares'][recvPacket['TreeID']])

3617 errorCode = STATUS_SUCCESS

3618 else:

3619 # STATUS_SMB_BAD_TID

3620 errorCode = STATUS_SMB_BAD_TID

3621

3622 smbServer.setConnectionData(connId, connData)

3623 return [respSMBCommand], None, errorCode

3624

3625 @staticmethod

3626 def smb2Logoff(connId, smbServer, recvPacket):

3627 connData = smbServer.getConnectionData(connId)

3628

3629 respSMBCommand = smb2.SMB2Logoff_Response()

3630

3631 if recvPacket['SessionID'] != connData['Uid']:

3632 # STATUS_SMB_BAD_UID

3633 errorCode = STATUS_SMB_BAD_UID

3634 else:

3635 errorCode = STATUS_SUCCESS

3636

3637 connData['Uid'] = 0

3638 connData['Authenticated'] = False

3639

3640 smbServer.setConnectionData(connId, connData)

3641 return [respSMBCommand], None, errorCode

3642

3643 @staticmethod

3644 def smb2Ioctl(connId, smbServer, recvPacket):

3645 connData = smbServer.getConnectionData(connId)

3646

3647 respSMBCommand = smb2.SMB2Ioctl_Response()

3648 ioctlRequest = smb2.SMB2Ioctl(recvPacket['Data'])

3649

3650 ioctls = smbServer.getIoctls()

3651 if ioctlRequest['CtlCode'] in ioctls:

3652 outputData, errorCode = ioctls[ioctlRequest['CtlCode']](connId, smbServer, ioctlRequest)

3653 if errorCode == STATUS_SUCCESS:

3654 respSMBCommand['CtlCode'] = ioctlRequest['CtlCode']

3655 respSMBCommand['FileID'] = ioctlRequest['FileID']

3656 respSMBCommand['InputOffset'] = 0

3657 respSMBCommand['InputCount'] = 0

3658 respSMBCommand['OutputOffset'] = 0x70

3659 respSMBCommand['OutputCount'] = len(outputData)

3660 respSMBCommand['Flags'] = 0

3661 respSMBCommand['Buffer'] = outputData

3662 else:

3663 respSMBCommand = outputData

3664 else:

3665 smbServer.log("Ioctl not implemented command: 0x%x" % ioctlRequest['CtlCode'], logging.DEBUG)

3666 errorCode = STATUS_INVALID_DEVICE_REQUEST

3667 respSMBCommand = smb2.SMB2Error()

3668

3669 smbServer.setConnectionData(connId, connData)

3670 return [respSMBCommand], None, errorCode

3671

3672 @staticmethod

3673 def smb2Lock(connId, smbServer, recvPacket):

3674 connData = smbServer.getConnectionData(connId)

3675

3676 respSMBCommand = smb2.SMB2Lock_Response()

3677

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

3679 errorCode = STATUS_SUCCESS

3680

3681 smbServer.setConnectionData(connId, connData)

3682 return [respSMBCommand], None, errorCode

3683

3684 @staticmethod

3685 def smb2Cancel(connId, smbServer, recvPacket):

3686 # I'm actually doing nothing

3687 return [smb2.SMB2Error()], None, STATUS_CANCELLED

3688

3689 @staticmethod

3690 def default(connId, smbServer, recvPacket):

3691 # By default we return an SMB Packet with error not implemented

3692 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'], logging.DEBUG)

3693 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED

3694

3695

3696 class Ioctls:

3697 @staticmethod

3698 def fsctlDfsGetReferrals(connId, smbServer, ioctlRequest):

3699 return smb2.SMB2Error(), STATUS_FS_DRIVER_REQUIRED

3700

3701 @staticmethod

3702 def fsctlPipeTransceive(connId, smbServer, ioctlRequest):

3703 connData = smbServer.getConnectionData(connId)

3704

3705 ioctlResponse = ''

3706

3707 if ioctlRequest['FileID'].getData() in connData['OpenedFiles']:

3708 fileHandle = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['FileHandle']

3709 errorCode = STATUS_SUCCESS

3710 try:

3711 if fileHandle != PIPE_FILE_DESCRIPTOR:

3712 errorCode = STATUS_INVALID_DEVICE_REQUEST

3713 else:

3714 sock = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['Socket']

3715 sock.sendall(ioctlRequest['Buffer'])

3716 ioctlResponse = sock.recv(ioctlRequest['MaxOutputResponse'])

3717 except Exception as e:

3718 smbServer.log('fsctlPipeTransceive: %s ' % e, logging.ERROR)

3719 errorCode = STATUS_ACCESS_DENIED

3720 else:

3721 errorCode = STATUS_INVALID_DEVICE_REQUEST

3722

3723 smbServer.setConnectionData(connId, connData)

3724 return ioctlResponse, errorCode

3725

3726 @staticmethod

3727 def fsctlValidateNegotiateInfo(connId, smbServer, ioctlRequest):

3728 connData = smbServer.getConnectionData(connId)

3729

3730 errorCode = STATUS_SUCCESS

3731

3732 validateNegotiateInfo = smb2.VALIDATE_NEGOTIATE_INFO(ioctlRequest['Buffer'])

3733 validateNegotiateInfoResponse = smb2.VALIDATE_NEGOTIATE_INFO_RESPONSE()

3734 validateNegotiateInfoResponse['Capabilities'] = 0

3735 validateNegotiateInfoResponse['Guid'] = b'A' * 16

3736 validateNegotiateInfoResponse['SecurityMode'] = 1

3737 validateNegotiateInfoResponse['Dialect'] = smb2.SMB2_DIALECT_002

3738

3739 smbServer.setConnectionData(connId, connData)

3740 return validateNegotiateInfoResponse.getData(), errorCode

3741

3742

3743 class SMBSERVERHandler(socketserver.BaseRequestHandler):

3744 def __init__(self, request, client_address, server, select_poll=False):

3745 self.__SMB = server

3746 # In case of AF_INET6 the client_address contains 4 items, ignore the last 2

3747 self.__ip, self.__port = client_address[:2]

3748 self.__request = request

3749 self.__connId = threading.currentThread().getName()

3750 self.__timeOut = 60 * 5

3751 self.__select_poll = select_poll

3752 # self.__connId = os.getpid()

3753 socketserver.BaseRequestHandler.__init__(self, request, client_address, server)

3754

3755 def handle(self):

3756 self.__SMB.log("Incoming connection (%s,%d)" % (self.__ip, self.__port))

3757 self.__SMB.addConnection(self.__connId, self.__ip, self.__port)

3758 while True:

3759 try:

3760 # First of all let's get the NETBIOS packet

3761 session = nmb.NetBIOSTCPSession(self.__SMB.getServerName(), 'HOST', self.__ip, sess_port=self.__port,

3762 sock=self.__request, select_poll=self.__select_poll)

3763 try:

3764 p = session.recv_packet(self.__timeOut)

3765 except nmb.NetBIOSTimeout:

3766 raise

3767 except nmb.NetBIOSError:

3768 break

3769

3770 if p.get_type() == nmb.NETBIOS_SESSION_REQUEST:

3771 # Someone is requesting a session, we're gonna accept them all :)

3772 _, rn, my = p.get_trailer().split(b' ')

3773 remote_name = nmb.decode_name(b'\x20' + rn)

3774 myname = nmb.decode_name(b'\x20' + my)

3775 self.__SMB.log(

3776 "NetBIOS Session request (%s,%s,%s)" % (self.__ip, remote_name[1].strip(), myname[1]))

3777 r = nmb.NetBIOSSessionPacket()

3778 r.set_type(nmb.NETBIOS_SESSION_POSITIVE_RESPONSE)

3779 r.set_trailer(p.get_trailer())

3780 self.__request.send(r.rawData())

3781 else:

3782 resp = self.__SMB.processRequest(self.__connId, p.get_trailer())

3783 # Send all the packets received. Except for big transactions this should be

3784 # a single packet

3785 for i in resp:

3786 if hasattr(i, 'getData'):

3787 session.send_packet(i.getData())

3788 else:

3789 session.send_packet(i)

3790 except Exception as e:

3791 self.__SMB.log("Handle: %s" % e)

3792 # import traceback

3793 # traceback.print_exc()

3794 break

3795

3796 def finish(self):

3797 # Thread/process is dying, we should tell the main SMB thread to remove all this thread data

3798 self.__SMB.log("Closing down connection (%s,%d)" % (self.__ip, self.__port))

3799 self.__SMB.removeConnection(self.__connId)

3800 return socketserver.BaseRequestHandler.finish(self)

3801

3802

3803 class SMBSERVER(socketserver.ThreadingMixIn, socketserver.TCPServer):

3804 # class SMBSERVER(socketserver.ForkingMixIn, socketserver.TCPServer):

3805 def __init__(self, server_address, handler_class=SMBSERVERHandler, config_parser=None):

3806 socketserver.TCPServer.allow_reuse_address = True

3807 socketserver.TCPServer.__init__(self, server_address, handler_class)

3808

3809 # Server name and OS to be presented whenever is necessary

3810 self.__serverName = ''

3811 self.__serverOS = ''

3812 self.__serverDomain = ''

3813 self.__challenge = ''

3814 self.__log = None

3815

3816 # Our ConfigParser data

3817 self.__serverConfig = config_parser

3818

3819 # Our credentials to be used during the server's lifetime

3820 self.__credentials = {}

3821

3822 # Our log file

3823 self.__logFile = ''

3824

3825 # Registered Named Pipes, format is PipeName,Socket

3826 self.__registeredNamedPipes = {}

3827

3828 # JTR dump path

3829 self.__jtr_dump_path = ''

3830

3831 # SMB2 Support flag = default not active

3832 self.__SMB2Support = False

3833

3834 # Our list of commands we will answer, by default the NOT IMPLEMENTED one

3835 self.__smbCommandsHandler = SMBCommands()

3836 self.__smbTrans2Handler = TRANS2Commands()

3837 self.__smbTransHandler = TRANSCommands()

3838 self.__smbNTTransHandler = NTTRANSCommands()

3839 self.__smb2CommandsHandler = SMB2Commands()

3840 self.__IoctlHandler = Ioctls()

3841

3842 self.__smbNTTransCommands = {

3843 # NT IOCTL, can't find doc for this

3844 0xff: self.__smbNTTransHandler.default

3845 }

3846

3847 self.__smbTransCommands = {

3848 '\\PIPE\\LANMAN': self.__smbTransHandler.lanMan,

3849 smb.SMB.TRANS_TRANSACT_NMPIPE: self.__smbTransHandler.transactNamedPipe,

3850 }

3851 self.__smbTrans2Commands = {

3852 smb.SMB.TRANS2_FIND_FIRST2: self.__smbTrans2Handler.findFirst2,

3853 smb.SMB.TRANS2_FIND_NEXT2: self.__smbTrans2Handler.findNext2,

3854 smb.SMB.TRANS2_QUERY_FS_INFORMATION: self.__smbTrans2Handler.queryFsInformation,

3855 smb.SMB.TRANS2_QUERY_PATH_INFORMATION: self.__smbTrans2Handler.queryPathInformation,

3856 smb.SMB.TRANS2_QUERY_FILE_INFORMATION: self.__smbTrans2Handler.queryFileInformation,

3857 smb.SMB.TRANS2_SET_FILE_INFORMATION: self.__smbTrans2Handler.setFileInformation,

3858 smb.SMB.TRANS2_SET_PATH_INFORMATION: self.__smbTrans2Handler.setPathInformation

3859 }

3860

3861 self.__smbCommands = {

3862 # smb.SMB.SMB_COM_FLUSH: self.__smbCommandsHandler.smbComFlush,

3863 smb.SMB.SMB_COM_CREATE_DIRECTORY: self.__smbCommandsHandler.smbComCreateDirectory,

3864 smb.SMB.SMB_COM_DELETE_DIRECTORY: self.__smbCommandsHandler.smbComDeleteDirectory,

3865 smb.SMB.SMB_COM_RENAME: self.__smbCommandsHandler.smbComRename,

3866 smb.SMB.SMB_COM_DELETE: self.__smbCommandsHandler.smbComDelete,

3867 smb.SMB.SMB_COM_NEGOTIATE: self.__smbCommandsHandler.smbComNegotiate,

3868 smb.SMB.SMB_COM_SESSION_SETUP_ANDX: self.__smbCommandsHandler.smbComSessionSetupAndX,

3869 smb.SMB.SMB_COM_LOGOFF_ANDX: self.__smbCommandsHandler.smbComLogOffAndX,

3870 smb.SMB.SMB_COM_TREE_CONNECT_ANDX: self.__smbCommandsHandler.smbComTreeConnectAndX,

3871 smb.SMB.SMB_COM_TREE_DISCONNECT: self.__smbCommandsHandler.smbComTreeDisconnect,

3872 smb.SMB.SMB_COM_ECHO: self.__smbCommandsHandler.smbComEcho,

3873 smb.SMB.SMB_COM_QUERY_INFORMATION: self.__smbCommandsHandler.smbQueryInformation,

3874 smb.SMB.SMB_COM_TRANSACTION2: self.__smbCommandsHandler.smbTransaction2,

3875 smb.SMB.SMB_COM_TRANSACTION: self.__smbCommandsHandler.smbTransaction,

3876 # Not needed for now

3877 smb.SMB.SMB_COM_NT_TRANSACT: self.__smbCommandsHandler.smbNTTransact,

3878 smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: self.__smbCommandsHandler.smbQueryInformationDisk,

3879 smb.SMB.SMB_COM_OPEN_ANDX: self.__smbCommandsHandler.smbComOpenAndX,

3880 smb.SMB.SMB_COM_QUERY_INFORMATION2: self.__smbCommandsHandler.smbComQueryInformation2,

3881 smb.SMB.SMB_COM_READ_ANDX: self.__smbCommandsHandler.smbComReadAndX,

3882 smb.SMB.SMB_COM_READ: self.__smbCommandsHandler.smbComRead,

3883 smb.SMB.SMB_COM_WRITE_ANDX: self.__smbCommandsHandler.smbComWriteAndX,

3884 smb.SMB.SMB_COM_WRITE: self.__smbCommandsHandler.smbComWrite,

3885 smb.SMB.SMB_COM_CLOSE: self.__smbCommandsHandler.smbComClose,

3886 smb.SMB.SMB_COM_LOCKING_ANDX: self.__smbCommandsHandler.smbComLockingAndX,

3887 smb.SMB.SMB_COM_NT_CREATE_ANDX: self.__smbCommandsHandler.smbComNtCreateAndX,

3888 0xFF: self.__smbCommandsHandler.default

3889 }

3890

3891 self.__smb2Ioctls = {

3892 smb2.FSCTL_DFS_GET_REFERRALS: self.__IoctlHandler.fsctlDfsGetReferrals,

3893 # smb2.FSCTL_PIPE_PEEK: self.__IoctlHandler.fsctlPipePeek,

3894 # smb2.FSCTL_PIPE_WAIT: self.__IoctlHandler.fsctlPipeWait,

3895 smb2.FSCTL_PIPE_TRANSCEIVE: self.__IoctlHandler.fsctlPipeTransceive,

3896 # smb2.FSCTL_SRV_COPYCHUNK: self.__IoctlHandler.fsctlSrvCopyChunk,

3897 # smb2.FSCTL_SRV_ENUMERATE_SNAPSHOTS: self.__IoctlHandler.fsctlSrvEnumerateSnapshots,

3898 # smb2.FSCTL_SRV_REQUEST_RESUME_KEY: self.__IoctlHandler.fsctlSrvRequestResumeKey,

3899 # smb2.FSCTL_SRV_READ_HASH: self.__IoctlHandler.fsctlSrvReadHash,

3900 # smb2.FSCTL_SRV_COPYCHUNK_WRITE: self.__IoctlHandler.fsctlSrvCopyChunkWrite,

3901 # smb2.FSCTL_LMR_REQUEST_RESILIENCY: self.__IoctlHandler.fsctlLmrRequestResiliency,

3902 # smb2.FSCTL_QUERY_NETWORK_INTERFACE_INFO: self.__IoctlHandler.fsctlQueryNetworkInterfaceInfo,

3903 # smb2.FSCTL_SET_REPARSE_POINT: self.__IoctlHandler.fsctlSetReparsePoint,

3904 # smb2.FSCTL_DFS_GET_REFERRALS_EX: self.__IoctlHandler.fsctlDfsGetReferralsEx,

3905 # smb2.FSCTL_FILE_LEVEL_TRIM: self.__IoctlHandler.fsctlFileLevelTrim,

3906 smb2.FSCTL_VALIDATE_NEGOTIATE_INFO: self.__IoctlHandler.fsctlValidateNegotiateInfo,

3907 }

3908

3909 self.__smb2Commands = {

3910 smb2.SMB2_NEGOTIATE: self.__smb2CommandsHandler.smb2Negotiate,

3911 smb2.SMB2_SESSION_SETUP: self.__smb2CommandsHandler.smb2SessionSetup,

3912 smb2.SMB2_LOGOFF: self.__smb2CommandsHandler.smb2Logoff,

3913 smb2.SMB2_TREE_CONNECT: self.__smb2CommandsHandler.smb2TreeConnect,

3914 smb2.SMB2_TREE_DISCONNECT: self.__smb2CommandsHandler.smb2TreeDisconnect,

3915 smb2.SMB2_CREATE: self.__smb2CommandsHandler.smb2Create,

3916 smb2.SMB2_CLOSE: self.__smb2CommandsHandler.smb2Close,

3917 smb2.SMB2_FLUSH: self.__smb2CommandsHandler.smb2Flush,

3918 smb2.SMB2_READ: self.__smb2CommandsHandler.smb2Read,

3919 smb2.SMB2_WRITE: self.__smb2CommandsHandler.smb2Write,

3920 smb2.SMB2_LOCK: self.__smb2CommandsHandler.smb2Lock,

3921 smb2.SMB2_IOCTL: self.__smb2CommandsHandler.smb2Ioctl,

3922 smb2.SMB2_CANCEL: self.__smb2CommandsHandler.smb2Cancel,

3923 smb2.SMB2_ECHO: self.__smb2CommandsHandler.smb2Echo,

3924 smb2.SMB2_QUERY_DIRECTORY: self.__smb2CommandsHandler.smb2QueryDirectory,

3925 smb2.SMB2_CHANGE_NOTIFY: self.__smb2CommandsHandler.smb2ChangeNotify,

3926 smb2.SMB2_QUERY_INFO: self.__smb2CommandsHandler.smb2QueryInfo,

3927 smb2.SMB2_SET_INFO: self.__smb2CommandsHandler.smb2SetInfo,

3928 # smb2.SMB2_OPLOCK_BREAK: self.__smb2CommandsHandler.smb2SessionSetup,

3929 0xFF: self.__smb2CommandsHandler.default

3930 }

3931

3932 # List of active connections

3933 self.__activeConnections = {}

3934

3935 def getIoctls(self):

3936 return self.__smb2Ioctls

3937

3938 def getCredentials(self):

3939 return self.__credentials

3940

3941 def removeConnection(self, name):

3942 try:

3943 del (self.__activeConnections[name])

3944 except:

3945 pass

3946 self.log("Remaining connections %s" % list(self.__activeConnections.keys()))

3947

3948 def addConnection(self, name, ip, port):

3949 self.__activeConnections[name] = {}

3950 # Let's init with some know stuff we will need to have

3951 # TODO: Document what's in there

3952 # print "Current Connections", self.__activeConnections.keys()

3953 self.__activeConnections[name]['PacketNum'] = 0

3954 self.__activeConnections[name]['ClientIP'] = ip

3955 self.__activeConnections[name]['ClientPort'] = port

3956 self.__activeConnections[name]['Uid'] = 0

3957 self.__activeConnections[name]['ConnectedShares'] = {}

3958 self.__activeConnections[name]['OpenedFiles'] = {}

3959 # SID results for findfirst2

3960 self.__activeConnections[name]['SIDs'] = {}

3961 self.__activeConnections[name]['LastRequest'] = {}

3962 self.__activeConnections[name]['SignatureEnabled'] = False

3963 self.__activeConnections[name]['SigningChallengeResponse'] = ''

3964 self.__activeConnections[name]['SigningSessionKey'] = b''

3965 self.__activeConnections[name]['Authenticated'] = False

3966

3967 def getActiveConnections(self):

3968 return self.__activeConnections

3969

3970 def setConnectionData(self, connId, data):

3971 self.__activeConnections[connId] = data

3972 # print "setConnectionData"

3973 # print self.__activeConnections

3974

3975 def getConnectionData(self, connId, checkStatus=True):

3976 conn = self.__activeConnections[connId]

3977 if checkStatus is True:

3978 if ('Authenticated' in conn) is not True:

3979 # Can't keep going further

3980 raise Exception("User not Authenticated!")

3981 return conn

3982

3983 def getRegisteredNamedPipes(self):

3984 return self.__registeredNamedPipes

3985

3986 def registerNamedPipe(self, pipeName, address):

3987 self.__registeredNamedPipes[str(pipeName)] = address

3988 return True

3989

3990 def unregisterNamedPipe(self, pipeName):

3991 if pipeName in self.__registeredNamedPipes:

3992 del (self.__registeredNamedPipes[str(pipeName)])

3993 return True

3994 return False

3995

3996 def unregisterTransaction(self, transCommand):

3997 if transCommand in self.__smbTransCommands:

3998 del (self.__smbTransCommands[transCommand])

3999

4000 def hookTransaction(self, transCommand, callback):

4001 # If you call this function, callback will replace

4002 # the current Transaction sub command.

4003 # (don't get confused with the Transaction smbCommand)

4004 # If the transaction sub command doesn't not exist, it is added

4005 # If the transaction sub command exists, it returns the original function # replaced

4006 #

4007 # callback MUST be declared as:

4008 # callback(connId, smbServer, recvPacket, parameters, data, maxDataCount=0)

4009 #

4010 # WHERE:

4011 #

4012 # connId : the connection Id, used to grab/update information about

4013 # the current connection

4014 # smbServer : the SMBServer instance available for you to ask

4015 # configuration data

4016 # recvPacket : the full SMBPacket that triggered this command

4017 # parameters : the transaction parameters

4018 # data : the transaction data

4019 # maxDataCount: the max amount of data that can be transferred agreed

4020 # with the client

4021 #

4022 # and MUST return:

4023 # respSetup, respParameters, respData, errorCode

4024 #

4025 # WHERE:

4026 #

4027 # respSetup: the setup response of the transaction

4028 # respParameters: the parameters response of the transaction

4029 # respData: the data response of the transaction

4030 # errorCode: the NT error code

4031

4032 if transCommand in self.__smbTransCommands:

4033 originalCommand = self.__smbTransCommands[transCommand]

4034 else:

4035 originalCommand = None

4036

4037 self.__smbTransCommands[transCommand] = callback

4038 return originalCommand

4039

4040 def unregisterTransaction2(self, transCommand):

4041 if transCommand in self.__smbTrans2Commands:

4042 del (self.__smbTrans2Commands[transCommand])

4043

4044 def hookTransaction2(self, transCommand, callback):

4045 # Here we should add to __smbTrans2Commands

4046 # Same description as Transaction

4047 if transCommand in self.__smbTrans2Commands:

4048 originalCommand = self.__smbTrans2Commands[transCommand]

4049 else:

4050 originalCommand = None

4051

4052 self.__smbTrans2Commands[transCommand] = callback

4053 return originalCommand

4054

4055 def unregisterNTTransaction(self, transCommand):

4056 if transCommand in self.__smbNTTransCommands:

4057 del (self.__smbNTTransCommands[transCommand])

4058

4059 def hookNTTransaction(self, transCommand, callback):

4060 # Here we should add to __smbNTTransCommands

4061 # Same description as Transaction

4062 if transCommand in self.__smbNTTransCommands:

4063 originalCommand = self.__smbNTTransCommands[transCommand]

4064 else:

4065 originalCommand = None

4066

4067 self.__smbNTTransCommands[transCommand] = callback

4068 return originalCommand

4069

4070 def unregisterSmbCommand(self, smbCommand):

4071 if smbCommand in self.__smbCommands:

4072 del (self.__smbCommands[smbCommand])

4073

4074 def hookSmbCommand(self, smbCommand, callback):

4075 # Here we should add to self.__smbCommands

4076 # If you call this function, callback will replace

4077 # the current smbCommand.

4078 # If smbCommand doesn't not exist, it is added

4079 # If SMB command exists, it returns the original function replaced

4080 #

4081 # callback MUST be declared as:

4082 # callback(connId, smbServer, SMBCommand, recvPacket)

4083 #

4084 # WHERE:

4085 #

4086 # connId : the connection Id, used to grab/update information about

4087 # the current connection

4088 # smbServer : the SMBServer instance available for you to ask

4089 # configuration data

4090 # SMBCommand: the SMBCommand itself, with its data and parameters.

4091 # Check smb.py:SMBCommand() for a reference

4092 # recvPacket: the full SMBPacket that triggered this command

4093 #

4094 # and MUST return:

4095 # <list of respSMBCommands>, <list of packets>, errorCode

4096 # <list of packets> has higher preference over commands, in case you

4097 # want to change the whole packet

4098 # errorCode: the NT error code

4099 #

4100 # For SMB_COM_TRANSACTION2, SMB_COM_TRANSACTION and SMB_COM_NT_TRANSACT

4101 # the callback function is slightly different:

4102 #

4103 # callback(connId, smbServer, SMBCommand, recvPacket, transCommands)

4104 #

4105 # WHERE:

4106 #

4107 # transCommands: a list of transaction subcommands already registered

4108 #

4109

4110 if smbCommand in self.__smbCommands:

4111 originalCommand = self.__smbCommands[smbCommand]

4112 else:

4113 originalCommand = None

4114

4115 self.__smbCommands[smbCommand] = callback

4116 return originalCommand

4117

4118 def unregisterSmb2Command(self, smb2Command):

4119 if smb2Command in self.__smb2Commands:

4120 del (self.__smb2Commands[smb2Command])

4121

4122 def hookSmb2Command(self, smb2Command, callback):

4123 if smb2Command in self.__smb2Commands:

4124 originalCommand = self.__smb2Commands[smb2Command]

4125 else:

4126 originalCommand = None

4127

4128 self.__smb2Commands[smb2Command] = callback

4129 return originalCommand

4130

4131 def log(self, msg, level=logging.INFO):

4132 self.__log.log(level, msg)

4133

4134 def getServerName(self):

4135 return self.__serverName

4136

4137 def getServerOS(self):

4138 return self.__serverOS

4139

4140 def getServerDomain(self):

4141 return self.__serverDomain

4142

4143 def getSMBChallenge(self):

4144 return self.__challenge

4145

4146 def getServerConfig(self):

4147 return self.__serverConfig

4148

4149 def setServerConfig(self, config):

4150 self.__serverConfig = config

4151

4152 def getJTRdumpPath(self):

4153 return self.__jtr_dump_path

4154

4155 def verify_request(self, request, client_address):

4156 # TODO: Control here the max amount of processes we want to launch

4157 # returning False, closes the connection

4158 return True

4159

4160 def signSMBv1(self, connData, packet, signingSessionKey, signingChallengeResponse):

4161 # This logic MUST be applied for messages sent in response to any of the higher-layer actions and in

4162 # compliance with the message sequencing rules.

4163 # * The client or server that sends the message MUST provide the 32-bit sequence number for this

4164 # message, as specified in sections 3.2.4.1 and 3.3.4.1.

4165 # * The SMB_FLAGS2_SMB_SECURITY_SIGNATURE flag in the header MUST be set.

4166 # * To generate the signature, a 32-bit sequence number is copied into the

4167 # least significant 32 bits of the SecuritySignature field and the remaining

4168 # 4 bytes are set to 0x00.

4169 # * The MD5 algorithm, as specified in [RFC1321], MUST be used to generate a hash of the SMB

4170 # message from the start of the SMB Header, which is defined as follows.

4171 # CALL MD5Init( md5context )

4172 # CALL MD5Update( md5context, Connection.SigningSessionKey )

4173 # CALL MD5Update( md5context, Connection.SigningChallengeResponse )

4174 # CALL MD5Update( md5context, SMB message )

4175 # CALL MD5Final( digest, md5context )

4176 # SET signature TO the first 8 bytes of the digest

4177 # The resulting 8-byte signature MUST be copied into the SecuritySignature field of the SMB Header,

4178 # after which the message can be transmitted.

4179

4180 # print "seq(%d) signingSessionKey %r, signingChallengeResponse %r" % (connData['SignSequenceNumber'], signingSessionKey, signingChallengeResponse)

4181 packet['SecurityFeatures'] = struct.pack('<q', connData['SignSequenceNumber'])

4182 # Sign with the sequence

4183 m = hashlib.md5()

4184 m.update(signingSessionKey)

4185 m.update(signingChallengeResponse)

4186 if hasattr(packet, 'getData'):

4187 m.update(packet.getData())

4188 else:

4189 m.update(packet)

4190 # Replace sequence with acual hash

4191 packet['SecurityFeatures'] = m.digest()[:8]

4192 connData['SignSequenceNumber'] += 2

4193

4194 def signSMBv2(self, packet, signingSessionKey):

4195 packet['Signature'] = b'\x00' * 16

4196 packet['Flags'] |= smb2.SMB2_FLAGS_SIGNED

4197 signature = hmac.new(signingSessionKey, packet.getData(), hashlib.sha256).digest()

4198 packet['Signature'] = signature[:16]

4199 # print "%s" % packet['Signature'].encode('hex')

4200

4201 def processRequest(self, connId, data):

4202

4203 # TODO: Process batched commands.

4204 isSMB2 = False

4205 SMBCommand = None

4206 try:

4207 packet = smb.NewSMBPacket(data=data)

4208 SMBCommand = smb.SMBCommand(packet['Data'][0])

4209 except:

4210 # Maybe a SMB2 packet?

4211 packet = smb2.SMB2Packet(data=data)

4212 connData = self.getConnectionData(connId, False)

4213 self.signSMBv2(packet, connData['SigningSessionKey'])

4214 isSMB2 = True

4215

4216 connData = self.getConnectionData(connId, False)

4217

4218 # We might have compound requests

4219 compoundedPacketsResponse = []

4220 compoundedPackets = []

4221 try:

4222 # Search out list of implemented commands

4223 # We provide them with:

4224 # connId : representing the data for this specific connection

4225 # self : the SMBSERVER if they want to ask data to it

4226 # SMBCommand : the SMBCommand they are expecting to process

4227 # packet : the received packet itself, in case they need more data than the actual command

4228 # Only for Transactions

4229 # transCommand: a list of transaction subcommands

4230 # We expect to get:

4231 # respCommands: a list of answers for the commands processed

4232 # respPacket : if the commands chose to directly craft packet/s, we use this and not the previous

4233 # this MUST be a list

4234 # errorCode : self explanatory

4235 if isSMB2 is False:

4236 # Is the client authenticated already?

4237 if connData['Authenticated'] is False and packet['Command'] not in (

4238 smb.SMB.SMB_COM_NEGOTIATE, smb.SMB.SMB_COM_SESSION_SETUP_ANDX):

4239 # Nope.. in that case he should only ask for a few commands, if not throw him out.

4240 errorCode = STATUS_ACCESS_DENIED

4241 respPackets = None

4242 respCommands = [smb.SMBCommand(packet['Command'])]

4243 else:

4244 if packet['Command'] == smb.SMB.SMB_COM_TRANSACTION2:

4245 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4246 connId,

4247 self,

4248 SMBCommand,

4249 packet,

4250 self.__smbTrans2Commands)

4251 elif packet['Command'] == smb.SMB.SMB_COM_NT_TRANSACT:

4252 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4253 connId,

4254 self,

4255 SMBCommand,

4256 packet,

4257 self.__smbNTTransCommands)

4258 elif packet['Command'] == smb.SMB.SMB_COM_TRANSACTION:

4259 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4260 connId,

4261 self,

4262 SMBCommand,

4263 packet,

4264 self.__smbTransCommands)

4265 else:

4266 if packet['Command'] in self.__smbCommands:

4267 if self.__SMB2Support is True:

4268 if packet['Command'] == smb.SMB.SMB_COM_NEGOTIATE:

4269 try:

4270 respCommands, respPackets, errorCode = self.__smb2Commands[smb2.SMB2_NEGOTIATE](

4271 connId, self, packet, True)

4272 isSMB2 = True

4273 except Exception as e:

4274 import traceback

4275 traceback.print_exc()

4276 self.log('SMB2_NEGOTIATE: %s' % e, logging.ERROR)

4277 # If something went wrong, let's fallback to SMB1

4278 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4279 connId,

4280 self,

4281 SMBCommand,

4282 packet)

4283 # self.__SMB2Support = False

4284 pass

4285 else:

4286 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4287 connId,

4288 self,

4289 SMBCommand,

4290 packet)

4291 else:

4292 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](

4293 connId,

4294 self,

4295 SMBCommand,

4296 packet)

4297 else:

4298 respCommands, respPackets, errorCode = self.__smbCommands[255](connId, self, SMBCommand,

4299 packet)

4300

4301 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))

4302 compoundedPackets.append(packet)

4303

4304 else:

4305 # Is the client authenticated already?

4306 if connData['Authenticated'] is False and packet['Command'] not in (

4307 smb2.SMB2_NEGOTIATE, smb2.SMB2_SESSION_SETUP):

4308 # Nope.. in that case he should only ask for a few commands, if not throw him out.

4309 errorCode = STATUS_ACCESS_DENIED

4310 respPackets = None

4311 respCommands = ['']

4312 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))

4313 compoundedPackets.append(packet)

4314 else:

4315 done = False

4316 while not done:

4317 if packet['Command'] in self.__smb2Commands:

4318 if self.__SMB2Support is True:

4319 respCommands, respPackets, errorCode = self.__smb2Commands[packet['Command']](

4320 connId,

4321 self,

4322 packet)

4323 else:

4324 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)

4325 else:

4326 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)

4327 # Let's store the result for this compounded packet

4328 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))

4329 compoundedPackets.append(packet)

4330 if packet['NextCommand'] != 0:

4331 data = data[packet['NextCommand']:]

4332 packet = smb2.SMB2Packet(data=data)

4333 else:

4334 done = True

4335

4336 except Exception as e:

4337 # import traceback

4338 # traceback.print_exc()

4339 # Something wen't wrong, defaulting to Bad user ID

4340 self.log('processRequest (0x%x,%s)' % (packet['Command'], e), logging.ERROR)

4341 raise

4342

4343 # We prepare the response packet to commands don't need to bother about that.

4344 connData = self.getConnectionData(connId, False)

4345

4346 # Force reconnection loop.. This is just a test.. client will send me back credentials :)

4347 # connData['PacketNum'] += 1

4348 # if connData['PacketNum'] == 15:

4349 # connData['PacketNum'] = 0

4350 # # Something wen't wrong, defaulting to Bad user ID

4351 # self.log('Sending BAD USER ID!', logging.ERROR)

4352 # #raise

4353 # packet['Flags1'] |= smb.SMB.FLAGS1_REPLY

4354 # packet['Flags2'] = 0

4355 # errorCode = STATUS_SMB_BAD_UID

4356 # packet['ErrorCode'] = errorCode >> 16

4357 # packet['ErrorClass'] = errorCode & 0xff

4358 # return [packet]

4359

4360 self.setConnectionData(connId, connData)

4361

4362 packetsToSend = []

4363 for packetNum in range(len(compoundedPacketsResponse)):

4364 respCommands, respPackets, errorCode = compoundedPacketsResponse[packetNum]

4365 packet = compoundedPackets[packetNum]

4366 if respPackets is None:

4367 for respCommand in respCommands:

4368 if isSMB2 is False:

4369 respPacket = smb.NewSMBPacket()

4370 respPacket['Flags1'] = smb.SMB.FLAGS1_REPLY

4371

4372 # TODO this should come from a per session configuration

4373 respPacket[

4374 'Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \

4375 packet['Flags2'] & smb.SMB.FLAGS2_UNICODE

4376 # respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES

4377 # respPacket['Flags1'] = 0x98

4378 # respPacket['Flags2'] = 0xc807

4379

4380 respPacket['Tid'] = packet['Tid']

4381 respPacket['Mid'] = packet['Mid']

4382 respPacket['Pid'] = packet['Pid']

4383 respPacket['Uid'] = connData['Uid']

4384

4385 respPacket['ErrorCode'] = errorCode >> 16

4386 respPacket['_reserved'] = errorCode >> 8 & 0xff

4387 respPacket['ErrorClass'] = errorCode & 0xff

4388 respPacket.addCommand(respCommand)

4389

4390 if connData['SignatureEnabled']:

4391 respPacket['Flags2'] |= smb.SMB.FLAGS2_SMB_SECURITY_SIGNATURE

4392 self.signSMBv1(connData, respPacket, connData['SigningSessionKey'],

4393 connData['SigningChallengeResponse'])

4394

4395 packetsToSend.append(respPacket)

4396 else:

4397 respPacket = smb2.SMB2Packet()

4398 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR

4399 if packetNum > 0:

4400 respPacket['Flags'] |= smb2.SMB2_FLAGS_RELATED_OPERATIONS

4401 respPacket['Status'] = errorCode

4402 respPacket['CreditRequestResponse'] = packet['CreditRequestResponse']

4403 respPacket['Command'] = packet['Command']

4404 respPacket['CreditCharge'] = packet['CreditCharge']

4405 # respPacket['CreditCharge'] = 0

4406 respPacket['Reserved'] = packet['Reserved']

4407 respPacket['SessionID'] = connData['Uid']

4408 respPacket['MessageID'] = packet['MessageID']

4409 respPacket['TreeID'] = packet['TreeID']

4410 if hasattr(respCommand, 'getData'):

4411 respPacket['Data'] = respCommand.getData()

4412 else:

4413 respPacket['Data'] = str(respCommand)

4414

4415 if connData['SignatureEnabled']:

4416 self.signSMBv2(respPacket, connData['SigningSessionKey'])

4417

4418 packetsToSend.append(respPacket)

4419 else:

4420 # The SMBCommand took care of building the packet

4421 packetsToSend = respPackets

4422

4423 if isSMB2 is True:

4424 # Let's build a compound answer

4425 finalData = b''

4426 i = 0

4427 for i in range(len(packetsToSend) - 1):

4428 packet = packetsToSend[i]

4429 # Align to 8-bytes

4430 padLen = (8 - (len(packet) % 8)) % 8

4431 packet['NextCommand'] = len(packet) + padLen

4432 if hasattr(packet, 'getData'):

4433 finalData += packet.getData() + padLen * b'\x00'

4434 else:

4435 finalData += packet + padLen * b'\x00'

4436

4437 # Last one

4438 if hasattr(packetsToSend[len(packetsToSend) - 1], 'getData'):

4439 finalData += packetsToSend[len(packetsToSend) - 1].getData()

4440 else:

4441 finalData += packetsToSend[len(packetsToSend) - 1]

4442 packetsToSend = [finalData]

4443

4444 # We clear the compound requests

4445 connData['LastRequest'] = {}

4446

4447 return packetsToSend

4448

4449 def processConfigFile(self, configFile=None):

4450 # TODO: Do a real config parser

4451 if self.__serverConfig is None:

4452 if configFile is None:

4453 configFile = 'smb.conf'

4454 self.__serverConfig = configparser.ConfigParser()

4455 self.__serverConfig.read(configFile)

4456

4457 self.__serverName = self.__serverConfig.get('global', 'server_name')

4458 self.__serverOS = self.__serverConfig.get('global', 'server_os')

4459 self.__serverDomain = self.__serverConfig.get('global', 'server_domain')

4460 self.__logFile = self.__serverConfig.get('global', 'log_file')

4461 if self.__serverConfig.has_option('global', 'challenge'):

4462 self.__challenge = unhexlify(self.__serverConfig.get('global', 'challenge'))

4463 else:

4464 self.__challenge = b'A' * 16

4465

4466 if self.__serverConfig.has_option("global", "jtr_dump_path"):

4467 self.__jtr_dump_path = self.__serverConfig.get("global", "jtr_dump_path")

4468

4469 if self.__serverConfig.has_option("global", "SMB2Support"):

4470 self.__SMB2Support = self.__serverConfig.getboolean("global", "SMB2Support")

4471 else:

4472 self.__SMB2Support = False

4473

4474 if self.__logFile != 'None':

4475 logging.basicConfig(filename=self.__logFile,

4476 level=logging.DEBUG,

4477 format="%(asctime)s: %(levelname)s: %(message)s",

4478 datefmt='%m/%d/%Y %I:%M:%S %p')

4479 self.__log = LOG

4480

4481 # Process the credentials

4482 credentials_fname = self.__serverConfig.get('global', 'credentials_file')

4483 if credentials_fname != "":

4484 cred = open(credentials_fname)

4485 line = cred.readline()

4486 while line:

4487 name, uid, lmhash, nthash = line.split(':')

4488 self.__credentials[name.lower()] = (uid, lmhash, nthash.strip('\r\n'))

4489 line = cred.readline()

4490 cred.close()

4491 self.log('Config file parsed')

4492

4493 def addCredential(self, name, uid, lmhash, nthash):

4494 # If we have hashes, normalize them

4495 if lmhash != '' or nthash != '':

4496 if len(lmhash) % 2:

4497 lmhash = '0%s' % lmhash

4498 if len(nthash) % 2:

4499 nthash = '0%s' % nthash

4500 try: # just in case they were converted already

4501 lmhash = a2b_hex(lmhash)

4502 nthash = a2b_hex(nthash)

4503 except:

4504 pass

4505 self.__credentials[name.lower()] = (uid, lmhash, nthash)

4506

4507

4508# For windows platforms, opening a directory is not an option, so we set a void FD

4509 VOID_FILE_DESCRIPTOR = -1

4510 PIPE_FILE_DESCRIPTOR = -2

4511

4512######################################################################

4513# HELPER CLASSES

4514######################################################################

4515

4516 from impacket.dcerpc.v5.rpcrt import DCERPCServer

4517 from impacket.dcerpc.v5.dtypes import NULL

4518 from impacket.dcerpc.v5.srvs import NetrShareEnum, NetrShareEnumResponse, SHARE_INFO_1, NetrServerGetInfo, \

4519 NetrServerGetInfoResponse, NetrShareGetInfo, NetrShareGetInfoResponse

4520 from impacket.dcerpc.v5.wkst import NetrWkstaGetInfo, NetrWkstaGetInfoResponse

4521 from impacket.system_errors import ERROR_INVALID_LEVEL

4522

4523

4524 class WKSTServer(DCERPCServer):

4525 def __init__(self):

4526 DCERPCServer.__init__(self)

4527 self.wkssvcCallBacks = {

4528 0: self.NetrWkstaGetInfo,

4529 }

4530 self.addCallbacks(('6BFFD098-A112-3610-9833-46C3F87E345A', '1.0'), '\\PIPE\\wkssvc', self.wkssvcCallBacks)

4531

4532 def NetrWkstaGetInfo(self, data):

4533 request = NetrWkstaGetInfo(data)

4534 self.log("NetrWkstaGetInfo Level: %d" % request['Level'])

4535

4536 answer = NetrWkstaGetInfoResponse()

4537

4538 if request['Level'] not in (100, 101):

4539 answer['ErrorCode'] = ERROR_INVALID_LEVEL

4540 return answer

4541

4542 answer['WkstaInfo']['tag'] = request['Level']

4543

4544 if request['Level'] == 100:

4545 # Windows. Decimal value 500.

4546 answer['WkstaInfo']['WkstaInfo100']['wki100_platform_id'] = 0x000001F4

4547 answer['WkstaInfo']['WkstaInfo100']['wki100_computername'] = NULL

4548 answer['WkstaInfo']['WkstaInfo100']['wki100_langroup'] = NULL

4549 answer['WkstaInfo']['WkstaInfo100']['wki100_ver_major'] = 5

4550 answer['WkstaInfo']['WkstaInfo100']['wki100_ver_minor'] = 0

4551 else:

4552 # Windows. Decimal value 500.

4553 answer['WkstaInfo']['WkstaInfo101']['wki101_platform_id'] = 0x000001F4

4554 answer['WkstaInfo']['WkstaInfo101']['wki101_computername'] = NULL

4555 answer['WkstaInfo']['WkstaInfo101']['wki101_langroup'] = NULL

4556 answer['WkstaInfo']['WkstaInfo101']['wki101_ver_major'] = 5

4557 answer['WkstaInfo']['WkstaInfo101']['wki101_ver_minor'] = 0

4558 answer['WkstaInfo']['WkstaInfo101']['wki101_lanroot'] = NULL

4559

4560 return answer

4561

4562

4563 class SRVSServer(DCERPCServer):

4564 def __init__(self):

4565 DCERPCServer.__init__(self)

4566

4567 self._shares = {}

4568 self.__serverConfig = None

4569 self.__logFile = None

4570

4571 self.srvsvcCallBacks = {

4572 15: self.NetrShareEnum,

4573 16: self.NetrShareGetInfo,

4574 21: self.NetrServerGetInfo,

4575 }

4576

4577 self.addCallbacks(('4B324FC8-1670-01D3-1278-5A47BF6EE188', '3.0'), '\\PIPE\\srvsvc', self.srvsvcCallBacks)

4578

4579 def setServerConfig(self, config):

4580 self.__serverConfig = config

4581

4582 def processConfigFile(self, configFile=None):

4583 if configFile is not None:

4584 self.__serverConfig = configparser.ConfigParser()

4585 self.__serverConfig.read(configFile)

4586 sections = self.__serverConfig.sections()

4587 # Let's check the log file

4588 self.__logFile = self.__serverConfig.get('global', 'log_file')

4589 if self.__logFile != 'None':

4590 logging.basicConfig(filename=self.__logFile,

4591 level=logging.DEBUG,

4592 format="%(asctime)s: %(levelname)s: %(message)s",

4593 datefmt='%m/%d/%Y %I:%M:%S %p')

4594

4595 # Remove the global one

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

4597 self._shares = {}

4598 for i in sections:

4599 self._shares[i] = dict(self.__serverConfig.items(i))

4600

4601 def NetrShareGetInfo(self, data):

4602 request = NetrShareGetInfo(data)

4603 self.log("NetrGetShareInfo Level: %d" % request['Level'])

4604

4605 s = request['NetName'][:-1].upper()

4606 answer = NetrShareGetInfoResponse()

4607 if s in self._shares:

4608 share = self._shares[s]

4609

4610 answer['InfoStruct']['tag'] = 1

4611 answer['InfoStruct']['ShareInfo1']['shi1_netname'] = s + '\x00'

4612 answer['InfoStruct']['ShareInfo1']['shi1_type'] = share['share type']

4613 answer['InfoStruct']['ShareInfo1']['shi1_remark'] = share['comment'] + '\x00'

4614 answer['ErrorCode'] = 0

4615 else:

4616 answer['InfoStruct']['tag'] = 1

4617 answer['InfoStruct']['ShareInfo1'] = NULL

4618 answer['ErrorCode'] = 0x0906 # WERR_NET_NAME_NOT_FOUND

4619

4620 return answer

4621

4622 def NetrServerGetInfo(self, data):

4623 request = NetrServerGetInfo(data)

4624 self.log("NetrServerGetInfo Level: %d" % request['Level'])

4625 answer = NetrServerGetInfoResponse()

4626 answer['InfoStruct']['tag'] = 101

4627 # PLATFORM_ID_NT = 500

4628 answer['InfoStruct']['ServerInfo101']['sv101_platform_id'] = 500

4629 answer['InfoStruct']['ServerInfo101']['sv101_name'] = request['ServerName']

4630 # Windows 7 = 6.1

4631 answer['InfoStruct']['ServerInfo101']['sv101_version_major'] = 6

4632 answer['InfoStruct']['ServerInfo101']['sv101_version_minor'] = 1

4633 # Workstation = 1

4634 answer['InfoStruct']['ServerInfo101']['sv101_type'] = 1

4635 answer['InfoStruct']['ServerInfo101']['sv101_comment'] = NULL

4636 answer['ErrorCode'] = 0

4637 return answer

4638

4639 def NetrShareEnum(self, data):

4640 request = NetrShareEnum(data)

4641 self.log("NetrShareEnum Level: %d" % request['InfoStruct']['Level'])

4642 shareEnum = NetrShareEnumResponse()

4643 shareEnum['InfoStruct']['Level'] = 1

4644 shareEnum['InfoStruct']['ShareInfo']['tag'] = 1

4645 shareEnum['TotalEntries'] = len(self._shares)

4646 shareEnum['InfoStruct']['ShareInfo']['Level1']['EntriesRead'] = len(self._shares)

4647 shareEnum['ErrorCode'] = 0

4648

4649 for i in self._shares:

4650 shareInfo = SHARE_INFO_1()

4651 shareInfo['shi1_netname'] = i + '\x00'

4652 shareInfo['shi1_type'] = self._shares[i]['share type']

4653 shareInfo['shi1_remark'] = self._shares[i]['comment'] + '\x00'

4654 shareEnum['InfoStruct']['ShareInfo']['Level1']['Buffer'].append(shareInfo)

4655

4656 return shareEnum

4657

4658

4659 class SimpleSMBServer:

4660 """

4661 SimpleSMBServer class - Implements a simple, customizable SMB Server

4662

4663 :param string listenAddress: the address you want the server to listen on

4664 :param integer listenPort: the port number you want the server to listen on

4665 :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

4666 """

4667

4668 def __init__(self, listenAddress='0.0.0.0', listenPort=445, configFile=''):

4669 if configFile != '':

4670 self.__server = SMBSERVER((listenAddress, listenPort))

4671 self.__server.processConfigFile(configFile)

4672 self.__smbConfig = None

4673 else:

4674 # Here we write a mini config for the server

4675 self.__smbConfig = configparser.ConfigParser()

4676 self.__smbConfig.add_section('global')

4677 self.__smbConfig.set('global', 'server_name',

4678 ''.join([random.choice(string.ascii_letters) for _ in range(8)]))

4679 self.__smbConfig.set('global', 'server_os', ''.join([random.choice(string.ascii_letters) for _ in range(8)])

4680 )

4681 self.__smbConfig.set('global', 'server_domain',

4682 ''.join([random.choice(string.ascii_letters) for _ in range(8)])

4683 )

4684 self.__smbConfig.set('global', 'log_file', 'None')

4685 self.__smbConfig.set('global', 'rpc_apis', 'yes')

4686 self.__smbConfig.set('global', 'credentials_file', '')

4687 self.__smbConfig.set('global', 'challenge', "A" * 16)

4688

4689 # IPC always needed

4690 self.__smbConfig.add_section('IPC$')

4691 self.__smbConfig.set('IPC$', 'comment', '')

4692 self.__smbConfig.set('IPC$', 'read only', 'yes')

4693 self.__smbConfig.set('IPC$', 'share type', '3')

4694 self.__smbConfig.set('IPC$', 'path', '')

4695 self.__server = SMBSERVER((listenAddress, listenPort), config_parser=self.__smbConfig)

4696 self.__server.processConfigFile()

4697

4698 # Now we have to register the MS-SRVS server. This specially important for

4699 # Windows 7+ and Mavericks clients since they WON'T (specially OSX)

4700 # ask for shares using MS-RAP.

4701

4702 self.__srvsServer = SRVSServer()

4703 self.__srvsServer.daemon = True

4704 self.__wkstServer = WKSTServer()

4705 self.__wkstServer.daemon = True

4706 self.__server.registerNamedPipe('srvsvc', ('127.0.0.1', self.__srvsServer.getListenPort()))

4707 self.__server.registerNamedPipe('wkssvc', ('127.0.0.1', self.__wkstServer.getListenPort()))

4708

4709 def start(self):

4710 self.__srvsServer.start()

4711 self.__wkstServer.start()

4712 self.__server.serve_forever()

4713

4714 def registerNamedPipe(self, pipeName, address):

4715 return self.__server.registerNamedPipe(pipeName, address)

4716

4717 def unregisterNamedPipe(self, pipeName):

4718 return self.__server.unregisterNamedPipe(pipeName)

4719

4720 def getRegisteredNamedPipes(self):

4721 return self.__server.getRegisteredNamedPipes()

4722

4723 def addShare(self, shareName, sharePath, shareComment='', shareType='0', readOnly='no'):

4724 share = shareName.upper()

4725 self.__smbConfig.add_section(share)

4726 self.__smbConfig.set(share, 'comment', shareComment)

4727 self.__smbConfig.set(share, 'read only', readOnly)

4728 self.__smbConfig.set(share, 'share type', shareType)

4729 self.__smbConfig.set(share, 'path', sharePath)

4730 self.__server.setServerConfig(self.__smbConfig)

4731 self.__srvsServer.setServerConfig(self.__smbConfig)

4732 self.__server.processConfigFile()

4733 self.__srvsServer.processConfigFile()

4734

4735 def removeShare(self, shareName):

4736 self.__smbConfig.remove_section(shareName.upper())

4737 self.__server.setServerConfig(self.__smbConfig)

4738 self.__srvsServer.setServerConfig(self.__smbConfig)

4739 self.__server.processConfigFile()

4740 self.__srvsServer.processConfigFile()

4741

4742 def setSMBChallenge(self, challenge):

4743 if challenge != '':

4744 self.__smbConfig.set('global', 'challenge', challenge)

4745 self.__server.setServerConfig(self.__smbConfig)

4746 self.__server.processConfigFile()

4747

4748 def setLogFile(self, logFile):

4749 self.__smbConfig.set('global', 'log_file', logFile)

4750 self.__server.setServerConfig(self.__smbConfig)

4751 self.__server.processConfigFile()

4752

4753 def setCredentialsFile(self, logFile):

4754 self.__smbConfig.set('global', 'credentials_file', logFile)

4755 self.__server.setServerConfig(self.__smbConfig)

4756 self.__server.processConfigFile()

4757

4758 def addCredential(self, name, uid, lmhash, nthash):

4759 self.__server.addCredential(name, uid, lmhash, nthash)

4760

4761 def setSMB2Support(self, value):

4762 if value is True:

4763 self.__smbConfig.set("global", "SMB2Support", "True")

4764 else:

4765 self.__smbConfig.set("global", "SMB2Support", "False")

4766 self.__server.setServerConfig(self.__smbConfig)

4767 self.__server.processConfigFile()