Projects STRLCPY Synergy-httpx Commits 388f637d
🤬
  • ■ ■ ■ ■ ■ ■
    synergy_httpx.py
     1 +#!/bin/python3
     2 +#
     3 +# Author: Panagiotis Chartas (t3l3machus)
     4 +# https://github.com/t3l3machus
     5 + 
     6 +from http.server import HTTPServer, BaseHTTPRequestHandler
     7 +import ssl, sys, base64, re, os, argparse
     8 +from warnings import filterwarnings
     9 +from datetime import date, datetime
     10 +from urllib.parse import unquote, urlparse
     11 +from threading import Thread
     12 +from io import StringIO
     13 +from time import sleep
     14 +from subprocess import check_output
     15 +from string import ascii_uppercase, ascii_lowercase, digits
     16 +from platform import system as get_system_type
     17 +from copy import deepcopy
     18 + 
     19 +if get_system_type() == 'Linux':
     20 + import gnureadline as global_readline
     21 +else:
     22 + import readline as global_readline
     23 + 
     24 +filterwarnings("ignore", category = DeprecationWarning)
     25 + 
     26 +''' Colors '''
     27 +MAIN = '\001\033[38;5;85m\002'
     28 +GREEN = '\001\033[38;5;82m\002'
     29 +GRAY = PLOAD = '\001\033[38;5;246m\002'
     30 +NAME = '\001\033[38;5;228m\002'
     31 +RED = '\001\033[1;31m\002'
     32 +FAIL = '\001\033[1;91m\002'
     33 +ORANGE = '\001\033[0;38;5;214m\002'
     34 +LRED = '\001\033[0;38;5;196m\002'
     35 +BOLD = '\001\033[1m\002'
     36 +PURPLE = '\001\033[0;38;5;141m\002'
     37 +BLUE = '\001\033[0;38;5;12m\002'
     38 +UNDERLINE = '\001\033[4m\002'
     39 +UNSTABLE = '\001\033[5m\002'
     40 +END = '\001\033[0m\002'
     41 + 
     42 + 
     43 +''' MSG Prefixes '''
     44 +INFO = f'[{MAIN}Info{END}]'
     45 +WARN = f'[{ORANGE}Warning{END}]'
     46 +DBG = f'[{ORANGE}Debug{END}]'
     47 +IMPORTANT = f'[{ORANGE}Important{END}]'
     48 +FAILED = f'[{LRED}Error{END}]'
     49 +POST_REQ = f'[{PURPLE}POST-request{END}]'
     50 +GET_REQ = f'[{BLUE}GET-request{END}]'
     51 +META = '[\001\033[38;5;93m\002M\001\033[38;5;129m\002e\001\033[38;5;165m\002t\001\033[38;5;201m\002a\001\033[0m\002]'
     52 + 
     53 + 
     54 +parser = argparse.ArgumentParser()
     55 +parser.add_argument("-c", "--cert", action="store", help = "Your certificate.")
     56 +parser.add_argument("-k", "--key", action="store", help = "The private key for your certificate. ")
     57 +parser.add_argument("-p", "--port", action="store", help = "Server port.", type = int)
     58 +parser.add_argument("-q", "--quiet", action="store_true", help = "Do not print the banner on startup.")
     59 + 
     60 +args = parser.parse_args()
     61 + 
     62 + 
     63 +# -------------- General Functions -------------- #
     64 +def haxor_print(text, leading_spaces = 0):
     65 + 
     66 + text_chars = list(text)
     67 + current, mutated = '', ''
     68 + 
     69 + for i in range(len(text)):
     70 +
     71 + original = text_chars[i]
     72 + current += original
     73 + mutated += f'\033[1;38;5;82m{text_chars[i].upper()}\033[0m'
     74 + print(f'\r{" " * leading_spaces}{mutated}', end = '')
     75 + sleep(0.05)
     76 + print(f'\r{" " * leading_spaces}{current}', end = '')
     77 + mutated = current
     78 + 
     79 + print(f'\r{" " * leading_spaces}{text}\n')
     80 + 
     81 + 
     82 +def print_banner():
     83 + 
     84 + S = [['░', '█','▀','▀', '░'], ['░', '▀','▀','▄', '░'], ['░', '▀','▀','▀', '░']]
     85 + Y = [['█', '░','░','█','░'], ['█', '▄','▄','█','░'], ['▄','▄','▄','▀','░']]
     86 + N = [['█', '▀','▀','▄', '░'], ['█', '░','░','█','░'], ['▀', '░','░','▀','░']]
     87 + E = [['█','▀','▀','▀', '░'], ['█','▀','▀','▀', '░'], ['▀','▀','▀', '▀', '░']]
     88 + R = [['█', '▀','▀','▄', '░'], ['█', '▄','▄','▀','░'], ['▀', '░', '▀','▀','░']]
     89 + G = [['█','▀','▀','▀', '░'], ['█', '░','▀','▄' '░'], ['▀','▀','▀','▀','░']]
     90 + Y = [['█', '░','░','█','░'], ['█', '▄','▄','█','░'], ['▄','▄','▄','▀','░']]
     91 + H = [['░','░','█','░','░','█','░'], ['░','░','█', '█','▀','█','░'], ['░','░','█','░','░','█','░']]
     92 + T = [['▀', '█','▀','░'], ['░', '█','░','░'], ['░', '█','░','░']]
     93 + P = [['▄', '▀','▀','▄','░'], ['█', '▄','▄','█','░'], ['█','░','░','░','░']]
     94 + X = [['█','░','░','█'], [' ', '▀','▄',''], ['█','░','░','█']]
     95 + 
     96 + banner = [S,Y,N,E,R,G,Y,H,T,T,P,X]
     97 + final = []
     98 + print('\r')
     99 + init_color = 31
     100 + txt_color = init_color
     101 + cl = 0
     102 + 
     103 + for charset in range(0, 3):
     104 + for pos in range(0, len(banner)):
     105 + for i in range(0, len(banner[pos][charset])):
     106 + clr = f'\033[38;5;{txt_color}m'
     107 + char = f'{clr}{banner[pos][charset][i]}'
     108 + final.append(char)
     109 + cl += 1
     110 + txt_color = txt_color + 36 if cl <= 3 else txt_color
     111 + 
     112 + cl = 0
     113 + 
     114 + txt_color = init_color
     115 + init_color += 31
     116 + 
     117 + if charset < 2: final.append('\n ')
     118 +
     119 +
     120 + print(f" {''.join(final)}")
     121 + haxor_print('by t3l3machus', 48)
     122 + 
     123 + 
     124 +def print_meta():
     125 + print(f'{META} Created by t3l3machus')
     126 + print(f'{META} Follow on Twitter, HTB, GitHub: @t3l3machus')
     127 + print(f'{META} Thank you!\n')
     128 + 
     129 + 
     130 +def print_green(msg):
     131 + print(f'{GREEN}{msg}{END}')
     132 + 
     133 + 
     134 +def debug(msg):
     135 + print(f'{DBG} {msg}{END}')
     136 + 
     137 + 
     138 +def failure(msg):
     139 + print(f'{FAIL} {msg}{END}')
     140 + 
     141 + 
     142 +def restore_prompt():
     143 + Main_prompt.rst_prompt() if Main_prompt.ready else Main_prompt.set_main_prompt_ready()
     144 + 
     145 + 
     146 +def print_to_prompt(msg):
     147 + print('\r' + msg)
     148 + restore_prompt()
     149 + 
     150 + 
     151 +def clone_dict_keys(_dict):
     152 + clone = deepcopy(_dict)
     153 + clone_keys = clone.keys()
     154 + return clone_keys
     155 + 
     156 + 
     157 +def print_columns(strings):
     158 +
     159 + columns, lines_ = os.get_terminal_size()
     160 + mid = (len(strings) + 1) // 2
     161 + max_length1 = max(len(s) for s in strings[:mid])
     162 + max_length2 = max(len(s) for s in strings[mid:])
     163 + 
     164 + if max_length1 + max_length2 + 4 <= columns:
     165 + # Print the strings in two evenly spaced columns
     166 + for i in range(mid):
     167 +
     168 + col1 = strings[i].ljust(max_length1)
     169 + try: col2 = strings[i+mid].ljust(max_length2)
     170 + except: col2 = ''
     171 + print(col1 + " " * 4 + col2)
     172 + 
     173 + else:
     174 + # Print the strings in one column
     175 + max_length = max(len(s) for s in strings)
     176 + 
     177 + for s in strings:
     178 + print(s.ljust(max_length))
     179 + 
     180 + print('\n', end='')
     181 + 
     182 + 
     183 +def get_file_contents(path):
     184 + 
     185 + try:
     186 + f = open(path, 'r')
     187 + content = f.read()
     188 + f.close()
     189 + return [True, content]
     190 +
     191 + except Exception as e:
     192 + return [False, e]
     193 + 
     194 + 
     195 +def chill():
     196 + pass
     197 + 
     198 + 
     199 +# -------------- HTTPS Server -------------- #
     200 +class Synergy_Httpx(BaseHTTPRequestHandler):
     201 + 
     202 + basic_endpoints = {
     203 + 'GET' : 'x3Rty7',
     204 + 'POST' : 'aWq8tY'
     205 + }
     206 + 
     207 + 
     208 + user_defined_endpoints = {
     209 + 'GET' : {
     210 + # Below is an example endpoint, in case you wish to predifine a few
     211 + 'example-path-to-index' : '/var/www/html/index.html'
     212 + },
     213 +
     214 + 'POST' : {}
     215 + }
     216 + 
     217 + @staticmethod
     218 + def get_endpoints():
     219 + endpoints = list(Synergy_Httpx.user_defined_endpoints['GET'].keys())\
     220 + + list(Synergy_Httpx.user_defined_endpoints['POST'].keys())
     221 + return endpoints.extend([Synergy_Httpx.basic_endpoints['GET'], Synergy_Httpx.basic_endpoints['POST']])
     222 + 
     223 + 
     224 + @staticmethod
     225 + def get_endpoint_local_path(endpoint, method):
     226 + if endpoint in Synergy_Httpx.user_defined_endpoints[method].keys():
     227 + return Synergy_Httpx.user_defined_endpoints[method][endpoint]
     228 + return None
     229 + 
     230 + 
     231 + def do_GET(self):
     232 +
     233 + try:
     234 +
     235 + response_body = Synergy_Httpx.get_endpoint_local_path(self.path[1:], 'GET')
     236 +
     237 + if self.path == f'/{self.basic_endpoints["GET"]}' or response_body:
     238 + self.server_version = "Apache/2.4.1"
     239 + self.sys_version = ""
     240 + self.send_response(200)
     241 + self.send_header('Content-type', 'text/javascript; charset=UTF-8')
     242 + self.send_header('Access-Control-Allow-Origin', '*')
     243 + self.end_headers()
     244 +
     245 + print_to_prompt(f'{GET_REQ} Received for {ORANGE}{self.path}{END} from {ORANGE}{self.client_address[0]}{END}')
     246 +
     247 + if response_body:
     248 + content = get_file_contents(response_body)
     249 + 
     250 + if not content[0]:
     251 + print_to_prompt(f'{FAILED} An error occured while reading the file associated with server path {ORANGE}{self.path}{END} ({content[1]})')
     252 + content = [0, ""]
     253 + 
     254 + else:
     255 + content = [None, "Move on mate."]
     256 +
     257 + self.wfile.write(bytes(content[1], "utf-8"))
     258 + except:
     259 + pass
     260 +
     261 + 
     262 + 
     263 + def do_POST(self):
     264 + 
     265 + try:
     266 + response_body = Synergy_Httpx.get_endpoint_local_path(self.path[1:], 'POST')
     267 + 
     268 + if self.path == f'/{self.basic_endpoints["POST"]}' or response_body:
     269 + self.server_version = "Apache/2.4.1"
     270 + self.sys_version = ""
     271 + self.send_response(200)
     272 + self.send_header('Access-Control-Allow-Origin', '*')
     273 + self.send_header('Content-Type', 'text/plain')
     274 + self.end_headers()
     275 + 
     276 + content_len = int(self.headers.get('Content-Length'))
     277 + post_data = self.rfile.read(content_len)
     278 + print_to_prompt(f'{POST_REQ} Received for {ORANGE}{self.path}{END} from {ORANGE}{self.client_address[0]}{END}')
     279 + print_to_prompt(post_data.decode('utf-8', 'ignore').replace('<br>', '\n'))
     280 + self.wfile.write(b'OK')
     281 + 
     282 + if response_body:
     283 + content = get_file_contents(response_body)
     284 + 
     285 + if not content[0]:
     286 + print_to_prompt(f'{FAILED} An error occured while reading the file associated with server path {ORANGE}{self.path}{END} ({content[1]})')
     287 + content = [None, ""]
     288 + 
     289 + else:
     290 + content = [None, "Move on mate."]
     291 +
     292 + self.wfile.write(bytes(content[1], "utf-8"))
     293 + 
     294 + except:
     295 + pass
     296 + 
     297 + 
     298 + 
     299 + def do_OPTIONS(self):
     300 + 
     301 + self.server_version = "Apache/2.4.1"
     302 + self.sys_version = ""
     303 + self.send_response(200)
     304 + self.send_header('Access-Control-Allow-Origin', self.headers["Origin"])
     305 + self.send_header('Vary', "Origin")
     306 + self.send_header('Access-Control-Allow-Credentials', 'true')
     307 + #self.send_header('Access-Control-Allow-Headers', 'X-form-responseHeaders')
     308 + self.end_headers()
     309 + self.wfile.write(b'OK')
     310 + 
     311 + 
     312 + 
     313 + def log_message(self, format, *args):
     314 + return
     315 + 
     316 + 
     317 + 
     318 +class PrompHelp:
     319 +
     320 + commands = {
     321 +
     322 + 'serve' : {
     323 + 'details' : f'''
     324 + \rCreates a mapping between an server path name and a local file to serve.
     325 + \r
     326 + \r {ORANGE}serve <GET|POST> <HTTP PATH NAME> <LOCAL FILE PATH>{END}
     327 + ''',
     328 + 'least_args' : 3,
     329 + 'max_args' : 3
     330 + },
     331 + 
     332 + 
     333 + 'release' : {
     334 + 'details' : f'''
     335 + \rRemove a resource mapping (stop serving it).
     336 + \r
     337 + \r {ORANGE}release <HTTP PATH NAME>{END}
     338 + ''',
     339 + 'least_args' : 1,
     340 + 'max_args' : 1
     341 + },
     342 + 
     343 +
     344 + 'help' : {
     345 + 'details' : f'''
     346 + \rReally?
     347 + ''',
     348 + 'least_args' : 0,
     349 + 'max_args' : 1
     350 + },
     351 + 
     352 + 'endpoints' : {
     353 + 'details' : f'''
     354 + \rLists all available server endpoints.
     355 + ''',
     356 + 'least_args' : 0,
     357 + 'max_args' : 0
     358 + },
     359 + 
     360 + 'exit' : {
     361 + 'details' : f'''
     362 + \rTerminate the synergy server.
     363 + ''',
     364 + 'least_args' : 0,
     365 + 'max_args' : 0
     366 + },
     367 + 
     368 + 'clear' : {
     369 + 'details' : f'''
     370 + \rCome on man.
     371 + ''',
     372 + 'least_args' : 0,
     373 + 'max_args' : 0
     374 + },
     375 +
     376 + }
     377 +
     378 +
     379 + @staticmethod
     380 + def print_main_help_msg():
     381 +
     382 + print(
     383 + f'''
     384 + \r Command Description
     385 + \r ------- -----------
     386 + \r help [+] Print this message.
     387 + \r serve [+] Add a resource to the synergy server's content.
     388 + \r release [+] Remove a resource from the synergy server's content.
     389 + \r endpoints List all hosted resources.
     390 + \r clear Clear screen.
     391 + \r exit Terminate the synergy server.
     392 + \r
     393 + \r Commands with [+] may require additional arguments.
     394 + \r For details use: {ORANGE}help <COMMAND>{END}
     395 + ''')
     396 +
     397 + 
     398 + 
     399 + @staticmethod
     400 + def print_detailed(cmd):
     401 + print(PrompHelp.commands[cmd]['details']) if cmd in PrompHelp.commands.keys() else print(f'No details for command "{cmd}".')
     402 + 
     403 + 
     404 + 
     405 + @staticmethod
     406 + def validate(cmd, num_of_args):
     407 +
     408 + valid = True
     409 +
     410 + if cmd not in PrompHelp.commands.keys():
     411 + print('Unknown command.')
     412 + valid = False
     413 +
     414 + elif num_of_args < PrompHelp.commands[cmd]['least_args']:
     415 + print('Missing arguments.')
     416 + valid = False
     417 +
     418 + elif num_of_args > PrompHelp.commands[cmd]['max_args']:
     419 + print('Too many arguments. Use "help <COMMAND>" for details.')
     420 + valid = False
     421 +
     422 + return valid
     423 + 
     424 + 
     425 +
     426 +# Tab Auto-Completer
     427 +class Completer(object):
     428 +
     429 + def __init__(self):
     430 +
     431 + self.tab_counter = 0
     432 + self.main_prompt_commands = clone_dict_keys(PrompHelp.commands)
     433 + self.generate_arguments = []
     434 +
     435 +
     436 + def reset_counter(self):
     437 + sleep(0.4)
     438 + self.tab_counter = 0
     439 +
     440 +
     441 + def get_possible_cmds(self, cmd_frag):
     442 +
     443 + matches = []
     444 +
     445 + for cmd in self.main_prompt_commands:
     446 + if re.match(f"^{cmd_frag}", cmd):
     447 + matches.append(cmd)
     448 +
     449 + return matches
     450 +
     451 +
     452 + def get_match_from_list(self, cmd_frag, wordlist):
     453 +
     454 + matches = []
     455 +
     456 + for w in wordlist:
     457 + if re.match(f"^{cmd_frag}", w):
     458 + matches.append(w)
     459 +
     460 + if len(matches) == 1:
     461 + return matches[0]
     462 +
     463 + elif len(matches) > 1:
     464 +
     465 + char_count = 0
     466 +
     467 + while True:
     468 + char_count += 1
     469 + new_search_term_len = (len(cmd_frag) + char_count)
     470 + new_word_frag = matches[0][0:new_search_term_len]
     471 + unique = []
     472 +
     473 + for m in matches:
     474 +
     475 + if re.match(f"^{new_word_frag}", m):
     476 + unique.append(m)
     477 +
     478 + if len(unique) < len(matches):
     479 +
     480 + if self.tab_counter <= 1:
     481 + return new_word_frag[0:-1]
     482 +
     483 + else:
     484 + print('\n')
     485 + print_columns(matches)
     486 + Main_prompt.rst_prompt()
     487 + return False
     488 +
     489 + elif len(unique) == 1:
     490 + return False
     491 +
     492 + else:
     493 + continue
     494 +
     495 + else:
     496 + return False
     497 + 
     498 + 
     499 + def find_common_prefix(self, strings):
     500 +
     501 + if not strings:
     502 + return ""
     503 + 
     504 + prefix = ""
     505 + shortest_string = min(strings, key=len)
     506 + 
     507 + for i, c in enumerate(shortest_string):
     508 + 
     509 + if all(s[i] == c for s in strings):
     510 + prefix += c
     511 + else:
     512 + break
     513 +
     514 + return prefix
     515 + 
     516 + 
     517 + def path_autocompleter(self, root, search_term):
     518 +
     519 + # Check if root or subdir
     520 + path_level = search_term.split(os.sep)
     521 +
     522 + if re.search(os.sep, search_term) and len(path_level) > 1:
     523 + search_term = path_level[-1]
     524 +
     525 + for i in range(0, len(path_level)-1):
     526 + root += f'{os.sep}{path_level[i]}'
     527 +
     528 + dirs = next(os.walk(root))[1]
     529 + match = [d + os.sep for d in dirs if re.match(f'^{re.escape(search_term)}', d)]
     530 + 
     531 + files = next(os.walk(root))[2]
     532 + match += [f for f in files if re.match(f'^{re.escape(search_term)}', f)]
     533 + 
     534 + # Appending match substring
     535 + typed = len(search_term)
     536 +
     537 + if len(match) == 1:
     538 + global_readline.insert_text(match[0][typed:])
     539 + self.tab_counter = 0
     540 + else:
     541 + common_prefix = self.find_common_prefix(match)
     542 + global_readline.insert_text(common_prefix[typed:])
     543 +
     544 + # Print all matches
     545 + if len(match) > 1 and self.tab_counter > 1:
     546 + print('\n')
     547 + print_columns(match)
     548 + self.tab_counter = 0
     549 + Main_prompt.rst_prompt()
     550 + 
     551 +
     552 + def update_prompt(self, typed, new_content, lower = False):
     553 + global_readline.insert_text(new_content[typed:])
     554 + 
     555 + 
     556 + def complete(self, text, state):
     557 +
     558 + text_cursor_position = global_readline.get_endidx()
     559 + self.tab_counter += 1
     560 + line_buffer_val_full = global_readline.get_line_buffer().strip()
     561 + line_buffer_val = line_buffer_val_full[0:text_cursor_position]
     562 + #line_buffer_remains = line_buffer_val_full[text_cursor_position:]
     563 + line_buffer_list = re.sub(' +', ' ', line_buffer_val).split(' ')
     564 + line_buffer_list_len = len(line_buffer_list) if line_buffer_list != [''] else 0
     565 +
     566 + # Return no input or input already matches a command
     567 + if (line_buffer_list_len == 0):
     568 + return
     569 +
     570 + main_cmd = line_buffer_list[0].lower()
     571 +
     572 + # Get prompt command from word fragment
     573 + if line_buffer_list_len == 1:
     574 +
     575 + match = self.get_match_from_list(main_cmd, self.main_prompt_commands)
     576 + self.update_prompt(len(line_buffer_list[0]), match) if match else chill()
     577 +
     578 +
     579 + # Autocomplete endpoints
     580 + elif (main_cmd in ['release']) and (line_buffer_list_len > 1) and (line_buffer_list[-1][0] not in ["/", "~"]):
     581 +
     582 + if line_buffer_list[-1] in Synergy_Httpx.get_endpoints():
     583 + pass
     584 +
     585 + else:
     586 + word_frag = line_buffer_list[-1]
     587 + match = self.get_match_from_list(line_buffer_list[-1], Synergy_Httpx.get_endpoints())
     588 + self.update_prompt(len(line_buffer_list[-1]), match) if match else chill()
     589 + 
     590 + 
     591 + # Autocomplete help
     592 + elif (main_cmd == 'help') and (line_buffer_list_len > 1):
     593 +
     594 + word_frag = line_buffer_list[-1].lower()
     595 + match = self.get_match_from_list(line_buffer_list[-1], self.main_prompt_commands)
     596 + self.update_prompt(len(line_buffer_list[-1]), match, lower = True) if match else chill()
     597 + 
     598 +
     599 + # Autocomplete paths
     600 + elif (main_cmd in ['serve']) and (line_buffer_list_len > 1) and (line_buffer_list[-1][0] in [os.sep, "~"]):
     601 +
     602 + root = os.sep if (line_buffer_list[-1][0] == os.sep) else os.path.expanduser('~')
     603 + search_term = line_buffer_list[-1] if (line_buffer_list[-1][0] != '~') else line_buffer_list[-1].replace('~', os.sep)
     604 + self.path_autocompleter(root, search_term)
     605 +
     606 + # Reset tab counter after 0.5s of inactivity
     607 + Thread(name="reset_counter", target=self.reset_counter).start()
     608 + return
     609 + 
     610 + 
     611 + 
     612 +''' Command Prompt Settings '''
     613 +class Main_prompt:
     614 +
     615 + original_prompt = prompt = f"{UNDERLINE}Synergy-httpx{END} > "
     616 + ready = True
     617 + SPACE = '#>SPACE$<#'
     618 + exec_active = False
     619 + 
     620 +
     621 + @staticmethod
     622 + def rst_prompt(prompt = prompt, prefix = '\r'):
     623 +
     624 + Main_prompt.ready = True
     625 + Main_prompt.exec_active = False
     626 + sys.stdout.write(prefix + Main_prompt.prompt + global_readline.get_line_buffer())
     627 + 
     628 + 
     629 + @staticmethod
     630 + def set_main_prompt_ready():
     631 + Main_prompt.exec_active = False
     632 + Main_prompt.ready = True
     633 + 
     634 + 
     635 +def main():
     636 + 
     637 + print_banner() if not args.quiet else chill()
     638 + 
     639 + try:
     640 + server_port = args.port if args.port else 8080
     641 + 
     642 + try:
     643 + httpd = HTTPServer(('0.0.0.0', server_port), Synergy_Httpx)
     644 + 
     645 + except OSError:
     646 + exit(f'\n{FAILED} Port {server_port} seems to already be in use.{END}\n')
     647 +
     648 + protocol = 'http'
     649 +
     650 + if args.cert and args.key:
     651 +
     652 + try:
     653 + httpd.socket = ssl.wrap_socket (
     654 + httpd.socket,
     655 + keyfile = args.key,
     656 + certfile = args.cert,
     657 + server_side = True,
     658 + ssl_version=ssl.PROTOCOL_TLS
     659 + )
     660 +
     661 + protocol = 'https'
     662 +
     663 + except Exception as e:
     664 + debug(f'Failed to establish SSL: {e}')
     665 + exit(1)
     666 +
     667 +
     668 + server = Thread(target = httpd.serve_forever, args = ())
     669 + server.daemon = True
     670 + server.start()
     671 + print(f'[{ORANGE}0.0.0.0{END}:{ORANGE}{server_port}{END}] Synergy {protocol} server is up and running!')
     672 + 
     673 + try:
     674 + server_public_ip = check_output("curl --connect-timeout 3.14 -s ifconfig.me", shell = True).decode(sys.stdout.encoding)
     675 +
     676 + except:
     677 + server_public_ip = '127.0.0.1'
     678 + pass
     679 +
     680 + for key,val in Synergy_Httpx.basic_endpoints.items():
     681 + print(f'{INFO} Basic {key} endpoint: {protocol}://{server_public_ip}:{server_port}/{val}')
     682 + 
     683 + 
     684 + except KeyboardInterrupt:
     685 + exit(0)
     686 +
     687 + except Exception as e:
     688 + debug(f'Something went wrong: {e}')
     689 + exit(1)
     690 + 
     691 +
     692 + ''' Start tab autoComplete '''
     693 + comp = Completer()
     694 + global_readline.set_completer_delims(' \t\n;')
     695 + global_readline.parse_and_bind("tab: complete")
     696 + global_readline.set_completer(comp.complete)
     697 +
     698 +
     699 + ''' +---------[ Command prompt ]---------+ '''
     700 + while True:
     701 +
     702 + try:
     703 +
     704 + if Main_prompt.ready:
     705 +
     706 + user_input = input(Main_prompt.prompt).strip()
     707 + 
     708 + if user_input == '':
     709 + continue
     710 + 
     711 + # Handle single/double quoted arguments
     712 + quoted_args_single = re.findall("'{1}[\s\S]*'{1}", user_input)
     713 + quoted_args_double = re.findall('"{1}[\s\S]*"{1}', user_input)
     714 + quoted_args = quoted_args_single + quoted_args_double
     715 +
     716 + if len(quoted_args):
     717 +
     718 + for arg in quoted_args:
     719 + space_escaped = arg.replace(' ', Main_prompt.SPACE)
     720 +
     721 + if (space_escaped[0] == "'" and space_escaped[-1] == "'") or (space_escaped[0] == '"' and space_escaped[-1] == '"'):
     722 + space_escaped = space_escaped[1:-1]
     723 +
     724 + user_input = user_input.replace(arg, space_escaped)
     725 +
     726 +
     727 + # Create cmd-line args list
     728 + user_input = user_input.split(' ')
     729 + cmd_list = [w.replace(Main_prompt.SPACE, ' ') for w in user_input if w]
     730 + cmd_list_len = len(cmd_list)
     731 + cmd = cmd_list[0].lower() if cmd_list else ''
     732 +
     733 + # Validate number of args
     734 + valid = PrompHelp.validate(cmd, (cmd_list_len - 1))
     735 +
     736 + if not valid:
     737 + continue
     738 + 
     739 + 
     740 + if cmd == 'help':
     741 + if cmd_list_len == 1:
     742 + PrompHelp.print_main_help_msg()
     743 +
     744 + elif cmd_list_len == 2:
     745 + PrompHelp.print_detailed(cmd_list[1]) if cmd_list[1] in PrompHelp.commands.keys() \
     746 + else print(f'Command {cmd_list[1] if len(cmd_list[1]) <= 10 else f"{cmd_list[1][0:4]}..{cmd_list[1][-4:]}" } does not exist.')
     747 +
     748 + 
     749 + elif cmd == 'serve':
     750 +
     751 + method = cmd_list[1].upper()
     752 + # Check if user supplied a valid method
     753 + if method not in ['GET', 'POST']:
     754 + print('Illegal method.')
     755 + continue
     756 + 
     757 + # Check if path name already mapped
     758 + if cmd_list[2] in Synergy_Httpx.get_endpoints():
     759 + print('This endpoint is already mapped to a resource.')
     760 + continue
     761 +
     762 + # Check if path to local file is valid
     763 + if not os.path.isfile(cmd_list[3]):
     764 + print('File not found.')
     765 + continue
     766 +
     767 + Synergy_Httpx.user_defined_endpoints[method][cmd_list[2]] = cmd_list[3]
     768 + print(f'Resource mapping succesfully added!')
     769 + 
     770 + 
     771 + elif cmd == 'release':
     772 +
     773 + # Check if path name is mapped
     774 + if cmd_list[1] not in Synergy_Httpx.user_defined_endpoints[method].keys():
     775 + print('Endpoint not found.')
     776 + continue
     777 +
     778 + del Synergy_Httpx.user_defined_endpoints[method][cmd_list[1]]
     779 + print(f'Resource mapping succesfully removed.')
     780 + continue
     781 + 
     782 + 
     783 + elif cmd == 'endpoints':
     784 + print(f'\n{BOLD}Basic endpoints{END}:')
     785 + for method in Synergy_Httpx.basic_endpoints.keys():
     786 + print(f'/{Synergy_Httpx.basic_endpoints[method]} ({method})')
     787 + 
     788 + print(f'\n{BOLD}User Defined{END}:')
     789 + for method in Synergy_Httpx.user_defined_endpoints.keys():
     790 + for key,value in Synergy_Httpx.user_defined_endpoints[method].items():
     791 + print(f'/{key} : {value} ({method})')
     792 + print('')
     793 + 
     794 + 
     795 + elif cmd == 'clear':
     796 + os.system('clear')
     797 + 
     798 + 
     799 + elif cmd == 'exit':
     800 + raise KeyboardInterrupt
     801 + 
     802 + else:
     803 + continue
     804 + 
     805 + except KeyboardInterrupt:
     806 +
     807 + Main_prompt.ready = True
     808 + choice = input('\nAre you sure you wish to exit? [y/n]: ').lower().strip()
     809 + verified = True if choice in ['yes', 'y'] else False
     810 +
     811 + if verified:
     812 + print_meta()
     813 + sys.exit(0)
     814 + 
     815 + 
     816 +if __name__ == '__main__':
     817 + main()
     818 + 
  • ■ ■ ■ ■ ■ ■
    synergy_httpx_server.py
    1  -#!/bin/python3
    2  -#
    3  -# Author: Panagiotis Chartas (t3l3machus)
    4  -# https://github.com/t3l3machus
    5  - 
    6  -from http.server import HTTPServer, BaseHTTPRequestHandler
    7  -import ssl, sys, base64, re, os, argparse
    8  -from warnings import filterwarnings
    9  -from datetime import date, datetime
    10  -from urllib.parse import unquote, urlparse
    11  -from threading import Thread
    12  -from io import StringIO
    13  -from time import sleep
    14  -from subprocess import check_output
    15  - 
    16  -filterwarnings("ignore", category = DeprecationWarning)
    17  - 
    18  -''' Colors '''
    19  -MAIN = '\033[38;5;50m'
    20  -GREEN = '\033[38;5;82m'
    21  -BLUE = '\033[0;38;5;12m'
    22  -LPURPLE = '\033[0;38;5;201m'
    23  -ORANGE = '\033[0;38;5;214m'
    24  -ORANGEB = '\033[1;38;5;214m'
    25  -PURPLE = '\033[0;38;5;141m'
    26  -B_PURPLE = '\033[45m'
    27  -YELLOW="\033[0;38;5;11m"
    28  -RED = '\033[1;31m'
    29  -B_RED = '\033[41m'
    30  -END = '\033[0m'
    31  -B_END = '\033[49m'
    32  -BOLD = '\033[1m'
    33  -ULINE = '\033[4m'
    34  - 
    35  - 
    36  -''' MSG Prefixes '''
    37  -INFO = f'[{MAIN}Info{END}]'
    38  -WARN = f'[{ORANGE}Warning{END}]'
    39  -DBG = f'[{ORANGE}Debug{END}]'
    40  -IMPORTANT = f'[{ORANGE}Important{END}]'
    41  -FAILED = f'[{RED}Fail{END}]'
    42  -POST_DATA = f'[{PURPLE}Post-data{END}]'
    43  - 
    44  -parser = argparse.ArgumentParser()
    45  -parser.add_argument("-c", "--cert", action="store", help = "Your certificate.")
    46  -parser.add_argument("-k", "--key", action="store", help = "The private key for your certificate. ")
    47  -parser.add_argument("-p", "--port", action="store", help = "Server port.", type = int)
    48  -parser.add_argument("-f", "--file", action="store", help = "File to be served via GET.")
    49  - 
    50  -args = parser.parse_args()
    51  - 
    52  -def haxor_print(text, leading_spaces = 0):
    53  - 
    54  - text_chars = list(text)
    55  - current, mutated = '', ''
    56  - 
    57  - for i in range(len(text)):
    58  -
    59  - original = text_chars[i]
    60  - current += original
    61  - mutated += f'\033[1;38;5;82m{text_chars[i].upper()}\033[0m'
    62  - print(f'\r{" " * leading_spaces}{mutated}', end = '')
    63  - sleep(0.05)
    64  - print(f'\r{" " * leading_spaces}{current}', end = '')
    65  - mutated = current
    66  - 
    67  - print(f'\r{" " * leading_spaces}{text}\n')
    68  - 
    69  - 
    70  -def print_banner():
    71  - 
    72  - padding = ' '
    73  - 
    74  - S = [['░', '█','▀','▀', '░'], ['░', '▀','▀','▄', '░'], ['░', '▀','▀','▀', '░']]
    75  - Y = [['█', '░','░','█','░'], ['█', '▄','▄','█','░'], ['▄','▄','▄','▀','░']]
    76  - N = [['█', '▀','▀','▄', '░'], ['█', '░','░','█','░'], ['▀', '░','░','▀','░']]
    77  - E = [['█','▀','▀','▀', '░'], ['█','▀','▀','▀', '░'], ['▀','▀','▀', '▀', '░']]
    78  - R = [['█', '▀','▀','▄', '░'], ['█', '▄','▄','▀','░'], ['▀', '░', '▀','▀','░']]
    79  - G = [['█','▀','▀','▀', '░'], ['█', '░','▀','▄' '░'], ['▀','▀','▀','▀','░']]
    80  - Y = [['█', '░','░','█','░'], ['█', '▄','▄','█','░'], ['▄','▄','▄','▀','░']]
    81  - H = [['░','░','░','█','░','░','█','░'], ['░','░','░','█', '█','▀','█','░'], ['░','░','░','█','░','░','█','░']]
    82  - T = [['▀', '█','▀','░'], ['░', '█','░','░'], ['░', '█','░','░']]
    83  - P = [['▄', '▀','▀','▄','░'], ['█', '▄','▄','█','░'], ['█','░','░','░','░']]
    84  - X = [['█','░','░','█'], [' ', '▀','▄',''], ['█','░','░','█']]
    85  - 
    86  - banner = [S,Y,N,E,R,G,Y,H,T,T,P,X]
    87  - final = []
    88  - print('\r')
    89  - init_color = 31
    90  - txt_color = init_color
    91  - cl = 0
    92  - 
    93  - for charset in range(0, 3):
    94  - for pos in range(0, len(banner)):
    95  - for i in range(0, len(banner[pos][charset])):
    96  - clr = f'\033[38;5;{txt_color}m'
    97  - char = f'{clr}{banner[pos][charset][i]}'
    98  - final.append(char)
    99  - cl += 1
    100  - txt_color = txt_color + 36 if cl <= 3 else txt_color
    101  - 
    102  - cl = 0
    103  - 
    104  - txt_color = init_color
    105  - init_color += 31
    106  - 
    107  - if charset < 2: final.append('\n ')
    108  -
    109  -
    110  - print(f" {''.join(final)}")
    111  - haxor_print('by t3l3machus', 50)
    112  - 
    113  - 
    114  - 
    115  -def print_green(msg):
    116  - print(f'{GREEN}{msg}{END}')
    117  - 
    118  - 
    119  -def debug(msg):
    120  - print(f'{DBG} {msg}{END}')
    121  - 
    122  - 
    123  -def chill():
    124  - pass
    125  - 
    126  - 
    127  -# -------------- HTTPS Server -------------- #
    128  -class Https_Server(BaseHTTPRequestHandler):
    129  - 
    130  - valid_endpoints = {
    131  - 'GET' : 'x3Rty7',
    132  - 'POST' : 'aWq8tY'
    133  - }
    134  - 
    135  - 
    136  - def do_GET(self):
    137  -
    138  - try:
    139  - if self.path == f'/{self.valid_endpoints["GET"]}':
    140  - self.server_version = "Apache/2.4.1"
    141  - self.sys_version = ""
    142  - self.send_response(200)
    143  - self.send_header('Content-type', 'text/javascript; charset=UTF-8')
    144  - self.send_header('Access-Control-Allow-Origin', '*')
    145  - self.end_headers()
    146  -
    147  - try:
    148  - print(f'GET {self.path} :\nFrom: {self.client_address[0]}\nCookies: {self.headers["Cookies"]}')
    149  - except:
    150  - print(f'GET {self.path} :\nFrom: {self.client_address[0]}\nCookies: ')
    151  -
    152  - if args.file:
    153  - f = open(args.file, 'r')
    154  - content = f.read()
    155  - f.close()
    156  -
    157  - else:
    158  - content = "Move on mate."
    159  -
    160  - self.wfile.write(bytes(content, "utf-8"))
    161  -
    162  - except:
    163  - pass
    164  - 
    165  - 
    166  - 
    167  - 
    168  - def do_POST(self):
    169  - 
    170  - try:
    171  - if self.path == f'/{self.valid_endpoints["POST"]}':
    172  - self.server_version = "Apache/2.4.1"
    173  - self.sys_version = ""
    174  - self.send_response(200)
    175  - self.send_header('Access-Control-Allow-Origin', '*')
    176  - self.send_header('Content-Type', 'text/plain')
    177  - self.end_headers()
    178  - self.wfile.write(b'OK')
    179  - real_action = self.headers["Action"]
    180  - content_len = int(self.headers.get('Content-Length'))
    181  - post_data = self.rfile.read(content_len)
    182  - print(f'{POST_DATA} Received from {self.client_address[0]}')
    183  - print(post_data.decode('utf-8', 'ignore').replace('<br>', '\n'))
    184  - except:
    185  - pass
    186  - 
    187  - 
    188  - 
    189  - def do_OPTIONS(self):
    190  - 
    191  - self.server_version = "Apache/2.4.1"
    192  - self.sys_version = ""
    193  - self.send_response(200)
    194  - self.send_header('Access-Control-Allow-Origin', self.headers["Origin"])
    195  - self.send_header('Vary', "Origin")
    196  - self.send_header('Access-Control-Allow-Credentials', 'true')
    197  - #self.send_header('Access-Control-Allow-Headers', 'X-form-responseHeaders')
    198  - self.end_headers()
    199  - self.wfile.write(b'OK')
    200  - 
    201  - 
    202  - 
    203  - def log_message(self, format, *args):
    204  - return
    205  - 
    206  - 
    207  - 
    208  -def main():
    209  - print_banner()
    210  - try:
    211  - server_port = args.port if args.port else 443
    212  - 
    213  - try:
    214  - httpd = HTTPServer(('0.0.0.0', server_port), Https_Server)
    215  - 
    216  - except OSError:
    217  - exit(f'\n{FAILED} Port {server_port} seems to already be in use.{END}\n')
    218  -
    219  - protocol = 'http'
    220  -
    221  - if args.cert and args.key:
    222  -
    223  - try:
    224  - httpd.socket = ssl.wrap_socket (
    225  - httpd.socket,
    226  - keyfile = args.key,
    227  - certfile = args.cert,
    228  - server_side = True,
    229  - ssl_version=ssl.PROTOCOL_TLS
    230  - )
    231  -
    232  - protocol = 'https'
    233  -
    234  - except Exception as e:
    235  - debug(f'Failed to establish SSL: {e}')
    236  - exit(1)
    237  -
    238  -
    239  - server = Thread(target = httpd.serve_forever, args = ())
    240  - server.daemon = True
    241  - server.start()
    242  - print(f'{INFO} Synergy {protocol} server is up and running!')
    243  - 
    244  - try:
    245  - server_public_ip = check_output("curl --connect-timeout 3.14 -s ifconfig.me", shell = True).decode(sys.stdout.encoding)
    246  -
    247  - except:
    248  - server_public_ip = '127.0.0.1'
    249  - pass
    250  -
    251  - for key,val in Https_Server.valid_endpoints.items():
    252  - print(f'{INFO} {key} endpoint: {protocol}://{server_public_ip}:{server_port}/{val}')
    253  - 
    254  - while True:
    255  - sleep(5)
    256  -
    257  - except KeyboardInterrupt:
    258  - exit(0)
    259  -
    260  - except Exception as e:
    261  - debug(f'Something went wrong: {e}')
    262  - exit(1)
    263  - 
    264  - 
    265  - 
    266  -if __name__ == '__main__':
    267  - main()
    268  - 
Please wait...
Page is in error, reload to recover