NetExec: SMB: incorrect share permissions

Describe the bug Working on Proving Grounds machine Craft2 from Offsec , I encountered a situation when NetExec reported share permissions as READ only, even though WRITE was allowed (and actually required for exploitation).

To Reproduce List SMB shares:

┌──(kali㉿kali)-[~/craft2]
└─$ nxc smb 192.168.229.188 -u thecybergeek -p winniethepooh --shares                               
SMB         192.168.229.188 445    CRAFT2           [*] Windows 10 / Server 2019 Build 17763 x64 (name:CRAFT2) (domain:CRAFT2) (signing:False) (SMBv1:False)
SMB         192.168.229.188 445    CRAFT2           [+] CRAFT2\thecybergeek:winniethepooh 
SMB         192.168.229.188 445    CRAFT2           [*] Enumerated shares
SMB         192.168.229.188 445    CRAFT2           Share           Permissions     Remark
SMB         192.168.229.188 445    CRAFT2           -----           -----------     ------
SMB         192.168.229.188 445    CRAFT2           ADMIN$                          Remote Admin
SMB         192.168.229.188 445    CRAFT2           C$                              Default share
SMB         192.168.229.188 445    CRAFT2           IPC$            READ            Remote IPC
SMB         192.168.229.188 445    CRAFT2           WebApp          READ

Share WebApp has only permission READ listed. We can however upload a file in this share:

┌──(kali㉿kali)-[~/craft2]
└─$ echo test > test.txt    
                                                                                                                                                               
┌──(kali㉿kali)-[~/craft2]
└─$ nxc smb 192.168.229.188 -u thecybergeek -p winniethepooh --share WebApp --put-file test.txt '\\test.txt'
SMB         192.168.229.188 445    CRAFT2           [*] Windows 10 / Server 2019 Build 17763 x64 (name:CRAFT2) (domain:CRAFT2) (signing:False) (SMBv1:False)
SMB         192.168.229.188 445    CRAFT2           [+] CRAFT2\thecybergeek:winniethepooh 
SMB         192.168.229.188 445    CRAFT2           [*] Copying test.txt to \\test.txt
SMB         192.168.229.188 445    CRAFT2           [+] Created file test.txt on \\WebApp\\\test.txt
                                                                                                                                                               
┌──(kali㉿kali)-[~/craft2]
└─$ nxc smb 192.168.229.188 -u thecybergeek -p winniethepooh --share WebApp --get-file '\\test.txt' verify.txt
SMB         192.168.229.188 445    CRAFT2           [*] Windows 10 / Server 2019 Build 17763 x64 (name:CRAFT2) (domain:CRAFT2) (signing:False) (SMBv1:False)
SMB         192.168.229.188 445    CRAFT2           [+] CRAFT2\thecybergeek:winniethepooh 
SMB         192.168.229.188 445    CRAFT2           [*] Copying "\\test.txt" to "verify.txt"
SMB         192.168.229.188 445    CRAFT2           [+] File "\\test.txt" was downloaded to "verify.txt"
                                                                                                                                                               
┌──(kali㉿kali)-[~/craft2]
└─$ cat verify.txt 
test

Expected behavior Correctly recognize share permissions.

NetExec info

  • OS: Kali
  • Version of nxc: 1.1.0
  • Installed from: github (pipx install git+https://github.com/Pennyw0rth/NetExec

About this issue

  • Original URL
  • State: open
  • Created 4 months ago
  • Comments: 22 (5 by maintainers)

Most upvoted comments

I will create a separate issue there. Nevertheless, the change in neff-fix-share-privs probably makes sense to merge as it can help in different scenarios.

@mpgn Weirdly, I am actually able to successfully delete the directory using smbclient.

I don’t think so. I can try to give you more details regarding the error, but sadly without having subscription from Offsec, you probably cannot have access to the machine :face_exhaling:

I did a small change to print more details: image

[...]
           INFO     Creating directory \tiTVMWUhOn in share WebApp                                                                                   smb.py:743
           DEBUG    Error checking WRITE access on share: STATUS_ACCESS_DENIED                                                                       smb.py:752
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /home/kali/.cache/pypoetry/virtualenvs/netexec-cOeBu7w8-py3.11/lib/python3.11/site-packages/impa │
│ cket/smbconnection.py:652 in createDirectory                                                     │
│                                                                                                  │
│   649 │   │   :raise SessionError: if error                                                      │
│   650 │   │   """                                                                                │
│   651 │   │   try:                                                                               │
│ ❱ 652 │   │   │   return self._SMBConnection.mkdir(shareName, pathName)                          │
│   653 │   │   except (smb.SessionError, smb3.SessionError) as e:                                 │
│   654 │   │   │   raise SessionError(e.get_error_code(), e.get_error_packet())                   │
│   655                                                                                            │
│                                                                                                  │
│ ╭────────────────────────────────── locals ───────────────────────────────────╮                  │
│ │  pathName = '\\tiTVMWUhOn'                                                  │                  │
│ │      self = <impacket.smbconnection.SMBConnection object at 0x7f0064eaca10> │                  │
│ │ shareName = 'WebApp'                                                        │                  │
│ ╰─────────────────────────────────────────────────────────────────────────────╯                  │
│                                                                                                  │
│ /home/kali/.cache/pypoetry/virtualenvs/netexec-cOeBu7w8-py3.11/lib/python3.11/site-packages/impa │
│ cket/smb3.py:1789 in mkdir                                                                       │
│                                                                                                  │
│   1786 │   │                                                                                     │
│   1787 │   │   fileId = None                                                                     │
│   1788 │   │   try:                                                                              │
│ ❱ 1789 │   │   │   fileId = self.create(treeId, pathName, GENERIC_ALL, FILE_SHARE_READ | FILE_S  │
│   1790 │   │   │   │   │   │   │   │    FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FIL  │
│   1791 │   │   finally:                                                                          │
│   1792 │   │   │   if fileId is not None:                                                        │
│                                                                                                  │
│ ╭───────────────────────── locals ──────────────────────────╮                                    │
│ │    fileId = None                                          │                                    │
│ │  password = None                                          │                                    │
│ │  pathName = 'tiTVMWUhOn'                                  │                                    │
│ │      self = <impacket.smb3.SMB3 object at 0x7f0065d69910> │                                    │
│ │ shareName = 'WebApp'                                      │                                    │
│ │    treeId = 9                                             │                                    │
│ ╰───────────────────────────────────────────────────────────╯                                    │
│                                                                                                  │
│ /home/kali/.cache/pypoetry/virtualenvs/netexec-cOeBu7w8-py3.11/lib/python3.11/site-packages/impa │
│ cket/smb3.py:1261 in create                                                                      │
│                                                                                                  │
│   1258 │   │                                                                                     │
│   1259 │   │   packetID = self.sendSMB(packet)                                                   │
│   1260 │   │   ans = self.recvSMB(packetID)                                                      │
│ ❱ 1261 │   │   if ans.isValidAnswer(STATUS_SUCCESS):                                             │
│   1262 │   │   │   createResponse = SMB2Create_Response(ans['Data'])                             │
│   1263 │   │   │                                                                                 │
│   1264 │   │   │   openFile = copy.deepcopy(OPEN)                                                │
│                                                                                                  │
│ ╭───────────────────────────────────── locals ─────────────────────────────────────╮             │
│ │                 ans = <impacket.smb3structs.SMB2Packet object at 0x7f0064efd550> │             │
│ │      createContexts = None                                                       │             │
│ │ creationDisposition = 2                                                          │             │
│ │     creationOptions = 33                                                         │             │
│ │       desiredAccess = 268435456                                                  │             │
│ │      fileAttributes = 0                                                          │             │
│ │           fileEntry = {                                                          │             │
│ │                       │   'OpenTable': [],                                       │             │
│ │                       │   'LeaseKey': b"!\x91\xd5_H\xa0@*B^\x824\xd8]\x94'",     │             │
│ │                       │   'LeaseState': 0,                                       │             │
│ │                       │   'LeaseEpoch': 0                                        │             │
│ │                       }                                                          │             │
│ │            fileName = 'tiTVMWUhOn'                                               │             │
│ │  impersonationLevel = 2                                                          │             │
│ │         oplockLevel = 0                                                          │             │
│ │              packet = <impacket.smb3structs.SMB3Packet object at 0x7f006444bb50> │             │
│ │            packetID = 91                                                         │             │
│ │            pathName = '\\\\192.168.229.188\\tiTVMWUhOn'                          │             │
│ │       securityFlags = 0                                                          │             │
│ │                self = <impacket.smb3.SMB3 object at 0x7f0065d69910>              │             │
│ │           shareMode = 7                                                          │             │
│ │          smb2Create = <impacket.smb3structs.SMB2Create object at 0x7f0064eede50> │             │
│ │              treeId = 9                                                          │             │
│ ╰──────────────────────────────────────────────────────────────────────────────────╯             │
│                                                                                                  │
│ /home/kali/.cache/pypoetry/virtualenvs/netexec-cOeBu7w8-py3.11/lib/python3.11/site-packages/impa │
│ cket/smb3structs.py:458 in isValidAnswer                                                         │
│                                                                                                  │
│    455 │   def isValidAnswer(self, status):                                                      │
│    456 │   │   if self['Status'] != status:                                                      │
│    457 │   │   │   from . import smb3                                                            │
│ ❱  458 │   │   │   raise smb3.SessionError(self['Status'], self)                                 │
│    459 │   │   return True                                                                       │
│    460 │                                                                                         │
│    461 │   def __init__(self, data = None):                                                      │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │   self = <impacket.smb3structs.SMB2Packet object at 0x7f0064efd550>                          │ │
│ │   smb3 = <module 'impacket.smb3' from                                                        │ │
│ │          '/home/kali/.cache/pypoetry/virtualenvs/netexec-cOeBu7w8-py3.11/lib/python3.11/sit… │ │
│ │ status = 0                                                                                   │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
SessionError: SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.)

During handling of the above exception, another exception occurred:

╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /home/kali/craft2/NetExec/nxc/protocols/smb.py:744 in shares                                     │
│                                                                                                  │
│    741 │   │   │   if not self.args.no_write_check:                                              │
│    742 │   │   │   │   try:                                                                      │
│    743 │   │   │   │   │   self.logger.info(f"Creating directory {temp_dir} in share {share_nam  │
│ ❱  744 │   │   │   │   │   self.conn.createDirectory(share_name, temp_dir)                       │
│    745 │   │   │   │   │   self.logger.info(f"Deleting directory {temp_dir} in share {share_nam  │
│    746 │   │   │   │   │   self.conn.deleteDirectory(share_name, temp_dir)                       │
│    747 │   │   │   │   │   write = True                                                          │
│                                                                                                  │
│ ╭────────────────────────────────────── locals ───────────────────────────────────────╮          │
│ │      Console = <class 'rich.console.Console'>                                       │          │
│ │            e = SessionError()                                                       │          │
│ │        error = 'STATUS_ACCESS_DENIED'                                               │          │
│ │  permissions = [                                                                    │          │
│ │                │   {'name': 'ADMIN$', 'remark': 'Remote Admin', 'access': []},      │          │
│ │                │   {'name': 'C$', 'remark': 'Default share', 'access': []},         │          │
│ │                │   {'name': 'IPC$', 'remark': 'Remote IPC', 'access': ['READ']}     │          │
│ │                ]                                                                    │          │
│ │         read = True                                                                 │          │
│ │         self = <protocol.smb object at 0x7f0064eacf90>                              │          │
│ │        share = <impacket.dcerpc.v5.srvs.SHARE_INFO_1 object at 0x7f006451c7d0>      │          │
│ │   share_info = {'name': 'WebApp', 'remark': '', 'access': ['READ']}                 │          │
│ │   share_name = 'WebApp'                                                             │          │
│ │ share_remark = ''                                                                   │          │
│ │       shares = [                                                                    │          │
│ │                │   <impacket.dcerpc.v5.srvs.SHARE_INFO_1 object at 0x7f006451c350>, │          │
│ │                │   <impacket.dcerpc.v5.srvs.SHARE_INFO_1 object at 0x7f006451c4d0>, │          │
│ │                │   <impacket.dcerpc.v5.srvs.SHARE_INFO_1 object at 0x7f006451c650>, │          │
│ │                │   <impacket.dcerpc.v5.srvs.SHARE_INFO_1 object at 0x7f006451c7d0>  │          │
│ │                ]                                                                    │          │
│ │     temp_dir = '\\tiTVMWUhOn'                                                       │          │
│ │      user_id = 2                                                                    │          │
│ │        write = False                                                                │          │
│ ╰─────────────────────────────────────────────────────────────────────────────────────╯          │
│                                                                                                  │
│ /home/kali/.cache/pypoetry/virtualenvs/netexec-cOeBu7w8-py3.11/lib/python3.11/site-packages/impa │
│ cket/smbconnection.py:654 in createDirectory                                                     │
│                                                                                                  │
│   651 │   │   try:                                                                               │
│   652 │   │   │   return self._SMBConnection.mkdir(shareName, pathName)                          │
│   653 │   │   except (smb.SessionError, smb3.SessionError) as e:                                 │
│ ❱ 654 │   │   │   raise SessionError(e.get_error_code(), e.get_error_packet())                   │
│   655 │                                                                                          │
│   656 │   def deleteDirectory(self, shareName, pathName):                                        │
│   657 │   │   """                                                                                │
│                                                                                                  │
│ ╭────────────────────────────────── locals ───────────────────────────────────╮                  │
│ │  pathName = '\\tiTVMWUhOn'                                                  │                  │
│ │      self = <impacket.smbconnection.SMBConnection object at 0x7f0064eaca10> │                  │
│ │ shareName = 'WebApp'                                                        │                  │
│ ╰─────────────────────────────────────────────────────────────────────────────╯                  │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
SessionError: SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.)
SMB         192.168.229.188 445    CRAFT2           [*] Enumerated shares
           INFO     SMB         192.168.229.188 445    CRAFT2           [*] Enumerated shares                                                     logger.py:121
SMB         192.168.229.188 445    CRAFT2           Share           Permissions     Remark
           INFO     SMB         192.168.229.188 445    CRAFT2           Share           Permissions     Remark                                    logger.py:121
SMB         192.168.229.188 445    CRAFT2           -----           -----------     ------
           INFO     SMB         192.168.229.188 445    CRAFT2           -----           -----------     ------                                    logger.py:121
SMB         192.168.229.188 445    CRAFT2           ADMIN$                          Remote Admin
           INFO     SMB         192.168.229.188 445    CRAFT2           ADMIN$                          Remote Admin                              logger.py:121
SMB         192.168.229.188 445    CRAFT2           C$                              Default share
           INFO     SMB         192.168.229.188 445    CRAFT2           C$                              Default share                             logger.py:121
SMB         192.168.229.188 445    CRAFT2           IPC$            READ            Remote IPC
           INFO     SMB         192.168.229.188 445    CRAFT2           IPC$            READ            Remote IPC                                logger.py:121
SMB         192.168.229.188 445    CRAFT2           WebApp          READ            
           INFO     SMB         192.168.229.188 445    CRAFT2           WebApp          READ                                                      logger.py:121

I think I understand why, can you delete the directory you just created ?

We check if you can create and delete, but if delete fails, then it’s like create didn’t work either

try:
    self.conn.createDirectory(share_name, temp_dir)
    self.conn.deleteDirectory(share_name, temp_dir)
    write = True
    share_info["access"].append("WRITE")
except SessionError as e:
    error = get_error_string(e)
    self.logger.debug(f"Error checking WRITE access on share: {error}")

So my guess, you can create but not delete which seem about what we saw on the get acl output

         BUILTIN\Users Allow  ReadAndExecute, Synchronize
         BUILTIN\Users Allow  AppendData
         BUILTIN\Users Allow  CreateFiles

So yep, this is a bug, congratz for the finding ! 🎉

https://github.com/Pennyw0rth/NetExec/blob/fe179b006a7bdca7887af67e60b0afe80e4fd9f2/nxc/protocols/smb.py#L743C20-L744C68