UnboundLocalError: parentDir in SMB3.create() for root-level opens when the server supports directory leasing
Description
In impacket/smb3.py, SMB3.create() assigns the local variable parentDir inside an if len(fileName.split('\\')) > 2: guard, but then references it unconditionally on the very next line. When the branch is entered for a file that is at the root (e.g. a named pipe such as \lsarpc), parentDir is never assigned and Python raises UnboundLocalError.
The branch is guarded by self._Connection['SupportsDirectoryLeasing'] is True, so it only executes when the connected server advertises directory leasing. Stock Impacket never negotiates the SMB2_GLOBAL_CAP_DIRECTORY_LEASING client capability, so the branch is currently dormant — but the logic is incorrect and crashes as soon as that path is taken (e.g. a client that advertises directory leasing, or any code that sets SupportsDirectoryLeasing).
Affected code
impacket/smb3.py, SMB3.create() (line numbers as of master @ 8ea54feb; identical in the 0.13.1 release):
1274: if self._Connection['Dialect'] >= SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True:
1275: # Is this file NOT on the root directory?
1276: if len(fileName.split('\\')) > 2:
1277: parentDir = ntpath.dirname(pathName)
1278: if parentDir in self.GlobalFileTable: # <-- referenced outside the guard above
1279: raise Exception("Don't know what to do now! :-o")
1280: else:
1281: parentEntry = copy.deepcopy(FILE)
1282: parentEntry['LeaseKey'] = uuid.generate()
1283: parentEntry['LeaseState'] = SMB2_LEASE_NONE
1284: self.GlobalFileTable[parentDir] = parentEntry
For a root-level fileName (e.g. \lsarpc), len(fileName.split('\\')) > 2 is False, so parentDir is never bound before line 1278 uses it.
Steps to reproduce
Against any SMB3 server, force the directory-leasing branch and open a root-level pipe:
from impacket.smbconnection import SMBConnection
c = SMBConnection(host, host)
c.login(user, password, domain)
# A directory-leasing-capable server negotiate response sets this; set it directly to reproduce:
c._SMBConnection._Connection['SupportsDirectoryLeasing'] = True
tid = c.connectTree('IPC$')
c.openFile(tid, r'\lsarpc') # root-level named pipe
Expected behavior
Root-level opens (files/pipes with no parent directory) skip the parent-directory bookkeeping and create() succeeds.
Actual behavior
UnboundLocalError: cannot access local variable 'parentDir' where it is not associated with a value
File "impacket/smb3.py", line 1278, in create
if parentDir in self.GlobalFileTable:
Suggested fix
Nest the parentDir usage inside the same guard that assigns it:
if self._Connection['Dialect'] >= SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True:
# Is this file NOT on the root directory?
if len(fileName.split('\\')) > 2:
parentDir = ntpath.dirname(pathName)
- if parentDir in self.GlobalFileTable:
- raise Exception("Don't know what to do now! :-o")
- else:
- parentEntry = copy.deepcopy(FILE)
- parentEntry['LeaseKey'] = uuid.generate()
- parentEntry['LeaseState'] = SMB2_LEASE_NONE
- self.GlobalFileTable[parentDir] = parentEntry
+ if parentDir in self.GlobalFileTable:
+ raise Exception("Don't know what to do now! :-o")
+ else:
+ parentEntry = copy.deepcopy(FILE)
+ parentEntry['LeaseKey'] = uuid.generate()
+ parentEntry['LeaseState'] = SMB2_LEASE_NONE
+ self.GlobalFileTable[parentDir] = parentEntry
Environment
- Impacket: confirmed present in the latest release 0.13.1 (tag
impacket_0_13_1, 2026-05-15) and on master (8ea54feb).
- Python: 3.12
- File:
impacket/smb3.py
UnboundLocalError: parentDirinSMB3.create()for root-level opens when the server supports directory leasingDescription
In
impacket/smb3.py,SMB3.create()assigns the local variableparentDirinside anif len(fileName.split('\\')) > 2:guard, but then references it unconditionally on the very next line. When the branch is entered for a file that is at the root (e.g. a named pipe such as\lsarpc),parentDiris never assigned and Python raisesUnboundLocalError.The branch is guarded by
self._Connection['SupportsDirectoryLeasing'] is True, so it only executes when the connected server advertises directory leasing. Stock Impacket never negotiates theSMB2_GLOBAL_CAP_DIRECTORY_LEASINGclient capability, so the branch is currently dormant — but the logic is incorrect and crashes as soon as that path is taken (e.g. a client that advertises directory leasing, or any code that setsSupportsDirectoryLeasing).Affected code
impacket/smb3.py,SMB3.create()(line numbers as ofmaster@8ea54feb; identical in the 0.13.1 release):For a root-level
fileName(e.g.\lsarpc),len(fileName.split('\\')) > 2isFalse, soparentDiris never bound before line 1278 uses it.Steps to reproduce
Against any SMB3 server, force the directory-leasing branch and open a root-level pipe:
Expected behavior
Root-level opens (files/pipes with no parent directory) skip the parent-directory bookkeeping and
create()succeeds.Actual behavior
Suggested fix
Nest the
parentDirusage inside the same guard that assigns it:if self._Connection['Dialect'] >= SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True: # Is this file NOT on the root directory? if len(fileName.split('\\')) > 2: parentDir = ntpath.dirname(pathName) - if parentDir in self.GlobalFileTable: - raise Exception("Don't know what to do now! :-o") - else: - parentEntry = copy.deepcopy(FILE) - parentEntry['LeaseKey'] = uuid.generate() - parentEntry['LeaseState'] = SMB2_LEASE_NONE - self.GlobalFileTable[parentDir] = parentEntry + if parentDir in self.GlobalFileTable: + raise Exception("Don't know what to do now! :-o") + else: + parentEntry = copy.deepcopy(FILE) + parentEntry['LeaseKey'] = uuid.generate() + parentEntry['LeaseState'] = SMB2_LEASE_NONE + self.GlobalFileTable[parentDir] = parentEntryEnvironment
impacket_0_13_1, 2026-05-15) and onmaster(8ea54feb).impacket/smb3.py