识别 Linux 密码文件
Identify Linux passwd file
我需要帮助编写一个函数(最好是 python)来识别文件是 /etc/passwd 还是 etc/shadow。到目前为止,我已经尝试使用 print(pw.getpwall())
但这会从 os 环境中读取文件。我需要一个接受输入并可以判断文件是否为 passwd/shadow 文件的库
passwd 和 shadow 文件格式不同。
你可以写一个短函数或者class。第一次迭代将是:
- 找到root用户,几乎100%正确root是第一个条目
- 检查第 2、6 和 7 列(分隔符为 : 符号)
- 如果第 2 个是 x,第 6 个是 /root,第 7 个是 /bin/*sh那么几乎是100%的密码文件
- 如果第 2 个是盐和哈希(格式:$salt$hash),第 6 个是数字,第 7 个是 empy 那么它几乎是 100% 的影子文件
自然会有问题:
- Linux配置为不使用影子文件。在这种情况下,密码文件第二列包含密码
- Linux配置为不使用盐(我猜是不是可以)
请查看手册:man 5 passwd
和 man 5 shadow
编辑,2020-04-24:
这是我更正后的 pwd.py:
#!/usr/bin/env python3
import os
import sys
passwd_file=('./passwd')
# path conversion handlers
def __nullpathconv(path):
return path
def __unixpathconv(path):
return path
# decide what field separator we can try to use - Unix standard, with
# the platform's path separator as an option. No special field conversion
# handler is required when using the platform's path separator as field
# separator, but are required for the home directory and shell fields when
# using the standard Unix (":") field separator.
__field_sep = {':': __unixpathconv}
if os.pathsep:
if os.pathsep != ':':
__field_sep[os.pathsep] = __nullpathconv
# helper routine to identify which separator character is in use
def __get_field_sep(record):
fs = None
for c in list(__field_sep.keys()):
# there should be 6 delimiter characters (for 7 fields)
if record.count(c) == 6:
fs = c
break
if fs:
return fs
else:
raise KeyError
# class to match the new record field name accessors.
# the resulting object is intended to behave like a read-only tuple,
# with each member also accessible by a field name.
class Passwd:
def __init__(self, name, passwd, uid, gid, gecos, dir, shell):
self.__dict__['pw_name'] = name
self.__dict__['pw_passwd'] = passwd
self.__dict__['pw_uid'] = uid
self.__dict__['pw_gid'] = gid
self.__dict__['pw_gecos'] = gecos
self.__dict__['pw_dir'] = dir
self.__dict__['pw_shell'] = shell
self.__dict__['_record'] = (self.pw_name, self.pw_passwd,
self.pw_uid, self.pw_gid,
self.pw_gecos, self.pw_dir,
self.pw_shell)
def __len__(self):
return 7
def __getitem__(self, key):
return self._record[key]
def __setattr__(self, name, value):
raise AttributeError('attribute read-only: %s' % name)
def __repr__(self):
return str(self._record)
def __cmp__(self, other):
this = str(self._record)
if this == other:
return 0
elif this < other:
return -1
else:
return 1
# read the whole file, parsing each entry into tuple form
# with dictionaries to speed recall by UID or passwd name
def __read_passwd_file():
if passwd_file:
passwd = open(passwd_file, 'r')
else:
raise KeyError
uidx = {}
namx = {}
sep = None
while 1:
entry = passwd.readline().strip()
if len(entry) > 6:
if sep is None:
sep = __get_field_sep(entry)
fields = entry.split(sep)
for i in (2, 3):
fields[i] = int(fields[i])
for i in (5, 6):
fields[i] = __field_sep[sep](fields[i])
record = Passwd(*fields)
if fields[2] not in uidx:
uidx[fields[2]] = record
if fields[0] not in namx:
namx[fields[0]] = record
elif len(entry) > 0:
pass # skip empty or malformed records
else:
break
passwd.close()
if len(uidx) == 0:
raise KeyError
return (uidx, namx)
# return the passwd database entry by UID
def getpwuid(uid):
u, n = __read_passwd_file()
return u[uid]
# return the passwd database entry by passwd name
def getpwnam(name):
u, n = __read_passwd_file()
return n[name]
# return all the passwd database entries
def getpwall():
u, n = __read_passwd_file()
return list(n.values())
# test harness
if __name__ == '__main__':
print(getpwall())
我需要帮助编写一个函数(最好是 python)来识别文件是 /etc/passwd 还是 etc/shadow。到目前为止,我已经尝试使用 print(pw.getpwall())
但这会从 os 环境中读取文件。我需要一个接受输入并可以判断文件是否为 passwd/shadow 文件的库
passwd 和 shadow 文件格式不同。
你可以写一个短函数或者class。第一次迭代将是:
- 找到root用户,几乎100%正确root是第一个条目
- 检查第 2、6 和 7 列(分隔符为 : 符号)
- 如果第 2 个是 x,第 6 个是 /root,第 7 个是 /bin/*sh那么几乎是100%的密码文件
- 如果第 2 个是盐和哈希(格式:$salt$hash),第 6 个是数字,第 7 个是 empy 那么它几乎是 100% 的影子文件
自然会有问题:
- Linux配置为不使用影子文件。在这种情况下,密码文件第二列包含密码
- Linux配置为不使用盐(我猜是不是可以)
请查看手册:man 5 passwd
和 man 5 shadow
编辑,2020-04-24: 这是我更正后的 pwd.py:
#!/usr/bin/env python3
import os
import sys
passwd_file=('./passwd')
# path conversion handlers
def __nullpathconv(path):
return path
def __unixpathconv(path):
return path
# decide what field separator we can try to use - Unix standard, with
# the platform's path separator as an option. No special field conversion
# handler is required when using the platform's path separator as field
# separator, but are required for the home directory and shell fields when
# using the standard Unix (":") field separator.
__field_sep = {':': __unixpathconv}
if os.pathsep:
if os.pathsep != ':':
__field_sep[os.pathsep] = __nullpathconv
# helper routine to identify which separator character is in use
def __get_field_sep(record):
fs = None
for c in list(__field_sep.keys()):
# there should be 6 delimiter characters (for 7 fields)
if record.count(c) == 6:
fs = c
break
if fs:
return fs
else:
raise KeyError
# class to match the new record field name accessors.
# the resulting object is intended to behave like a read-only tuple,
# with each member also accessible by a field name.
class Passwd:
def __init__(self, name, passwd, uid, gid, gecos, dir, shell):
self.__dict__['pw_name'] = name
self.__dict__['pw_passwd'] = passwd
self.__dict__['pw_uid'] = uid
self.__dict__['pw_gid'] = gid
self.__dict__['pw_gecos'] = gecos
self.__dict__['pw_dir'] = dir
self.__dict__['pw_shell'] = shell
self.__dict__['_record'] = (self.pw_name, self.pw_passwd,
self.pw_uid, self.pw_gid,
self.pw_gecos, self.pw_dir,
self.pw_shell)
def __len__(self):
return 7
def __getitem__(self, key):
return self._record[key]
def __setattr__(self, name, value):
raise AttributeError('attribute read-only: %s' % name)
def __repr__(self):
return str(self._record)
def __cmp__(self, other):
this = str(self._record)
if this == other:
return 0
elif this < other:
return -1
else:
return 1
# read the whole file, parsing each entry into tuple form
# with dictionaries to speed recall by UID or passwd name
def __read_passwd_file():
if passwd_file:
passwd = open(passwd_file, 'r')
else:
raise KeyError
uidx = {}
namx = {}
sep = None
while 1:
entry = passwd.readline().strip()
if len(entry) > 6:
if sep is None:
sep = __get_field_sep(entry)
fields = entry.split(sep)
for i in (2, 3):
fields[i] = int(fields[i])
for i in (5, 6):
fields[i] = __field_sep[sep](fields[i])
record = Passwd(*fields)
if fields[2] not in uidx:
uidx[fields[2]] = record
if fields[0] not in namx:
namx[fields[0]] = record
elif len(entry) > 0:
pass # skip empty or malformed records
else:
break
passwd.close()
if len(uidx) == 0:
raise KeyError
return (uidx, namx)
# return the passwd database entry by UID
def getpwuid(uid):
u, n = __read_passwd_file()
return u[uid]
# return the passwd database entry by passwd name
def getpwnam(name):
u, n = __read_passwd_file()
return n[name]
# return all the passwd database entries
def getpwall():
u, n = __read_passwd_file()
return list(n.values())
# test harness
if __name__ == '__main__':
print(getpwall())