我正在使用一个pam_python
模块来记录 SSH 尝试中使用的用户名和密码。
我/etc/pam.d/sshd
添加了这一行:
auth requisite pam_python.so /lib64/security/pwreveal.py
这是/lib64/security/pwreveal.py
:
import crypt, spwd, syslog
def auth_log(msg):
"""Send errors to default auth log"""
syslog.openlog(facility=syslog.LOG_AUTH)
syslog.syslog("SSH Attack Logged: " + msg)
syslog.closelog()
def check_pw(user, password):
auth_log("User: " + user + " Password: " + password)
"""Check the password matches local unix password on file"""
# try:
hashed_pw = spwd.getspnam(user)[1]
# except KeyError,e:
# return False
return crypt.crypt(password, hashed_pw) == hashed_pw
def pam_sm_authenticate(pamh, flags, argv):
try:
user = pamh.get_user()
except pamh.exception, e:
return e.pam_result
if not user:
return pamh.PAM_USER_UNKNOWN
try:
resp = pamh.conversation(pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, 'Password:'))
except pamh.exception, e:
return e.pam_result
if not check_pw(user, resp.resp):
auth_log("Remote Host: %s (%s:%s)" % (pamh.rhost, user, resp.resp))
return pamh.PAM_AUTH_ERR
return pamh.PAM_SUCCESS
def pam_sm_setcred(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_acct_mgmt(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_open_session(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_close_session(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_chauthtok(pamh, flags, argv):
return pamh.PAM_SUCCESS
/var/log/messages
这在一定程度上是有效的,在SSH 尝试失败后我看到以下输出(在本例中,我来自另一个本地开发服务器):
Mar 3 14:35:59 localhost sshd: SSH Attack Logged: Remote Host: 192.168.1.7 (root:root123)
我的问题是,无论用户名/密码组合是否正确,脚本总是在 /var/log/secure 中输出相同的错误并无法验证(因此当我使用这个 python 脚本时,SSH 实际上已经损坏):
Mar 3 14:50:41 localhost /lib64/security/pwreveal.py[13328]: Traceback (most recent call last):
Mar 3 14:50:41 localhost /lib64/security/pwreveal.py[13328]: File "/lib64/security/pwreveal.py", line 32, in pam_sm_authenticate
Mar 3 14:50:41 localhost /lib64/security/pwreveal.py[13328]: if not check_pw(user, resp.resp):
Mar 3 14:50:41 localhost /lib64/security/pwreveal.py[13328]: File "/lib64/security/pwreveal.py", line 13, in check_pw
Mar 3 14:50:41 localhost /lib64/security/pwreveal.py[13328]: hashed_pw = spwd.getspnam(user)[1]
Mar 3 14:50:41 localhost /lib64/security/pwreveal.py[13328]: KeyError: getspnam(): name not found
我知道“spwd”是影子密码数据库,我在网上找到了一些信息,表明在这种情况下,“未找到名称”更准确地描述为“权限被拒绝”。因此,作为测试,我确保 sshd 用户具有对 /etc/shadow 的读取权限 - 但这没有帮助。
我不确定我搜索的思路是否正确。有什么帮助吗?
注意:我意识到在服务器上运行 SSH 密码记录并不是一件好事。我正在一个只有我才能访问的个人开发箱上执行此操作。这是一个“只是为了好玩”的项目。
编辑- 作为测试,我创建了一个独立的 python 脚本,它简单地执行以下操作:
import spwd
test = spwd.getspnam("myusername")[1]
print test
以 root 身份运行此脚本会提取“myusername”密码的哈希密码。如果我故意拼错该用户名,那么我实际上是在尝试查找影子文件中不存在的用户名,我会收到此错误:
KeyError: 'getspnam(): name not found'
完全相同的错误。
因此,我可以假设,当通过 pamspwd.getspnam()
从内部运行时pwreveal.py
,它无法找到用户。尽管当它在单独的脚本中独立运行时,它可以找到。
为什么会这样?
答案1
我不确定我是否正确理解了您的问题,但您试图说您的脚本在从 PAM 脚本调用时始终会回溯。当您写入无效用户(不存在用户)时,您可以模拟相同的行为。
那么让我们开始长话短说吧。KeyError
异常告诉您由于某种原因未在数据库中找到该用户。在您的第二个示例中,这显然是因为用户实际上不存在,并且这是预期的行为——这就是 Python 中异常的工作方式,不是吗?
您最初的问题很可能是由 CentOS 中的 SELinux 策略引起的。正常进程无法读取/etc/shadow
。当您从 pam 堆栈下启动 python 脚本时,暂停它并查看进程上下文(使用ps auxfZ
),您将看到脚本正在使用的实际上下文。您还很可能会在/var/log/audit/audit.log
(或ausearch -m AVC
)中看到一些 AVC 消息,说明哪个进程被禁止访问该shadow
文件。
那么如何解决呢?第一种可能性是暂时将 SELinux 切换到以permissive
确保它是原因(setenfoce 0
)。然后,它应该开始工作。但你不应该满足于此,如果你想正确地做到这一点,你应该尝试调整/编写脚本的策略,但这不符合 Serverfault 上答案的格式。