在linux上使用python从smb共享中获取文件的所有者

3b6akqbq  于 2023-08-03  发布在  Linux
关注(0)|答案(2)|浏览(162)

我需要为我正在编写的脚本找出谁是smb共享中文件的真正所有者(当然在我的服务器上使用mount -t cifs挂载,在windows机器上使用net use挂载)。
事实证明,在linux服务器上使用python找到这些信息是一个真实的的挑战。
我尝试使用许多SMB库(如smbprotocol,smbclient和其他),没有任何工作。
我找到了一些windows的解决方案,它们都使用pywin32或其他windows特定的软件包。
我也设法做到这一点从bash使用smbcalcs,但不能做到干净,但使用subprocess.popen('smbcacls') ..
有办法解决吗?

xdyibdwo

xdyibdwo1#

令人难以置信的是,这不是一个微不足道的任务,不幸的是,答案并不像我希望的那样简单。
我张贴这个答案,如果有人将被困在这个同样的问题在未来,但希望也许有人会张贴一个更好的解决方案早些时候
为了找到所有者,我使用了this library with its examples

from smb.SMBConnection import SMBConnection

conn = SMBConnection(username='<username>', password='<password>', domain='<domain>', my_name='<some pc name>', remote_name='<server name>')
conn.connect('<server name>')

sec_att = conn.getSecurity('<share name>', r'\some\file\path')
owner_sid = sec_att.owner

字符串
问题是pysmb包只会给予你所有者的SID而不是他的名字。
为了得到他的名字,你需要make an ldap query like in this answer(重新发布代码):

from ldap3 import Server, Connection, ALL
from ldap3.utils.conv import escape_bytes

s = Server('my_server', get_info=ALL)
c = Connection(s, 'my_user', 'my_password')
c.bind()

binary_sid = b'....'  # your sid must be in binary format

c.search('my_base', '(objectsid=' + escape_bytes(binary_sid) + ')', attributes=['objectsid', 'samaccountname'])
print(c.entries)


但是当然没有什么是容易的,我花了几个小时才找到一种方法在python中将字符串SID转换为二进制SID,最后这解决了它:

# posting the needed functions and omitting the class part
def byte(strsid):
    '''
    Convert a SID into bytes
        strdsid - SID to convert into bytes
    '''
    sid = str.split(strsid, '-')
    ret = bytearray()
    sid.remove('S')
    for i in range(len(sid)):
        sid[i] = int(sid[i])
    sid.insert(1, len(sid)-2)
    ret += longToByte(sid[0], size=1)
    ret += longToByte(sid[1], size=1)
    ret += longToByte(sid[2], False, 6)
    for i in range(3, len(sid)):
        ret += cls.longToByte(sid[i])
    return ret

def byteToLong(byte, little_endian=True):
    '''
    Convert bytes into a Python integer
        byte - bytes to convert
        little_endian - True (default) or False for little or big endian
    '''
    if len(byte) > 8:
        raise Exception('Bytes too long. Needs to be <= 8 or 64bit')
    else:
        if little_endian:
            a = byte.ljust(8, b'\x00')
            return struct.unpack('<q', a)[0]
        else:
            a = byte.rjust(8, b'\x00')
            return struct.unpack('>q', a)[0]


最后,你有了完整的解决方案!enjoy:(

pjngdqdw

pjngdqdw2#

我添加这个答案是为了让您了解使用smbprotocol的选项;以及在误解术语的情况下进行扩展。

SMBProtocol Owner信息

也可以使用smbprotocol库获取SID(就像使用pysmb库一样)。
这是在github issues section of the smbprotocol repo中提出的,沿着an example of how to do it。所提供的示例非常棒,并且工作得非常完美。一个极度精简的版本
但是,这也只是检索SID,需要一个辅助库来执行查找。
下面是一个获取所有者SID的函数(只是将要点 Package 在函数中)。包括在这里的情况下,要旨被删除或丢失的任何原因)。

import smbclient
from ldap3 import Server, Connection, ALL,NTLM,SUBTREE
def getFileOwner(smb: smbclient, conn: Connection, filePath: str):
    from smbprotocol.file_info import InfoType
    from smbprotocol.open import FilePipePrinterAccessMask,SMB2QueryInfoRequest, SMB2QueryInfoResponse
    from smbprotocol.security_descriptor import SMB2CreateSDBuffer
    
    class SecurityInfo:
        # 100% just pulled from gist example
        Owner = 0x00000001
        Group = 0x00000002
        Dacl = 0x00000004
        Sacl = 0x00000008
        Label = 0x00000010
        Attribute = 0x00000020
        Scope = 0x00000040
        Backup = 0x00010000    
    def guid2hex(text_sid):
        """convert the text string SID to a hex encoded string"""
        s = ['\\{:02X}'.format(ord(x)) for x in text_sid]
        return ''.join(s)
    def get_sd(fd, info):
        """ Get the Security Descriptor for the opened file. """
        query_req = SMB2QueryInfoRequest()
        query_req['info_type'] = InfoType.SMB2_0_INFO_SECURITY
        query_req['output_buffer_length'] = 65535
        query_req['additional_information'] = info
        query_req['file_id'] = fd.file_id

        req = fd.connection.send(query_req, sid=fd.tree_connect.session.session_id, tid=fd.tree_connect.tree_connect_id)
        resp = fd.connection.receive(req)
        query_resp = SMB2QueryInfoResponse()
        query_resp.unpack(resp['data'].get_value())

        security_descriptor = SMB2CreateSDBuffer()
        security_descriptor.unpack(query_resp['buffer'].get_value())

        return security_descriptor

    with smbclient.open_file(filePath, mode='rb', buffering=0,
                         desired_access=FilePipePrinterAccessMask.READ_CONTROL) as fd:
        sd = get_sd(fd.fd, SecurityInfo.Owner | SecurityInfo.Dacl)
        # returns SID
        _sid = sd.get_owner()
    try:
        # Don't forget to convert the SID string-like object to a string
        # or you get an error related to "0" not existing
        sid = guid2hex(str(_sid))
    except:
        print(f"Failed to convert SID {_sid} to HEX")
        raise
    conn.search('DC=dell,DC=com',f"(&(objectSid={sid}))",SUBTREE)    
    # Will return an empty array if no results are found
    return [res['dn'].split(",")[0].replace("CN=","") for res in conn.response if 'dn' in res]

字符串
用途:

# Client config is required if on linux, not if running on windows
smbclient.ClientConfig(username=username, password=password)

# Setup LDAP session
server = Server('mydomain.com',get_info=ALL,use_ssl = True)
# you can turn off raise_exceptions, or leave it out of the ldap connection
# but I prefer to know when there are issues vs. silently failing
conn = Connection(server, user="domain\username", password=password, raise_exceptions=True,authentication=NTLM)
conn.start_tls()
conn.open()
conn.bind()

# Run the check
fileCheck = r"\\shareserver.server.com\someNetworkShare\someFile.txt"
owner = getFileOwner(smbclient, conn, fileCheck)

# Unbind ldap session
# I'm not clear if this is 100% required, I don't THINK so
# but better safe than sorry
conn.unbind()

# Print results
print(owner)


现在,这不是超级高效。它需要6秒,我运行这个一个单一的文件。因此,如果你想运行某种所有权扫描,那么你可能只想用C++或其他低级语言编写程序,而不是尝试使用Python。但对于一些快速和肮脏的东西,这确实有效。您还可以设置线程池并运行批处理。耗时最长的部分是连接到文件本身,而不是运行ldap查询,所以如果您能找到一种更有效的方法来完成这项工作,那就太好了。

术语警告,楼主!=创建者/作者

最后一点。Owner!=文件作者。许多域环境(特别是SMB共享)会自动将所有权从创建者更改为组。在我的情况下,上述结果是:x1c 0d1x的数据
我其实是在找文件的创建者。文件创建者和修改者不是windows默认跟踪的属性。管理员可以启用策略来审核共享中的文件更改,或者可以使用单个文件的安全->高级->审核功能(该功能无法帮助您确定创建者)逐个文件启用审核。
也就是说,一些应用程序为自己存储这些信息。例如,如果您正在查找Excel,this answer提供了一种获取任何xls或xlsx文件的创建者的方法(由于文件的二进制性质,不适用于xlsb)。不幸的是,很少有文件存储这种信息。在我的例子中,我希望获得tblu,pbix和其他报告类型文件的信息。但是,它们不包含此信息(从隐私的Angular 来看,这是好的)。
因此,如果有人发现这个答案试图解决同样的事情,我没有-你最好的办法(以获得实际的作者信息)是与您的域IT管理员获得审计设置。

相关问题