├── LICENSE ├── README.md ├── above.py ├── above_oui_dict.py ├── banner └── banner.png └── setup.py /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Above 2 | 3 | Network security sniffer for finding vulnerabilities in the network. Designed for pentesters and security engineers. 4 | 5 | ![](/banner/banner.png) 6 | 7 | ``` 8 | Above: Invisible network protocol sniffer 9 | Designed for pentesters and security engineers 10 | 11 | Author: Magama Bazarov, 12 | Pseudonym: Caster 13 | Version: 2.8 14 | Codename: Rubens Barrichello 15 | ``` 16 | 17 | # Disclaimer 18 | 19 | **All information contained in this repository is provided for educational and research purposes only. The author is not responsible for any illegal use of this tool**. 20 | 21 | **It is a specialized network security tool that helps both pentesters and security professionals**. 22 | 23 | --- 24 | 25 | # Mechanics 26 | 27 | Above is a invisible network sniffer for finding vulnerabilities in network equipment. It is based entirely on network traffic analysis, so it does not make any noise on the air. He's invisible. Completely based on the Scapy library. 28 | 29 | > Above allows pentesters to automate the process of finding vulnerabilities in network hardware. Discovery protocols, dynamic routing, 802.1Q, Resolution protocols, ICS, FHRP, STP, LLMNR/NBT-NS, etc. 30 | 31 | ## Supported protocols 32 | 33 | Detects up to 28 protocols: 34 | 35 | ``` 36 | MACSec (802.1X AE) 37 | EAPOL (Checking 802.1X versions) 38 | ARP (Host Discovery) 39 | CDP (Cisco Discovery Protocol) 40 | DTP (Dynamic Trunking Protocol) 41 | LLDP (Link Layer Discovery Protocol) 42 | VLAN (802.1Q) 43 | S7COMM (Siemens) (SCADA) 44 | OMRON (SCADA) 45 | TACACS+ (Terminal Access Controller Access Control System Plus) 46 | ModbusTCP (SCADA) 47 | STP (Spanning Tree Protocol) 48 | OSPF (Open Shortest Path First) 49 | EIGRP (Enhanced Interior Gateway Routing Protocol) 50 | BGP (Border Gateway Protocol) 51 | VRRP (Virtual Router Redundancy Protocol) 52 | HSRP (Host Standby Redundancy Protocol) 53 | GLBP (Gateway Load Balancing Protocol) 54 | IGMP (Internet Group Management Protocol) 55 | LLMNR (Link Local Multicast Name Resolution) 56 | NBT-NS (NetBIOS Name Service) 57 | MDNS (Multicast DNS) 58 | DHCP (Dynamic Host Configuration Protocol) 59 | DHCPv6 (Dynamic Host Configuration Protocol v6) 60 | ICMPv6 (Internet Control Message Protocol v6) 61 | SSDP (Simple Service Discovery Protocol) 62 | MNDP (MikroTik Neighbor Discovery Protocol) 63 | SNMP (Simple Network Management Protocol) 64 | ``` 65 | ## Operating Mechanism 66 | 67 | Above works in two modes: 68 | 69 | - Hot mode: Sniffing on your interface specifying a timer 70 | - Cold mode: Analyzing traffic dumps 71 | 72 | The tool is very simple in its operation and is driven by arguments: 73 | 74 | - Interface: Specifying the network interface on which sniffing will be performed 75 | - Timer: Time during which traffic analysis will be performed 76 | - Input: The tool takes an already prepared `.pcap` as input and looks for protocols in it 77 | - Output: Above will record the listened traffic to `.pcap` file, its name you specify yourself 78 | - Passive ARP: Detecting hosts in a segment using Passive ARP 79 | - VLAN Search: Search for VLAN segments by extracting VLAN IDs in traffic 80 | 81 | ``` 82 | usage: above.py [-h] [--interface INTERFACE] [--timer TIMER] [--output OUTPUT] [--input INPUT] [--passive-arp] [--search-vlan] 83 | 84 | options: 85 | -h, --help show this help message and exit 86 | --interface INTERFACE 87 | Interface for traffic listening 88 | --timer TIMER Time in seconds to capture packets, default: not set 89 | --output OUTPUT File name where the traffic will be recorded, default: not set 90 | --input INPUT File name of the traffic dump 91 | --passive-arp Passive ARP (Host Discovery) 92 | --search-vlan VLAN Search 93 | ``` 94 | 95 | --- 96 | 97 | ## Information about protocols 98 | 99 | The information obtained will be useful not only to the pentester, but also to the security engineer, he will know what he needs to pay attention to. 100 | 101 | When Above detects a protocol, it outputs the necessary information to indicate the attack vector or security issue: 102 | 103 | - Impact: What kind of attack can be performed on this protocol; 104 | 105 | - Tools: What tool can be used to launch an attack; 106 | 107 | - Technical information: Required information for the pentester, sender MAC/IP addresses, FHRP group IDs, OSPF/EIGRP domains, etc. 108 | 109 | - Mitigation: Recommendations for fixing the security problems 110 | 111 | - Source/Destination Addresses: For protocols, Above displays information about the source and destination MAC addresses and IP addresses 112 | 113 | --- 114 | 115 | # Installation 116 | 117 | ### Linux 118 | You can install Above directly from the Kali Linux repositories 119 | ```bash 120 | caster@kali:~$ sudo apt update && sudo apt install above 121 | ``` 122 | 123 | Or: 124 | 125 | ```bash 126 | :~$ sudo apt-get install python3-scapy python3-colorama python3-setuptools 127 | :~$ git clone https://github.com/casterbyte/above 128 | :~$ cd above/ 129 | :~/above$ sudo python3 setup.py install 130 | ``` 131 | 132 | ### macOS: 133 | ```bash 134 | # Install python3 first 135 | brew install python3 136 | # Then install required dependencies 137 | sudo pip3 install scapy colorama setuptools 138 | 139 | # Clone the repo 140 | git clone https://github.com/casterbyte/above 141 | cd above/ 142 | sudo python3 setup.py install 143 | ``` 144 | 145 | Don't forget to **deactivate** your firewall on macOS! 146 | #### Settings > Network > Firewall 147 | -------------------------- 148 | 149 | # How to Use 150 | 151 | ## Hot mode 152 | 153 | > Above requires root access for sniffing 154 | 155 | Above can be run with or without a timer: 156 | 157 | ```bash 158 | caster@kali:~$ sudo above --interface eth0 --timer 120 159 | ``` 160 | > To stop traffic sniffing, press CTRL + С 161 | > 162 | 163 | Example: 164 | 165 | ```bash 166 | caster@kali:~$ sudo above --interface eth0 --timer 120 167 | 168 | ___ _ 169 | / _ \| | 170 | / /_\ \ |__ _____ _____ 171 | | _ | '_ \ / _ \ \ / / _ \ 172 | | | | | |_) | (_) \ V / __/ 173 | \_| |_/_.__/ \___/ \_/ \___| 174 | 175 | Invisible network protocol sniffer. Designed for security engineers 176 | 177 | Author: Magama Bazarov, 178 | Alias: Caster 179 | Version: 2.8 180 | Codename: Rubens Barrichello 181 | 182 | [!] Above does NOT perform MITM or credential capture. Passive analysis only 183 | [!] Unauthorized use in third-party networks may violate local laws 184 | [!] The developer assumes NO liability for improper or illegal use 185 | 186 | [*] OUI Database Loaded. Entries: 36858 187 | ----------------------------------------------------------------------------------------- 188 | [+] Start sniffing... 189 | 190 | [*] After the protocol is detected - all necessary information about it will be displayed 191 | ============================== 192 | [+] Detected STP Frame 193 | [*] Attack Impact: Partial MITM 194 | [*] Tools: Yersinia, Scapy 195 | [*] STP Root Switch MAC: 78:9a:18:4d:55:63 196 | [*] STP Root ID: 32768 197 | [*] STP Root Path Cost: 0 198 | [*] Mitigation: Enable BPDU Guard 199 | [*] Vendor: Routerboard.com 200 | ============================== 201 | [+] Detected MDNS Packet 202 | [*] Attack Impact: MDNS Spoofing, Credentials Interception 203 | [*] Tools: Responder 204 | [*] MDNS Spoofing works specifically against Windows machines 205 | [*] You cannot get NetNTLMv2-SSP from Apple devices 206 | [*] MDNS Speaker IP: 10.10.100.252 207 | [*] MDNS Speaker MAC: 02:10:de:64:f2:34 208 | [*] Mitigation: Monitor mDNS traffic, this protocol can't just be turned off 209 | [*] Vendor: Unknown Vendor 210 | ``` 211 | 212 | If you need to record the sniffed traffic, use the `--output` argument 213 | 214 | ```bash 215 | caster@kali:~$ sudo above --interface eth0 --timer 120 --output above.pcap 216 | ``` 217 | > If you interrupt the tool with CTRL+C, the traffic is still written to the file 218 | 219 | ## Cold mode 220 | 221 | If you already have some recorded traffic, you can use the `--input` argument to look for potential security issues 222 | 223 | ```bash 224 | caster@kali:~$ above --input ospf-md5.cap 225 | ``` 226 | 227 | Example: 228 | 229 | ```bash 230 | caster@kali:~$ sudo above --input dopamine.cap 231 | 232 | [*] OUI Database Loaded. Entries: 36858 233 | [+] Analyzing pcap file... 234 | 235 | ============================== 236 | [+] Detected DHCP Discovery 237 | [*] DHCP Discovery can lead to unauthorized network configuration 238 | [*] DHCP Client IP: 0.0.0.0 (Broadcast) 239 | [*] DHCP Speaker MAC: 00:11:5a:c6:1f:ea 240 | [*] Mitigation: Use DHCP Snooping 241 | [*] Vendor: Ivoclar Vivadent AG 242 | ============================== 243 | [+] Detected HSRPv2 Packet 244 | [*] Attack Impact: MITM 245 | [*] Tools: Loki 246 | [!] HSRPv2 has not yet been implemented in Scapy 247 | [!] Check priority and state manually using Wireshark 248 | [!] If the Active Router priority is less than 255 and you were able to break MD5 authentication, you can do a MITM 249 | [*] HSRPv2 Speaker MAC: 00:00:0c:9f:f0:01 250 | [*] HSRPv2 Speaker IP: 10.0.0.10 251 | [*] Mitigation: Priority 255, Authentication, Extended ACL 252 | [*] Vendor: Cisco Systems 253 | ``` 254 | 255 | # Passive ARP 256 | 257 | This can be very useful if an attacker doesn't want to make noise on the air with ARP scans and quietly discover hosts. This function is run with `--passive-arp` and all hosts found will be written to the `above_passive_arp.txt` file. 258 | 259 | ```bash 260 | caster@kali:~$ sudo above --interface eth0 --passive-arp 261 | 262 | [+] Starting Host Discovery... 263 | [*] IP and MAC addresses will be saved to 'above_passive_arp.txt' 264 | ``` 265 | 266 | > If you want, you can specify a timer for how long to listen to ARP frames to find hosts. By default, no timer is set. 267 | 268 | Once started, the terminal will be completely cleared and a table consisting of a mapping of IP Address and MAC Address will be displayed: 269 | 270 | ```bash 271 | +--------------------+------------------------------+--------------------+ 272 | | IP Address | MAC Address | ARP Type | 273 | +--------------------+------------------------------+--------------------+ 274 | | 172.16.120.12 | f0:27:65:ba:1c:42 | ARP Response | 275 | | 172.16.120.45 | 6d:9f:84:2b:33:ea | ARP Request | 276 | | 172.16.120.78 | 3a:7c:19:d8:4e:21 | ARP Response | 277 | | 172.16.120.103 | c4:12:76:ae:50:bb | ARP Request | 278 | | 172.16.120.127 | 89:3b:df:92:6a:54 | ARP Response | 279 | | 172.16.120.156 | b7:5d:49:cb:72:99 | ARP Request | 280 | | 172.16.120.189 | 1e:47:ac:3d:15:f8 | ARP Response | 281 | | 172.16.120.222 | 43:9a:df:e0:84:3c | ARP Request | 282 | +--------------------+------------------------------+--------------------+ 283 | 284 | ``` 285 | 286 | The contents of the `above_passive_arp.txt` file will look like this: 287 | 288 | ```bash 289 | caster@kali:~$ cat above_passive_arp.txt 290 | Above: Passive ARP Host Discovery 291 | Time: 2024-08-16 17:30:16 292 | -------------------------------------------------- 293 | 172.16.120.12 - f0:27:65:ba:1c:42 294 | 172.16.120.45 - 6d:9f:84:2b:33:ea 295 | 172.16.120.78 - 3a:7c:19:d8:4e:21 296 | 172.16.120.103 - c4:12:76:ae:50:bb 297 | 172.16.120.127 - 89:3b:df:92:6a:54 298 | 172.16.120.156 - b7:5d:49:cb:72:99 299 | 172.16.120.189 - 1e:47:ac:3d:15:f8 300 | 172.16.120.222 - 43:9a:df:e0:84:3c 301 | ``` 302 | 303 | This is how Above with ARP frame learning can help discover hosts in a segment without noise in the air. 304 | 305 | # VLAN Segments Search 306 | 307 | Above can also find VLAN IDs in traffic. This is a useful option if the attacker is on a trunk port under some circumstances during the pentest. And the problem is that on a trunk port, all traffic is tagged with 802.1Q tags belonging to VLAN segments. Above can extract all VLAN IDs from the air or from a traffic dump: 308 | 309 | ```bash 310 | caster@kali:~$ sudo above --interface eth0 --search-vlan 311 | ``` 312 | 313 | When you run this function, the terminal will also be cleared and a table will be displayed and updated: 314 | 315 | ```bash 316 | +------------------------------+---------------+----------------------------------------+ 317 | |VLAN ID |Frames Count |How to Jump | 318 | +------------------------------+---------------+----------------------------------------+ 319 | |120 |8 |sudo vconfig add eth0 120 | 320 | |80 |8 |sudo vconfig add eth0 80 | 321 | |251 |7 |sudo vconfig add eth0 251 | 322 | |190 |6 |sudo vconfig add eth0 190 | 323 | +------------------------------+---------------+----------------------------------------+ 324 | ``` 325 | 326 | Also, the result of this function will be written to the `above_discovered_vlan.txt` file: 327 | 328 | ```bash 329 | caster@kali:~$ cat above_discovered_vlan.txt 330 | Above: Discovered VLAN ID 331 | Time: 2024-08-16 17:41:19 332 | -------------------------------------------------------------------------------- 333 | VLAN ID Frames Count How to Jump 334 | -------------------------------------------------------------------------------- 335 | 1 208 sudo vconfig add eth0 1 336 | 5 972 sudo vconfig add eth0 5 337 | 6 904 sudo vconfig add eth0 6 338 | 10 20 sudo vconfig add eth0 10 339 | 12 20 sudo vconfig add eth0 12 340 | 13 20 sudo vconfig add eth0 13 341 | 11 20 sudo vconfig add eth0 11 342 | 2000 1 sudo vconfig add eth0 2000 343 | 1000 2 sudo vconfig add eth0 1000 344 | -------------------------------------------------------------------------------- 345 | ``` 346 | 347 | This is how you can find information about VLAN segments based on traffic operations alone. But it is worth considering that this is a specific scenario, it is not often that an attacker will be on a trunk port. Either he will be lucky with DTP or he will stumble upon a switch port forgotten by the administrator. 348 | 349 | # MAC Lookup 350 | 351 | As of version 2.8 Above is now able to identify the vendor by MAC address, specifically by the first 24 bits. This is done by using a [downloaded database](https://standards-oui.ieee.org/), then converting it into the `above_oui_dict.py` module, which is a dictionary consisting of unique OUIs and vendor names. 352 | 353 | # Copyright 354 | 355 | Copyright (c) 2025 Magama Bazarov. This project is licensed under the Apache 2.0 License 356 | 357 | # Outro 358 | 359 | When I create this instrument, I am inspired by the track KOAN Sound - View From Above (VIP) 360 | This track was everything to me when I was working on this tool. 361 | 362 | --- 363 | 364 | -------------------------------------------------------------------------------- /above.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | logging.getLogger("scapy.runtime").setLevel(logging.ERROR) 5 | import argparse 6 | from scapy.all import sniff, rdpcap, wrpcap, Ether, Dot1Q, IP, VRRP, VRRPv3, STP, IPv6, AH, Dot3, ARP, TCP, UDP, CookedLinux 7 | from scapy.contrib.macsec import MACsec, MACsecSCI 8 | from scapy.contrib.eigrp import EIGRP, EIGRPAuthData 9 | from scapy.contrib.ospf import OSPF_Hdr 10 | from scapy.contrib.cdp import CDPv2_HDR, CDPMsgDeviceID, CDPMsgPlatform, CDPMsgPortID, CDPAddrRecordIPv4, CDPMsgSoftwareVersion 11 | from scapy.contrib.dtp import DTP 12 | from scapy.layers.hsrp import HSRP, HSRPmd5 13 | from scapy.layers.llmnr import LLMNRQuery 14 | from scapy.contrib.modbus import ModbusADURequest, ModbusADUResponse 15 | from scapy.layers.eap import EAPOL 16 | from scapy.contrib.tacacs import TacacsHeader 17 | from scapy.contrib.bgp import BGPHeader, BGPOpen 18 | from scapy.layers.dhcp import DHCP, BOOTP 19 | from scapy.contrib.igmp import IGMP 20 | from scapy.contrib.igmpv3 import * 21 | from scapy.layers.inet6 import ICMPv6ND_RS 22 | from scapy.contrib.lldp import LLDPDU, LLDPDUSystemName, LLDPDUSystemDescription, LLDPDUPortID, LLDPDUManagementAddress 23 | from colorama import Fore, Style, init 24 | import socket 25 | import signal 26 | import sys 27 | import os 28 | import sys 29 | from collections import defaultdict 30 | from datetime import datetime 31 | from scapy.layers.snmp import SNMP 32 | from above_oui_dict import above_oui 33 | 34 | # For colors (colorama) 35 | init(autoreset=True) 36 | 37 | # banner 38 | banner = r""" 39 | ___ _ 40 | / _ \| | 41 | / /_\ \ |__ _____ _____ 42 | | _ | '_ \ / _ \ \ / / _ \ 43 | | | | | |_) | (_) \ V / __/ 44 | \_| |_/_.__/ \___/ \_/ \___| 45 | """ 46 | 47 | indent = " " 48 | 49 | # right indented banner output 50 | print(indent + banner.replace("\n", "\n" + indent)) 51 | print(indent + Fore.YELLOW + "Invisible network protocol sniffer. Designed for security engineers\n") 52 | print(indent + Fore.YELLOW + "Author: " + Style.RESET_ALL + "Magama Bazarov, ") 53 | print(indent + Fore.YELLOW + "Alias: " + Style.RESET_ALL + "Caster") 54 | print(indent + Fore.YELLOW + "Version: " + Style.RESET_ALL + "2.8") 55 | print(indent + Fore.YELLOW + "Codename: " + Style.RESET_ALL + "Rubens Barrichello\n") 56 | print(indent + Fore.YELLOW + "[!] Above does NOT perform MITM or credential capture. Passive analysis only") 57 | print(indent + Fore.YELLOW + "[!] Unauthorized use in third-party networks may violate local laws") 58 | print(indent + Fore.YELLOW + "[!] The developer assumes NO liability for improper or illegal use\n" + Style.RESET_ALL) 59 | 60 | def get_mac_vendor(mac_address): 61 | mac_clean = mac_address.replace(":", "").upper()[:6] 62 | return above_oui.get(mac_clean, "Unknown Vendor") 63 | 64 | def get_mac_from_packet(packet, protocol=None): 65 | if protocol == "STP" and packet.haslayer(STP): 66 | return str(packet[STP].rootmac) 67 | 68 | if protocol == "DTP" and packet.haslayer(Dot3): 69 | return packet[Dot3].src 70 | 71 | if packet.haslayer(Ether): 72 | return packet[Ether].src 73 | elif packet.haslayer(CookedLinux): 74 | return 'Unknown (Cooked Capture)' 75 | 76 | return 'Unknown' 77 | 78 | print(indent + Fore.GREEN + f"[*] OUI Database Loaded. Entries: {len(above_oui)}") 79 | 80 | 81 | # Parsing pcaps 82 | def analyze_pcap(pcap_path): 83 | packets = rdpcap(pcap_path) 84 | for packet in packets: 85 | packet_detection(packet) 86 | 87 | # Packet Processing 88 | def packet_detection(packet): 89 | if (packet.haslayer(OSPF_Hdr) or packet.haslayer(CDPv2_HDR) or packet.haslayer(MACsec) or packet.haslayer(EAPOL) 90 | or packet.haslayer(EIGRP) or packet.haslayer(DTP) or packet.haslayer(STP) or packet.haslayer(LLDPDU) 91 | or packet.haslayer(HSRP) or packet.haslayer(VRRP) or packet.haslayer(VRRPv3) or packet.haslayer(ModbusADURequest) 92 | or packet.haslayer(ModbusADUResponse) or packet.haslayer(BGPOpen) or packet.haslayer(BGPHeader) 93 | or packet.haslayer(Dot1Q) or packet.haslayer(Dot3) or packet.haslayer(BOOTP) or packet.haslayer(DHCP) 94 | or packet.haslayer(IGMP) or packet.haslayer(ICMPv6ND_RS) or packet.haslayer(IPv6) 95 | or (packet.haslayer(UDP) and packet[UDP].dport in [137, 161, 5353, 5355, 5678, 3222, 546, 547, 1900, 9600]) 96 | or (packet.haslayer(TCP) and packet[TCP].dport == 102) 97 | or (packet.haslayer(IP) and packet.haslayer(UDP) and packet[IP].dst == "224.0.0.102" and packet[UDP].dport == 1985)): 98 | packets.append(packet) 99 | 100 | # MACSec 101 | if packet.haslayer(MACsec): 102 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 103 | print(Fore.WHITE + Style.BRIGHT + "[*] Detected MACSec") 104 | print(Fore.YELLOW + Style.BRIGHT + "[*] Most likely the infrastructure used is 802.1X-2010, keep in mind") 105 | packets.append(packet) 106 | try: 107 | print(Fore.GREEN + Style.BRIGHT + "[*] System Identifier: " + Fore.WHITE + Style.BRIGHT + packet[0][MACsec][MACsecSCI].system_identifier) 108 | except: 109 | print(Fore.GREEN + Style.BRIGHT + "[*] System Identifier: " + Fore.WHITE + Style.BRIGHT + "Not Found") 110 | 111 | # OSPF 112 | if packet.haslayer(OSPF_Hdr): 113 | def hex_to_string(hex): 114 | if hex[:2] == '0x': 115 | hex = hex[2:] 116 | string_value = bytes.fromhex(hex).decode('utf-8') 117 | return string_value 118 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 119 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected OSPF Packet") 120 | print(Fore.GREEN + Style.BRIGHT + "[+] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Subnets Discovery, Blackhole, Evil Twin, Routing Table Overflow") 121 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Loki, Scapy, FRRouting") 122 | print(Fore.GREEN + Style.BRIGHT + "[*] OSPF Area ID: " + Fore.WHITE + Style.BRIGHT + str(packet[OSPF_Hdr].area)) 123 | print(Fore.GREEN + Style.BRIGHT + "[*] OSPF Neighbor IP: " + Fore.WHITE + Style.BRIGHT + str(packet[OSPF_Hdr].src)) 124 | packets.append(packet) 125 | 126 | if packet.haslayer(Ether): 127 | mac_src = packet[Ether].src 128 | elif packet.haslayer(CookedLinux): 129 | mac_src = 'Unknown (Cooked Capture)' 130 | else: 131 | mac_src = 'Unknown' 132 | 133 | mac_src = get_mac_from_packet(packet) 134 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 135 | 136 | print(Fore.GREEN + Style.BRIGHT + "[*] OSPF Neighbor MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 137 | 138 | # Authentication Checking 139 | if packet[OSPF_Hdr].authtype == 0x0: 140 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: No") 141 | elif packet[OSPF_Hdr].authtype == 0x1: 142 | raw = packet[OSPF_Hdr].authdata 143 | hex_value = hex(raw) 144 | string = hex_to_string(hex_value) 145 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: Plaintext Phrase: " + string) 146 | elif packet[OSPF_Hdr].authtype == 0x02: 147 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: MD5 or SHA-256") 148 | print(Fore.YELLOW + Style.BRIGHT + "[*] Tools for bruteforce: Ettercap, John the Ripper") 149 | print(Fore.GREEN + Style.BRIGHT + "[*] OSPF Key ID: " + Fore.WHITE + Style.BRIGHT + str(packet[OSPF_Hdr].keyid)) 150 | 151 | # Mitigation 152 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Passive interfaces, Authentication, Extended ACL") 153 | # Vendor 154 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 155 | 156 | # BGP 157 | if packet.haslayer(BGPHeader): 158 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 159 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected BGP Packet") 160 | print(Fore.GREEN + Style.BRIGHT + "[+] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Route Hijacking") 161 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Scapy, FRRouting") 162 | packets.append(packet) 163 | 164 | bgp_header = packet.getlayer(BGPHeader) 165 | if bgp_header: 166 | print(Fore.GREEN + Style.BRIGHT + "[*] BGP Header Fields: " + Fore.WHITE + Style.BRIGHT + str(bgp_header.fields)) 167 | 168 | if packet.haslayer(BGPOpen): 169 | bgp_open = packet.getlayer(BGPOpen) 170 | print(Fore.GREEN + Style.BRIGHT + "[*] Source AS Number: " + Fore.WHITE + Style.BRIGHT + str(bgp_open.my_as)) 171 | print(Fore.GREEN + Style.BRIGHT + "[*] Peer IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].src)) 172 | print(Fore.GREEN + Style.BRIGHT + "[*] Hold Time: " + Fore.WHITE + Style.BRIGHT + str(bgp_open.hold_time)) 173 | 174 | if packet.haslayer(Ether): 175 | mac_src = packet[Ether].src 176 | elif packet.haslayer(CookedLinux): 177 | mac_src = 'Unknown (Cooked Capture)' 178 | else: 179 | mac_src = 'Unknown' 180 | 181 | mac_src = get_mac_from_packet(packet) 182 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 183 | 184 | print(Fore.GREEN + Style.BRIGHT + "[*] Peer MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 185 | 186 | # Mitigation 187 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Use authentication, filter routes") 188 | # Vendor 189 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 190 | 191 | # HSRP (v1) 192 | if packet.haslayer(HSRP) and packet[HSRP].state == 16: 193 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 194 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected HSRP Packet") 195 | print(Fore.GREEN + Style.BRIGHT + "[*] HSRP Active Router Priority: " + Fore.WHITE + Style.BRIGHT + str(packet[HSRP].priority)) 196 | print(Fore.GREEN + Style.BRIGHT + "[+] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "MITM") 197 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Loki, Scapy, Yersinia") 198 | print(Fore.GREEN + Style.BRIGHT + "[*] HSRP Group Number: " + Fore.WHITE + Style.BRIGHT + str(packet[HSRP].group)) 199 | print(Fore.GREEN + Style.BRIGHT + "[+] HSRP Virtual IP Address: " + Fore.WHITE + Style.BRIGHT + str(packet[HSRP].virtualIP)) 200 | print(Fore.GREEN + Style.BRIGHT + "[*] HSRP Speaker IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].src)) 201 | packets.append(packet) 202 | 203 | if packet.haslayer(Ether): 204 | mac_src = packet[Ether].src 205 | elif packet.haslayer(CookedLinux): 206 | mac_src = 'Unknown (Cooked Capture)' 207 | else: 208 | mac_src = 'Unknown' 209 | 210 | mac_src = get_mac_from_packet(packet) 211 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 212 | print(Fore.GREEN + Style.BRIGHT + "[*] HSRP Speaker MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 213 | 214 | # Authentication Checking 215 | if packet.haslayer(HSRPmd5): 216 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: " + Fore.WHITE + Style.BRIGHT + "MD5") 217 | print(Fore.YELLOW + Style.BRIGHT + "[*] Tools for bruteforce: hsrp2john.py, John the Ripper") 218 | elif packet[HSRP].auth: 219 | hsrpv1_plaintext = packet[HSRP].auth 220 | simplehsrppass = hsrpv1_plaintext.decode("UTF-8") 221 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: " + Fore.WHITE + Style.BRIGHT + "Plaintext Phrase: " + simplehsrppass) 222 | 223 | # Mitigation 224 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Priority 255, Authentication, Extended ACL") 225 | # Vendor 226 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 227 | 228 | # HSRP (v2) 229 | if packet.haslayer(IP) and packet.haslayer(UDP): 230 | if packet[IP].dst == "224.0.0.102" and packet[UDP].dport == 1985: 231 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 232 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected HSRPv2 Packet") 233 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "MITM") 234 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Loki") 235 | # Caution 236 | print(Fore.YELLOW + Style.BRIGHT + "[!] HSRPv2 has not yet been implemented in Scapy") 237 | print(Fore.YELLOW + Style.BRIGHT + "[!] Check priority and state manually using Wireshark") 238 | print(Fore.YELLOW + Style.BRIGHT + "[!] If the Active Router priority is less than 255 and you were able to break MD5 authentication, you can do a MITM") 239 | packets.append(packet) 240 | 241 | if packet.haslayer(Ether): 242 | mac_src = packet[Ether].src 243 | elif packet.haslayer(CookedLinux): 244 | mac_src = 'Unknown (Cooked Capture)' 245 | else: 246 | mac_src = 'Unknown' 247 | 248 | mac_src = get_mac_from_packet(packet) 249 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 250 | 251 | print(Fore.GREEN + Style.BRIGHT + "[*] HSRPv2 Speaker MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 252 | print(Fore.GREEN + Style.BRIGHT + "[*] HSRPv2 Speaker IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].src)) 253 | # Mitigation 254 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Priority 255, Authentication, Extended ACL") 255 | # Vendor 256 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 257 | 258 | # VRRPv2 259 | if packet.haslayer(VRRP): 260 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 261 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected VRRPv2 Packet") 262 | packets.append(packet) 263 | 264 | if packet.haslayer(AH): 265 | print (Fore.YELLOW + Style.BRIGHT + "[!] Authentication: AH Header detected, VRRP packet is encrypted") 266 | return 0 267 | 268 | if packet.haslayer(VRRP): 269 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv2 Master Router Priority: " + Fore.WHITE + Style.BRIGHT + str(packet[VRRP].priority)) 270 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "MITM") 271 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Scapy, Loki") 272 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv2 Group Number: " + Fore.WHITE + Style.BRIGHT + str(packet[VRRP].vrid)) 273 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv2 Speaker IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].src)) 274 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv2 Virtual IP Address: " + Fore.WHITE + Style.BRIGHT + ', '.join(packet[VRRP].addrlist)) 275 | 276 | if packet.haslayer(Ether): 277 | mac_src = packet[Ether].src 278 | elif packet.haslayer(CookedLinux): 279 | mac_src = 'Unknown (Cooked Capture)' 280 | else: 281 | mac_src = 'Unknown' 282 | 283 | mac_src = get_mac_from_packet(packet) 284 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 285 | 286 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv2 Speaker MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 287 | 288 | if packet[VRRP].authtype == 0: 289 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: No") 290 | elif packet[VRRP].authtype == 0x1: 291 | print(Fore.WHITE + Style.BRIGHT + "[*] Authentication: Plaintext") 292 | try: 293 | auth1_bytes = packet[VRRP].auth1.to_bytes(4, byteorder='big') 294 | auth2_bytes = packet[VRRP].auth2.to_bytes(4, byteorder='big') 295 | plaintext_password = (auth1_bytes + auth2_bytes).decode(errors="ignore").strip("\x00") 296 | print(Fore.YELLOW + Style.BRIGHT + "[!] Extracted VRRP Password: " + Fore.WHITE + Style.BRIGHT + plaintext_password) 297 | except Exception as e: 298 | print(Fore.RED + Style.BRIGHT + "[!] Failed to extract password: " + str(e)) 299 | elif packet[VRRP].authtype == 254: 300 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: MD5") 301 | 302 | # Mitigation 303 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Authentication, ACL") 304 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 305 | 306 | 307 | # VRRPv3 308 | if packet.haslayer(VRRPv3): 309 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 310 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected VRRPv3 Packet") 311 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv3 master router priority: " + Fore.WHITE + Style.BRIGHT + str(packet[VRRPv3].priority)) 312 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "MITM") 313 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Scapy, Loki") 314 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv3 Group Number: " + Fore.WHITE + Style.BRIGHT + str(packet[VRRPv3].vrid)) 315 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv3 Speaker IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].src)) 316 | packets.append(packet) 317 | 318 | if packet.haslayer(Ether): 319 | mac_src = packet[Ether].src 320 | elif packet.haslayer(CookedLinux): 321 | mac_src = 'Unknown (Cooked Capture)' 322 | else: 323 | mac_src = 'Unknown' 324 | 325 | mac_src = get_mac_from_packet(packet) 326 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 327 | 328 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv3 Speaker MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 329 | print(Fore.GREEN + Style.BRIGHT + "[*] VRRPv3 Virtual IP Address: " + Fore.WHITE + Style.BRIGHT + ', '.join(packet[VRRPv3].addrlist)) 330 | 331 | # Mitigation 332 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Filter VRRP traffic using ACL") 333 | # Vendor 334 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 335 | 336 | # GLBP 337 | if packet.haslayer(IP) and packet.haslayer(UDP): 338 | if packet[IP].dst == "224.0.0.102" and packet[UDP].dport == 3222: 339 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 340 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected GLBP Packet") 341 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "MITM") 342 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Loki") 343 | # Caution 344 | print(Fore.YELLOW + Style.BRIGHT + "[!] GLBP has not yet been implemented by Scapy") 345 | print(Fore.YELLOW + Style.BRIGHT + "[!] Check AVG router priority values manually using Wireshark") 346 | print(Fore.YELLOW + Style.BRIGHT + "[!] If the AVG router's priority value is less than 255, you have a chance of launching a MITM attack.") 347 | packets.append(packet) 348 | 349 | if packet.haslayer(Ether): 350 | mac_src = packet[Ether].src 351 | elif packet.haslayer(CookedLinux): 352 | mac_src = 'Unknown (Cooked Capture)' 353 | else: 354 | mac_src = 'Unknown' 355 | 356 | mac_src = get_mac_from_packet(packet) 357 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 358 | 359 | print(Fore.GREEN + Style.BRIGHT + "[*] GLBP Speaker MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 360 | print(Fore.GREEN + Style.BRIGHT + "[*] GLBP Speaker IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].src)) 361 | 362 | # Mitigation 363 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Use priority 255, use authentication") 364 | # Vendor 365 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 366 | 367 | # DTP 368 | if packet.haslayer(DTP): 369 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 370 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected DTP Frame") 371 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "VLAN Segmentation Bypass") 372 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Yersinia, Scapy") 373 | packets.append(packet) 374 | 375 | if packet.haslayer(Dot3): 376 | mac_src = packet[Dot3].src 377 | elif packet.haslayer(Ether): 378 | mac_src = packet[Ether].src 379 | elif packet.haslayer(CookedLinux): 380 | mac_src = 'Unknown (Cooked Capture)' 381 | else: 382 | mac_src = 'Unknown' 383 | 384 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 385 | 386 | print(Fore.GREEN + Style.BRIGHT + "[*] DTP Neighbor MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 387 | 388 | # Mitigation 389 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Disable DTP") 390 | # Vendor 391 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 392 | 393 | 394 | # STP 395 | if packet.haslayer(STP): 396 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 397 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected STP Frame") 398 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Partial MITM") 399 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Yersinia, Scapy") 400 | packets.append(packet) 401 | 402 | if packet.haslayer(Ether): 403 | root_switch_mac = str(packet[STP].rootmac) 404 | elif packet.haslayer(Dot3): 405 | root_switch_mac = packet[Dot3].src 406 | elif packet.haslayer(CookedLinux): 407 | root_switch_mac = 'Unknown (Cooked Capture)' 408 | else: 409 | root_switch_mac = 'Unknown' 410 | 411 | # Vendor lookup 412 | vendor = get_mac_vendor(root_switch_mac) if root_switch_mac != 'Unknown' else 'N/A' 413 | 414 | print(Fore.GREEN + Style.BRIGHT + "[*] STP Root Switch MAC: " + Fore.WHITE + Style.BRIGHT + root_switch_mac) 415 | print(Fore.GREEN + Style.BRIGHT + "[*] STP Root ID: " + Fore.WHITE + Style.BRIGHT + str(packet[STP].rootid)) 416 | print(Fore.GREEN + Style.BRIGHT + "[*] STP Root Path Cost: " + Fore.WHITE + Style.BRIGHT + str(packet[STP].pathcost)) 417 | 418 | # Mitigation 419 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Enable BPDU Guard") 420 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 421 | 422 | # CDP 423 | if packet.haslayer(CDPv2_HDR): 424 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 425 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected CDP Frame") 426 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Information Gathering, CDP Flood") 427 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Wireshark, Yersinia") 428 | 429 | hostname = packet[CDPMsgDeviceID].val.decode() if packet.haslayer(CDPMsgDeviceID) else "Unknown" 430 | os_version = packet[CDPMsgSoftwareVersion].val.decode() if packet.haslayer(CDPMsgSoftwareVersion) else "Unknown" 431 | platform = packet[CDPMsgPlatform].val.decode() if packet.haslayer(CDPMsgPlatform) else "Unknown" 432 | port_id = packet[CDPMsgPortID].iface.decode() if packet.haslayer(CDPMsgPortID) else "Unknown" 433 | ip_address = packet[CDPAddrRecordIPv4].addr if packet.haslayer(CDPAddrRecordIPv4) else "Not Found" 434 | 435 | print(Fore.GREEN + Style.BRIGHT + "[*] Hostname: " + Fore.WHITE + Style.BRIGHT + hostname) 436 | print(Fore.GREEN + Style.BRIGHT + "[*] OS Version: " + Fore.WHITE + Style.BRIGHT + os_version) 437 | print(Fore.GREEN + Style.BRIGHT + "[*] Platform: " + Fore.WHITE + Style.BRIGHT + platform) 438 | print(Fore.GREEN + Style.BRIGHT + "[*] Port ID: " + Fore.WHITE + Style.BRIGHT + port_id) 439 | print(Fore.GREEN + Style.BRIGHT + "[*] IP Address: " + Fore.WHITE + Style.BRIGHT + ip_address) 440 | 441 | packets.append(packet) 442 | 443 | if packet.haslayer(Ether): 444 | mac_src = packet[Ether].src 445 | elif packet.haslayer(Dot3): 446 | mac_src = packet[Dot3].src 447 | elif packet.haslayer(CookedLinux): 448 | mac_src = 'Unknown (Cooked Capture)' 449 | else: 450 | mac_src = 'Unknown' 451 | 452 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 453 | 454 | print(Fore.GREEN + Style.BRIGHT + "[*] CDP Neighbor MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 455 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 456 | 457 | # Mitigation 458 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Disable CDP if not required, be careful with VoIP") 459 | 460 | 461 | # EIGRP 462 | if packet.haslayer(EIGRP): 463 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 464 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected EIGRP Packet") 465 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Subnets Discovery, Blackhole, Evil Twin") 466 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Loki, Scapy, FRRouting") 467 | print(Fore.GREEN + Style.BRIGHT + "[*] AS Number: " + Fore.WHITE + Style.BRIGHT + str(packet[EIGRP].asn)) 468 | packets.append(packet) 469 | 470 | if packet.haslayer(IP): 471 | print(Fore.GREEN + Style.BRIGHT + "[*] EIGRP Neighbor IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].src)) 472 | elif packet.haslayer(IPv6): 473 | print(Fore.GREEN + Style.BRIGHT + "[*] EIGRP Neighbor IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IPv6].src)) 474 | 475 | if packet.haslayer(Ether): 476 | neighbor_mac = packet[Ether].src 477 | elif packet.haslayer(CookedLinux): 478 | neighbor_mac = 'Unknown (Cooked Capture)' 479 | else: 480 | neighbor_mac = 'Unknown' 481 | 482 | neighbor_mac = get_mac_from_packet(packet) 483 | vendor = get_mac_vendor(neighbor_mac) if neighbor_mac != 'Unknown' else 'N/A' 484 | 485 | print(Fore.GREEN + Style.BRIGHT + "[*] EIGRP Neighbor MAC: " + Fore.WHITE + Style.BRIGHT + neighbor_mac) 486 | 487 | # Authentication Checking 488 | if packet.haslayer(EIGRPAuthData): 489 | print(Fore.YELLOW + Style.BRIGHT + "[!] There is EIGRP Authentication") 490 | authtype = packet[EIGRPAuthData].authtype 491 | if authtype == 2: 492 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: MD5") 493 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools for bruteforce: eigrp2john.py, John the Ripper") 494 | elif authtype == 3: 495 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: SHA-256") 496 | else: 497 | print(Fore.YELLOW + Style.BRIGHT + "[!] Authentication: No") 498 | 499 | # Mitigation 500 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Enable passive interfaces, use authentication") 501 | # Vendor 502 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 503 | 504 | # LLMNR 505 | if packet.haslayer(UDP) and packet[UDP].dport == 5355: 506 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 507 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected LLMNR Packet") 508 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "LLMNR Spoofing, Credentials Interception") 509 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Responder") 510 | packets.append(packet) 511 | 512 | try: 513 | llmnr_query_name = packet[LLMNRQuery].qd.qname.decode() 514 | except: 515 | llmnr_query_name = "Not Found" 516 | print(Fore.GREEN + Style.BRIGHT + "[*] LLMNR Query Name: " + Fore.WHITE + Style.BRIGHT + llmnr_query_name) 517 | 518 | try: 519 | llmnr_trans_id = packet[LLMNRQuery].id 520 | except: 521 | llmnr_trans_id = "Not Found" 522 | print(Fore.GREEN + Style.BRIGHT + "[*] LLMNR Packet Transaction ID: " + Fore.WHITE + Style.BRIGHT + str(llmnr_trans_id)) 523 | 524 | if packet.haslayer(IP): 525 | ip_src = packet[IP].src 526 | elif packet.haslayer(IPv6): 527 | ip_src = packet[IPv6].src 528 | else: 529 | print(Fore.RED + Style.BRIGHT + "[!] No IP layer found") 530 | return 531 | 532 | if packet.haslayer(Ether): 533 | mac_src = packet[Ether].src 534 | elif packet.haslayer(CookedLinux): 535 | mac_src = 'Unknown (Cooked Capture)' 536 | else: 537 | mac_src = 'Unknown' 538 | 539 | mac_src = get_mac_from_packet(packet) 540 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 541 | 542 | print(Fore.GREEN + Style.BRIGHT + "[*] LLMNR Speaker IP: " + Fore.WHITE + Style.BRIGHT + ip_src) 543 | print(Fore.GREEN + Style.BRIGHT + "[*] LLMNR Speaker MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 544 | 545 | # Mitigation 546 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Disable LLMNR") 547 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 548 | 549 | # NBT-NS 550 | if packet.haslayer(UDP) and packet[UDP].dport == 137: 551 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 552 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected NBT-NS Packet") 553 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "NBT-NS Spoofing, Credentials Interception") 554 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Responder") 555 | packets.append(packet) 556 | 557 | try: 558 | print(Fore.GREEN + Style.BRIGHT + "[*] NBT-NS Question Name: " + Fore.WHITE + Style.BRIGHT + str(packet[0]["NBNS registration request"].QUESTION_NAME.decode())) 559 | except: 560 | print(Fore.GREEN + Style.BRIGHT + "[*] NBT-NS Question Name: " + Fore.WHITE + Style.BRIGHT + "Not Found") 561 | 562 | try: 563 | print(Fore.GREEN + Style.BRIGHT + "[*] NBT-NS Packet Transaction ID: " + Fore.WHITE + Style.BRIGHT + str(packet[0]["NBNS Header"].NAME_TRN_ID)) 564 | except: 565 | print(Fore.GREEN + Style.BRIGHT + "[*] NBT-NS Packet Transaction ID: " + Fore.WHITE + Style.BRIGHT + "Not Found") 566 | 567 | print(Fore.GREEN + Style.BRIGHT + "[*] NBT-NS Speaker IP: " + Fore.WHITE + Style.BRIGHT + str(packet[0][IP].src)) 568 | 569 | if packet.haslayer(Ether): 570 | speaker_mac = packet[Ether].src 571 | elif packet.haslayer(CookedLinux): 572 | speaker_mac = 'Unknown (Cooked Capture)' 573 | else: 574 | speaker_mac = 'Unknown' 575 | 576 | speaker_mac = get_mac_from_packet(packet) 577 | vendor = get_mac_vendor(speaker_mac) if speaker_mac != 'Unknown' else 'N/A' 578 | 579 | print(Fore.GREEN + Style.BRIGHT + "[*] NBT-NS Speaker MAC: " + Fore.WHITE + Style.BRIGHT + speaker_mac) 580 | 581 | # Mitigation 582 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Disable NBT-NS") 583 | # Vendor 584 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 585 | 586 | # MDNS 587 | if packet.haslayer(UDP) and packet[UDP].dport == 5353: 588 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 589 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected MDNS Packet") 590 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "MDNS Spoofing, Credentials Interception") 591 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Responder") 592 | print(Fore.YELLOW + Style.BRIGHT + "[*] MDNS Spoofing works specifically against Windows machines") 593 | print(Fore.YELLOW + Style.BRIGHT + "[*] You cannot get NetNTLMv2-SSP from Apple devices") 594 | packets.append(packet) 595 | 596 | if packet.haslayer(IP): 597 | ip_src = packet[IP].src 598 | elif packet.haslayer(IPv6): 599 | ip_src = packet[IPv6].src 600 | 601 | if packet.haslayer(Ether): 602 | mac_src = packet[Ether].src 603 | elif packet.haslayer(CookedLinux): 604 | mac_src = 'Unknown (Cooked Capture)' 605 | else: 606 | mac_src = 'Unknown' 607 | 608 | mac_src = get_mac_from_packet(packet) 609 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 610 | 611 | print(Fore.GREEN + Style.BRIGHT + "[*] MDNS Speaker IP: " + Fore.WHITE + Style.BRIGHT + str(ip_src)) 612 | print(Fore.GREEN + Style.BRIGHT + "[*] MDNS Speaker MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 613 | 614 | # Mitigation 615 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Monitor mDNS traffic, this protocol can't just be turned off") 616 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 617 | 618 | # EAPOL 619 | if packet.haslayer(EAPOL): 620 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 621 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected EAPOL") 622 | packets.append(packet) 623 | if packet[EAPOL].version == 3: 624 | print (Fore.YELLOW + Style.BRIGHT + "[*] 802.1X Version: 2010") 625 | elif packet[EAPOL].version == 2: 626 | print (Fore.YELLOW + Style.BRIGHT + "[*] 802.1X Version: 2004") 627 | elif packet[EAPOL].version == 1: 628 | print (Fore.YELLOW + Style.BRIGHT + "[*] 802.1X Version: 2001") 629 | else: 630 | print (Fore.YELLOW + Style.BRIGHT + "[*] 802.1X Version: Unknown") 631 | 632 | # DHCP Discover 633 | if packet.haslayer(UDP) and packet[UDP].dport == 67 and packet.haslayer(DHCP): 634 | packets.append(packet) 635 | dhcp_options = packet[DHCP].options 636 | for option in dhcp_options: 637 | if option[0] == 'message-type' and option[1] == 1: 638 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 639 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected DHCP Discovery") 640 | print(Fore.YELLOW + Style.BRIGHT + "[*] DHCP Discovery can lead to unauthorized network configuration") 641 | print(Fore.GREEN + Style.BRIGHT + "[*] DHCP Client IP: " + Fore.WHITE + Style.BRIGHT + "0.0.0.0 (Broadcast)") 642 | 643 | if packet.haslayer(Ether): 644 | mac_src = packet[Ether].src 645 | elif packet.haslayer(CookedLinux): 646 | mac_src = 'Unknown (Cooked Capture)' 647 | else: 648 | mac_src = 'Unknown' 649 | 650 | mac_src = get_mac_from_packet(packet) 651 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 652 | 653 | print(Fore.GREEN + Style.BRIGHT + "[*] DHCP Speaker MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 654 | 655 | # Mitigation 656 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Use DHCP Snooping") 657 | # Vendor 658 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 659 | 660 | # IGMP 661 | if packet.haslayer(IGMP): 662 | igmp_type = packet[IGMP].type 663 | igmp_types = { 664 | 0x11: "Membership Query", 0x12: "Version 1 - Membership Report", 665 | 0x16: "Version 2 - Membership Report", 0x17: "Leave Group", 0x22: "Version 3 - Membership Report" 666 | } 667 | packets.append(packet) 668 | igmp_type_description = igmp_types.get(igmp_type, "Unknown IGMP Type") 669 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 670 | print(Fore.WHITE + Style.BRIGHT + f"[+] Detected IGMP Packet: {igmp_type_description}") 671 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "IGMP Sniffing, IGMP Flood") 672 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Scapy, Wireshark") 673 | print(Fore.YELLOW + Style.BRIGHT + "[*] IGMP is used to manage multicast groups") 674 | print(Fore.YELLOW + Style.BRIGHT + "[*] IGMP types include queries, reports, and leaves") 675 | print(Fore.GREEN + Style.BRIGHT + "[*] IGMP Speaker IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].src)) 676 | print(Fore.GREEN + Style.BRIGHT + "[*] Multicast Address: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].dst)) 677 | 678 | # Mitigation 679 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "If there is a lot of multicast traffic, use IGMP Snooping") 680 | 681 | # ICMPv6 RS 682 | if packet.haslayer(ICMPv6ND_RS): 683 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 684 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected ICMPv6 Router Solicitation (RS)") 685 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Potential for DoS attacks and network reconnaissance") 686 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Scapy") 687 | print(Fore.YELLOW + Style.BRIGHT + "[*] ICMPv6 RS messages are used by devices to locate routers") 688 | print(Fore.GREEN + Style.BRIGHT + "[*] IPv6 Source Address: " + Fore.WHITE + Style.BRIGHT + str(packet[IPv6].src)) 689 | print(Fore.GREEN + Style.BRIGHT + "[*] Target of Solicitation: " + Fore.WHITE + Style.BRIGHT + "All Routers Multicast Address (typically ff02::2)") 690 | packets.append(packet) 691 | 692 | # LLDP 693 | if packet.haslayer(LLDPDU): 694 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 695 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected LLDP Frame") 696 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Information Gathering") 697 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Wireshark") 698 | packets.append(packet) 699 | 700 | hostname = packet[LLDPDUSystemName].system_name.decode() if packet.haslayer(LLDPDUSystemName) and isinstance(packet[LLDPDUSystemName].system_name, bytes) else packet[LLDPDUSystemName].system_name if packet.haslayer(LLDPDUSystemName) else "Not Found" 701 | os_version = packet[LLDPDUSystemDescription].description.decode() if packet.haslayer(LLDPDUSystemDescription) and isinstance(packet[LLDPDUSystemDescription].description, bytes) else packet[LLDPDUSystemDescription].description if packet.haslayer(LLDPDUSystemDescription) else "Not Found" 702 | port_id = packet[LLDPDUPortID].id.decode() if packet.haslayer(LLDPDUPortID) and isinstance(packet[LLDPDUPortID].id, bytes) else packet[LLDPDUPortID].id if packet.haslayer(LLDPDUPortID) else "Not Found" 703 | print(Fore.GREEN + Style.BRIGHT + "[*] Hostname: " + Fore.WHITE + Style.BRIGHT + hostname) 704 | print(Fore.GREEN + Style.BRIGHT + "[*] OS Version: " + Fore.WHITE + Style.BRIGHT + os_version) 705 | print(Fore.GREEN + Style.BRIGHT + "[*] Port ID: " + Fore.WHITE + Style.BRIGHT + port_id) 706 | 707 | try: 708 | lldp_mgmt_address_bytes = packet[LLDPDUManagementAddress].management_address 709 | decoded_mgmt_address = socket.inet_ntoa(lldp_mgmt_address_bytes) 710 | print(Fore.GREEN + Style.BRIGHT + "[*] IP Address: " + Fore.WHITE + Style.BRIGHT + decoded_mgmt_address) 711 | except: 712 | print(Fore.GREEN + Style.BRIGHT + "[*] IP Address: " + Fore.WHITE + Style.BRIGHT + "Not Found") 713 | 714 | if packet.haslayer(Ether): 715 | source_mac = packet[Ether].src 716 | elif packet.haslayer(CookedLinux): 717 | source_mac = 'Unknown (Cooked Capture)' 718 | else: 719 | source_mac = 'Unknown' 720 | 721 | source_mac = get_mac_from_packet(packet) 722 | vendor = get_mac_vendor(source_mac) if source_mac != 'Unknown' else 'N/A' 723 | 724 | print(Fore.GREEN + Style.BRIGHT + "[*] LLDP Source MAC: " + Fore.WHITE + Style.BRIGHT + source_mac) 725 | 726 | # Mitigation 727 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Disable LLDP if not required, be careful with VoIP") 728 | # Vendor 729 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 730 | 731 | # MNDP 732 | if packet.haslayer(UDP) and packet[UDP].sport == 5678 and packet[UDP].dport == 5678: 733 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 734 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected MNDP Packet") 735 | print(Fore.WHITE + Style.BRIGHT + "[*] MikroTik device may have been detected") 736 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Information Gathering") 737 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Wireshark") 738 | packets.append(packet) 739 | 740 | if packet.haslayer(IP): 741 | speaker_ip = str(packet[IP].src) 742 | elif packet.haslayer(IPv6): 743 | speaker_ip = str(packet[IPv6].src) 744 | else: 745 | speaker_ip = "Unknown" 746 | print(Fore.GREEN + Style.BRIGHT + "[*] MNDP Speaker IP: " + Fore.WHITE + Style.BRIGHT + speaker_ip) 747 | 748 | if packet.haslayer(Ether): 749 | speaker_mac = packet[Ether].src 750 | elif packet.haslayer(CookedLinux): 751 | speaker_mac = 'Unknown (Cooked Capture)' 752 | else: 753 | speaker_mac = 'Unknown' 754 | 755 | speaker_mac = get_mac_from_packet(packet) 756 | vendor = get_mac_vendor(speaker_mac) if speaker_mac != 'Unknown' else 'N/A' 757 | 758 | print(Fore.GREEN + Style.BRIGHT + "[*] MNDP Speaker MAC: " + Fore.WHITE + Style.BRIGHT + speaker_mac) 759 | 760 | print(Fore.YELLOW + Style.BRIGHT + "[*] You can get more information from the packet in Wireshark") 761 | print(Fore.YELLOW + Style.BRIGHT + "[*] The MNDP protocol is not yet implemented in Scapy") 762 | 763 | # Mitigation 764 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Disable MNDP if not required") 765 | # Vendor 766 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 767 | 768 | # DHCPv6 769 | if packet.haslayer(UDP) and (packet[UDP].sport == 546 or packet[UDP].dport == 546 or packet[UDP].sport == 547 or packet[UDP].dport == 547): 770 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 771 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected DHCPv6 Packet") 772 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Potential DNS IPv6 Spoofing") 773 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "mitm6") 774 | packets.append(packet) 775 | 776 | if packet.haslayer(Ether): 777 | mac_src = packet[Ether].src 778 | ip_src = packet[IPv6].src 779 | elif packet.haslayer(CookedLinux): 780 | mac_src = 'Unknown (Cooked Capture)' 781 | ip_src = packet[IPv6].src 782 | else: 783 | mac_src = 'Unknown' 784 | ip_src = 'Unknown' 785 | 786 | mac_src = get_mac_from_packet(packet) 787 | vendor = get_mac_vendor(mac_src) if mac_src != 'Unknown' else 'N/A' 788 | 789 | print(Fore.GREEN + Style.BRIGHT + "[*] DHCPv6 Speaker MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 790 | print(Fore.GREEN + Style.BRIGHT + "[*] DHCPv6 Speaker IP: " + Fore.WHITE + Style.BRIGHT + ip_src) 791 | 792 | # Mitigation 793 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Enable DHCPv6 Snooping") 794 | # Vendor 795 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 796 | 797 | # SSDP 798 | if packet.haslayer(UDP) and packet[UDP].dport == 1900: 799 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 800 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected SSDP Packet") 801 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Potential for UPnP Device Exploitation, MITM") 802 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "evil-ssdp") 803 | print(Fore.YELLOW + Style.BRIGHT + "[*] Not every SSDP packet tells you that an attack is possible") 804 | packets.append(packet) 805 | 806 | if packet.haslayer(IP): 807 | print(Fore.GREEN + Style.BRIGHT + "[*] SSDP Source IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IP].src)) 808 | elif packet.haslayer(IPv6): 809 | print(Fore.GREEN + Style.BRIGHT + "[*] SSDP Source IP: " + Fore.WHITE + Style.BRIGHT + str(packet[IPv6].src)) 810 | 811 | if packet.haslayer(Ether): 812 | source_mac = packet[Ether].src 813 | elif packet.haslayer(CookedLinux): 814 | source_mac = 'Unknown (Cooked Capture)' 815 | else: 816 | source_mac = 'Unknown' 817 | 818 | source_mac = get_mac_from_packet(packet) 819 | vendor = get_mac_vendor(source_mac) if source_mac != 'Unknown' else 'N/A' 820 | 821 | print(Fore.GREEN + Style.BRIGHT + "[*] SSDP Source MAC: " + Fore.WHITE + Style.BRIGHT + source_mac) 822 | 823 | # Mitigation 824 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: "+ Fore.WHITE + Style.BRIGHT + "Ensure UPnP is disabled on all devices unless absolutely necessary, monitor UPnP and SSDP traffic") 825 | # Vendor 826 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Vendor: " + Fore.WHITE + Style.BRIGHT + vendor) 827 | 828 | # Modbus TCP (Request & Response Detecton) 829 | if packet.haslayer(ModbusADURequest): 830 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 831 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected Modbus ADU Request Packet") 832 | print(Fore.YELLOW + Style.BRIGHT + "[!] SCADA device may have been detected") 833 | print(Fore.GREEN + Style.BRIGHT + "[*] Transaction ID: " + Fore.WHITE + Style.BRIGHT + str(packet[ModbusADURequest].transId)) 834 | print(Fore.GREEN + Style.BRIGHT + "[*] Protocol ID: " + Fore.WHITE + Style.BRIGHT + str(packet[ModbusADURequest].protoId)) 835 | print(Fore.GREEN + Style.BRIGHT + "[*] Unit ID: " + Fore.WHITE + Style.BRIGHT + str(packet[ModbusADURequest].unitId)) 836 | packets.append(packet) 837 | 838 | if packet.haslayer(Ether): 839 | mac_src = packet[Ether].src 840 | mac_dst = packet[Ether].dst 841 | print(Fore.YELLOW + Style.BRIGHT + "[+] Source MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 842 | print(Fore.YELLOW + Style.BRIGHT + "[+] Destination MAC: " + Fore.WHITE + Style.BRIGHT + mac_dst) 843 | 844 | vendor_src = get_mac_vendor(mac_src) 845 | vendor_dst = get_mac_vendor(mac_dst) 846 | 847 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Source Vendor: " + Fore.WHITE + Style.BRIGHT + vendor_src) 848 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Destination Vendor: " + Fore.WHITE + Style.BRIGHT + vendor_dst) 849 | if packet.haslayer(IP): 850 | print(Fore.YELLOW + Style.BRIGHT + "[+] Source IP: " + Fore.WHITE + Style.BRIGHT + packet[IP].src) 851 | print(Fore.YELLOW + Style.BRIGHT + "[+] Destination IP: " + Fore.WHITE + Style.BRIGHT + packet[IP].dst) 852 | if packet.haslayer(TCP): 853 | print(Fore.WHITE + Style.BRIGHT + "[+] Source TCP Port: " + Fore.WHITE + Style.BRIGHT + str(packet[TCP].sport)) 854 | print(Fore.WHITE + Style.BRIGHT + "[+] Destination TCP Port: " + Fore.WHITE + Style.BRIGHT + str(packet[TCP].dport)) 855 | 856 | if packet.haslayer(ModbusADUResponse): 857 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 858 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected Modbus ADU Response Packet") 859 | print(Fore.YELLOW + Style.BRIGHT + "[!] SCADA device may have been detected") 860 | print(Fore.GREEN + Style.BRIGHT + "[*] Transaction ID: " + Fore.WHITE + Style.BRIGHT + str(packet[ModbusADUResponse].transId)) 861 | print(Fore.GREEN + Style.BRIGHT + "[*] Protocol ID: " + Fore.WHITE + Style.BRIGHT + str(packet[ModbusADUResponse].protoId)) 862 | print(Fore.GREEN + Style.BRIGHT + "[*] Unit ID: " + Fore.WHITE + Style.BRIGHT + str(packet[ModbusADUResponse].unitId)) 863 | packets.append(packet) 864 | 865 | if packet.haslayer(Ether): 866 | mac_src = packet[Ether].src 867 | mac_dst = packet[Ether].dst 868 | print(Fore.YELLOW + Style.BRIGHT + "[+] Source MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 869 | print(Fore.YELLOW + Style.BRIGHT + "[+] Destination MAC: " + Fore.WHITE + Style.BRIGHT + mac_dst) 870 | 871 | vendor_src = get_mac_vendor(mac_src) 872 | vendor_dst = get_mac_vendor(mac_dst) 873 | 874 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Source Vendor: " + Fore.WHITE + Style.BRIGHT + vendor_src) 875 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Destination Vendor: " + Fore.WHITE + Style.BRIGHT + vendor_dst) 876 | if packet.haslayer(IP): 877 | print(Fore.YELLOW + Style.BRIGHT + "[+] Source IP: " + Fore.WHITE + Style.BRIGHT + packet[IP].src) 878 | print(Fore.YELLOW + Style.BRIGHT + "[+] Destination IP: " + Fore.WHITE + Style.BRIGHT + packet[IP].dst) 879 | if packet.haslayer(TCP): 880 | print(Fore.WHITE + Style.BRIGHT + "[+] Source TCP Port: " + Fore.WHITE + Style.BRIGHT + str(packet[TCP].sport)) 881 | print(Fore.WHITE + Style.BRIGHT + "[+] Destination TCP Port: " + Fore.WHITE + Style.BRIGHT + str(packet[TCP].dport)) 882 | 883 | # OMRON 884 | if packet.haslayer(UDP) and packet[UDP].dport == 9600: 885 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 886 | print(Fore.WHITE + Style.BRIGHT + "[+] Possible OMRON packet detection") 887 | print(Fore.YELLOW + Style.BRIGHT + "[!] SCADA device may have been detected") 888 | packets.append(packet) 889 | if packet.haslayer(Ether): 890 | mac_src = packet[Ether].src 891 | mac_dst = packet[Ether].dst 892 | print(Fore.YELLOW + Style.BRIGHT + "[+] Source MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 893 | print(Fore.YELLOW + Style.BRIGHT + "[+] Destination MAC: " + Fore.WHITE + Style.BRIGHT + mac_dst) 894 | 895 | vendor_src = get_mac_vendor(mac_src) 896 | vendor_dst = get_mac_vendor(mac_dst) 897 | 898 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Source Vendor: " + Fore.WHITE + Style.BRIGHT + vendor_src) 899 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Destination Vendor: " + Fore.WHITE + Style.BRIGHT + vendor_dst) 900 | if packet.haslayer(IP): 901 | print(Fore.YELLOW + Style.BRIGHT + "[+] Source IP: " + Fore.WHITE + Style.BRIGHT + packet[IP].src) 902 | print(Fore.YELLOW + Style.BRIGHT + "[+] Destination IP: " + Fore.WHITE + Style.BRIGHT + packet[IP].dst) 903 | if packet.haslayer(UDP): 904 | print(Fore.WHITE + Style.BRIGHT + "[+] Source UDP Port: " + Fore.WHITE + Style.BRIGHT + str(packet[UDP].sport)) 905 | print(Fore.WHITE + Style.BRIGHT + "[+] Destination UDP Port: " + Fore.WHITE + Style.BRIGHT + str(packet[UDP].dport)) 906 | 907 | # S7COMM 908 | if packet.haslayer(TCP) and packet[TCP].dport == 102: 909 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 910 | print(Fore.WHITE + Style.BRIGHT + "[+] Possible S7COMM packet detection") 911 | print(Fore.YELLOW + Style.BRIGHT + "[!] SCADA device may have been detected") 912 | packets.append(packet) 913 | if packet.haslayer(Ether): 914 | mac_src = packet[Ether].src 915 | mac_dst = packet[Ether].dst 916 | print(Fore.YELLOW + Style.BRIGHT + "[+] Source MAC: " + Fore.WHITE + Style.BRIGHT + mac_src) 917 | print(Fore.YELLOW + Style.BRIGHT + "[+] Destination MAC: " + Fore.WHITE + Style.BRIGHT + mac_dst) 918 | 919 | vendor_src = get_mac_vendor(mac_src) 920 | vendor_dst = get_mac_vendor(mac_dst) 921 | 922 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Source Vendor: " + Fore.WHITE + Style.BRIGHT + vendor_src) 923 | print(Fore.MAGENTA + Style.BRIGHT + "[*] Destination Vendor: " + Fore.WHITE + Style.BRIGHT + vendor_dst) 924 | if packet.haslayer(IP): 925 | print(Fore.YELLOW + Style.BRIGHT + "[+] Source IP: " + Fore.WHITE + Style.BRIGHT + packet[IP].src) 926 | print(Fore.YELLOW + Style.BRIGHT + "[+] Destination IP: " + Fore.WHITE + Style.BRIGHT + packet[IP].dst) 927 | if packet.haslayer(TCP): 928 | print(Fore.WHITE + Style.BRIGHT + "[+] Source TCP Port: " + Fore.WHITE + Style.BRIGHT + str(packet[TCP].sport)) 929 | print(Fore.WHITE + Style.BRIGHT + "[+] Destination TCP Port: " + Fore.WHITE + Style.BRIGHT + str(packet[TCP].dport)) 930 | 931 | # TACACS+ 932 | if packet.haslayer(TacacsHeader): 933 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 934 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected TACACS Packet") 935 | packets.append(packet) 936 | header = packet[TacacsHeader] 937 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "Loki") 938 | print(Fore.YELLOW + Style.BRIGHT + "[!] Warning: MITM is required to capture TACACS+ traffic before brute-forcing the key!") 939 | print(Fore.GREEN + Style.BRIGHT + "[+] TACACS Type: " + Fore.WHITE + Style.BRIGHT + f"{header.type}") 940 | print(Fore.GREEN + Style.BRIGHT + "[+] TACACS Flags: " + Fore.WHITE + Style.BRIGHT + f"{header.flags}") 941 | print(Fore.GREEN + Style.BRIGHT + "[+] TACACS Session ID: " + Fore.WHITE + Style.BRIGHT + f"{header.session_id}") 942 | print(Fore.GREEN + Style.BRIGHT + "[+] TACACS Length: " + Fore.WHITE + Style.BRIGHT + f"{header.length}") 943 | 944 | if packet.haslayer(IP): 945 | src_ip = packet[IP].src 946 | dst_ip = packet[IP].dst 947 | print(Fore.GREEN + Style.BRIGHT + "[*] Source IP: " + Fore.WHITE + Style.BRIGHT + f"{src_ip}") 948 | print(Fore.GREEN + Style.BRIGHT + "[*] Destination IP: " + Fore.WHITE + Style.BRIGHT + f"{dst_ip}") 949 | 950 | # Further analysis 951 | if packet[TacacsHeader].type == 1: # Authentication 952 | print(Fore.YELLOW + Style.BRIGHT + "[*] TACACS Authentication Request Detected") 953 | 954 | elif packet[TacacsHeader].type == 2: # Authorization 955 | print(Fore.YELLOW + Style.BRIGHT + "[*] TACACS Authorization Request Detected") 956 | 957 | elif header.type == 3: # Accounting 958 | print(Fore.YELLOW + Style.BRIGHT + "[*] TACACS Accounting Request Detected") 959 | 960 | # Mitigation 961 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Use strong passwords, monitor unusual activities") 962 | 963 | # SNMP 964 | if packet.haslayer(UDP) and packet[UDP].dport == 161: 965 | print(Fore.WHITE + Style.BRIGHT + '=' * 30) 966 | print(Fore.WHITE + Style.BRIGHT + "[+] Detected SNMP Packet") 967 | print(Fore.GREEN + Style.BRIGHT + "[*] Attack Impact: " + Fore.YELLOW + Style.BRIGHT + "Information Gathering") 968 | print(Fore.GREEN + Style.BRIGHT + "[*] Tools: " + Fore.WHITE + Style.BRIGHT + "onesixtyone, snmpwalk, snmp_enum from Metasploit") 969 | packets.append(packet) 970 | 971 | if packet.haslayer(IP): 972 | ip_src = packet[IP].src 973 | ip_dst = packet[IP].dst 974 | print(Fore.GREEN + Style.BRIGHT + "[*] Source IP: " + Fore.WHITE + Style.BRIGHT + f"{ip_src}") 975 | print(Fore.GREEN + Style.BRIGHT + "[*] Destination IP: " + Fore.WHITE + Style.BRIGHT + f"{ip_dst}") 976 | 977 | # Checking for SNMP community string 978 | if packet.haslayer(SNMP): 979 | community_string = str(packet[SNMP].community) 980 | print(Fore.GREEN + Style.BRIGHT + "[*] SNMP Community String: " + Fore.WHITE + Style.BRIGHT + f"{community_string}") 981 | 982 | # Warning for default community strings 983 | if community_string.lower() in ["public", "private"]: 984 | print(Fore.YELLOW + Style.BRIGHT + "[!] Warning: Default SNMP community string used ('public' or 'private'). This is a security risk!") 985 | 986 | # Mitigation 987 | print(Fore.CYAN + Style.BRIGHT + "[*] Mitigation: " + Fore.WHITE + Style.BRIGHT + "Restrict SNMP access, use strong community strings, monitor SNMP traffic") 988 | 989 | # list for packets processing 990 | packets = [] 991 | 992 | # Passive ARP # 993 | arp_table = defaultdict(lambda: {"mac": "", "type": ""}) 994 | 995 | # write ips and macs to file 996 | def save_to_file_passive_arp(file_name="above_passive_arp.txt"): 997 | # write file 998 | with open(file_name, "w") as file: 999 | # timestamps 1000 | file.write("Above: Passive ARP Host Discovery\n") 1001 | file.write(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") 1002 | file.write("-" * 50 + "\n") 1003 | 1004 | # write ips and macs 1005 | for ip, info in arp_table.items(): 1006 | file.write(f"{ip} - {info['mac']}\n") 1007 | 1008 | # ARP Frames Sniffing 1009 | def passive_arp_monitor(packet): 1010 | # Displaying Table 1011 | def display_arp_table(): 1012 | print("\033c", end="") 1013 | # Table Header 1014 | print(Fore.WHITE + Style.BRIGHT + "+" + "-" * 20 + "+" + "-" * 30 + "+" + "-" * 20 + "+") 1015 | print(f"|{'IP Address':<20}|{'MAC Address':<30}|{'ARP Type':<20}|") 1016 | print(Fore.WHITE + Style.BRIGHT + "+" + "-" * 20 + "+" + "-" * 30 + "+" + "-" * 20 + "+") 1017 | 1018 | for ip, info in arp_table.items(): 1019 | mac = info["mac"] 1020 | arp_type = info["type"] 1021 | print(f"|{ip:<20}|{mac:<30}|{arp_type:<20}|") 1022 | 1023 | # Bottom 1024 | print(Fore.WHITE + Style.BRIGHT + "+" + "-" * 20 + "+" + "-" * 30 + "+" + "-" * 20 + "+") 1025 | 1026 | if packet.haslayer(ARP): 1027 | ip_address = packet[ARP].psrc 1028 | mac_address = packet[ARP].hwsrc 1029 | 1030 | # types of ARP frames 1031 | if packet[ARP].op == 1: 1032 | arp_type = "ARP Request" 1033 | elif packet[ARP].op == 2: 1034 | arp_type = "ARP Response" 1035 | else: 1036 | arp_type = "Unknown" 1037 | 1038 | # dict update 1039 | arp_table[ip_address] = {"mac": mac_address, "type": arp_type} 1040 | # info update 1041 | display_arp_table() 1042 | # save to text file 1043 | save_to_file_passive_arp() 1044 | 1045 | # Dict for VLAN ID 1046 | vlan_table = defaultdict(int) 1047 | 1048 | # Search VLAN ID (802.1Q) 1049 | def search_vlan(packet): 1050 | if packet.haslayer(Dot1Q): 1051 | vlan_id = packet[Dot1Q].vlan 1052 | vlan_table[vlan_id] += 1 1053 | display_vlan_table() 1054 | 1055 | # Record VLAN ID's to file "above_discovered_vlan.txt" 1056 | def save_vlan_to_file_vlan_id(file_name="above_discovered_vlan.txt"): 1057 | with open(file_name, "w") as file: 1058 | # Header 1059 | file.write("Above: Discovered VLAN ID\n") 1060 | file.write(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") 1061 | file.write("-" * 80 + "\n") 1062 | file.write(f"{'VLAN ID':<30}{'Frames Count':<15}{'How to Jump':<40}\n") 1063 | file.write("-" * 80 + "\n") 1064 | # writing data 1065 | for vlan_id, count in vlan_table.items(): 1066 | jump_command = f"sudo vconfig add eth0 {vlan_id}" 1067 | file.write(f"{vlan_id:<30}{count:<15}{jump_command:<40}\n") 1068 | 1069 | file.write("-" * 80 + "\n") 1070 | 1071 | # VLAN ID Table Display 1072 | def display_vlan_table(): 1073 | print("\033c", end="") 1074 | print(Fore.WHITE + Style.BRIGHT + "+" + "-" * 30 + "+" + "-" * 15 + "+" + "-" * 40 + "+") 1075 | print(f"|{'VLAN ID':<30}|{'Frames Count':<15}|{'How to Jump':<40}|") 1076 | print(Fore.WHITE + Style.BRIGHT + "+" + "-" * 30 + "+" + "-" * 15 + "+" + "-" * 40 + "+") 1077 | 1078 | for vlan_id, count in vlan_table.items(): 1079 | jump_command = f"sudo vconfig add eth0 {vlan_id}" 1080 | print(f"|{vlan_id:<30}|{count:<15}|{jump_command:<40}|") 1081 | 1082 | print(Fore.WHITE + Style.BRIGHT + "+" + "-" * 30 + "+" + "-" * 15 + "+" + "-" * 40 + "+") 1083 | save_vlan_to_file_vlan_id() 1084 | 1085 | # Parse VLAN ID from pcaps 1086 | def analyze_pcap_for_vlan(pcap_path): 1087 | packets = rdpcap(pcap_path) 1088 | for packet in packets: 1089 | search_vlan(packet) 1090 | display_vlan_table() 1091 | 1092 | def main(): 1093 | parser = argparse.ArgumentParser() 1094 | parser.add_argument('--interface', type=str, required=False, help='Interface for traffic listening') 1095 | parser.add_argument('--timer', type=int, help='Time in seconds to capture packets, default: not set') 1096 | parser.add_argument('--output', type=str, help='File name where the traffic will be recorded, default: not set') 1097 | parser.add_argument('--input', type=str, help='File name of the traffic dump') 1098 | parser.add_argument('--passive-arp', action='store_true', help='Passive ARP (Host Discovery)') 1099 | parser.add_argument('--search-vlan', action='store_true', help='VLAN Search') 1100 | args = parser.parse_args() 1101 | 1102 | def signal_handler(sig, frame): 1103 | print("\n[!] CTRL+C pressed. Exiting...") 1104 | if args.output and packets: 1105 | try: 1106 | wrpcap(args.output, packets) 1107 | print(Fore.YELLOW + Style.BRIGHT + f"\n[*] Saved {len(packets)} packets to {args.output}") 1108 | except Exception as e: 1109 | print(Fore.RED + Style.BRIGHT + f"Error saving packets to {args.output}: {e}") 1110 | sys.exit(0) 1111 | 1112 | signal.signal(signal.SIGINT, signal_handler) 1113 | 1114 | if args.output and (args.passive_arp or args.search_vlan): 1115 | print(Fore.RED + "[!] The '--output' argument cannot be used with '--passive-arp' or '--search-vlan'") 1116 | return 1117 | if args.passive_arp and args.input: 1118 | print(Fore.RED + "[!] The '--passive-arp' argument cannot be used with '--input'") 1119 | return 1120 | if not any(vars(args).values()): 1121 | print(indent + "[*] Use --help to see usage instructions") 1122 | return 1123 | if args.input: 1124 | if args.search_vlan: 1125 | print(indent + "[+] Analyzing pcap file for VLAN tags...\n") 1126 | analyze_pcap_for_vlan(args.input) 1127 | else: 1128 | print(indent + "[+] Analyzing pcap file...\n") 1129 | analyze_pcap(args.input) 1130 | return 1131 | if os.getuid() != 0: 1132 | print(indent + "[!] Sniffing traffic requires root privileges. Please run as root.") 1133 | return 1134 | if args.passive_arp: 1135 | print(indent + "[+] Starting Host Discovery...") 1136 | print(Fore.CYAN + "[*] IP and MAC addresses will be saved to 'above_passive_arp.txt'") 1137 | sniff(iface=args.interface, timeout=args.timer, prn=passive_arp_monitor, store=0) 1138 | elif args.search_vlan: 1139 | print(indent + "[+] Searching for VLAN tags...") 1140 | sniff(iface=args.interface, timeout=args.timer, prn=search_vlan, store=0) 1141 | display_vlan_table() 1142 | elif args.interface: 1143 | print("-----------------------------------------------------------------------------------------") 1144 | print("[+] Start sniffing...\n") 1145 | print("[*] After the protocol is detected - all necessary information about it will be displayed") 1146 | sniff(iface=args.interface, timeout=args.timer if args.timer is not None else None, prn=packet_detection, store=0) 1147 | 1148 | if packets and args.output: 1149 | try: 1150 | wrpcap(args.output, packets) 1151 | print(Fore.YELLOW + Style.BRIGHT + f"\n[*] Saved {len(packets)} packets to {args.output}") 1152 | except Exception as e: 1153 | print(Fore.RED + Style.BRIGHT + f"Error saving packets to {args.output}: {e}") 1154 | 1155 | if __name__ == "__main__": 1156 | main() -------------------------------------------------------------------------------- /banner/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casterbyte/Above/1f4ff3e80792640256936b17423025e21db183f6/banner/banner.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name="above", 5 | version="2.8", 6 | url="https://github.com/casterbyte/above", 7 | author="Magama Bazarov", 8 | author_email="magamabazarov@mailbox.org", 9 | scripts=['above.py'], 10 | description="Invisible Network Protocol Sniffer", 11 | long_description=open('README.md', encoding="utf8").read(), 12 | long_description_content_type='text/markdown', 13 | license="Apache-2.0", 14 | keywords=['network security', 'network sniffer'], 15 | packages=find_packages(), 16 | install_requires=[ 17 | 'scapy', 18 | 'colorama', 19 | ], 20 | py_modules=['above_oui_dict'], 21 | entry_points={ 22 | "console_scripts": ["above = above:main"], 23 | }, 24 | python_requires='>=3.11', 25 | include_package_data=True, 26 | ) --------------------------------------------------------------------------------