Introduction
Following up from my last post about a two-factor SSH authentication python module, I thought I'd add one more module that has yielded some interesting results as well. If you watch your authorization log closely you'll undoubtedly notice multiple failed login attempts per day primarily for root/privileged users. These scans are largely automated by using lists of popular passwords and SSH cracking programs such as Beleth. Typical authorization logs from SSHd look similar to the entry below.
Jan 1 18:10:51 lan sshd[19972]: Failed password for root from 61.xxx.xxx.34 port 33302 ssh2
I thought it would be interesting to see not only which users were commonly targets of attack, but also the wordlists being actively used by these bots. The module below will log a copy of the attempted username and password for all failed login attempts. I've also included a copy of all failed attempts logged after running this module for a week. The log entry below is how auth.log will look while using the module.
Jan 1 19:58:39 lan sshd: SSH Attack Logged: Remote Host: 61.xxx.xxx.34 (root:password1)
The Source
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): """Check the password matches local unix password on file""" hashed_pw = spwd.getspnam(user)[1] 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
Configuration
Configuration is similar to the STAMP 2-factor authentication module. Instead of adding an additional authentication requirement, we're simply going to replace the standard password entry in /etc/pam.d/sshd with our new module. Save a copy of the source code to /lib/security/pwreveal.py. Now, open up /etc/pam.d/sshd and insert the line below.
#@include common-auth auth requisite pam_python.so pwreveal.py
Resources
You can run similar experiments by using any number of widely available honeypot projects. Kippo is an SSH based honeypot that includes not only password logging, but also places attackers in a sandboxed shell that logs all of their commands. If you don't want to deal directly with the kippo software, it's included as part of Honeydrive, a full featured VM honeypot.
Hey stderr, Ion here, author of HoneyDrive :)
ReplyDeleteVery nice post, keep up the good work!
Regards.
Hi, I was wondering which distro/version/ssh this works with.
ReplyDeleteHere is a post of my experiences.
http://www.erisresearch.org/2014/02/attempting-to-observe-openssh-passwords.html
The page does not exist in your blog:
Delete"Sorry, the page you were looking for in this blog does not exist. "
It should be fairly universal. I tested the script originally on Debian Wheezy using OpenSSH_6.0p1. If you have any more problems setting it up, let me know.
ReplyDeleteAlright, I'll try this configuration this weekend, thanks!
ReplyDeleteFYI - e in return e.pam_result is undefined.
ReplyDeleteWorks for me on Fedora. Good job. Thanks!
ReplyDeleteWorked in everyway except I could no longer SSH into my computer, kept getting access denied until I removed the line inserted into /etc/pam.d/sshd.
ReplyDeleteAlso I believe the August commenter was using Python 3, which requires "exception as e", the above "exception, e" is Python 1.
Hello, regular sized humans
ReplyDeleteThank you for this script!
I modified check_pw so as to stop exploding when the user doesn't exist. However, resp.resp can't be printed in that case (when the user doesn't have an account). Suggestions? I don't know much python...
def check_pw(user, 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
I'm interested as well..
DeleteIm getting ''PAM unable to dlopen(/lib64/security/pwreveal.py): /lib64/security/pwreveal.py: invalid ELF header'' error, any suggestions? thanks.
ReplyDeleteIt doesn't work for me in a AWS instance of pristine ubuntu trusty. After debug, I see that for some reason the "pam_sm_setcred" method is called also *at the beginning*, and even it returns "pamh.PAM_SUCCESS", PAM doesn't go forward to "pam_sm_authenticate" until the password is correct. (From pam_sm_setcred man page: "It should only be called after the user has been authenticated"). Any ideas?
ReplyDeleteHas anyone gotten this to work on Centos 7? If so how?
ReplyDeleteAnonymous - yes I have it working on Centos7, what problem are you having?
ReplyDeleteWorks great to retrieve passwords from SecureCRT session files! Thanks!!
ReplyDeleteLater they ended up basic in different locales of Canada. Obviously it was not by an error - log houses spread to Canada in light of the plenitude of required development materials and the overwhelming icy.https://howdoesyourgardenmow.com/chicago-electric-chainsaw/
ReplyDelete