diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index bbb30b6..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.travis.yml b/.travis.yml index 2707405..6f71764 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,31 @@ language: python + +os: + - linux + +addons: + apt: + packages: + - graphviz + - python-tk + - tshark + python: - "2.7" - "3.6" + matrix: allow_failures: - - python: "3.6" + - python: "2.7" + before_install: - - pip install -U pytest pytest-cov - - pip install codecov - - pip install flake8 + - pip install -U pytest pytest-cov + - pip install codecov + - pip install flake8 + install: - pip install -r requirements.txt + before_script: # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics diff --git a/LICENSE b/LICENSE index 505975c..d159169 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,339 @@ -MIT License - -Copyright (c) 2017 Srinivas Piskala Ganesh Babu - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. diff --git a/Source/Module/communication_details_fetch.py b/Source/Module/communication_details_fetch.py new file mode 100644 index 0000000..eca965c --- /dev/null +++ b/Source/Module/communication_details_fetch.py @@ -0,0 +1,43 @@ +import memory + +# Library Import +import ipwhois +from dns import reversename, resolver +import socket +# Module Import +import pcap_reader +import netaddr + +# Class Communication or Traffic Details Fetch + +class trafficDetailsFetch(): + + def __init__(self, option): + for host in memory.destination_hosts: + if not memory.destination_hosts[host]: + if option == "whois": + memory.destination_hosts[host] = self.whois_info_fetch(host) + else: + memory.destination_hosts[host] = self.dns(host) + + def whois_info_fetch(self, ip): + try: + whois_info = ipwhois.IPWhois(ip).lookup_rdap() + except: + whois_info = "NoWhoIsInfo" + return whois_info + + def dns(self, ip): + try: + dns_info = socket.gethostbyaddr(ip)[0] + except: + dns_info = "NotResolvable" + return dns_info + +def main(): + capture = pcap_reader.PcapEngine('examples/test.pcap', "scapy") + details = trafficDetailsFetch("sock") + print(memory.destination_hosts) + print("\n") + +#main() diff --git a/Source/Module/device_details_fetch.py b/Source/Module/device_details_fetch.py new file mode 100644 index 0000000..72ed20a --- /dev/null +++ b/Source/Module/device_details_fetch.py @@ -0,0 +1,66 @@ +""" +Module device_details +""" +# Library Import +import urllib#.request +import json +import logging +# Module Import +import pcap_reader +import memory +import threading +from netaddr import * + +class fetchDeviceDetails: + + def __init__(self, option="ieee"): + """ + Init + """ + self.target_oui_database = option + + def fetch_info(self): + for ip in memory.lan_hosts: + if self.target_oui_database == "api": + memory.lan_hosts[ip]["device_vendor"] = self.oui_identification_via_api(memory.lan_hosts[ip]["mac"]) + else: + memory.lan_hosts[ip]["device_vendor"], memory.lan_hosts[ip]["vendor_address"] = self.oui_identification_via_ieee(memory.lan_hosts[ip]["mac"]) + mac = memory.lan_hosts[ip]["mac"].replace(":",".") + if ":" in ip: + ip_san = ip.replace(":",".") + else: + ip_san = ip + memory.lan_hosts[ip]["node"] = ip_san+"\n"+mac+"\n"+memory.lan_hosts[ip]['device_vendor'] + + def oui_identification_via_api(self, mac): + url = "http://macvendors.co/api/" + mac + api_request = urllib.request.Request(url, headers={'User-Agent':'PcapXray'}) + try: + apiResponse = urllib.request.urlopen(api_request) + details = json.loads(apiResponse.read()) + #reportThread = threading.Thread(target=reportGen.reportGen().deviceDetailsReport,args=(details,)) + #reportThread.start() + return details["result"]["company"], details["result"]["address"] + except Exception as e: + logging.info("device_details module: oui identification failure via api" + str(e)) + return "Unknown", "Unknown" + + def oui_identification_via_ieee(self, mac): + try: + mac_obj = EUI(mac) + mac_oui = mac_obj.oui + return mac_oui.registration().org, mac_oui.registration().address + except Exception as e: + logging.info("device_details module: oui identification failure via ieee " + str(e)) + return "Unknown", "Unknown" + +def main(): + filename = "test.pcap" + pcap_reader.PcapEngine('examples/test.pcap', "scapy") + fetchDeviceDetails("ieee").fetch_info() + print(memory.lan_hosts) + +#main() + +# MAC Oui Identification Module +# LAN IP and Getway Identification diff --git a/Source/Module/examples/tamu_drivebyinc_0_intrusion.pcap b/Source/Module/examples/tamu_drivebyinc_0_intrusion.pcap new file mode 100644 index 0000000..a123f74 Binary files /dev/null and b/Source/Module/examples/tamu_drivebyinc_0_intrusion.pcap differ diff --git a/Source/Module/examples/tamu_microservice_0_intrusion.pcap b/Source/Module/examples/tamu_microservice_0_intrusion.pcap new file mode 100644 index 0000000..a123f74 Binary files /dev/null and b/Source/Module/examples/tamu_microservice_0_intrusion.pcap differ diff --git a/Source/Module/examples/tamu_readingrainbow_0_network_enumeration.pcap b/Source/Module/examples/tamu_readingrainbow_0_network_enumeration.pcap new file mode 100644 index 0000000..11d73ea Binary files /dev/null and b/Source/Module/examples/tamu_readingrainbow_0_network_enumeration.pcap differ diff --git a/Source/Module/legacy/backup b/Source/Module/legacy/backup new file mode 100644 index 0000000..b6e57b9 --- /dev/null +++ b/Source/Module/legacy/backup @@ -0,0 +1,335 @@ + +import logging +import sys +from netaddr import * + +class PcapEngine(): + + def __init__(self, pcap_file_name, pcap_parser_engine="scapy"): + self.packetDB = {} + self.destination_address = {} + if pcap_parser_engine == "scapy": + try: + from scapy.all import rdpcap + except: + logging.error("Cannot import selected pcap engine: Scapy!") + sys.exit() + # Scapy sessions and other types use more O(N) iterations so just + # use rdpcap + self.packets = rdpcap(pcap_file_name) + elif pcap_parser_engine == "pyshark": + try: + import pyshark + except: + logging.error("Cannot import selected pcap engine: PyShark!") + sys.exit() + self.packets = pyshark.FileCapture(pcap_file_name) + # Add other pcap engine modules to generate packetDB + self.analyse_packet_data() + + def analyse_packet_data(self): + """ + PcapXray runs only one O(N) packets once to memoize + # - Parse the packets to create a usable DB + # - Store the db as json on a file in cache folder (to not repeat read) + # - All the protocol parsing should be included here + """ + for packet in self.packets: # O(N) packet iteration + if "IP" in packet: + # Handle IP packets that originated from LAN (Internal Network) + if IPAddress(packet["IP"].src).is_private(): + key = packet["IP"].src + " => " + packet["IP"].dst + ":" + packet["TCP"].dport if "TCP" in packet else packet["UDP"].dport + source_private_ip = key + if source_private_ip not in self.packetDB: + self.packetDB[source_private_ip] = {} + # TCP + if "TCP" in packet and "TCP" not in self.packetDB[source_private_ip]: + self.packetDB[source_private_ip]["TCP"] = {} + # UDP + if "UDP" in packet and "UDP" not in self.packetDB[source_private_ip]: + self.packetDB[source_private_ip]["UDP"] = {} + # Ethernet Layer ( Mac address ) + if "Ether" in packet and "Ethernet" not in self.packetDB[source_private_ip]: + self.packetDB[source_private_ip]["Ethernet"] = packet["Ethernet"].src + # HTTP Packets + if "TCP" in packet and packet["TCP"].dport == 80: + if "HTTP" not in self.packetDB[source_private_ip]["TCP"]: + self.packetDB[source_private_ip]["TCP"]["HTTP"] = {} + if "Server" not in self.packetDB[source_private_ip]["TCP"]["HTTP"]: + self.packetDB[source_private_ip]["TCP"]["HTTP"]["Server"] = [] + if "Payload" not in self.packetDB[source_private_ip]["TCP"]["HTTP"]: + self.packetDB[source_private_ip]["TCP"]["HTTP"]["Payload"] = [] + # Destination Address + self.packetDB[source_private_ip]["TCP"]["HTTP"]["Server"].append(packet["IP"].dst) + # Server Details + self.packetDB[source_private_ip]["TCP"]["HTTP"]["Server"] = list(set(self.packetDB[packet["IP"].src]["TCP"]["HTTP"]["Server"])) + # Payload recording + self.packetDB[source_private_ip]["TCP"]["HTTP"]["Payload"].append(packet) + # SSL Packets + # - Get handshake details? + if "TCP" in packet and packet["TCP"].dport == 443: + if "HTTPS" not in self.packetDB[source_private_ip["TCP"]: + self.packetDB[source_private_ip]["TCP"]["HTTPS"] = [] + if packet["IP"].dst not in self.packetDB[source_private_ip]["TCP"]["HTTPS"]: + self.packetDB[source_private_ip]["TCP"]["HTTPS"].append(packet["IP"].dst) + self.packetDB[source_private_ip]["TCP"]["HTTPS"] = list(set(self.packetDB[source_private_ip]["TCP"]["HTTPS"])) + if "TCP" in packet: + if "PortsConnected" not in self.packetDB[source_private_ip]["TCP"]: + self.packetDB[source_private_ip]["TCP"]["PortsConnected"] = [] + destinaton_port = packet["TCP"].dport + destination_ip = packet["IP"].dst + if (destination_ip,destination_port) not in self.packetDB[source_private_ip]["TCP"]["PortsConnected"]: + self.packetDB[source_private_ip]["TCP"]["PortsConnected"].append((destination_ip,destination_port)) + if packet.haslayer(UDP): + if "PortsConnected" not in self.packetDB[source_private_ip]["UDP"]: + self.packetDB[source_private_ip]["UDP"]["PortsConnected"] = [] + destination_port = packet.getlayer(UDP).dport + destination_ip = packet.getlayer(IP).dst + if (destination_ip,destination_port) not in self.packetDB[source_private_ip]["UDP"]["PortsConnected"]: + self.packetDB[source_private_ip]["UDP"]["PortsConnected"].append((destination_ip,destination_port)) + + #HTTPS + #Tor + #Malicious + # HTTP Payload Decrypt + if IPAddress(packet.getlayer(IP).dst).is_private(): + if packet.getlayer(IP).dst not in self.packetDB: + self.packetDB[packet.getlayer(IP).dst] = {} + if packet.haslayer(TCP) and "TCP" not in self.packetDB[packet.getlayer(IP).dst]: + self.packetDB[packet.getlayer(IP).dst]["TCP"] = {} + if packet.haslayer(UDP) and "UDP" not in self.packetDB[packet.getlayer(IP).dst]: + self.packetDB[packet.getlayer(IP).dst]["UDP"] = {} + if packet.haslayer(Ether) and "Ethernet" not in self.packetDB[packet.getlayer(IP).dst]: + self.packetDB[packet.getlayer(IP).dst]["Ethernet"] = packet.getlayer(Ether).dst + if packet.haslayer(TCP) and packet.getlayer(TCP).sport == 80: + if "HTTP" not in self.packetDB[packet.getlayer(IP).dst]["TCP"]: + self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTP"] = {} + if "Server" not in self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTP"]: + self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTP"]["Server"] = [] + if "Payload" not in self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTP"]: + self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTP"]["Payload"] = [] + self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTP"]["Server"].append(packet.getlayer(IP).src) + self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTP"]["Server"] = list(set(self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTP"]["Server"])) + self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTP"]["Payload"].append(packet) + if packet.haslayer(TCP) and packet.getlayer(TCP).sport == 443: + if "HTTPS" not in self.packetDB[packet.getlayer(IP).dst]["TCP"]: + self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTPS"] = [] + if packet.getlayer(IP).src not in self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTPS"]: + self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTPS"].append(packet.getlayer(IP).src) + self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTPS"] = list(set(self.packetDB[packet.getlayer(IP).dst]["TCP"]["HTTPS"])) + if packet.haslayer(TCP): + if "PortsConnected" not in self.packetDB[packet.getlayer(IP).dst]["TCP"]: + self.packetDB[packet.getlayer(IP).dst]["TCP"]["PortsConnected"] = [] + port = packet.getlayer(TCP).sport + ip = packet.getlayer(IP).src + if (ip,port) not in self.packetDB[packet.getlayer(IP).dst]["TCP"]["PortsConnected"]: + self.packetDB[packet.getlayer(IP).dst]["TCP"]["PortsConnected"].append((ip,port)) + if packet.haslayer(UDP): + if "PortsConnected" not in self.packetDB[packet.getlayer(IP).dst]["UDP"]: + self.packetDB[packet.getlayer(IP).dst]["UDP"]["PortsConnected"] = [] + port = packet.getlayer(UDP).sport + ip = packet.getlayer(IP).src + if (ip, port) not in self.packetDB[packet.getlayer(IP).dst]["UDP"]["PortsConnected"]: + self.packetDB[packet.getlayer(IP).dst]["UDP"]["PortsConnected"].append((ip, port)) + + +# Module Driver +def main(): + pcapfile = PcapEngine('examples/test.pcap', "pyshark") + #print pcapfile.packetDB + #print(self.packetDB["TCP 192.168.0.26:64707 > 172.217.12.174:443"].summary()) + #print(self.packetDB["TCP 172.217.12.174:443 > 192.168.0.26:64707"].summary()) + #self.packetDB.conversations(type="jpg", target="> test.jpg") + +main() + + + + + + + + + + +import logging +import sys +from netaddr import * + +class PcapEngine(): + + def __init__(self, pcap_file_name, pcap_parser_engine="scapy"): + """ + PcapEngine: To support different pcap parser backend engine to operate reading pcap + Current Support: + * Scapy + * Pyshark + - The init function imports libraries based on the parser engine selected + Return: + * packetDB ==> Full Duplex Packet Streams + - Used while finally plotting streams as graph + - dump packets during report generation + * lan_hosts ==> Private IP (LAN) list + - device details + * destination_hosts ==> Destination Hosts + - communication details + - tor identification + - malicious identification + """ + self.packetDB = {} + self.lan_hosts = {} + self.destination_hosts = {} + self.engine = pcap_parser_engine + if pcap_parser_engine == "scapy": + try: + from scapy.all import rdpcap + except: + logging.error("Cannot import selected pcap engine: Scapy!") + sys.exit() + # Scapy sessions and other types use more O(N) iterations so just + # - use rdpcap + our own iteration (create full duplex streams) + self.packets = rdpcap(pcap_file_name) + elif pcap_parser_engine == "pyshark": + try: + import pyshark + except: + logging.error("Cannot import selected pcap engine: PyShark!") + sys.exit() + self.packets = pyshark.FileCapture(pcap_file_name) + + # Pyshark has different TCP port access change + Ethernet layer access + self.analyse_packet_data_with_pyshark() + + # Add other pcap engine modules to generate packetDB + + + def analyse_packet_data_with_scapy(self): + """ + PcapXray runs only one O(N) packets once to memoize + # - Parse the packets to create a usable DB + # - Store the db as json on a file in cache folder (to not repeat read) + # - All the protocol parsing should be included here + """ + for packet in self.packets: # O(N) packet iteration + if "IP" in packet: + # Handle IP packets that originated from LAN (Internal Network) + source_private_ip = None + if "TCP" in packet or "UDP" in packet: + if IPAddress(packet["IP"].src).is_private(): + key = packet["IP"].src + "/" + packet["IP"].dst + "/" + str(packet["TCP"].dport) if "TCP" in packet else str(packet["UDP"].dport) + source_private_ip = key + elif IPAddress(packet["IP"].dst).is_private(): + key = packet["IP"].dst + "/" + packet["IP"].src + "/" + str(packet["TCP"].sport) if "TCP" in packet else str(packet["UDP"].sport) + source_private_ip = key + elif "ICMP" in packet: + key = packet["IP"].src + "/" + packet["IP"].dst + "/" + "ICMP" + source_private_ip = key + + # IntraNetwork vs InterNetwork Hosts list + if private_source: + self.lan_hosts[packet["IP"].src] = "" + else: + self.destination_hosts[packet["IP"].src] = {} + if private_destination: + self.lan_hosts[packet["IP"].dst] = "" + else: + self.destination_hosts[packet["IP"].dst] = {} + + if source_private_ip: + if source_private_ip not in self.packetDB: + self.packetDB[source_private_ip] = {} + # Ethernet Layer ( Mac address ) + if "Ether" in packet and "Ethernet" not in self.packetDB[source_private_ip]: + self.packetDB[source_private_ip]["Ethernet"] = {} + self.packetDB[source_private_ip]["Ethernet"]["src"] = packet["Ethernet"].src + self.packetDB[source_private_ip]["Ethernet"]["dst"] = packet["Ethernet"].dst + # HTTP Packets + if "Payloads" not in packet: + # Payload recording + self.packetDB[source_private_ip]["Payload"] = [] + self.packetDB[source_private_ip]["Payload"].append(packet["Raw"].load if "Raw" in packet else None) + + def analyse_packet_data_with_pyshark(self): + """ + PcapXray runs only one O(N) packets once to memoize + # - Parse the packets to create a usable DB + # - Store the db as json on a file in cache folder (to not repeat read) + # - All the protocol parsing should be included here + """ + for packet in self.packets: # O(N) packet iteration + if "IP" in packet: + # Handle IP packets that originated from LAN (Internal Network) + source_private_ip = None + private_source = IPAddress(packet["IP"].src).is_private() + private_destination = IPAddress(packet["IP"].src).is_private() + + if "TCP" in packet or "UDP" in packet: + # Sort out indifferences in pcap engine + if self.engine == "pyshark": + tcp_src = str(packet["TCP"].srcport if "TCP" in packet else packet["UDP"].srcport) + tcp_dst = str(packet["TCP"].dstport if "TCP" in packet else packet["UDP"].dstport) + else: + tcp_src = str(packet["TCP"].sport if "TCP" in packet else packet["UDP"].sport) + tcp_dst = str(packet["TCP"].dport if "TCP" in packet else packet["UDP"].dport) + + if private_source: + key = packet["IP"].src + "/" + packet["IP"].dst + "/" + tcp_dst + source_private_ip = key + elif private_destination: + key = packet["IP"].dst + "/" + packet["IP"].src + "/" + tcp_src + source_private_ip = key + + elif "ICMP" in packet: + key = packet["IP"].src + "/" + packet["IP"].dst + "/" + "ICMP" + source_private_ip = key + + # IntraNetwork vs InterNetwork Hosts list + if private_source and not private_destination: + self.lan_hosts[packet["IP"].src] = "" + else: + self.destination_hosts[packet["IP"].src] = {} + if private_destination and not private_source: + self.lan_hosts[packet["IP"].dst] = "" + else: + self.destination_hosts[packet["IP"].dst] = {} + + if source_private_ip: + if source_private_ip not in self.packetDB: + self.packetDB[source_private_ip] = {} + # Ethernet Layer ( Mac address ) + if "Ethernet" not in self.packetDB[source_private_ip]: + self.packetDB[source_private_ip]["Ethernet"] = {} + # HTTP Packets + if "Payloads" not in self.packetDB: + # Payload recording + self.packetDB[source_private_ip]["Payload"] = [] + if self.engine == "pyshark": + self.packetDB[source_private_ip]["Ethernet"]["src"] = packet["ETH"].src + self.packetDB[source_private_ip]["Ethernet"]["dst"] = packet["ETH"].dst + if "data" in packet: + self.packetDB[source_private_ip]["Payload"].append(packet.data) + else: + self.packetDB[source_private_ip]["Ethernet"]["src"] = packet["Ethernet"].src + self.packetDB[source_private_ip]["Ethernet"]["dst"] = packet["Ethernet"].dst + if "Raw" in packet: + self.packetDB[source_private_ip]["Payload"].append(packet["Raw"].load) + +# Module Driver +def main(): + pcapfile = PcapEngine('examples/test.pcap', "pyshark") + print(pcapfile.packetDB) + print(pcapfile.lan_hosts) + print(pcapfile.destination_hosts) + #print(self.packetDB["TCP 192.168.0.26:64707 > 172.217.12.174:443"].summary()) + #print(self.packetDB["TCP 172.217.12.174:443 > 192.168.0.26:64707"].summary()) + #self.packetDB.conversations(type="jpg", target="> test.jpg") + +main() + +# Sort payload by time... +# SSL Packets +# - Get handshake details? +#HTTPS +#Tor +#Malicious +# HTTP Payload Decrypt diff --git a/Source/Module/communicationDetailsFetch.py b/Source/Module/legacy/communicationDetailsFetch.py similarity index 94% rename from Source/Module/communicationDetailsFetch.py rename to Source/Module/legacy/communicationDetailsFetch.py index 2617d6a..0a8fa0a 100644 --- a/Source/Module/communicationDetailsFetch.py +++ b/Source/Module/legacy/communicationDetailsFetch.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Library Import import ipwhois from dns import reversename, resolver @@ -31,7 +32,6 @@ def __init__(self, packetDB, out=None): if "ip_details" not in self.communication_details[ip]: self.communication_details[ip]["ip_details"] = {} self.communication_details[ip]["ip_details"] = {key: {} for key in ips} - #print self.communication_details[ip]["ip_details"].keys() self.dns(ip, self.communication_details[ip]["ip_details"].keys()) if out: out.put(self.communication_details) @@ -58,7 +58,6 @@ def whois_info_fetch(self, ip, ips): def dns(self, ip, ips): for i in ips: - # print ip, i if "dns" not in self.communication_details[ip]["ip_details"][i]: self.communication_details[ip]["ip_details"][i]["dns"] = "" try: @@ -69,9 +68,9 @@ def dns(self, ip, ips): def main(): capture = pcapReader.pcapReader("lanExample.pcap") - print "read" + print("read") details = trafficDetailsFetch(capture.packetDB) - print details.communication_details - print "\n" + print(details.communication_details) + print("\n") #main() diff --git a/Source/Module/deviceDetailsFetch.py b/Source/Module/legacy/deviceDetailsFetch.py similarity index 92% rename from Source/Module/deviceDetailsFetch.py rename to Source/Module/legacy/deviceDetailsFetch.py index 8110997..73ce68c 100644 --- a/Source/Module/deviceDetailsFetch.py +++ b/Source/Module/legacy/deviceDetailsFetch.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Library Import import urllib2 import json @@ -30,7 +31,7 @@ def main(): pcapfile = pcapReader.pcapReader('test.pcap') for ip in pcapfile.packetDB: macObj = fetchDeviceDetails(pcapfile.packetDB[ip]) - print macObj.oui_identification() + print(macObj.oui_identification()) #main() # MAC Oui Identification Module diff --git a/Source/Module/maliciousTrafficIdentifier.py b/Source/Module/legacy/maliciousTrafficIdentifier.py similarity index 91% rename from Source/Module/maliciousTrafficIdentifier.py rename to Source/Module/legacy/maliciousTrafficIdentifier.py index 5a56e18..8c789f0 100644 --- a/Source/Module/maliciousTrafficIdentifier.py +++ b/Source/Module/legacy/maliciousTrafficIdentifier.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Custom Module Import import pcapReader @@ -28,10 +29,10 @@ def malicious_traffic_detection(self, ip, sessions, dns): def main(): malicious_capture = pcapReader.pcapReader("torexample.pcapng") - print malicious_capture.packetDB + print(malicious_capture.packetDB) dns_details = {} mal_identify = maliciousTrafficIdentifier(malicious_capture.packetDB, dns_details) - print mal_identify.possible_malicious_traffic + print(mal_identify.possible_malicious_traffic) #main() diff --git a/Source/Module/pcapReader.py b/Source/Module/legacy/pcapReader.py similarity index 99% rename from Source/Module/pcapReader.py rename to Source/Module/legacy/pcapReader.py index 68cbd91..ab91d55 100644 --- a/Source/Module/pcapReader.py +++ b/Source/Module/legacy/pcapReader.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Import Section - Dependant Module inclusion from scapy.all import * from netaddr import * @@ -125,5 +126,5 @@ def populate(self, protocol): # Module Driver def main(): pcapfile = pcapReader('lanExample.pcap') - print pcapfile.packetDB + print(pcapfile.packetDB) #main() diff --git a/Source/Module/plotLanNetwork.py b/Source/Module/legacy/plotLanNetwork.py similarity index 96% rename from Source/Module/plotLanNetwork.py rename to Source/Module/legacy/plotLanNetwork.py index afeecdb..0a57edf 100644 --- a/Source/Module/plotLanNetwork.py +++ b/Source/Module/legacy/plotLanNetwork.py @@ -1,3 +1,4 @@ +from __future__ import print_function #File Import import pcapReader import communicationDetailsFetch @@ -6,7 +7,7 @@ import deviceDetailsFetch import networkx as nx -import matplotlib.pyplot as plt +#import matplotlib.pyplot as plt from graphviz import Digraph import threading @@ -80,7 +81,7 @@ def draw_graph(self,option="All"): f.attr('node', shape='circle') - print "Starting Graph Plotting" + print("Starting Graph Plotting") if option == "All": # add nodes @@ -122,7 +123,7 @@ def draw_graph(self,option="All"): try: f.edge(node, 'defaultGateway', label='HTTPS: ' +dest+": "+self.name_servers[node]["ip_details"][dest]["dns"], color = "blue") except: - print self.name_servers[node] + print(self.name_servers[node]) if option == "Tor": @@ -150,7 +151,7 @@ def draw_graph(self,option="All"): def main(): # draw example pcapfile = pcapReader.pcapReader('lanExample.pcap') - print "Reading Done...." + print("Reading Done....") details = communicationDetailsFetch.trafficDetailsFetch(pcapfile.packetDB) network = plotLan(pcapfile.packetDB, "network12345", details.communication_details,"HTTPS") diff --git a/Source/Module/reportGen.py b/Source/Module/legacy/reportGen.py similarity index 80% rename from Source/Module/reportGen.py rename to Source/Module/legacy/reportGen.py index 6316f3e..2a54857 100644 --- a/Source/Module/reportGen.py +++ b/Source/Module/legacy/reportGen.py @@ -13,21 +13,21 @@ def communicationDetailsReport(self, commDB): try: text_handle = open(self.directory + "/communicationDetailsReport.txt", "w") except Exception as e: - print "Could not create the report text file !!!!! Please debug error %s" % (str(e.message)) + print("Could not create the report text file !!!!! Please debug error %s" % (str(e.message))) text_handle.write("CommunicationDetails: %s" % json.dumps(commDB, indent=2,sort_keys=True)) def deviceDetailsReport(self, deviceDetails): try: text_handle = open(self.directory + "/deviceDetailsReport.txt", "w") except Exception as e: - print "Could not create the report text file !!!!! Please debug error %s" % (str(e.message)) + print("Could not create the report text file !!!!! Please debug error %s" % (str(e.message))) text_handle.write("deviceDetails: %s" % json.dumps(deviceDetails, indent=2,sort_keys=True)) def packetDetails(self, packetDB): try: text_handle = open(self.directory + "/packetDetailsReport.txt", "w") except Exception as e: - print "Could not create the report text file !!!!! Please debug error %s" % (str(e.message)) + print("Could not create the report text file !!!!! Please debug error %s" % (str(e.message))) for ip in packetDB: if "TCP" in packetDB[ip]: if "HTTP" in packetDB[ip]["TCP"]: diff --git a/Source/Module/torTrafficHandle.py b/Source/Module/legacy/torTrafficHandle.py similarity index 91% rename from Source/Module/torTrafficHandle.py rename to Source/Module/legacy/torTrafficHandle.py index 0d2222b..7da218b 100644 --- a/Source/Module/torTrafficHandle.py +++ b/Source/Module/legacy/torTrafficHandle.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Custom Module Imports import pcapReader @@ -36,9 +37,9 @@ def tor_traffic_detection(self, ip, sessions): def main(): tor_capture = pcapReader.pcapReader("torexample.pcapng") - print tor_capture.packetDB + print(tor_capture.packetDB) tor_identify = torTrafficHandle(tor_capture.packetDB) - print tor_identify.possible_tor_traffic + print(tor_identify.possible_tor_traffic) #main() diff --git a/Source/Module/userInterface.py b/Source/Module/legacy/userInterface.py similarity index 97% rename from Source/Module/userInterface.py rename to Source/Module/legacy/userInterface.py index a13f1c7..35ed315 100644 --- a/Source/Module/userInterface.py +++ b/Source/Module/legacy/userInterface.py @@ -1,3 +1,4 @@ +from __future__ import print_function from Tkinter import * import Tkinter, Tkconstants, tkFileDialog import ttk @@ -82,8 +83,8 @@ def browse_directory(self): #,("all files","*.*") #self.filename_field.delete(0, END) #self.filename_field.insert(0, self.pcap_file) - print self.filename - print self.pcap_file + print(self.filename) + print(self.pcap_file) def pcap_analyse(self): if os.path.exists(self.pcap_file.get()): @@ -145,23 +146,23 @@ def load_image(self): self.yscrollbar.config(command=self.canvas.yview) def map_select(self, *args): - print self.option.get() + print(self.option.get()) self.generate_graph() def zoom_in(self): - print "zoomin" + print("zoomin") self.zoom[0] += 100 self.zoom[1] += 100 if self.img: self.load_image() def zoom_out(self): - print "zoomout" + print("zoomout") if self.zoom[0] > 700 and self.zoom[1] > 700: self.zoom[0] -= 100 self.zoom[1] -= 100 else: - print "zoomout --> maximum" + print("zoomout --> maximum") if self.img: self.load_image() diff --git a/Source/Module/malicious_traffic_identifier.py b/Source/Module/malicious_traffic_identifier.py new file mode 100644 index 0000000..75cf146 --- /dev/null +++ b/Source/Module/malicious_traffic_identifier.py @@ -0,0 +1,36 @@ +# Custom Module Imports +import memory + +# Custom Module Import +import pcap_reader + +# Library Import + +# Module to Identify Possible Malicious Traffic + +class maliciousTrafficIdentifier: + + def __init__(self): + for session in memory.packet_db: + src, dst, port = session.split("/") + if port.isdigit() and self.malicious_traffic_detection(src, dst, int(port)) == 1: + memory.possible_mal_traffic.append(session) + + def malicious_traffic_detection(self, src, dst, port): + very_well_known_ports = [443] # used to differentiate possible mal vs serious mal + well_known_ports = [20, 21, 22, 23, 25, 53, 69, 80, 161, 179, 389, 443] + if (dst in memory.destination_hosts and memory.destination_hosts[dst] == "NotResolvable") or port not in well_known_ports: + return 1 + else: + return 0 + + # TODO: Covert communication module --> Add here + +def main(): + cap = pcap_reader.PcapEngine('examples/torExample.pcap', "scapy") + maliciousTrafficIdentifier() + print(memory.possible_mal_traffic) + +#main() + + diff --git a/Source/Module/memory.py b/Source/Module/memory.py new file mode 100644 index 0000000..628ddfb --- /dev/null +++ b/Source/Module/memory.py @@ -0,0 +1,15 @@ +#class memorySetting(): + + +global packet_db +packet_db = {} +global lan_hosts +lan_hosts = {} +global destination_hosts +destination_hosts = {} +global tor_nodes +tor_nodes = [] +global possible_tor_traffic +possible_tor_traffic = [] +global malicious_traffic +possible_mal_traffic = [] diff --git a/Source/Module/pcap_reader.py b/Source/Module/pcap_reader.py new file mode 100644 index 0000000..d7b9895 --- /dev/null +++ b/Source/Module/pcap_reader.py @@ -0,0 +1,201 @@ +""" +Module pcap_reader +""" +import logging +import sys +import memory +from netaddr import IPAddress +import threading +import base64 + +class PcapEngine(): + """ + PcapEngine: To support different pcap parser backend engine to operate reading pcap + Current Support: + * Scapy + * Pyshark + """ + + def __init__(self, pcap_file_name, pcap_parser_engine="scapy"): + """ + Init function imports libraries based on the parser engine selected + Return: + * packetDB ==> Full Duplex Packet Streams + - Used while finally plotting streams as graph + - dump packets during report generation + * lan_hosts ==> Private IP (LAN) list + - device details + * destination_hosts ==> Destination Hosts + - communication details + - tor identification + - malicious identification + """ + memory.packet_db = {} + memory.lan_hosts = {} + memory.destination_hosts = {} + memory.possible_mal_traffic = [] + memory.possible_tor_traffic = [] + self.engine = pcap_parser_engine + if pcap_parser_engine == "scapy": + try: + from scapy.all import rdpcap + except: + logging.error("Cannot import selected pcap engine: Scapy!") + sys.exit() + # Scapy sessions and other types use more O(N) iterations so just + # - use rdpcap + our own iteration (create full duplex streams) + self.packets = rdpcap(pcap_file_name) + elif pcap_parser_engine == "pyshark": + try: + import pyshark + except: + logging.error("Cannot import selected pcap engine: PyShark!") + sys.exit() + self.packets = pyshark.FileCapture(pcap_file_name, include_raw=True, use_json=True) + # Analyse capture to populate data + self.analyse_packet_data() + # Add other pcap engine modules to generate packetDB + + #@retry(tries=5, errors=memory.CouldNotLock) + def analyse_packet_data(self): + #with memory.get_lock(): + """ + PcapXray runs only one O(N) packets once to memoize + # - Parse the packets to create a usable DB + # - All the protocol parsing should be included here + """ + for packet in self.packets: # O(N) packet iteration + source_private_ip = None + if "IPv6" in packet or "IPV6" in packet: + if self.engine == "scapy": + IP = "IPv6" + else: + IP = "IPV6" + + # TODO: Fix weird ipv6 errors in pyshark engine + # * ExHandler as temperory fix + try: + private_source = IPAddress(packet[IP].src).is_private() + except: + private_source = None + try: + private_destination = IPAddress(packet[IP].dst).is_private() + except: + private_destination = None + elif "IP" in packet:# and packet["IP"].version == "4": + # Handle IP packets that originated from LAN (Internal Network) + #print(packet["IP"].version == "4") + IP = "IP" + private_source = IPAddress(packet[IP].src).is_private() + private_destination = IPAddress(packet[IP].dst).is_private() + + if "TCP" in packet or "UDP" in packet: + # Sort out indifferences in pcap engine + if self.engine == "pyshark": + eth_layer = "ETH" + tcp_src = str( + packet["TCP"].srcport if "TCP" in packet else packet["UDP"].srcport) + tcp_dst = str( + packet["TCP"].dstport if "TCP" in packet else packet["UDP"].dstport) + else: + eth_layer = "Ethernet" + tcp_src = str( + packet["TCP"].sport if "TCP" in packet else packet["UDP"].sport) + tcp_dst = str( + packet["TCP"].dport if "TCP" in packet else packet["UDP"].dport) + + if private_source and private_destination: # Communication within LAN + key1 = packet[IP].src + "/" + packet[IP].dst + "/" + tcp_dst + key2 = packet[IP].dst + "/" + packet[IP].src + "/" + tcp_src + if key2 in memory.packet_db: + source_private_ip = key2 + else: + source_private_ip = key1 + # IntraNetwork Hosts list + memory.lan_hosts[packet[IP].src] = {"mac": packet[eth_layer].src} + memory.lan_hosts[packet[IP].dst] = {"mac": packet[eth_layer].dst} + elif private_source: # Internetwork packet + key = packet[IP].src + "/" + packet[IP].dst + "/" + tcp_dst + source_private_ip = key + # IntraNetwork vs InterNetwork Hosts list + memory.lan_hosts[packet[IP].src] = {"mac": packet[eth_layer].src} + memory.destination_hosts[packet[IP].dst] = {} + elif private_destination: # Internetwork packet + #print(packet.show()) + key = packet[IP].dst + "/" + packet[IP].src + "/" + tcp_src + source_private_ip = key + # IntraNetwork vs InterNetwork Hosts list + memory.lan_hosts[packet[IP].dst] = {"mac": packet[eth_layer].dst} + memory.destination_hosts[packet[IP].src] = {} + + elif "ICMP" in packet: + key = packet[IP].src + "/" + packet[IP].dst + "/" + "ICMP" + source_private_ip = key + # Fill packetDB with generated key + #print(packet.show()) + if source_private_ip: + if source_private_ip not in memory.packet_db: + memory.packet_db[source_private_ip] = {} + # Ethernet Layer ( Mac address ) + if "Ethernet" not in memory.packet_db[source_private_ip]: + memory.packet_db[source_private_ip]["Ethernet"] = {} + # HTTP Packets + if "Payload" not in memory.packet_db: + # Payload recording + memory.packet_db[source_private_ip]["Payload"] = [] + if self.engine == "pyshark": + memory.packet_db[source_private_ip]["Ethernet"]["src"] = packet["ETH"].src + memory.packet_db[source_private_ip]["Ethernet"]["dst"] = packet["ETH"].dst + # Refer https://github.com/KimiNewt/pyshark/issues/264 + #memory.packet_db[source_private_ip]["Payload"].append(packet.get_raw_packet()) + else: + memory.packet_db[source_private_ip]["Ethernet"]["src"] = packet["Ethernet"].src + memory.packet_db[source_private_ip]["Ethernet"]["dst"] = packet["Ethernet"].dst + + if "TCP" in packet: + memory.packet_db[source_private_ip]["Payload"].append(str(packet["TCP"].payload)) + elif "UDP" in packet: + memory.packet_db[source_private_ip]["Payload"].append(str(packet["UDP"].payload)) + elif "ICMP" in packet: + memory.packet_db[source_private_ip]["Payload"].append(str(packet["ICMP"].payload)) + + # TODO: Add function memory to store all the memory data in files (DB) + # def memory_handle(): + """ + - Store the db as json on a file in cache folder (to not repeat read) + """ + +# Local Driver +def main(): + """ + Module Driver + """ + pcapfile = PcapEngine(sys.path[0]+'/examples/torExample.pcap', "scapy") + print(memory.packet_db.keys()) + ports = [] + + for key in memory.packet_db.keys(): + # if "192.168.11.4" in key: + print(key) + print(memory.packet_db[key]) + ip, port = key.split("/")[0], int(key.split("/")[-1]) + if ip == "10.187.195.95": + ports.append(port) + + + print(sorted(list(set(ports)))) + print(memory.lan_hosts) + print(memory.destination_hosts) + #print(memory.packet_db["TCP 192.168.0.26:64707 > 172.217.12.174:443"].summary()) + #print(memory.packet_db["TCP 172.217.12.174:443 > 192.168.0.26:64707"].summary()) + #memory.packet_db.conversations(type="jpg", target="> test.jpg") + +#main() + +# Sort payload by time... +# SSL Packets +# - Get handshake details? +#HTTPS +#Tor +#Malicious +# HTTP Payload Decrypt diff --git a/Source/Module/plot_lan_network.py b/Source/Module/plot_lan_network.py new file mode 100644 index 0000000..166ac17 --- /dev/null +++ b/Source/Module/plot_lan_network.py @@ -0,0 +1,369 @@ +#File Import +import pcap_reader +import communication_details_fetch +import tor_traffic_handle +import malicious_traffic_identifier +import device_details_fetch +import memory + +import networkx as nx +#import matplotlib.pyplot as plt + +from graphviz import Digraph +import threading +import os + +class plotLan: + + def __init__(self, filename, path, option="Tor"): + if not os.path.exists(path+"/Report"): + os.makedirs(path+"/Report") + self.filename = path+"/Report/"+filename+option + + self.styles = { + 'graph': { + 'label': 'PcapGraph', + 'fontsize': '16', + 'fontcolor': 'black', + 'bgcolor': 'grey', + 'rankdir': 'BT', + 'dpi':'600' + }, + 'nodes': { + 'fontname': 'Helvetica', + 'shape': 'circle', + 'fontcolor': 'black', + 'color': ' black', + 'style': 'filled', + 'fillcolor': 'yellow', + } + } + + self.sessions = memory.packet_db.keys() + device_details_fetch.fetchDeviceDetails("ieee").fetch_info() + if option == "Malicious" or option == "All": + self.mal_identify = malicious_traffic_identifier.maliciousTrafficIdentifier() + if option == "Tor" or option == "All": + self.tor_identify = tor_traffic_handle.torTrafficHandle().tor_traffic_detection() + self.draw_graph(option) + + def apply_styles(self, graph, styles): + graph.graph_attr.update( + ('graph' in styles and styles['graph']) or {} + ) + graph.node_attr.update( + ('nodes' in styles and styles['nodes']) or {} + ) + return graph + + def apply_custom_style(self, graph, color): + style = {'edges': { + 'style': 'dashed', + 'color': color, + 'arrowhead': 'open', + 'fontname': 'Courier', + 'fontsize': '12', + 'fontcolor': color, + }} + graph.edge_attr.update( + ('edges' in style and style['edges']) or {} + ) + return graph + + def draw_graph(self,option="All"): + f = Digraph('network_diagram - '+option, filename=self.filename, engine="dot", format="png") + f.attr(rankdir='LR', size='8,5') + + f.attr('node', shape='doublecircle') + f.node('defaultGateway') + + f.attr('node', shape='circle') + + print("Starting Graph Plotting") + + if option == "All": + # add nodes + for session in self.sessions: + src, dst, port = session.split("/") + + # TODO: Improvise this logic below + # * graphviz graph is not very good with the ":" in strings + if ":" in src: + map_src = src.replace(":",".") + else: + map_src = src + if ":" in dst: + map_dst = dst.replace(":", ".") + else: + map_dst = dst + + # Lan Host + if src not in memory.lan_hosts: + curr_node = map_src + f.node(curr_node) + else: + curr_node = memory.lan_hosts[src]["node"] + f.node(curr_node) + + # Destination + if dst in memory.destination_hosts: + destination = 'defaultGateway' + dlabel = memory.destination_hosts[dst] + else: + if dst in memory.lan_hosts: + destination = memory.lan_hosts[dst]["node"] + dlabel = "" + else: + destination = dst + dlabel = "" + + if session in memory.possible_tor_traffic: + f.edge(curr_node, destination, label='TOR: ' + str(map_dst) ,color="white") + elif session in memory.possible_mal_traffic: + f.edge(curr_node, destination, label='Malicious: ' + str(map_dst) ,color="red") + else: + if port == "443": + f.edge(curr_node, destination, label='HTTPS: ' + map_dst +": "+dlabel, color = "blue") + if port == "80": + f.edge(curr_node, destination, label='HTTP: ' + map_dst +": "+dlabel, color = "green") + if port == "ICMP": + f.edge(curr_node, destination, label='ICMP: ' + str(map_dst) ,color="black") + if port == "53": + f.edge(curr_node, destination, label='DNS: ' + str(map_dst) ,color="orange") + + elif option == "HTTP": + for session in self.sessions: + src, dst, port = session.split("/") + # TODO: Improvise this logic below + # * graphviz graph is not very good with the ":" in strings + if ":" in src: + map_src = src.replace(":",".") + else: + map_src = src + if ":" in dst: + map_dst = dst.replace(":", ".") + else: + map_dst = dst + + # Lan Host + if src not in memory.lan_hosts: + curr_node = map_src + f.node(curr_node) + else: + curr_node = memory.lan_hosts[src]["node"] + f.node(curr_node) + + # Destination Host + if dst in memory.destination_hosts: + destination = 'defaultGateway' + dlabel = memory.destination_hosts[dst] + else: + if dst in memory.lan_hosts: + destination = memory.lan_hosts[dst]["node"] + dlabel = "" + else: + destination = dst + dlabel = "" + + + if port == "80": + f.edge(curr_node, destination, label='HTTP: ' + str(map_dst)+": "+dlabel, color = "green") + + elif option == "HTTPS": + for session in self.sessions: + src, dst, port = session.split("/") + # TODO: Improvise this logic below + # * graphviz graph is not very good with the ":" in strings + if ":" in src: + map_src = src.replace(":",".") + else: + map_src = src + if ":" in dst: + map_dst = dst.replace(":", ".") + else: + map_dst = dst + + # Lan Host + if src not in memory.lan_hosts: + curr_node = map_src + f.node(curr_node) + else: + curr_node = memory.lan_hosts[src]["node"] + f.node(curr_node) + + # Destination Host + if dst in memory.destination_hosts: + destination = 'defaultGateway' + dlabel = memory.destination_hosts[dst] + else: + if dst in memory.lan_hosts: + destination = memory.lan_hosts[dst]["node"] + dlabel = "" + else: + destination = dst + dlabel = "" + + + if port == "443": + f.edge(curr_node, destination, label='HTTPS: ' + str(map_dst)+": "+dlabel, color = "blue") + + elif option == "Tor": + for session in self.sessions: + src, dst, port = session.split("/") + # TODO: Improvise this logic below + # * graphviz graph is not very good with the ":" in strings + if ":" in src: + map_src = src.replace(":",".") + else: + map_src = src + if ":" in dst: + map_dst = dst.replace(":", ".") + else: + map_dst = dst + + # Lan Host + if src not in memory.lan_hosts: + curr_node = map_src + f.node(curr_node) + else: + curr_node = memory.lan_hosts[src]["node"] + f.node(curr_node) + + # Destination Host + if dst in memory.destination_hosts: + destination = 'defaultGateway' + dlabel = memory.destination_hosts[dst] + else: + if dst in memory.lan_hosts: + destination = memory.lan_hosts[dst]["node"] + dlabel = "" + else: + destination = dst + dlabel = "" + + + if session in memory.possible_tor_traffic: + f.edge(curr_node, destination, label='TOR: ' + str(map_dst) ,color="white") + + elif option == "Malicious": + # TODO: would we need to iterate over and over all the session irrespective of the properties + for session in self.sessions: + src, dst, port = session.split("/") + # TODO: Improvise this logic below + # * graphviz graph is not very good with the ":" in strings + if ":" in src: + map_src = src.replace(":",".") + else: + map_src = src + if ":" in dst: + map_dst = dst.replace(":", ".") + else: + map_dst = dst + + # Lan Host + if src not in memory.lan_hosts: + curr_node = map_src + f.node(curr_node) + else: + curr_node = memory.lan_hosts[src]["node"] + f.node(curr_node) + + # Destination Host + if dst in memory.destination_hosts: + destination = 'defaultGateway' + dlabel = memory.destination_hosts[dst] + else: + if dst in memory.lan_hosts: + destination = memory.lan_hosts[dst]["node"] + dlabel = "" + else: + destination = dst + dlabel = "" + + if session in memory.possible_mal_traffic: + f.edge(curr_node, destination, label='Malicious: ' + str(map_dst) ,color="red") + + elif option == "ICMP": + for session in self.sessions: + src, dst, protocol = session.split("/") + if ":" in src: + map_src = src.replace(":",".") + else: + map_src = src + if ":" in dst: + map_dst = dst.replace(":", ".") + else: + map_dst = dst + + # Lan Host + if src not in memory.lan_hosts: + curr_node = map_src + f.node(curr_node) + else: + curr_node = memory.lan_hosts[src]["node"] + f.node(curr_node) + + # Destination Host + if dst in memory.destination_hosts: + destination = 'defaultGateway' + dlabel = memory.destination_hosts[dst] + else: + if dst in memory.lan_hosts: + destination = memory.lan_hosts[dst]["node"] + dlabel = "" + else: + destination = dst + dlabel = "" + + if protocol == "ICMP": + f.edge(curr_node, destination, label='ICMP: ' + str(map_dst) ,color="black") + + elif option == "DNS": + for session in self.sessions: + src, dst, port = session.split("/") + if ":" in src: + map_src = src.replace(":",".") + else: + map_src = src + if ":" in dst: + map_dst = dst.replace(":", ".") + else: + map_dst = dst + + # Lan Host + if src not in memory.lan_hosts: + curr_node = map_src + f.node(curr_node) + else: + curr_node = memory.lan_hosts[src]["node"] + f.node(curr_node) + + # Destination Host + if dst in memory.destination_hosts: + destination = 'defaultGateway' + dlabel = memory.destination_hosts[dst] + else: + if dst in memory.lan_hosts: + destination = memory.lan_hosts[dst]["node"] + dlabel = "" + else: + destination = dst + dlabel = "" + + if port == "53": + f.edge(curr_node, destination, label='DNS: ' + str(map_dst) ,color="orange") + + + self.apply_styles(f,self.styles) + f.render() + +def main(): + # draw example + pcapfile = pcap_reader.PcapEngine('examples/torExample.pcap', "scapy") + print("Reading Done....") + details = communication_details_fetch.trafficDetailsFetch("sock") + import sys + print(sys.path[0]) + network = plotLan("test", sys.path[0]) + +#main() diff --git a/Source/Module/report_generator.py b/Source/Module/report_generator.py new file mode 100644 index 0000000..9de9a14 --- /dev/null +++ b/Source/Module/report_generator.py @@ -0,0 +1,44 @@ +# Report Generation +import os, json +from scapy.all import * +import memory + +class reportGen: + + def __init__(self, path, filename): + if not os.path.exists(path+"/Report"): + os.makedirs(path+"/Report") + self.directory = path+"/Report" + self.filename = filename + + def communicationDetailsReport(self): + try: + text_handle = open(self.directory + "/" + self.filename + "communicationDetailsReport.txt", "w") + text_handle.write("CommunicationDetails: %s\n" % json.dumps(memory.destination_hosts, indent=2,sort_keys=True)) + text_handle.write("Tor Nodes: %s\n" % json.dumps(memory.tor_nodes, indent=2,sort_keys=True)) + text_handle.write("Tor Traffic: %s\n" % json.dumps(memory.possible_tor_traffic, indent=2,sort_keys=True)) + text_handle.write("Malicious Traffic: %s\n" % json.dumps(memory.possible_mal_traffic, indent=2,sort_keys=True)) + text_handle.write("Destination DNS: %s\n" % json.dumps(memory.destination_hosts, indent=2,sort_keys=True)) + text_handle.write("Lan Hosts: %s\n" % json.dumps(memory.lan_hosts, indent=2,sort_keys=True)) + except Exception as e: + print("Could not create the report text file !!!!! Please debug error %s" % (str(e))) + + def deviceDetailsReport(self): + try: + text_handle = open(self.directory + "/" + self.filename + "deviceDetailsReport.txt", "w") + text_handle.write("deviceDetails: %s\n" % json.dumps(memory.lan_hosts, indent=2,sort_keys=True)) + except Exception as e: + print("Could not create the report text file !!!!! Please debug error %s" % (str(e))) + + def packetDetails(self): + try: + text_handle = open(self.directory + "/" + self.filename + "packetDetailsReport.txt", "w") + for session in memory.packet_db: + text_handle.write("%s\n" % session) + text_handle.write("%s\n" % memory.packet_db[session]["Ethernet"]) + text_handle.write("\nPayload:\n") + payloads = "\n".join(memory.packet_db[session]["Payload"]) + if payloads: + text_handle.write("%s\n" % payloads) + except Exception as e: + print("Could not create the report text file !!!!! Please debug error %s" % (str(e))) diff --git a/Source/Module/tor_traffic_handle.py b/Source/Module/tor_traffic_handle.py new file mode 100644 index 0000000..bff9467 --- /dev/null +++ b/Source/Module/tor_traffic_handle.py @@ -0,0 +1,45 @@ +# Custom Module Imports +import memory + +# For tests +import pcap_reader + +# Library Import +from stem.descriptor import remote + +# Tor Traffic Module Class + +class torTrafficHandle(): + + def __init__(self): + if not memory.tor_nodes: + self.get_consensus_data() + + def get_consensus_data(self): + try: + for desc in remote.get_consensus().run(): + memory.tor_nodes.append((desc.address, desc.or_port)) + except Exception as exc: + print("Unable to retrieve the consensus: %s" % exc) + + def tor_traffic_detection(self): + for session in memory.packet_db: + current_session = session.split("/") + if current_session[2].isdigit() and (current_session[1], int(current_session[2])) in memory.tor_nodes: + memory.possible_tor_traffic.append(session) + +def main(): + pcap_reader.PcapEngine('examples/torExample.pcap', "scapy") + tor = torTrafficHandle() + #print(memory.packet_db) + print(memory.tor_nodes) + tor.tor_traffic_detection() + print(memory.possible_tor_traffic) + +#main() + + + + + + diff --git a/Source/Module/user_interface.py b/Source/Module/user_interface.py new file mode 100644 index 0000000..699302c --- /dev/null +++ b/Source/Module/user_interface.py @@ -0,0 +1,201 @@ +import sys +try: + # for Python2 + from Tkinter import * + import ttk + import tkFileDialog as fd + import Tkconstants + import tkMessageBox as mb + import Queue as q +except ImportError: + # for Python3 + from tkinter import * + from tkinter import ttk + from tkinter import filedialog as fd + from tkinter import messagebox as mb + import queue as q + +import pcap_reader +import plot_lan_network +import communication_details_fetch +import report_generator +import time +import threading +from PIL import Image,ImageTk +import os, sys + +class pcapXrayGui: + def __init__(self, base): + # Base Frame Configuration + self.base = base + base.title("PcapXray") + Label(base, text="PcapXray Tool - A LAN Network Analyzer") + + # Style Configuration + style = ttk.Style() + style.configure("BW.TLabel", foreground="black") + style.configure("BW.TEntry", foreground="black") + + # 1st Frame - Initial Frame + InitFrame = ttk.Frame(base, width=50, padding="10 10 10 10",relief= GROOVE) + InitFrame.grid(column=10, row=10, sticky=(N, W, E, S)) + InitFrame.columnconfigure(10, weight=1) + InitFrame.rowconfigure(10, weight=1) + + # Pcap File Entry + self.pcap_file = StringVar() + self.filename = "" + ttk.Label(InitFrame, text="Enter pcap file path: ",style="BW.TLabel").grid(column=0, row=0, sticky="W") + self.filename_field = ttk.Entry(InitFrame, width=32, textvariable=self.pcap_file, style="BW.TEntry").grid(column=1, row=0, sticky="W, E") + self.progressbar = ttk.Progressbar(InitFrame, orient="horizontal", length=200,value=0, maximum=200, mode="indeterminate") + # Browse button + #self.filename = StringVar() + ttk.Button(InitFrame, text="Browse", command=lambda: self.browse_directory("pcap")).grid(column=2, row=0, padx=10, pady=10,sticky="E") + ttk.Button(InitFrame, text="Analyze!", command=self.pcap_analyse).grid(column=3, row=0, padx=10, pady=10,sticky="E") + self.progressbar.grid(column=4, row=0, padx=10, pady=10, sticky="E") + + # First Frame with Report Directory + # Output and Results Frame + FirstFrame = ttk.Frame(base, width=50, padding="10 0 0 0", relief= GROOVE) + FirstFrame.grid(column=10, row=20, sticky=(N, W, E, S)) + FirstFrame.columnconfigure(10, weight=1) + FirstFrame.rowconfigure(10, weight=1) + self.destination_report = StringVar(value=sys.path[0]) + ttk.Label(FirstFrame, text="Output directory path: ",style="BW.TLabel").grid(column=0, row=0, sticky="W") + self.report_field = ttk.Entry(FirstFrame, width=30, textvariable=self.destination_report, style="BW.TEntry").grid(column=1, row=0, sticky="W, E") + # Browse button + ttk.Button(FirstFrame, text="Browse", command=lambda: self.browse_directory("report")).grid(column=2, row=0, padx=10, pady=10,sticky="E") + + # Second Frame with Options + SecondFrame = ttk.Frame(base, width=50, padding="10 10 10 10",relief= GROOVE) + SecondFrame.grid(column=10, row=30, sticky=(N, W, E, S)) + SecondFrame.columnconfigure(10, weight=1) + SecondFrame.rowconfigure(10, weight=1) + ttk.Label(SecondFrame, text="Options: ", style="BW.TLabel").grid(row=10,column=0,sticky="W") + self.option = StringVar() + self.options = {'All', 'HTTP', 'HTTPS', 'Tor', 'Malicious', 'ICMP', 'DNS'} + #self.option.set('Tor') + ttk.OptionMenu(SecondFrame,self.option,"Select",*self.options).grid(row=10,column=1,sticky="W") + self.zoom = [900,900] + self.img = "" + ttk.Button(SecondFrame, text="zoomIn", command=self.zoom_in).grid(row=10,column=10,padx=5,sticky="E") + ttk.Button(SecondFrame, text="zoomOut", command=self.zoom_out).grid(row=10,column=11,sticky="E") + + # Third Frame with Results and Descriptioms + self.ThirdFrame = ttk.Frame(base, width=100, height=100, padding="10 10 10 10",relief= GROOVE) + description = """It is a tool aimed to simplyfy the network analysis and speed the process of analysing the network traffic.\nThis prototype aims to accomplish 4 important modules, + \n 1. Web Traffic\n 2. Tor Traffic \n 3. Malicious Traffic \n 4. Device/Traffic Details \n 5. Covert Communication \n \nPlease contact me @ spg349@nyu.edu for any bugs or problems ! + """ + self.label = ttk.Label(self.ThirdFrame, text="Description: \nPcapXray tools is an aid for Network Forensics or Any Network Analysis!\n"+description, style="BW.TLabel") + self.label.grid(column=10, row=10,sticky="W") + self.xscrollbar = Scrollbar(self.ThirdFrame, orient=HORIZONTAL) + self.xscrollbar.grid(row=100, column=0, sticky=E + W) + self.yscrollbar = Scrollbar(self.ThirdFrame, orient=VERTICAL) + self.yscrollbar.grid(row=0, column=100, sticky=N + S) + self.ThirdFrame.grid(column=10, row=40, sticky=(N, W, E, S)) + self.ThirdFrame.columnconfigure(0, weight=1) + self.ThirdFrame.rowconfigure(0, weight=1) + self.name_servers = "" + #self.destination_report = "" + + def browse_directory(self, option): + if option == "pcap": + # Reference: http://effbot.org/tkinterbook/tkinter-dialog-windows.htm + self.pcap_file.set(fd.askopenfilename(initialdir = sys.path[0],title = "Select Packet Capture File!",filetypes = (("pcap files","*.pcap"),("pcapng files","*.pcapng")))) + self.filename = self.pcap_file.get().replace(".pcap","") + if "/" in self.filename: + self.filename = self.filename.split("/")[-1] + #,("all files","*.*") + #self.filename_field.delete(0, END) + #self.filename_field.insert(0, self.pcap_file) + print(self.filename) + print(self.pcap_file) + else: + self.destination_report.set(fd.askdirectory()) + + def pcap_analyse(self): + if os.path.exists(self.pcap_file.get()): + self.progressbar.start() + result = q.Queue() + packet_read = threading.Thread(target=pcap_reader.PcapEngine,args=(self.pcap_file.get(),"scapy")) + packet_read.start() + while packet_read.is_alive(): + self.progressbar.update() + packet_read.join() + self.progressbar.stop() + #packet_read.join() + #self.capture_read = result.get() + reportThreadpcap = threading.Thread(target=report_generator.reportGen(self.destination_report.get(), self.filename).packetDetails,args=()) + reportThreadpcap.start() + #self.option.set("Tor") + self.option.trace("w",self.map_select) + #self.option.set("Tor") + self.name_servers = "" + else: + mb.showerror("Error","File Not Found !") + + def generate_graph(self): + if self.name_servers == "": + result = q.Queue() + t = threading.Thread(target=communication_details_fetch.trafficDetailsFetch,args=("sock",)) + t.start() + self.progressbar.start() + while t.is_alive(): + self.progressbar.update() + t.join() + self.progressbar.stop() + #self.name_servers = result.get() + reportThread = threading.Thread(target=report_generator.reportGen(self.destination_report.get(), self.filename).communicationDetailsReport,args=()) + reportThread.start() + + if not os.path.exists(self.destination_report.get()+"/Report/"+self.filename+self.option.get()+".png"): + t1 = threading.Thread(target=plot_lan_network.plotLan, args=(self.filename, self.destination_report.get(), self.option.get(),)) + t1.start() + self.progressbar.start() + while t1.is_alive(): + self.progressbar.update() + t1.join() + self.progressbar.stop() + self.label.grid_forget() + self.load_image() + else: + self.label.grid_forget() + self.load_image() + + + def load_image(self): + self.canvas = Canvas(self.ThirdFrame, width=700,height=600, bd=0, bg="navy", xscrollcommand=self.xscrollbar.set, yscrollcommand=self.yscrollbar.set) + self.canvas.grid(row=0, column=0, sticky=N + S + E + W) + self.img = ImageTk.PhotoImage(Image.open(self.destination_report.get()+"/Report/"+self.filename+self.option.get()+".png").resize(tuple(self.zoom),Image.ANTIALIAS).convert('RGB')) + self.canvas.create_image(0,0, image=self.img) + self.canvas.config(scrollregion=self.canvas.bbox(ALL)) + self.xscrollbar.config(command=self.canvas.xview) + self.yscrollbar.config(command=self.canvas.yview) + + def map_select(self, *args): + print(self.option.get()) + self.generate_graph() + + def zoom_in(self): + print("zoomin") + self.zoom[0] += 100 + self.zoom[1] += 100 + if self.img: + self.load_image() + + def zoom_out(self): + print("zoomout") + if self.zoom[0] > 700 and self.zoom[1] > 700: + self.zoom[0] -= 100 + self.zoom[1] -= 100 + else: + print("zoomout --> maximum") + if self.img: + self.load_image() + + +def main(): + base = Tk() + pcapXrayGui(base) + base.mainloop() + diff --git a/Source/main.py b/Source/main.py index 310461b..bce680f 100644 --- a/Source/main.py +++ b/Source/main.py @@ -4,15 +4,22 @@ import os #-- default lib - packed with python import sys #-- default lib import datetime #-- default lib -from Tkinter import * -import ttk + +try: + # for Python2 + from Tkinter import * + import ttk +except ImportError: + # for Python3 + from tkinter import * + from tkinter import ttk # Import Custom Modules - Self created by the author if sys.path[0]: sys.path.insert(0,sys.path[0]+'/Module/') else: sys.path.insert(0, 'Module/') -import userInterface +import user_interface # Import 3rd party Libraries -- Needed to be installed using pip import warnings @@ -23,7 +30,8 @@ def main(): logo_file = os.path.join(os.path.dirname(__file__), 'Module/assets/logo.gif') icon = PhotoImage(file=logo_file) base.tk.call('wm','iconphoto',base._w,icon) - userInterface.pcapXrayGui(base) + user_interface.pcapXrayGui(base) base.mainloop() main() + diff --git a/Test/test_pcap_reader_module.py b/Test/test_pcap_reader_module.py index ff8d84b..28278c4 100644 --- a/Test/test_pcap_reader_module.py +++ b/Test/test_pcap_reader_module.py @@ -1,17 +1,17 @@ # Separate Module Tests - Assure proper pcap reading of the example/test.pcap file # Pending work.... import sys -print sys.path[0] if sys.path[0]: sys.path.insert(0, sys.path[0]+'/../Source/Module/') else: sys.path.insert(0,'/../Source/Module/') -import pcapReader +import pcap_reader +import memory def test_pcapreader(): - pcapfile = pcapReader.pcapReader(sys.path[0]+'examples/test.pcap') - if pcapfile.packetDB: + pcapfile = pcap_reader.PcapEngine(sys.path[0]+'examples/test.pcap', "scapy") + if memory.packet_db: assert True test_pcapreader() diff --git a/Test/test_sanity.py b/Test/test_sanity.py index aff2d57..cd34050 100644 --- a/Test/test_sanity.py +++ b/Test/test_sanity.py @@ -3,6 +3,7 @@ # Test System Setup import sys import os +import pytest if sys.path[0]: sys.path.insert(0, sys.path[0]+'/../Source/Module/') @@ -12,47 +13,58 @@ # All the Module imports # Report generation module -import reportGen +import report_generator # 1 - pcapReader Module -import pcapReader +import pcap_reader # 2 - communicationDetailsFetch module -import communicationDetailsFetch +import communication_details_fetch # 3 - deviceDetailsFetch module -import deviceDetailsFetch +import device_details_fetch # 4 - maliciousTrafficIdentifier module -import maliciousTrafficIdentifier +import malicious_traffic_identifier # 5 - plotLanNetwork module #import plotLanNetwork # 7 - userInterface module #import userInterface # 8 - torTrafficHandle module -import torTrafficHandle +import tor_traffic_handle +import memory # End to end Workflow Tests - All tests will be applied to example/test.pcap file def test_pcapreader(): - pcapfile = pcapReader.pcapReader(sys.path[0]+'examples/test.pcap') - if pcapfile.packetDB: + pcap_reader.PcapEngine(sys.path[0]+'examples/test.pcap', "scapy") + if memory.packet_db: assert True + # Testing pyshark engine for >= python3.0 + from sys import version_info + if version_info[0] >= 3: + pcapfile = pcap_reader.PcapEngine(sys.path[0]+'examples/torExample.pcap', "pyshark") + if memory.packet_db: + assert True + else: + # Python2.7 tests + # Ref: https://medium.com/python-pandemonium/testing-sys-exit-with-pytest-10c6e5f7726f + with pytest.raises(SystemExit): + pcap_reader.PcapEngine(sys.path[0]+'examples/torExample.pcap', "pyshark") def test_communication_details_fetch(): - capture = pcapReader.pcapReader(sys.path[0]+'examples/test.pcap') - details = communicationDetailsFetch.trafficDetailsFetch(capture.packetDB) - if details.communication_details: + pcap_reader.PcapEngine(sys.path[0]+'examples/test.pcap', "scapy") + communication_details_fetch.trafficDetailsFetch("sock") + if memory.destination_hosts: assert True def test_device_details_fetch(): - pcapfile = pcapReader.pcapReader(sys.path[0]+'examples/test.pcap') - for ip in pcapfile.packetDB: - macObj = deviceDetailsFetch.fetchDeviceDetails(pcapfile.packetDB[ip]) - if macObj.oui_identification(): - assert True + pcap_reader.PcapEngine(sys.path[0]+'examples/test.pcap', "scapy") + device_details_fetch.fetchDeviceDetails("ieee").fetch_info() + if memory.lan_hosts: + assert True def test_malicious_traffic_identifier(): - malicious_capture = pcapReader.pcapReader(sys.path[0]+'examples/test.pcap') - dns_details = {} - mal_identify = maliciousTrafficIdentifier.maliciousTrafficIdentifier(malicious_capture.packetDB, dns_details) - if mal_identify.possible_malicious_traffic: + pcap_reader.PcapEngine(sys.path[0]+'examples/test.pcap', "scapy") + communication_details_fetch.trafficDetailsFetch("sock") + malicious_traffic_identifier.maliciousTrafficIdentifier() + if memory.possible_mal_traffic: assert True #def test_plot_lan_network(): @@ -63,10 +75,14 @@ def test_malicious_traffic_identifier(): # assert True def test_report_gen(): - pcapfile = pcapReader.pcapReader(sys.path[0]+'examples/test.pcap') - if pcapfile.packetDB: - reportGen.reportGen().packetDetails(pcapfile.packetDB) - if os.path.isfile(sys.path[1]+"/../Report/communicationDetailsReport.txt") and os.path.isfile(sys.path[1]+"/../Report/deviceDetailsReport.txt") and os.path.isfile(sys.path[1]+"/../Report/packetDetailsReport.txt"): + directory = sys.path[0] + filename = "test" + pcap_reader.PcapEngine(directory + 'examples/' + filename + ".pcap", "scapy") + if memory.packet_db: + report_generator.reportGen(sys.path[0], filename).packetDetails() + report_generator.reportGen(sys.path[0], filename).communicationDetailsReport() + report_generator.reportGen(sys.path[0], filename).deviceDetailsReport() + if os.path.isfile(sys.path[0]+"/Report/testcommunicationDetailsReport.txt") and os.path.isfile(sys.path[0]+"/Report/testdeviceDetailsReport.txt") and os.path.isfile(sys.path[0]+"/Report/testpacketDetailsReport.txt"): assert True # 7 - userInterface module @@ -74,7 +90,7 @@ def test_report_gen(): # * Look at Travis Integrations for GUI Test def test_tor_traffic_handle(): - tor_capture = pcapReader.pcapReader(sys.path[0]+'examples/test.pcap') - tor_identify = torTrafficHandle.torTrafficHandle(tor_capture.packetDB) - if tor_identify: - assert True + pcap_reader.PcapEngine(sys.path[0]+'examples/test.pcap', "scapy") + tor_traffic_handle.torTrafficHandle().tor_traffic_detection() + if memory.possible_tor_traffic: + assert True \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index abdfa6b..ffb03d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ # Pcap Reader and Packet Handler scapy +pyshark # Not Needed for Linux, Needed only for MAC when Scapy is not properly downloaded #pcapy