最新消息:

暴力破解测试工具–Patator 源码分析

Python admin 2780浏览 0评论

转自:http://blog.csdn.net/yueguanghaidao/article/details/9473559

说到暴力破解大家首先想到的肯定是hydra,的确hydra的确非常强大,支持几乎所有的弱密码破解。hydra本身使用C语言开发,性能很高,很适合学习。

但今天我们使用的是Patator,Patator本身使用Python开发,最新版本也就4000行左右,可以说小到极致,这主要多亏了Python库的丰富和强大。

Patator下载地址:http://code.google.com/p/patator/downloads/list。

比较遗憾的是需要自己提供密码字典,下面我们就揭开Patator的真面目。

先来扫个telnet弱密码,看下效果:

其中telnet_login,意思是扫telnet,其中user.txt是用户名字典,passwd.txt是密码字典

 

 

Patator和hydra都会交叉用户名和密码文件,通过上诉比较我们可以看出,两者最大不同点在于,hydra直接可以输出成功的用户名和密码,但Patator只输出认证后信息。

这也导致了hydra比Patator更好用,但难免会有误报。

其中telnet模块源码如下:

 

[python] view plaincopy

  1. # Telnet {{{
  2. from telnetlib import Telnet
  3. class Telnet_login(TCP_Cache):
  4.   ””’Brute-force Telnet”’
  5.   usage_hints = (
  6.     “””%prog host=10.0.0.1 inputs=’FILE0\\nFILE1′ 0=logins.txt 1=passwords.txt persistent=0″””
  7.     “”” prompt_re=’Username:|Password:’ -x ignore:egrep=’Login incorrect.+Username:'”””,
  8.     )
  9.   available_options = (
  10.     (‘host’, ‘target host’),
  11.     (‘port’, ‘target port [23]’),
  12.     (‘inputs’, ‘list of values to input’),
  13.     (‘prompt_re’, ‘regular expression to match prompts [\w+]’),
  14.     (‘timeout’, ‘seconds to wait for a response and for prompt_re to match received data [20]’),
  15.     )
  16.   available_options += TCP_Cache.available_options
  17.   Response = Response_Base
  18.   def connect(self, host, port, timeout):
  19.     self.prompt_count = 0
  20.     fp = Telnet(host, int(port), int(timeout))
  21.     return TCP_Connection(fp)
  22.   def execute(self, host, port=’23’, inputs=None, prompt_re=’\w+:’, timeout=’20’, persistent=’1′):
  23.     fp, _ = self.bind(host, port, timeout=timeout)
  24.     trace = ”
  25.     timeout = int(timeout)
  26.     if self.prompt_count == 0:
  27.       _, _, raw = fp.expect([prompt_re], timeout=timeout)
  28.       logger.debug(‘raw banner: %s’ % repr(raw))
  29.       trace += raw
  30.       self.prompt_count += 1
  31.     if inputs is not None:
  32.       for val in inputs.split(r’\n’):
  33.         logger.debug(‘input: %s’ % val)
  34.         cmd = val + ‘\n’ #’\r\x00′
  35.         fp.write(cmd)
  36.         trace += cmd
  37.         _, _, raw = fp.expect([prompt_re], timeout=timeout)
  38.         logger.debug(‘raw %d: %s’ % (self.prompt_count, repr(raw)))
  39.         trace += raw
  40.         self.prompt_count += 1
  41.     if persistent == ‘0’:
  42.       self.reset()
  43.     mesg = repr(raw)[1:-1] # strip enclosing single quotes
  44.     return self.Response(0, mesg, trace)
  45. # }}}

把核心部分抽出来,流程是这样的。

 

创建一个连接到telnetlib.Telnet对象fp  –>> 等待出现匹配正则(\w+:)字符出现(如 login:,username等)

–>> 发送用户名 –>>继续等待(\w+:)出现(如passwd:,Password:) –>> 发送密码  ->> 等待(\w+:)出现,输出返回字符串

 

过程就是这么简单,其中expect函数接受二个参数,第一个是正则表达式的列表,可以是编译好的,也可以是字符串,第二个是超时时间。

返回一个三项的tuple,第一个是匹配到的正则序号(从0开始),第二个是匹配match的对象,第三个是直到正则的返回数据(包括匹配内容)

这说的有点抽象。

请看下面:

从上面我们可以很明显看出,匹配的正则是按顺序的,虽然返回数据中,‘bo’在’lo’之前,但由于在正则列表中‘lo’在前,所以数据一致读到’lo’。

到这里为止,大家应该对Patator如何去爆telnet弱密码基本了解了。

但如果我们想直接得出破解的用户名和密码,这该怎么办呢?

有一种思路就是提供一个错误的用户名和密码,保存返回的内容,然后每次获取要测试的用户名和密码的回内容,通过比较两次的结果就可以结果了。但由于返回内容会千奇百怪,所以要做较多的特殊处理。

如以下代码:

 

[python] view plaincopy

  1. import telnetlib
  2. class telnet_check:
  3.     def __init__(self,host,port=23,timeout=10):
  4.         self.host=host
  5.         self.port=port
  6.         self.timeout=timeout
  7.         self.promote=’\w+:’
  8.         self.errmsg=”
  9.         self.geterrmsg()
  10.     def connect(self):
  11.         return telnetlib.Telnet(self.host,self.port,self.timeout)
  12.     def geterrmsg(self):
  13.         f=self.connect()
  14.         f.expect([self.promote],self.timeout)
  15.         f.write(‘nullnull\r’)
  16.         f.expect([self.promote],self.timeout)
  17.         f.write(‘nullnull\r’)
  18.         _,_,self.errmsg=f.expect([self.promote],self.timeout)
  19.         self.errmsg=self.errmsg.strip()
  20.     def check(self,User,Passwd):
  21.         t=self.connect()
  22.         t.expect([self.promote],self.timeout)
  23.         t.write(User+’\r’)
  24.         _,_,p=t.expect([self.promote],self.timeout)
  25.         if p.strip() == self.errmsg:
  26.             return False
  27.         t.write(Passwd+’\r’)
  28.         _,_,loginmsg=t.expect([self.promote],self.timeout)
  29.         loginmsg=loginmsg.strip()
  30.         if self.errmsg != ” and self.errmsg != loginmsg:
  31.             return True
  32.         else:
  33.             return False
  34. if __name__==’__main__’:
  35.     tn=telnet_check(‘127.0.0.1’)
  36.     print tn.check(‘yihaibo’,’1′)
  37.     print tn.check(‘root’,’root’)
  38.     print tn.check(‘admin’,’admin’)

运行结果如下:

 

我们发现这的确是一种可行的办法。这也给hydra误报带来了解决办法。

由于每次都创建telnetlib.Telnet对象比较浪费资源,patator使用了缓存来解决这个问题,但这里我一直有一个疑问,为啥Telnet对象可以缓存,如果亲明白,教教我吧!

Telnet_login的父类TCP_Cache简化代码如下:

 

[python] view plaincopy

  1. class TCP_Cache:
  2.   def __init__(self):
  3.     self.cache = {} # {‘10.0.0.1:22’: (‘root’, conn1), ‘10.0.0.2:22’: (‘admin’, conn2),
  4.     self.curr = None
  5.   def __del__(self):
  6.     for _, (_, c) in self.cache.items():
  7.       c.close()
  8.     self.cache.clear()
  9.   def bind(self, host, port, *args, **kwargs):
  10.     hp = ‘%s:%s’ % (host, port)
  11.     key = ‘:’.join(args)
  12.     if hp in self.cache:
  13.       k, c = self.cache[hp]
  14.       if key == k:
  15.         self.curr = hp, k, c
  16.         return c.fp, c.banner
  17.       else:
  18.         c.close()
  19.         del self.cache[hp]
  20.     self.curr = None
  21.     conn = self.connect(host, port, *args, **kwargs)
  22.     self.cache[hp] = (key, conn)
  23.     self.curr = hp, key, conn
  24.     return conn.fp, conn.banner

我们可以看到,当开始新建一Telnet_login对象时,将把host:port信息加入cache,以后每次都会现在cache中寻找,如果有就直接返回连接对象(使用先前fp,fp.expect返回信息怎么还可用???)。

 

 

Patator基于模块化的设计,啥模块化的设计?因为支持多种协议,而各个协议之间都是独立的。举个简单的例子,你想测试telnet弱密码,使用的是 telnetlib库,属于标准库,所以你完全可以扫telnet弱密码,即使你没有安装pycurl(扫http弱密码使用的库,是curl库的 python封装)。哇塞,挺强大啊,那这又是怎么实现的呢?

不看不知道,一看吓一跳,相当的简单。

 

[python] view plaincopy

  1. warnings = []
  2. try:
  3.   import pycurl
  4. except ImportError:
  5.   warnings.append(‘pycurl’)
  6. dependencies = {
  7.   ‘paramiko’: [(‘ssh_login’,), ‘http://www.lag.net/paramiko/’, ‘1.7.7.1’],
  8.   ‘pycurl’: [(‘http_fuzz’,), ‘http://pycurl.sourceforge.net/’, ‘7.19.0’],
  9.   ‘openldap’: [(‘ldap_login’,), ‘http://www.openldap.org/’, ‘2.4.24’],
  10.   ‘impacket’: [(‘smb_login’,’smb_lookupsid’,’mssql_login’), ‘http://oss.coresecurity.com/projects/impacket.html’, ‘svn#765’],
  11.   ‘cx_Oracle’: [(‘oracle_login’,), ‘http://cx-oracle.sourceforge.net/’, ‘5.1.1’],
  12.   ‘mysql-python’: [(‘mysql_login’,), ‘http://sourceforge.net/projects/mysql-python/’, ‘1.2.3’],
  13.   ‘psycopg’: [(‘pgsql_login’,), ‘http://initd.org/psycopg/’, ‘2.4.5’],
  14.   ‘pycrypto’: [(‘vnc_login’,), ‘http://www.dlitz.net/software/pycrypto/’, ‘2.3’],
  15.   ‘dnspython’: [(‘dns_reverse’, ‘dns_forward’), ‘http://www.dnspython.org/’, ‘1.10.0’],
  16.   ‘IPy’: [(‘dns_reverse’, ‘dns_forward’), ‘https://github.com/haypo/python-ipy’, ‘0.75’],
  17.   ‘pysnmp’: [(‘snmp_login’,), ‘http://pysnmp.sf.net/’, ‘4.2.1’],
  18.   ‘unzip’: [(‘unzip_pass’,), ‘http://www.info-zip.org/’, ‘6.0’],
  19.   ‘java’: [(‘keystore_pass’,), ‘http://www.oracle.com/technetwork/java/javase/’, ‘6’],
  20.   ‘python’: [(‘ftp_login’,), ‘http://www.python.org/’, ‘2.7’],
  21.  }
  22.  for w in set(warnings):
  23.     mods, url, ver = dependencies[w]
  24.     if name in mods:
  25.       print(‘ERROR: %s %s (%s) is required to run %s.’ % (w, ver, url, name))
  26.       abort = True

上面这是我简化后的代码,从中可以看出,有一个全局变量列表warning,每次导入需要的库时,如果导入失败,则将库名加入warning,当我们通过 命令行传递使用哪个模块时,如我们使用pycurl,这时我们发现在warnings中含有“pycurl”,所以就提示我们需要安装库,而且很智能的将 库的地址也给我们了。

 

由于是弱密码扫描,字典肯定不小,那么为了追求速度,肯定要使用线程,patator默认使用10个线程去跑,这部分代码主要在控制部分中(class Controller),代码还是有点小复杂的。

 

[python] view plaincopy

  1. gqueues = [Queue(maxsize=10000) for _ in range(self.num_threads)]
  2.   # consumers
  3.   for num in range(self.num_threads):
  4.     pqueue = Queue(maxsize=1000)
  5.     t = Thread(target=self.consume, args=(gqueues[num], pqueue))
  6.     t.daemon = True
  7.     t.start()
  8.     self.thread_report.append(pqueue)
  9.     self.thread_progress.append(Progress())
  10.   # producer
  11.   t = Thread(target=self.produce, args=(gqueues,))
  12.   t.daemon = True
  13.   t.start()

 

正如你所看到的,启用了两个最经典的线程,一个produce,一个consume,两者通信使用的是Queue。self.num_threads是可配的,通过命名行中指定 -t,否则将是默认的10个线程。

到这里就要和大家说再见了,Patator源码还是值得一读的,剩下的就交给你们自己行动了。

转载请注明:jinglingshu的博客 » 暴力破解测试工具–Patator 源码分析

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址