From f2f73e13cb51245fe10ebc5d4ee931d287412ef7 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sat, 5 Oct 2019 17:07:22 -0400 Subject: [PATCH 01/69] added @dadav's tamagotchi name explainer --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b2e30f8..58c5a52 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,11 @@ full and half WPA handshakes. ![handshake](https://i.imgur.com/pdA4vCZ.png) -Specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) doc.) - - Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54) over time to **get better at pwning WiFi things** in the environments you expose it to. -**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi actually learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but **definitely listen to your pwnagotchi when it tells you it's bored!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) +More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) doc.) + +**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi actually learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but **definitely listen to your Pwnagotchi when it tells you it's bored!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) Multiple units within close physical proximity can "talk" to each other, advertising their own presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard. Over time, two or more units trained together will learn to cooperate upon detecting each other's presence by dividing the available channels among them for optimal pwnage. @@ -27,6 +26,8 @@ Multiple units within close physical proximity can "talk" to each other, adverti For hackers to learn reinforcement learning, WiFi networking, and have an excuse to get out for more walks. Also? **It's cute as f---**. +**In case you're curious about the name:** *Pwnagotchi* is a portmanteau of *pwn* (which we shouldn't have to explain if you are interested in this project :kissing_heart:) and *-gotchi*. It is a nostalgic reference made in homage to a very popular children's toy from the 1990s called the [Tamagotchi](https://en.wikipedia.org/wiki/Tamagotchi). The Tamagotchi (たまごっち, derived from *tamago* (たまご) "egg" + *uotchi* (ウオッチ) "watch") is a cultural touchstone for many Millennial hackers as a formative electronic toy from our collective childhoods. Were you lucky enough to possess a Tamagotchi as a kid? Well, with your Pwnagotchi, you too can enjoy the nostalgic delight of being strangely emotionally attached to a handheld automata *yet again!* Except, this time around...you get to #HackThePlanet. >:D + ## Documentation --- :warning: **THE FOLLOWING DOCUMENTATION IS BEING PREPARED FOR THE v1.0 RELEASE OF PWNAGOTCHI. Since this effort is an active (and unstable) work-in-progress, the docs displayed here are in various stages of [in]completion. There will be dead links and placeholders throughout as we are still building things out in preparation for the v1.0 release.** :warning: From 97283ba49a7efc9dfdf4d5f3e5ac253d7101ee68 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sat, 5 Oct 2019 17:21:50 -0400 Subject: [PATCH 02/69] minor copyediting --- docs/configure.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index d2bb0f5..a7c251f 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -1,6 +1,6 @@ # Connecting to your Pwnagotchi -Once you wrote the image file on the SD card, there're a few steps you'll have to follow in order to configure your unit properly, first, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. After a few seconds the board will boot and you will see a new Ethernet interface on your host computer. +Once you wrote the image file on the SD card, there're a few steps you'll have to follow in order to configure your unit properly. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. You'll need to configure it with a static IP address: @@ -17,9 +17,9 @@ You can now connect to your unit using SSH: ssh pi@10.0.0.2 ``` -The default password is `raspberry`, you should change it as soon as you log in for the first time by issuing the `passwd`command and selecting a new and more complex passphrase. +The default password is `raspberry`; you should change it as soon as you log in for the first time by issuing the `passwd` command and selecting a new and more complex passphrase. -Moreover, it is recommended that you copy your SSH public key among the unit's authorized ones, so you can directly log in without entering a password: +If you want to login directly without entering a password (recommended!), copy your SSH public key to the unit's authorized keys: ```bash ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 @@ -27,27 +27,27 @@ ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 ## Configuration -You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/). Create the `/root/custom.yml` file (either via SSH or by direclty editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. +You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/). Create the `/root/custom.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. ## Language Selection -For instance, you can change `main.lang` to one of the supported languages: +Pwnagotchi displays its UI in English by default, but it can speak several other languages! You can change `main.lang` to one of the supported languages: -- **english** (default) -- german -- dutch -- greek -- macedonian -- italian -- french -- russian -- swedish +- **English** *(default)* +- German +- Dutch +- Greek +- Macedonian +- Italian +- French +- Russian +- Swedish ## Display Selection Set the type of display you want to use via `ui.display.type` (if your display does not work after changing this setting, you might need to completely remove power from the Raspberry and make a clean boot). -You can configure the refresh interval of the display via `ui.fps`, we advise to use a slow refresh to not shorten the lifetime of your display. The default value is 0, which will only refresh when changes are made to the screen. +You can configure the refresh interval of the display via `ui.fps`. We recommend using a slow refresh rate to avoid shortening the lifetime of your e-ink display. The default value is `0`, which will *only* refresh when changes are made to the screen. ## Host Connection Share @@ -55,4 +55,4 @@ If you connect to the unit via `usb0` (thus using the data port), you might want ## Troubleshooting -If your network connection keeps flapping on your device connecting to your pwnagotchi, check if `usb0` (or equivalent) device is being controlled by NetworkManager. You can check this via `nmcli dev status`. +If your network connection keeps flapping on your device connecting to your Pwnagotchi, check if `usb0` (or equivalent) device is being controlled by NetworkManager. You can check this via `nmcli dev status`. From b765a642ae0147929cea9836d528da9890ab3b4a Mon Sep 17 00:00:00 2001 From: waxwing Date: Sat, 5 Oct 2019 17:50:26 -0400 Subject: [PATCH 03/69] minor copyediting + details about screens --- docs/install.md | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/docs/install.md b/docs/install.md index b1ee1d7..9f143e8 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,35 +2,47 @@ The project has been developed to run on a Raspberry Pi 0 W configured as an [USB Ethernet gadget](https://learn.adafruit.com/turning-your-raspberry-pi-zero-into-a-usb-gadget/ethernet-gadget) device in order to connect to it via USB. However, given the proper configuration tweaks, any GNU/Linux computer with a WiFi interface that supports monitor mode could be used. -**An important note about the AI:** a network trained with a specific WiFi interface will only work with another interface if it supports -the same exact WiFi channels of the first one. For instance, you can not use a neural network trained on a Raspberry Pi Zero W (that only supports 2.4Ghz channels) with a 5Ghz antenna, but you'll need to train one from scratch for those channels. +**An important note about the AI:** a network trained with a specific WiFi interface will ONLY work with another interface if it supports the *exact same* WiFi channels of the first one. For instance, you CANNOT use a neural network trained on a Raspberry Pi Zero W (that only supports 2.4Ghz channels) with a 5Ghz antenna; you will need to train one from scratch for those channels. ## Required Hardware -- [Raspberry Pi Zero W](https://www.raspberrypi.org/products/raspberry-pi-zero-w/). -- A micro SD card, 8GB recomended, **preferably of good quality and speed**. +- [Raspberry Pi Zero W](https://www.raspberrypi.org/products/raspberry-pi-zero-w/).† +- A micro SD card, 8GB recommended, **preferably of good quality and speed**. - A decent power bank (with 1500 mAh you get ~2 hours with AI on). - One of the supported displays (optional). +† Many users have gotten Pwnagotchi running on other types of Raspberry Pi, but the RPi0W is the "vanilla" hardware config for Pwnagotchi. + ### Display -The display is an optional component as the UI is also rendered via a web interface available via the USB cable. If you connect to `usb0` (by using the data port on the unit) and point your browser to the web ui (see config.yml), your unit can work in "headless mode". +The display is an optional component as the UI is also rendered via a web interface available via the USB cable. If you connect to `usb0` (by using the data port on the unit) and point your browser to the web ui (see `config.yml`), your unit can work in "headless mode". -If instead you want to fully enjoy walking around and literally looking at your unit's face, the supported display models are: +If, instead, you want to fully enjoy walking around and literally looking at your unit's face, the supported display models are: - [Waveshare eInk Display (both V1 and V2)](https://www.waveshare.com/2.13inch-e-paper-hat.htm) + - [Product comparison](https://www.waveshare.com/4.3inch-e-paper.htm) (scroll down to `Selection Guide`) + - [GitHub](https://github.com/waveshare/e-Paper/tree/master/RaspberryPi%26JetsonNano/python) - [Pimoroni Inky pHAT](https://shop.pimoroni.com/products/inky-phat) + - [Product page](https://shop.pimoroni.com/products/inky-phat) + - [GitHub](https://github.com/pimoroni/inky) - [PaPiRus eInk Screen](https://uk.pi-supply.com/products/papirus-zero-epaper-screen-phat-pi-zero) Needless to say, we are always happy to receive pull requests adding support for new models. -One thing to note, not all displays are created equaly, TFT displays for example work similar to an HDMI display, and they are not supported, currently all the displays supported are I2C displays. +**One thing to note:** Not all displays are created equally! TFT displays, for example, work similar to an HDMI display, and they are NOT supported. Currently, all the officially-supported displays are I2C displays. If you are still interested in using unsupported displays, you may be able to find a community-submitted hack in the [Screens](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md#screens) section of the [Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) page. We are not responsible for anything you break by trying to use any display that is not officially supported by the development team! -#### Color and Black & White displays +#### Color vs. Black & White displays -Some of the supported displays support Black & White and Coloured versions, one common question is regarding refresh speed of said displays. +Some of the supported displays support both **Black & White** and **Colored** versions. One common question whether there are meaningful differences between the two. There are: +- Color displays have a much slower refresh rate. In some cases, it can take up to 15 seconds; if slow refresh rates are something that you want to avoid, we recommend you use B&W displays. +- The 3-color 2.13" Waveshare displays have a slightly smaller pixel layout (104x212) compared to their B&W counterparts (122x250). -Color displays have a much slower refresh rate, in some cases it can take up to 15 seconds, if slow refresh rates is something that you want to avoid we advise you to use Black & White displays +#### Recommendations +- Avoid the Waveshare eInk **3-color** display. The refresh time is 15 seconds. +- Avoid the Pimoroni Inky pHAT **v1.** They're discontinued due to a faulty hardware part source used in manufacturing that resulted in high failure rates. +- Many users seem to prefer the Inky pHATs. There are two primary reasons: + - The Inkys feature better documentation and SDK support. + - Many Waveshare resellers do not disclose the version of the Waveshare boards they are selling (v1 vs v2), and the type they are selling can be fairly unclear (i.e., Waveshare 2.13 vs 2.13 B vs. 2.13C, and so on.) ## Flashing an Image From 7f72e9ae3e7c1a5f366202c03e1a9dd0dda4ddb2 Mon Sep 17 00:00:00 2001 From: hisakiyo <34187647+hisakiyo@users.noreply.github.com> Date: Sun, 6 Oct 2019 00:13:06 +0200 Subject: [PATCH 04/69] Update voice.po --- pwnagotchi/locale/fr/LC_MESSAGES/voice.po | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pwnagotchi/locale/fr/LC_MESSAGES/voice.po b/pwnagotchi/locale/fr/LC_MESSAGES/voice.po index 1172f7f..1d061f7 100644 --- a/pwnagotchi/locale/fr/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/fr/LC_MESSAGES/voice.po @@ -25,20 +25,20 @@ msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Bonjour, je suis Pwnagotchi! Démarrage ..." msgid "New day, new hunt, new pwns!" -msgstr "Nouvelle journée, nouvelle chasse, nouveau pwns!" +msgstr "Nouveau jour, nouvelle chasse, nouveaux pwns !" msgid "Hack the Planet!" msgstr "Hack la planète!" msgid "AI ready." -msgstr "IA prête." +msgstr "L'IA est prête." msgid "The neural network is ready." msgstr "Le réseau neuronal est prêt." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." -msgstr "Hey, le channel {channel} est libre! Ton AP va dis merci." +msgstr "Hey, le channel {channel} est libre! Ton point d'accès va te remercier." msgid "I'm bored ..." msgstr "Je m'ennuie ..." @@ -68,17 +68,17 @@ msgid "I pwn therefore I am." msgstr "Je pwn donc je suis." msgid "So many networks!!!" -msgstr "Autant de réseaux!!!" +msgstr "Tellement de réseaux!!!" msgid "I'm having so much fun!" msgstr "Je m'amuse tellement!" msgid "My crime is that of curiosity ..." -msgstr "Mon crime est celui de la curiosité ..." +msgstr "Mon crime, c'est la curiosité ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" -msgstr "Bonjour {name}! Ravis de te rencontrer. {name}" +msgstr "Bonjour {name}! Ravi de te rencontrer. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" @@ -145,7 +145,7 @@ msgstr "" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" -msgstr "Décidé à l'instant que {mac} n'a pas besoin de WiFi!" +msgstr "Je viens de décider que {mac} n'a pas besoin de WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" @@ -153,11 +153,11 @@ msgstr "Désauthentification de {mac}" #, python-brace-format msgid "Kickbanning {mac}!" -msgstr "" +msgstr "Je kick et je bannis {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" -msgstr "Cool, nous avons {num} nouveaux handshake{plural}!" +msgstr "Cool, on a {num} nouveaux handshake{plural}!" msgid "Ops, something went wrong ... Rebooting ..." msgstr "Oups, quelque chose s'est mal passé ... Redémarrage ..." @@ -188,7 +188,7 @@ msgid "" "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "J'ai pwn durant {duration} et kick {deauthed} clients! J'ai aussi rencontré " -"{associated} nouveaux amis and mangé {handshakes} handshakes! #pwnagotchi " +"{associated} nouveaux amis et dévoré {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" From 6bc507412a7888feeb16073847dbd08a49f632e6 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Sun, 6 Oct 2019 00:18:36 +0200 Subject: [PATCH 05/69] pypi upload script --- scripts/pypi_upload.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100755 scripts/pypi_upload.sh diff --git a/scripts/pypi_upload.sh b/scripts/pypi_upload.sh new file mode 100755 index 0000000..4a71125 --- /dev/null +++ b/scripts/pypi_upload.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +rm -rf build dist ergo_nn.egg-info && + python3 setup.py sdist bdist_wheel && + clear && + twine upload dist/* From 03318bdaef253dc54aecea7853daf363ac3edfb3 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 00:14:09 +0200 Subject: [PATCH 06/69] Change code --- scripts/preview.py | 226 ++++++++++++++++++++++----------------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/scripts/preview.py b/scripts/preview.py index 2b45933..1d29119 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -14,64 +14,26 @@ sys.path.insert(0, '../sdcard/rootfs/root/pwnagotchi/scripts/')) from pwnagotchi.ui.display import Display, VideoHandler - +from PIL import Image class CustomDisplay(Display): + def __init__(self, config, state): + self.last_image = None + super(CustomDisplay, self).__init__(config, state) + def _http_serve(self): - if self._video_address is not None: - self._httpd = HTTPServer((self._video_address, self._video_port), - CustomVideoHandler) - logging.info("ui available at http://%s:%d/" % (self._video_address, - self._video_port)) - self._httpd.serve_forever() - else: - logging.info("could not get ip of usb0, video server not starting") + # do nothing + pass def _on_view_rendered(self, img): - CustomVideoHandler.render(img) + self.last_image = img - if self._enabled: - self.canvas = (img if self._rotation == 0 else img.rotate(self._rotation)) - if self._render_cb is not None: - self._render_cb() - - -class CustomVideoHandler(VideoHandler): - - @staticmethod - def render(img): - with CustomVideoHandler._lock: - try: - img.save("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), format='PNG') - except BaseException: - logging.exception("could not write preview") - - def do_GET(self): - if self.path == '/': - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - try: - self.wfile.write( - bytes( - self._index % - ('localhost', 1000), "utf8")) - except BaseException: - pass - - elif self.path.startswith('/ui'): - with self._lock: - self.send_response(200) - self.send_header('Content-type', 'image/png') - self.end_headers() - try: - with open("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), 'rb') as fp: - shutil.copyfileobj(fp, self.wfile) - except BaseException: - logging.exception("could not open preview") - else: - self.send_response(404) + def get_image(self): + """ + Return the saved image + """ + return self.last_image class DummyPeer: @@ -79,21 +41,43 @@ class DummyPeer: def name(): return "beta" +def append_images(images, horizontal=True, xmargin=0, ymargin=0): + w, h = zip(*(i.size for i in images)) + + if horizontal: + t_w = sum(w) + t_h = max(h) + else: + t_w = max(w) + t_h = sum(h) + + result = Image.new('RGB', (t_w, t_h)) + + x_offset = 0 + y_offset = 0 + + for im in images: + result.paste(im, (x_offset,y_offset)) + if horizontal: + x_offset += im.size[0] + xmargin + else: + y_offset += im.size[1] + ymargin + + return result def main(): parser = argparse.ArgumentParser(description="This program emulates\ the pwnagotchi display") - parser.add_argument('--display', help="Which display to use.", + parser.add_argument('--displays', help="Which displays to use.", nargs="+", default="waveshare_2") - parser.add_argument('--port', help="Which port to use", - default=8080) - parser.add_argument('--sleep', type=int, help="Time between emotions", - default=2) parser.add_argument('--lang', help="Language to use", default="en") + parser.add_argument('--output', help="Path to output image (PNG)", default="preview.png") + parser.add_argument('--xmargin', type=int, default=5) + parser.add_argument('--ymargin', type=int, default=5) args = parser.parse_args() - CONFIG = yaml.load(''' + config_template = ''' main: lang: {lang} ui: @@ -107,65 +91,81 @@ def main(): video: enabled: true address: "0.0.0.0" - port: {port} - '''.format(display=args.display, - port=args.port, - lang=args.lang)) + port: 8080 + ''' - DISPLAY = CustomDisplay(config=CONFIG, state={'name': '%s>' % 'preview'}) + list_of_displays = list() + for display_type in args.displays: + config = yaml.safe_load(config_template.format(display=display_type, + lang=args.lang)) + display = CustomDisplay(config=config, state={'name': f"{display_type}>"}) + list_of_displays.append(display) - while True: - DISPLAY.on_starting() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_ai_ready() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_normal() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_new_peer(DummyPeer()) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_lost_peer(DummyPeer()) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_free_channel('6') - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.wait(args.sleep) - DISPLAY.update() - DISPLAY.on_bored() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_sad() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_motivated(1) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_demotivated(-1) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_excited() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_deauth({'mac': 'DE:AD:BE:EF:CA:FE'}) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_miss('test') - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_lonely() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_handshakes(1) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_rebooting() - DISPLAY.update() - time.sleep(args.sleep) + columns = list() + + for display in list_of_displays: + emotions = list() + # Starting + display.on_starting() + display.update() + emotions.append(display.get_image()) + display.on_ai_ready() + display.update() + emotions.append(display.get_image()) + display.on_normal() + display.update() + emotions.append(display.get_image()) + display.on_new_peer(DummyPeer()) + display.update() + emotions.append(display.get_image()) + display.on_lost_peer(DummyPeer()) + display.update() + emotions.append(display.get_image()) + display.on_free_channel('6') + display.update() + emotions.append(display.get_image()) + display.wait(2) + display.update() + emotions.append(display.get_image()) + display.on_bored() + display.update() + emotions.append(display.get_image()) + display.on_sad() + display.update() + emotions.append(display.get_image()) + display.on_motivated(1) + display.update() + emotions.append(display.get_image()) + display.on_demotivated(-1) + display.update() + emotions.append(display.get_image()) + display.on_excited() + display.update() + emotions.append(display.get_image()) + display.on_deauth({'mac': 'DE:AD:BE:EF:CA:FE'}) + display.update() + emotions.append(display.get_image()) + display.on_miss('test') + display.update() + emotions.append(display.get_image()) + display.on_lonely() + display.update() + emotions.append(display.get_image()) + display.on_handshakes(1) + display.update() + emotions.append(display.get_image()) + display.on_rebooting() + display.update() + emotions.append(display.get_image()) + + # append them all together (vertical) + columns.append(append_images(emotions, horizontal=False, xmargin=args.xmargin, ymargin=args.ymargin)) + + + # append columns side by side + final_image = append_images(columns, horizontal=True, xmargin=args.xmargin, ymargin=args.ymargin) + final_image.save(args.output, 'PNG') if __name__ == '__main__': SystemExit(main()) From b4a382e266c21b356189b3a22268e7e7f2f445c4 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 00:15:23 +0200 Subject: [PATCH 07/69] Update dev.md --- docs/dev.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/dev.md b/docs/dev.md index 75b7ea0..534b398 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -26,7 +26,7 @@ usage: ./scripts/create_sibling.sh [OPTIONS] `GLib-ERROR **: 20:50:46.361: getauxval () failed: No such file or directory` -- Affected DEB & Versions: QEMU <= 2.11 +- Affected DEB & Versions: QEMU <= 2.11 - Fix: Upgrade QEMU to >= 3.1 - Bug Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923289 @@ -55,7 +55,6 @@ If you changed the `voice.py`- File, the translations need an update. Do it like Now you can use the `preview.py`-script to preview the changes: ```shell -./scripts/preview.py --lang it --display ws2 --port 8080 & -./scripts/preview.py --lang it --display inky --port 8081 & -# Now open http://localhost:8080 and http://localhost:8081 +./scripts/preview.py --lang it --display ws1 ws2 inky --output preview.png +# Now open preview.png ``` From 0b07bf3621befdc890d467681d250944d83cd958 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Sun, 6 Oct 2019 00:27:21 +0200 Subject: [PATCH 08/69] misc: small fix or general refactoring i did not bother commenting --- scripts/preview.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/scripts/preview.py b/scripts/preview.py index 1d29119..b298865 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -1,21 +1,17 @@ #!/usr/bin/env python3 - import sys import os -import time import argparse -from http.server import HTTPServer -import shutil -import logging import yaml sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), - '../sdcard/rootfs/root/pwnagotchi/scripts/')) + '../pwnagotchi/')) from pwnagotchi.ui.display import Display, VideoHandler from PIL import Image + class CustomDisplay(Display): def __init__(self, config, state): @@ -41,6 +37,7 @@ class DummyPeer: def name(): return "beta" + def append_images(images, horizontal=True, xmargin=0, ymargin=0): w, h = zip(*(i.size for i in images)) @@ -57,14 +54,15 @@ def append_images(images, horizontal=True, xmargin=0, ymargin=0): y_offset = 0 for im in images: - result.paste(im, (x_offset,y_offset)) - if horizontal: - x_offset += im.size[0] + xmargin - else: - y_offset += im.size[1] + ymargin + result.paste(im, (x_offset, y_offset)) + if horizontal: + x_offset += im.size[0] + xmargin + else: + y_offset += im.size[1] + ymargin return result + def main(): parser = argparse.ArgumentParser(description="This program emulates\ the pwnagotchi display") @@ -97,11 +95,10 @@ def main(): list_of_displays = list() for display_type in args.displays: config = yaml.safe_load(config_template.format(display=display_type, - lang=args.lang)) + lang=args.lang)) display = CustomDisplay(config=config, state={'name': f"{display_type}>"}) list_of_displays.append(display) - columns = list() for display in list_of_displays: @@ -162,10 +159,10 @@ def main(): # append them all together (vertical) columns.append(append_images(emotions, horizontal=False, xmargin=args.xmargin, ymargin=args.ymargin)) - # append columns side by side final_image = append_images(columns, horizontal=True, xmargin=args.xmargin, ymargin=args.ymargin) final_image.save(args.output, 'PNG') + if __name__ == '__main__': SystemExit(main()) From 7580b3c30b9d8bb7096499a0e547e56def95e353 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sat, 5 Oct 2019 18:42:49 -0400 Subject: [PATCH 09/69] added FAQ questions TOC structure will fill in the actual content tomorrow --- docs/faq.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index ff19087..fcc6782 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,13 +1,55 @@ # FAQ -## Why eINK? +[**What can Pwnagotchi actually do?**](#what-can-pwnagotchi-actually-do) +* Does Pwnagotchi support both 2.4 GHz and 5.0 GHz? +* Just how politely *does* Pwnagotchi deauth? +* What kinds of handshakes does Pwnagotchi eat? +* Hey, I want to learn more about how Pwnagotchi actually works. -Because! +[**Building Your Pwnagotchi**](#building-your-pwnagotchi) +* What hardware do I need to create my very own Pwnagotchi? +* Is there any way to see my Pwnagotchi's face even if I don't have a display? +* I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case? +* Why does everybody use e-ink screens for their Pwnagotchis? +* How do I connect to my Pwnagotchi? -## Why the AI takes 30 minutes to load? +[**Customizing Your Pwnagotchi**](#customizing-your-pwnagotchi) +* How do I change my Pwnagotchi's name? +* I want to change the faces. What do I hack? +* I want my Pwnagotchi to speak a different language. Can it? +* I have a great idea for something cool I wish Pwnagotchi could do! -Because Python sucks and TF is huge. +[**Getting to Know Your Pwnagotchi**](#getting-to-know-your-pwnagotchi) +* What is MANU mode? What is AUTO mode? +* Why does the AI take 30 minutes to load? +* What is Pwnagotchi doing while it's waiting for the AI to load? +* How do I whitelist my home network so Pwnagotchi stops pwning me? -## Why ...? +[**Caring for Your Pwnagotchi**](#caring-for-your-pwnagotchi) +* What do all my Pwnagotchi's faces mean? +* Oh no, my Pwnagotchi is sad and bored! How do I entertain it?! +* How do I turn off my Pwnagotchi? -Because! +[**Known Quirks**](#known-quirks) +* My Pwnagotchi's log timestamps seem...unreliable. Huh? +* Help! My Pwnagotchi's SD card got corrupted. What gives? + +--- + +## What can Pwnagotchi actually do? +lorem ipsum dolor sit amet + +## Building Your Pwnagotchi +lorem ipsum dolor sit amet + +## Customizing Your Pwnagotchi +lorem ipsum dolor sit amet + +## Getting to Know Your Pwnagotchi +lorem ipsum dolor sit amet + +## Caring for Your Pwnagotchi +lorem ipsum dolor sit amet + +## Known Quirks +lorem ipsum dolor sit amet From 705040e07520f5843dfda3194fe73e433bf679d6 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Sun, 6 Oct 2019 00:44:24 +0200 Subject: [PATCH 10/69] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/mesh/advertise.py | 2 +- pwnagotchi/mesh/wifi.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pwnagotchi/mesh/advertise.py b/pwnagotchi/mesh/advertise.py index ff797a3..84286d2 100644 --- a/pwnagotchi/mesh/advertise.py +++ b/pwnagotchi/mesh/advertise.py @@ -152,7 +152,7 @@ class Advertiser(object): if self._is_broadcasted_advertisement(dot11): try: dot11elt = p.getlayer(Dot11Elt) - if dot11elt.ID == wifi.Dot11ElemID_Identity: + if dot11elt.ID == wifi.Dot11ElemID_Whisper: self._parse_identity(p[RadioTap], dot11, dot11elt) else: diff --git a/pwnagotchi/mesh/wifi.py b/pwnagotchi/mesh/wifi.py index 6a9a00a..6fe231e 100644 --- a/pwnagotchi/mesh/wifi.py +++ b/pwnagotchi/mesh/wifi.py @@ -1,6 +1,6 @@ SignatureAddress = 'de:ad:be:ef:de:ad' BroadcastAddress = 'ff:ff:ff:ff:ff:ff' -Dot11ElemID_Identity = 222 +Dot11ElemID_Whisper = 222 NumChannels = 140 def freq_to_channel(freq): @@ -30,7 +30,7 @@ def encapsulate(payload, addr_from, addr_to=BroadcastAddress): while data_left > 0: sz = min(chunk_size, data_left) chunk = payload[data_off: data_off + sz] - frame /= Dot11Elt(ID=Dot11ElemID_Identity, info=chunk, len=sz) + frame /= Dot11Elt(ID=Dot11ElemID_Whisper, info=chunk, len=sz) data_off += sz data_left -= sz From e369d596d689aff499944c6689feb19091ced163 Mon Sep 17 00:00:00 2001 From: SecurityWaffle Date: Sat, 5 Oct 2019 21:22:03 -0500 Subject: [PATCH 11/69] Fixes "No module named 'pwnagotchi'" error /usr/local/bin/pwnagotchi Traceback (most recent call last): File "/usr/local/bin/pwnagotchi", line 8, in import pwnagotchi ModuleNotFoundError: No module named 'pwnagotchi' --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a1e6e84..41d7475 100644 --- a/setup.py +++ b/setup.py @@ -19,9 +19,10 @@ setup(name='pwnagotchi', install_requires=required, scripts=['bin/pwnagotchi'], package_data={'pwnagotchi': ('pwnagotchi/defaults.yml',)}, + packages=find_packages(), classifiers=[ 'Programming Language :: Python :: 3', 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Environment :: Console', - ]) \ No newline at end of file + ]) From 3bb42549f67762868be2dbd14cf340bdd02e9cae Mon Sep 17 00:00:00 2001 From: gh0stshell Date: Sat, 5 Oct 2019 22:25:10 -0700 Subject: [PATCH 12/69] BMAPTOOL Check #2 update switched second check with faster builtin tool, tested fix --- scripts/create_sibling.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/create_sibling.sh b/scripts/create_sibling.sh index 02537cd..1e07a12 100755 --- a/scripts/create_sibling.sh +++ b/scripts/create_sibling.sh @@ -94,7 +94,7 @@ function provide_raspbian() { function setup_raspbian(){ # Detect the ability to create sparse files if [ "${OPT_SPARSE}" -eq 0 ]; then - if [ which bmaptool -eq 0 ]; then + if ! type "bmaptool" > /dev/null; then echo "[!] bmaptool not available, not creating a sparse image" else From 2682a5487a52e6f4625d5eb82893115ab00d8c91 Mon Sep 17 00:00:00 2001 From: gh0stshell Date: Sat, 5 Oct 2019 22:26:03 -0700 Subject: [PATCH 13/69] Removed extra return --- scripts/create_sibling.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/create_sibling.sh b/scripts/create_sibling.sh index 1e07a12..650d40c 100755 --- a/scripts/create_sibling.sh +++ b/scripts/create_sibling.sh @@ -96,7 +96,6 @@ function setup_raspbian(){ if [ "${OPT_SPARSE}" -eq 0 ]; then if ! type "bmaptool" > /dev/null; then echo "[!] bmaptool not available, not creating a sparse image" - else echo "[+] Defaulting to sparse image generation as bmaptool is available" OPT_SPARSE=1 From eb3836ba1e23819e8c1d1b5206cd9742b1c8780f Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 08:55:51 +0200 Subject: [PATCH 14/69] Fix path to pwnagotchi --- .gitignore | 1 + scripts/preview.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fc54ebf..0cac4ae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.img.bmap *.pcap *.po~ +preview.png __pycache__ _backups _emulation diff --git a/scripts/preview.py b/scripts/preview.py index b298865..4f69a3d 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -6,7 +6,7 @@ import yaml sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), - '../pwnagotchi/')) + '../')) from pwnagotchi.ui.display import Display, VideoHandler from PIL import Image From c7b31ae45698477d8cf313f699a6d88bf1c9745f Mon Sep 17 00:00:00 2001 From: Panos Vasilopoulos Date: Sun, 6 Oct 2019 07:59:33 +0000 Subject: [PATCH 15/69] fixed spacing error in greek translation --- pwnagotchi/locale/el/LC_MESSAGES/voice.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pwnagotchi/locale/el/LC_MESSAGES/voice.po b/pwnagotchi/locale/el/LC_MESSAGES/voice.po index f113ce1..b1cfa69 100644 --- a/pwnagotchi/locale/el/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/el/LC_MESSAGES/voice.po @@ -33,11 +33,11 @@ msgid "AI ready." msgstr "ΤΝ έτοιμη." msgid "The neural network is ready." -msgstr "Το νευρωνικό δίκτυοείναι έτοιμο." +msgstr "Το νευρωνικό δίκτυο είναι έτοιμο." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." -msgstr "Ε, το κανάλι {channel} είναιελεύθερο! Το AP σου θαείναι ευγνώμων." +msgstr "Ε, το κανάλι {channel} είναιελεύθερο! Το AP σου θα είναι ευγνώμων." msgid "I'm bored ..." msgstr "Βαριέμαι ..." From 2e6967bd1f92be95698bb3dbdd0da3a4e0855817 Mon Sep 17 00:00:00 2001 From: swedishmike Date: Sun, 6 Oct 2019 09:48:18 +0100 Subject: [PATCH 16/69] Added hdmion/hdmioff scripts --- builder/pwnagotchi.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index fab9c2e..2fe3ff9 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -291,6 +291,22 @@ #!/usr/bin/env bash ifconfig mon0 down && iw dev mon0 del + - name: create hdmion script + copy: + dest: /usr/bin/hdmion + mode: 0755 + content: | + #!/usr/bin/env bash + sudo /opt/vc/bin/tvservice -p + + - name: create hdmioff script + copy: + dest: /usr/bin/hdmioff + mode: 0755 + content: | + #!/usr/bin/env bash + sudo /opt/vc/bin/tvservice -o + - name: configure rc.local blockinfile: path: /etc/rc.local From acb09effce8a3358b2fee604d986c9d46dfe6369 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Sun, 6 Oct 2019 12:49:25 +0200 Subject: [PATCH 17/69] fix: pinned requirements versions (fixes #168) --- requirements.txt | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8fa5e8a..57579fd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,15 @@ -Crypto -requests -pyyaml -scapy -gym -stable-baselines -tensorflow -tweepy -file_read_backwards -numpy -inky -smbus -pillow +crypto==1.4.1 +requests==2.21.0 +PyYAML==3.13 +scapy==2.4.3 +gym==0.14.0 +stable-baselines==2.7.0 +tensorflow==1.13.1 +tensorflow-estimator==1.14.0 +tweepy==3.6.0 +file-read-backwards==2.0.0 +numpy==1.17.2 +inky==0.0.5 +smbus2==0.3.0 +Pillow==5.4.1 +spidev==3.4 \ No newline at end of file From a7b43b6d0dd38cf674eaa4614058953a7c883f4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Oct 2019 10:53:50 +0000 Subject: [PATCH 18/69] Bump pyyaml from 3.13 to 5.1 Bumps [pyyaml](https://github.com/yaml/pyyaml) from 3.13 to 5.1. - [Release notes](https://github.com/yaml/pyyaml/releases) - [Changelog](https://github.com/yaml/pyyaml/blob/master/CHANGES) - [Commits](https://github.com/yaml/pyyaml/compare/3.13...5.1) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 57579fd..f364dbc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ crypto==1.4.1 requests==2.21.0 -PyYAML==3.13 +PyYAML==5.1 scapy==2.4.3 gym==0.14.0 stable-baselines==2.7.0 From ac1f1ce8f034f67a8fff3c17392aa9250a60d4ba Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 06:56:15 -0400 Subject: [PATCH 19/69] reordered docs list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58c5a52..f492955 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,12 @@ For hackers to learn reinforcement learning, WiFi networking, and have an excuse --- - [About the Project](https://github.com/evilsocket/pwnagotchi/blob/master/docs/about.md) -- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) - [How to Install](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md) - [Configuration](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md) - [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md) - [Plugins](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) - [Development](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) +- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) - [Community Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) ## Links From 5737460ebdc7725e488b37280e470945807e83fc Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Sun, 6 Oct 2019 13:19:38 +0200 Subject: [PATCH 20/69] docs: WPA/WPA2 handshakes 101 (fixes #179) --- docs/about.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/about.md b/docs/about.md index eacbd73..f08fa43 100644 --- a/docs/about.md +++ b/docs/about.md @@ -17,6 +17,25 @@ Multiple units within close physical proximity can "talk" to each other, adverti Of course, it is possible to run your Pwnagotchi with the AI disabled (configurable in `config.yml`). Why might you want to do this? Perhaps you simply want to use your own fixed parameters (instead of letting the AI decide for you), or maybe you want to save battery and CPU cycles, or maybe it's just you have strong concerns about aiding and abetting baby Skynet. Whatever your particular reasons may be: an AI-disabled Pwnagotchi is still a simple and very effective automated deauther, WPA handshake sniffer, and portable [bettercap](https://www.bettercap.org/) + [webui](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#bettercaps-web-ui) dedicated hardware. +## WPA/WPA2 Handshakes 101 + +Before a device that's connecting to a wireless access point (say, your phone connecting to your home WiFi) is able to securely transmit and receive data, a process called *4-Way Handshake* needs to happen in order for WPA encryption keys to be generated. +This process consists in the exchange of four packets (therefore the "4" in the name) between the station and the AP that are used to derive session keys from the main access point WiFi password, once the packets are successfully +exchanged and the keys generated, the client station is authenticated and can start sending data packets that are secured by encryption. + +

+ +
+image taken from wifi-professionals.com +

+ +The catch here is that these four packets can be "sniffed" by an attacker and, through the use of dictionary and/or bruteforce attacks, the original WiFi key can be recovered from them. Technically speaking, the recovery of +the WiFi key doesn't necessarily need all four packets: an half-handshake (containing ony two of the four packets) can be cracked too, and in some (most) cases even just [a single packet is enough](https://hashcat.net/forum/thread-7717-post-41447.html), even without clients. + +In order to get these packets, Pwnagotchi will deauthenticate client stations it detects (thus forcing them to reauthenticate to their access point, resending the handshake packets) and send association frames to the access points +to try to force them to [leak the PMKID](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/). + +All the handshakes captured this way are saved into `.pcap` files (organized as one file per access point containing all the captured handshakes for that access point) that can later be [cracked with proper hardware and software](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2). ## License From 0d292cdd10cf1f84061b11b82d1629c58619697d Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 07:25:50 -0400 Subject: [PATCH 21/69] added hyperlinks and headers to all FAQ questions --- docs/faq.md | 202 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 174 insertions(+), 28 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index fcc6782..47305b4 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,55 +1,201 @@ # FAQ + [**What can Pwnagotchi actually do?**](#what-can-pwnagotchi-actually-do) -* Does Pwnagotchi support both 2.4 GHz and 5.0 GHz? -* Just how politely *does* Pwnagotchi deauth? -* What kinds of handshakes does Pwnagotchi eat? -* Hey, I want to learn more about how Pwnagotchi actually works. + +* [Why does Pwnagotchi eat handshakes?](#why-does-pwnagotchi-eat-handshakes) +* [What kinds of handshakes does Pwnagotchi eat?](#what-kinds-of-handshakes-does-pwnagotchi-eat) +* [Does Pwnagotchi support both 2.4 GHz and 5.0 GHz?](#does-pwnagotchi-support-both-24-ghz-and-50-ghz) +* [Just how politely *does* Pwnagotchi deauth?](#just-how-politely-does-pwnagotchi-deauth) +* [Hey, I want to learn more about how Pwnagotchi actually works.](#hey-i-want-to-learn-more-about-how-pwnagotchi-actually-works) +* [How is Pwnagotchi using bettercap?](#how-is-pwnagotchi-using-bettercap) +* [What happens if I run a Pwnagotchi without the AI enabled?](#what-happens-if-i-run-a-pwnagotchi-without-the-ai-enabled) +* [How easy is it to hack Pwnagotchi to add additional functionality?](#how-easy-is-it-to-hack-pwnagotchi-to-add-additional-functionality) [**Building Your Pwnagotchi**](#building-your-pwnagotchi) -* What hardware do I need to create my very own Pwnagotchi? -* Is there any way to see my Pwnagotchi's face even if I don't have a display? -* I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case? -* Why does everybody use e-ink screens for their Pwnagotchis? -* How do I connect to my Pwnagotchi? + +* [What hardware do I need to create my very own Pwnagotchi?](#what-hardware-do-i-need-to-create-my-very-own-pwnagotchi) +* [Is there any way to see my Pwnagotchi's face even if I don't have a display?](#is-there-any-way-to-see-my-pwnagotchis-face-even-if-i-dont-have-a-display) +* [How do I attach the screen to the Raspberry Pi?](#how-do-i-attach-the-screen-to-the-raspberry-pi) +* [I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case?](#i-love-my-new-pwnagotchi-but-it-kinda-looks-like-a-bomb-where-can-i-find-a-decent-case) +* [Why does everybody use e-ink screens for their Pwnagotchis?](#why-does-everybody-use-e-ink-screens-for-their-pwnagotchis) +* [How do I connect to my Pwnagotchi?](#how-do-i-connect-to-my-pwnagotchi) [**Customizing Your Pwnagotchi**](#customizing-your-pwnagotchi) -* How do I change my Pwnagotchi's name? -* I want to change the faces. What do I hack? -* I want my Pwnagotchi to speak a different language. Can it? -* I have a great idea for something cool I wish Pwnagotchi could do! + +* [How do I change my Pwnagotchi's name?](#how-do-i-change-my-pwnagotchis-name) +* [I want to change the faces. What do I hack?](#i-want-to-change-the-faces-what-do-i-hack) +* [I want my Pwnagotchi to speak a different language. Can it?](#i-want-my-pwnagotchi-to-speak-a-different-language-can-it) +* [I have a great idea for something cool I wish Pwnagotchi could do!](#i-have-a-great-idea-for-something-cool-i-wish-pwnagotchi-could-do) +* [Are there any unofficial community "hacks" for further customizing my Pwnagotchi?](#are-there-any-unofficial-community-"hacks"-for-further-customizing-my-pwnagotchi) [**Getting to Know Your Pwnagotchi**](#getting-to-know-your-pwnagotchi) -* What is MANU mode? What is AUTO mode? -* Why does the AI take 30 minutes to load? -* What is Pwnagotchi doing while it's waiting for the AI to load? -* How do I whitelist my home network so Pwnagotchi stops pwning me? + +* [What does everything on the screen mean?](#what-does-everything-on-the-screen-mean) +* [How do I whitelist my home network so Pwnagotchi stops pwning me?](#how-do-i-whitelist-my-home-network-so-pwnagotchi-stops-pwning-me) +* [What is MANU mode? What is AUTO mode?](#what-is-manu-mode-what-is-auto-mode) +* [Why does the AI take 30 minutes to load?](#why-does-the-ai-take-30-minutes-to-load) +* [What is Pwnagotchi doing while it's waiting for the AI to load?](#what-is-pwnagotchi-doing-while-its-waiting-for-the-ai-to-load) +* [How do I know when the AI is running?](#how-do-i-know-when-the-ai-is-running) +* [Where does Pwnagotchi store all the handshakes it's eaten?](#where-does-pwnagotchi-store-all-the-handshakes-its-eaten) [**Caring for Your Pwnagotchi**](#caring-for-your-pwnagotchi) -* What do all my Pwnagotchi's faces mean? -* Oh no, my Pwnagotchi is sad and bored! How do I entertain it?! -* How do I turn off my Pwnagotchi? + +* [What do all my Pwnagotchi's faces mean?](#what-do-all-my-pwnagotchis-faces-mean) +* [How do I feed my Pwnagotchi?](#how-do-i-feed-my-pwnagotchi) +* [Oh no, my Pwnagotchi is sad and bored! How do I entertain it?!](#oh-no,-my-pwnagotchi-is-sad-and-bored-how-do-i-entertain-it) +* [How do I update my Pwnagotchi?](#how-do-i-update-my-pwnagotchi) +* [I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain?](#im-extremely-emotionally-attached-to-my-pwnagotchi-how-can-i-back-up-its-brain) +* [How do I turn off my Pwnagotchi?](#how-do-i-turn-off-my-pwnagotchi) +* [Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating?](#uh-so-what-do-i-do-with-all-these-handshakes-my-pwnagotchi-has-been-eating) [**Known Quirks**](#known-quirks) -* My Pwnagotchi's log timestamps seem...unreliable. Huh? -* Help! My Pwnagotchi's SD card got corrupted. What gives? + +* [My Pwnagotchi's log timestamps seem...unreliable. Huh?](#my-pwnagotchis-log-timestamps-seemunreliable-huh) +* [Help! My Pwnagotchi's SD card got corrupted. What gives?](#help-my-pwnagotchis-sd-card-got-corrupted-what-gives) --- -## What can Pwnagotchi actually do? +## **What can Pwnagotchi actually do?** +### Why does Pwnagotchi eat handshakes? lorem ipsum dolor sit amet -## Building Your Pwnagotchi +--- +### What kinds of handshakes does Pwnagotchi eat? lorem ipsum dolor sit amet -## Customizing Your Pwnagotchi +--- +### Does Pwnagotchi support both 2.4 GHz and 5.0 GHz? lorem ipsum dolor sit amet -## Getting to Know Your Pwnagotchi +--- +### Just how politely *does* Pwnagotchi deauth? lorem ipsum dolor sit amet -## Caring for Your Pwnagotchi +--- +### Hey, I want to learn more about how Pwnagotchi actually works. lorem ipsum dolor sit amet -## Known Quirks +--- +### How is Pwnagotchi using bettercap? +lorem ipsum dolor sit amet + +--- +### What happens if I run a Pwnagotchi without the AI enabled? +lorem ipsum dolor sit amet + +--- +### How easy is it to hack Pwnagotchi to add additional functionality? +lorem ipsum dolor sit amet + +--- + +## **Building Your Pwnagotchi** +### What hardware do I need to create my very own Pwnagotchi? +lorem ipsum dolor sit amet + +--- +### Is there any way to see my Pwnagotchi's face even if I don't have a display? +lorem ipsum dolor sit amet + +--- +### How do I attach the screen to the Raspberry Pi? +lorem ipsum dolor sit amet + +--- +### I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case? +lorem ipsum dolor sit amet + +--- +### Why does everybody use e-ink screens for their Pwnagotchis? +lorem ipsum dolor sit amet + +--- +### How do I connect to my Pwnagotchi? +lorem ipsum dolor sit amet + +--------------------------------------------------------------------------------------------------------------- +## **Customizing Your Pwnagotchi** +### How do I change my Pwnagotchi's name? +lorem ipsum dolor sit amet + +--- +### I want to change the faces. What do I hack? +lorem ipsum dolor sit amet + +--- +### I want my Pwnagotchi to speak a different language. Can it? +lorem ipsum dolor sit amet + +--- +### I have a great idea for something cool I wish Pwnagotchi could do! +lorem ipsum dolor sit amet + +--- +### Are there any unofficial community "hacks" for further customizing my Pwnagotchi? +lorem ipsum dolor sit amet + +--------------------------------------------------------------------------------------------------------------- +## **Getting to Know Your Pwnagotchi** +### What does everything on the screen mean? +lorem ipsum dolor sit amet + +--- +### How do I whitelist my home network so Pwnagotchi stops pwning me? +lorem ipsum dolor sit amet + +--- +### What is MANU mode? What is AUTO mode? +lorem ipsum dolor sit amet + +--- +### Why does the AI take 30 minutes to load? +lorem ipsum dolor sit amet + +--- +### What is Pwnagotchi doing while it's waiting for the AI to load? +lorem ipsum dolor sit amet + +--- +### How do I know when the AI is running? +lorem ipsum dolor sit amet + +--- +### Where does Pwnagotchi store all the handshakes it's eaten? + +--------------------------------------------------------------------------------------------------------------- +## **Caring for Your Pwnagotchi** +### What do all my Pwnagotchi's faces mean? +lorem ipsum dolor sit amet + +--- +### How do I feed my Pwnagotchi? +lorem ipsum dolor sit amet + +--- +### Oh no, my Pwnagotchi is sad and bored! How do I entertain it?! +lorem ipsum dolor sit amet + +--- +### How do I update my Pwnagotchi? +lorem ipsum dolor sit amet + +--- +### I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain? +lorem ipsum dolor sit amet + +--- +### How do I turn off my Pwnagotchi? +lorem ipsum dolor sit amet + +--- +### Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating? + +--------------------------------------------------------------------------------------------------------------- +## **Known Quirks** +### My Pwnagotchi's log timestamps seem...unreliable. Huh? +lorem ipsum dolor sit amet + +--- +### Help! My Pwnagotchi's SD card got corrupted. What gives? lorem ipsum dolor sit amet From 7a4254a7a411ffb6c53e5dac0257dbb876317c56 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 07:36:47 -0400 Subject: [PATCH 22/69] fixed broken anchor lnks in TOC & missing lipsum txt --- docs/faq.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 47305b4..348e039 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -27,7 +27,7 @@ * [I want to change the faces. What do I hack?](#i-want-to-change-the-faces-what-do-i-hack) * [I want my Pwnagotchi to speak a different language. Can it?](#i-want-my-pwnagotchi-to-speak-a-different-language-can-it) * [I have a great idea for something cool I wish Pwnagotchi could do!](#i-have-a-great-idea-for-something-cool-i-wish-pwnagotchi-could-do) -* [Are there any unofficial community "hacks" for further customizing my Pwnagotchi?](#are-there-any-unofficial-community-"hacks"-for-further-customizing-my-pwnagotchi) +* [Are there any unofficial community "hacks" for further customizing my Pwnagotchi?](#are-there-any-unofficial-community-hacks-for-further-customizing-my-pwnagotchi) [**Getting to Know Your Pwnagotchi**](#getting-to-know-your-pwnagotchi) @@ -38,12 +38,13 @@ * [What is Pwnagotchi doing while it's waiting for the AI to load?](#what-is-pwnagotchi-doing-while-its-waiting-for-the-ai-to-load) * [How do I know when the AI is running?](#how-do-i-know-when-the-ai-is-running) * [Where does Pwnagotchi store all the handshakes it's eaten?](#where-does-pwnagotchi-store-all-the-handshakes-its-eaten) +* [What happens when my Pwnagotchi meets another Pwnagotchi?](#what-happens-when-my-pwnagotchi-meets-another-pwnagotchi) [**Caring for Your Pwnagotchi**](#caring-for-your-pwnagotchi) * [What do all my Pwnagotchi's faces mean?](#what-do-all-my-pwnagotchis-faces-mean) * [How do I feed my Pwnagotchi?](#how-do-i-feed-my-pwnagotchi) -* [Oh no, my Pwnagotchi is sad and bored! How do I entertain it?!](#oh-no,-my-pwnagotchi-is-sad-and-bored-how-do-i-entertain-it) +* [Oh no, my Pwnagotchi is sad and bored! How do I entertain it?!](#oh-no-my-pwnagotchi-is-sad-and-bored-how-do-i-entertain-it) * [How do I update my Pwnagotchi?](#how-do-i-update-my-pwnagotchi) * [I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain?](#im-extremely-emotionally-attached-to-my-pwnagotchi-how-can-i-back-up-its-brain) * [How do I turn off my Pwnagotchi?](#how-do-i-turn-off-my-pwnagotchi) @@ -162,6 +163,11 @@ lorem ipsum dolor sit amet --- ### Where does Pwnagotchi store all the handshakes it's eaten? +lorem ipsum dolor sit amet + +--- +### What happens when my Pwnagotchi meets another Pwnagotchi? +lorem ipsum dolor sit amet --------------------------------------------------------------------------------------------------------------- ## **Caring for Your Pwnagotchi** @@ -190,6 +196,7 @@ lorem ipsum dolor sit amet --- ### Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating? +lorem ipsum dolor sit amet --------------------------------------------------------------------------------------------------------------- ## **Known Quirks** From 7bde5a021057d9386d47b6e9fe2f4c11248aecf8 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 08:47:20 -0400 Subject: [PATCH 23/69] rephrased for clarity --- docs/about.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/about.md b/docs/about.md index eacbd73..2883206 100644 --- a/docs/about.md +++ b/docs/about.md @@ -17,6 +17,27 @@ Multiple units within close physical proximity can "talk" to each other, adverti Of course, it is possible to run your Pwnagotchi with the AI disabled (configurable in `config.yml`). Why might you want to do this? Perhaps you simply want to use your own fixed parameters (instead of letting the AI decide for you), or maybe you want to save battery and CPU cycles, or maybe it's just you have strong concerns about aiding and abetting baby Skynet. Whatever your particular reasons may be: an AI-disabled Pwnagotchi is still a simple and very effective automated deauther, WPA handshake sniffer, and portable [bettercap](https://www.bettercap.org/) + [webui](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#bettercaps-web-ui) dedicated hardware. +## WiFi Handshakes 101 + +In order to understand why it's valuable to have an AI that wants to eat handshakes, it's helpful to understand a little bit about how handshakes are used in the WPA/WPA2 wireless protocol. + +Before a client device that's connecting to a wireless access point—say, for instance, your phone connecting to your home WiFi network—is able to securely transmit to and receive data from that access point, a process called the **4-Way Handshake** needs to happen in order for the WPA encryption keys to be generated. This process consists of the exchange of four packets (hence the "4" in "4-Way") between the client device and the AP; these are used to derive session keys from the access point's WiFi password. Once the packets are successfully exchanged and the keys have been generated, the client device is authenticated and can start sending and receiving data packets to and from the wireless AP that are secured by encryption. + +

+ +
+image taken from wifi-professionals.com +

+ +So...what's the catch? Well, these four packets can easily be "sniffed" by an attacker monitoring nearby (say, with a Pwnagotchi :innocent:). And once recorded, that attacker can use [dictionary and/or bruteforce attacks](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2) to crack the handshakes and recover the original WiFi key. In fact, **successful recovery of the WiFi key doesn't necessarily even need all four packets!** A half-handshake (containing only two of the four packets) can be cracked, too—and in some *(most)* cases, just [a single packet is enough](https://hashcat.net/forum/thread-7717-post-41447.html), *even without clients.* + +In order to ~~eat~~ collect as many of these crackable handshake packets as possible, Pwnagotchi uses two strategies: + +- **Deauthenticating the client stations it detects.** A deauthenticated device must reauthenticate to its access point by resending the 4-Way Handshake, thereby giving Pwnagotchi another chance to sniff the handshake packets and collect more crackable material. +- **Send association frames directly to the access points themselves** +to try to force them to [leak the PMKID](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/). + +All the handshakes captured this way are saved into `.pcap` files on Pwnagotchi's filesystem. Each PCAP file that Pwnagotchi generates is organized according to access point; one PCAP will contain all the handshakes that Pwnagotchi has ever captured for that particular AP. These handshakes can later be [cracked with proper hardware and software](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2). ## License From 76f75c07515e1294b62f590dd4ef4e7e8fb88cce Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 09:28:19 -0400 Subject: [PATCH 24/69] added bluetooth face hack by Systemic (in Slack) --- docs/hacks.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/docs/hacks.md b/docs/hacks.md index 184cff0..5e47d51 100644 --- a/docs/hacks.md +++ b/docs/hacks.md @@ -36,3 +36,67 @@ Some of this guide will work with other framebuffer-based displays. - Reboot. And you should be good! + +--- +### Pwnagotchi face via Bluetooth +Last tested on | Pwnagotchi version | Working? | Reference +---------------|--------------------|----------|-----------| +2019 October 6 | Unknown | :white_check_mark: | on Android +2019 October 6 | Unknown | :white_check_mark: | on iPad iOS 9.3.5 + +A way to view your Pwnagotchi's ~~face~~ UI wirelessly via Bluetooth on a separate device. Refresh rate is the same as the e-ink display (every few seconds). This is NOT Bluetooth tethering; this is only Bluetooth as a server on pi side; you connect the Bluetooth and get a DHCP IP address and that's it. This hack cannot leverage the data connection. + +Contributed by Systemic in the Slack. + +##### 1. First Step +- Comment out the Bluetooth disable line from `/boot/config.txt` : `#dtoverlay=pi3-disable-bt` +- Change `/root/pwnagotchi/config.yml` to have `0.0.0.0` instead of `10.0.0.2` to listen as well on Bluetooth. +- Then launch the following commands: + +##### 2. Install required packages. + +```sudo apt-get install bluez bluez-tools bridge-utils dnsmasq``` + +##### 3. Configure Bluetooth and start it. +```sudo modprobe bnep +sudo brctl addbr pan0 +sudo brctl setfd pan0 0 +sudo brctl stp pan0 off +sudo ifconfig pan0 172.26.0.1 netmask 255.255.255.0 +sudo ip link set pan0 up +``` + +```cat <<- EOF > /tmp/dnsmasq_bt.conf``` + +```bind-interfaces +port=0 +interface=pan0 +listen-address=172.26.0.1 +dhcp-range=172.26.0.2,172.26.0.100,255.255.255.0,5m +dhcp-leasefile=/tmp/dnsmasq_bt.leases +dhcp-authoritative +log-dhcp +``` + +```EOF``` + +```sudo dnsmasq -C /tmp/dnsmasq_bt.conf +sudo bt-agent -c NoInputNoOutput& +sudo bt-adapter -a hci0 --set Discoverable 1 +sudo bt-adapter -a hci0 --set DiscoverableTimeout 0 +sudo bt-adapter -a hci0 --set Pairable 1 +sudo bt-adapter -a hci0 --set PairableTimeout 0 +sudo bt-network -a hci0 -s nap pan0 & +``` + +##### 4. Finally: on your phone, you have to disable all existing interfaces: + +- Shutdown WiFi. +- Shutdown mobile data. +- Connect to the newly available Bluetooth device (which has the name of your Pwnagotchi). + - Once connected, you can test: `http://172.26.0.1:8080` +- You can also install bettercap's UI (`sudo buttercap` then `ui.update`) + - You'll need to change the http caplets to change `127.0.0.1` to `0.0.0.0`. +- You can connect to the shell with a terminal emulator ... + +Happy tweaking. From 662193673544bb2270fdb38049870d745bb41cb9 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 09:38:50 -0400 Subject: [PATCH 25/69] typo --- docs/hacks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hacks.md b/docs/hacks.md index 5e47d51..6f58c3f 100644 --- a/docs/hacks.md +++ b/docs/hacks.md @@ -95,7 +95,7 @@ sudo bt-network -a hci0 -s nap pan0 & - Shutdown mobile data. - Connect to the newly available Bluetooth device (which has the name of your Pwnagotchi). - Once connected, you can test: `http://172.26.0.1:8080` -- You can also install bettercap's UI (`sudo buttercap` then `ui.update`) +- You can also install bettercap's UI (`sudo bettercap` then `ui.update`) - You'll need to change the http caplets to change `127.0.0.1` to `0.0.0.0`. - You can connect to the shell with a terminal emulator ... From 042e5adcbeb97f6560a478782ef0cade745811f3 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 09:40:08 -0400 Subject: [PATCH 26/69] minor edit --- docs/about.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about.md b/docs/about.md index 2883206..4777c21 100644 --- a/docs/about.md +++ b/docs/about.md @@ -33,7 +33,7 @@ So...what's the catch? Well, these four packets can easily be "sniffed" by an at In order to ~~eat~~ collect as many of these crackable handshake packets as possible, Pwnagotchi uses two strategies: -- **Deauthenticating the client stations it detects.** A deauthenticated device must reauthenticate to its access point by resending the 4-Way Handshake, thereby giving Pwnagotchi another chance to sniff the handshake packets and collect more crackable material. +- **Deauthenticating the client stations it detects.** A deauthenticated device must reauthenticate to its access point by re-performing the 4-Way Handshake with the AP, thereby giving Pwnagotchi another chance to sniff the handshake packets and collect more crackable material. - **Send association frames directly to the access points themselves** to try to force them to [leak the PMKID](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/). From a35d55400792620582e71c6b81b69caa2eccec63 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 10:26:11 -0400 Subject: [PATCH 27/69] minor copyediting --- docs/configure.md | 56 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index a7c251f..f85fd35 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -1,22 +1,26 @@ -# Connecting to your Pwnagotchi +# Configuration -Once you wrote the image file on the SD card, there're a few steps you'll have to follow in order to configure your unit properly. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. +Once you've [written the image file onto the SD card](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md#flashing-an-image), there're a few steps you'll have to follow in order to configure your new Pwnagotchi properly. -You'll need to configure it with a static IP address: +## Connect to your Pwnagotchi -- IP: `10.0.0.1` -- Netmask: `255.255.255.0` -- Gateway: `10.0.0.1` -- DNS (if required): `8.8.8.8` (or whatever) +1. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. +2. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. +3. You'll need to configure it with a static IP address: -If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` (if you haven't customized the hostname yet). + - IP: `10.0.0.1` + - Netmask: `255.255.255.0` + - Gateway: `10.0.0.1` + - DNS (if required): `8.8.8.8` (or whatever) -You can now connect to your unit using SSH: +4. If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` (if you haven't customized the hostname yet—if you have named your unit already, this address will be *your unit's name* + `.local`). + +5. **Congratulations!** You can now connect to your unit using SSH: ```bash ssh pi@10.0.0.2 ``` - +##### About your SSH connection The default password is `raspberry`; you should change it as soon as you log in for the first time by issuing the `passwd` command and selecting a new and more complex passphrase. If you want to login directly without entering a password (recommended!), copy your SSH public key to the unit's authorized keys: @@ -25,13 +29,17 @@ If you want to login directly without entering a password (recommended!), copy y ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 ``` -## Configuration +## Give your Pwnagotchi a name -You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/). Create the `/root/custom.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. +You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/)! -## Language Selection +Create the `/root/custom.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. -Pwnagotchi displays its UI in English by default, but it can speak several other languages! You can change `main.lang` to one of the supported languages: +## Choose your Pwnagotchi's language + +Pwnagotchi displays its UI in English by default, but it can speak several other languages! If you're fine with English, you don't need to do anything special. + +But if you want, you can change `main.lang` to one of the supported languages: - **English** *(default)* - German @@ -45,14 +53,26 @@ Pwnagotchi displays its UI in English by default, but it can speak several other ## Display Selection -Set the type of display you want to use via `ui.display.type` (if your display does not work after changing this setting, you might need to completely remove power from the Raspberry and make a clean boot). +**Set the type of display you want to use via `ui.display.type`.** +If your display does not work after changing this setting, you might need to completely remove power from the Raspberry Pi and make a clean boot. -You can configure the refresh interval of the display via `ui.fps`. We recommend using a slow refresh rate to avoid shortening the lifetime of your e-ink display. The default value is `0`, which will *only* refresh when changes are made to the screen. +**You can configure the refresh interval of the display via `ui.fps`.** We recommend using a slow refresh rate to avoid shortening the lifetime of your e-ink display. The default value is `0`, which will *only* refresh when changes are made to the screen. ## Host Connection Share -If you connect to the unit via `usb0` (thus using the data port), you might want to use the `scripts/linux_connection_share.sh`, `scripts/macos_connection_share.sh` or `scripts/win_connection_share.ps1` script to bring the interface up on your end and share internet connectivity from another interface, so you can update the unit and generally download things from the internet on it. +Want to be able to update your Pwnagotchi and access things from the internet on it? *Sure you do!* + +1. Connect to the Pwnagotchi unit via `usb0` (A.K.A., using the data port). +2. Run the appropriate connection sharing script to bring the interface up on your end and share internet connectivity from another interface: + +OS | Script Location +------|--------------------------- +Linux | `scripts/linux_connection_share.sh` +Mac OS X | `scripts/macos_connection_share.sh` +Windows | `scripts/win_connection_share.ps1` ## Troubleshooting -If your network connection keeps flapping on your device connecting to your Pwnagotchi, check if `usb0` (or equivalent) device is being controlled by NetworkManager. You can check this via `nmcli dev status`. +##### If your network connection keeps flapping on your device connecting to your Pwnagotchi. +* Check if `usb0` (or equivalent) device is being controlled by NetworkManager. +* You can check this via `nmcli dev status`. From f9435a5ad2a45f4bb889c0ebbf8d3d284ab0c3cb Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 10:31:34 -0400 Subject: [PATCH 28/69] minor copyediting --- docs/configure.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index f85fd35..b232e3e 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -7,13 +7,13 @@ Once you've [written the image file onto the SD card](https://github.com/evilsoc 1. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. 2. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. 3. You'll need to configure it with a static IP address: + - IP: `10.0.0.1` + - Netmask: `255.255.255.0` + - Gateway: `10.0.0.1` + - DNS (if required): `8.8.8.8` (or whatever) - - IP: `10.0.0.1` - - Netmask: `255.255.255.0` - - Gateway: `10.0.0.1` - - DNS (if required): `8.8.8.8` (or whatever) - -4. If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` (if you haven't customized the hostname yet—if you have named your unit already, this address will be *your unit's name* + `.local`). +4. If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` + * If you have already customized the hostname of your Pwnagotchi, `pwnagotchi.local` won't work. Instead, try *your unit's hostname* + `.local`. 5. **Congratulations!** You can now connect to your unit using SSH: From fae6115e445c65f0d61db46e8526ff7dbb1e8813 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Sun, 6 Oct 2019 16:36:25 +0200 Subject: [PATCH 29/69] fix: using sha256 for the public key fingerprint --- pwnagotchi/mesh/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/mesh/__init__.py b/pwnagotchi/mesh/__init__.py index 14403d2..9e68dc8 100644 --- a/pwnagotchi/mesh/__init__.py +++ b/pwnagotchi/mesh/__init__.py @@ -11,4 +11,4 @@ def get_identity(config): pubkey = None with open(config['main']['pubkey']) as fp: pubkey = RSA.importKey(fp.read()) - return pubkey, hashlib.sha1(pubkey.exportKey('DER')).hexdigest() + return pubkey, hashlib.sha256(pubkey.exportKey('DER')).hexdigest() From 4dd7365cf58ba228d63757e77532411a8da29cd8 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 12:51:29 -0400 Subject: [PATCH 30/69] added UI diagram (will copyedit text later) --- docs/usage.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 8883cb7..fff0fb1 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -4,13 +4,16 @@ The UI is available either via display if installed, or via http://pwnagotchi.local:8080/ if you connect to the unit via `usb0` and set a static address on the network interface (change `pwnagotchi` with the hostname of your unit). -![ui](https://i.imgur.com/XgIrcur.png) +![ui](https://i.imgur.com/c7xh4hN.png) * **CH**: Current channel the unit is operating on or `*` when hopping on all channels. * **APS**: Number of access points on the current channel and total visible access points. * **UP**: Time since the unit has been activated. * **PWND**: Number of handshakes captured in this session and number of unique networks we own at least one handshake of, from the beginning. -* **AUTO**: This indicates that the algorithm is running with AI disabled (or still loading), it disappears once the AI dependencies have been bootrapped and the neural network loaded. +* **MODE**: + * **AUTO:** This indicates that the Pwnagotchi algorithm is running in AUTOMATIC mode, with AI disabled (or still loading); it disappears once the AI dependencies have been bootstrapped and the neural network has finished loading. + * **MANU:** This appears when the unit is running in MANUAL mode. +* **FRIEND:** If another unit is nearby, its presence will be indicated here. If more than one unit is nearby, only one—whichever has the stronger signal strength—will be displayed. ## Training the AI From b4d7ea7dbf2b8c6aed3b03854fcdd0d01f579da1 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 19:59:03 +0200 Subject: [PATCH 31/69] Add peer --- scripts/preview.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/scripts/preview.py b/scripts/preview.py index 4f69a3d..06be3c6 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -8,6 +8,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), '../')) +import pwnagotchi.ui.faces as faces from pwnagotchi.ui.display import Display, VideoHandler from PIL import Image @@ -33,10 +34,26 @@ class CustomDisplay(Display): class DummyPeer: + + def __init__(self): + self.rssi = -50 + @staticmethod def name(): return "beta" + @staticmethod + def pwnd_run(): + return 50 + + @staticmethod + def pwnd_total(): + return 100 + + @staticmethod + def face(): + return faces.FRIEND + def append_images(images, horizontal=True, xmargin=0, ymargin=0): w, h = zip(*(i.size for i in images)) @@ -71,8 +88,9 @@ def main(): parser.add_argument('--lang', help="Language to use", default="en") parser.add_argument('--output', help="Path to output image (PNG)", default="preview.png") - parser.add_argument('--xmargin', type=int, default=5) - parser.add_argument('--ymargin', type=int, default=5) + parser.add_argument('--show-peer', dest="showpeer", help="This options will show a dummy peer", action="store_true") + parser.add_argument('--xmargin', help="Add X-Margin", type=int, default=5) + parser.add_argument('--ymargin', help="Add Y-Margin", type=int, default=5) args = parser.parse_args() config_template = ''' @@ -103,7 +121,8 @@ def main(): for display in list_of_displays: emotions = list() - # Starting + if args.showpeer: + display.set_closest_peer(DummyPeer()) display.on_starting() display.update() emotions.append(display.get_image()) From b7c3f41e656634913f7d01f8d7a6ad78b20ec614 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sat, 5 Oct 2019 15:39:14 +0200 Subject: [PATCH 32/69] Add wigle plugin --- pwnagotchi/plugins/default/wigle.py | 261 ++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 pwnagotchi/plugins/default/wigle.py diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py new file mode 100644 index 0000000..b87e71b --- /dev/null +++ b/pwnagotchi/plugins/default/wigle.py @@ -0,0 +1,261 @@ +__author__ = '33197631+dadav@users.noreply.github.com' +__version__ = '1.0.0' +__name__ = 'wigle' +__license__ = 'GPL3' +__description__ = 'This plugin automatically uploades collected wifis to wigle.net' + +import os +import logging +import json +from io import StringIO +import csv +from datetime import datetime +import requests +from pwnagotchi.mesh.wifi import freq_to_channel +from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA + +READY = False +ALREADY_UPLOADED = None +SKIP = None +OPTIONS = dict() + +AKMSUITE_TYPES = { + 0x00: "Reserved", + 0x01: "802.1X", + 0x02: "PSK", +} + +def _handle_packet(packet, result): + """ + Analyze each packet and extract the data from Dot11 layers + """ + + if hasattr(packet, 'cap') and 'privacy' in packet.cap: + # packet is encrypted + if 'encryption' not in result: + result['encryption'] = set() + + if packet.haslayer(Dot11Beacon): + if packet.haslayer(Dot11Beacon)\ + or packet.haslayer(Dot11ProbeResp)\ + or packet.haslayer(Dot11AssoReq)\ + or packet.haslayer(Dot11ReassoReq): + if 'bssid' not in result and hasattr(packet[Dot11], 'addr3'): + result['bssid'] = packet[Dot11].addr3 + if 'essid' not in result and hasattr(packet[Dot11Elt], 'info'): + result['essid'] = packet[Dot11Elt].info + if 'channel' not in result and hasattr(packet[Dot11Elt:3], 'info'): + result['channel'] = int(ord(packet[Dot11Elt:3].info)) + + if packet.haslayer(RadioTap): + if 'rssi' not in result and hasattr(packet[RadioTap], 'dBm_AntSignal'): + result['rssi'] = packet[RadioTap].dBm_AntSignal + if 'channel' not in result and hasattr(packet[RadioTap], 'ChannelFrequency'): + result['channel'] = freq_to_channel(packet[RadioTap].ChannelFrequency) + + # see: https://fossies.org/linux/scapy/scapy/layers/dot11.py + if packet.haslayer(Dot11EltRSN): + if hasattr(packet[Dot11EltRSN], 'akm_suites'): + auth = AKMSUITE_TYPES.get(packet[Dot11EltRSN].akm_suites[0].suite) + result['encryption'].add(f"WPA2/{auth}") + else: + result['encryption'].add("WPA2") + + if packet.haslayer(Dot11EltVendorSpecific)\ + and (packet.haslayer(Dot11EltMicrosoftWPA) + or packet.info.startswith(b'\x00P\xf2\x01\x01\x00')): + + if hasattr(packet, 'akm_suites'): + auth = AKMSUITE_TYPES.get(packet.akm_suites[0].suite) + result['encryption'].add(f"WPA2/{auth}") + else: + result['encryption'].add("WPA2") + # end see + + return result + + +def _analyze_pcap(pcap): + """ + Iterate over the packets and extract data + """ + result = dict() + + try: + packets = rdpcap(pcap) + for packet in packets: + result = _handle_packet(packet, result) + except Scapy_Exception as sc_e: + raise sc_e + + return result + + +def on_loaded(): + """ + Gets called when the plugin gets loaded + """ + global READY + global ALREADY_UPLOADED + global SKIP + + SKIP = list() + + if 'api_key' not in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None): + logging.error("WIGLE: api_key isn't set. Can't upload to wigle.net") + return + + try: + with open('/root/.wigle_uploads', 'r') as f: + ALREADY_UPLOADED = f.read().splitlines() + except OSError: + logging.warning('WIGLE: No upload-file found.') + ALREADY_UPLOADED = [] + + READY = True + + +def _extract_gps_data(path): + """ + Extract data from gps-file + + return json-obj + """ + + try: + with open(path, 'r') as json_file: + return json.load(json_file) + except OSError as os_err: + logging.error("WIGLE: %s", os_err) + except json.JSONDecodeError as json_err: + logging.error("WIGLE: %s", json_err) + + return None + +def _format_auth(data): + out = "" + for auth in data: + out = f"{out}[{auth}]" + return out + +def _transform_wigle_entry(gps_data, pcap_data): + """ + Transform to wigle entry in file + """ + dummy = StringIO() + # write kismet header + dummy.write("WigleWifi-1.4,appRelease=20190201,model=Kismet,release=2019.02.01.{},device=kismet,display=kismet,board=kismet,brand=kismet\n") + dummy.write("MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type") + + writer = csv.writer(dummy, delimiter=",", quoting=csv.QUOTE_NONE) + writer.writerow([ + pcap_data['bssid'], + pcap_data['essid'].decode('utf-8'), + _format_auth(pcap_data['encryption']), + datetime.strptime(gps_data['Updated'].rsplit('.')[0], + "%Y-%m-%dT%H:%M:%S").strftime('%Y-%m-%d %H:%M:%S'), + pcap_data['channel'], + pcap_data['rssi'], + gps_data['Latitude'], + gps_data['Longitude'], + gps_data['Altitude'], + 0, # accuracy? + 'WIFI']) + return dummy.getvalue() + +def _send_to_wigle(lines, api_key, timeout=30): + """ + Uploads the file to wigle-net + """ + + dummy = StringIO() + + for line in lines: + dummy.write(f"{line}") + + dummy.seek(0) + + headers = {'Authorization': f"Basic {api_key}", + 'Accept': 'application/json'} + data = {'donate': 'false'} + payload = {'file': dummy, 'type': 'text/csv'} + + try: + res = requests.post('https://api.wigle.net/api/v2/file/upload', + data=data, + headers=headers, + files=payload, + timeout=timeout) + json_res = res.json() + if not json_res['success']: + raise requests.exceptions.RequestException(json_res['message']) + except requests.exceptions.RequestException as re_e: + raise re_e + + +def on_internet_available(display, config, log): + """ + Called in manual mode when there's internet connectivity + """ + global ALREADY_UPLOADED + global SKIP + + if READY: + handshake_dir = config['bettercap']['handshakes'] + all_files = os.listdir(handshake_dir) + gps_files = [os.path.join(handshake_dir, filename) + for filename in all_files + if filename.endswith('.gps.json')] + gps_new = set(gps_files) - set(ALREADY_UPLOADED) - set(SKIP) + + if gps_new: + logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net") + + lines = list() + for gps_file in gps_new: + pcap_filename = gps_file.replace('.gps.json', '.pcap') + + if not os.path.exists(pcap_filename): + logging.error("WIGLE: Can't find pcap for %s", gps_file) + SKIP.append(gps_file) + continue + + gps_data = _extract_gps_data(gps_file) + try: + pcap_data = _analyze_pcap(pcap_filename) + except Scapy_Exception as sc_e: + logging.error("WIGLE: %s", sc_e) + SKIP.append(gps_file) + continue + + if 'encryption' in pcap_data: + if not pcap_data['encryption']: + pcap_data['encryption'].add('WEP') + else: + pcap_data['encryption'] = set() + pcap_data['encryption'].add('OPN') + + if len(pcap_data) < 5: + # not enough data + SKIP.append(gps_file) + continue + + new_entry = _transform_wigle_entry(gps_data, pcap_data) + lines.append(new_entry) + + if lines: + display.set('status', "Uploading gps-data to wigle.net ...") + display.update(force=True) + try: + _send_to_wigle(lines, OPTIONS['api_key']) + ALREADY_UPLOADED += gps_new + with open('/root/.wigle_uploads', 'a') as up_file: + for gps in gps_new: + up_file.write(gps + "\n") + logging.info("WIGLE: Successfuly uploaded %d files", len(gps_new)) + except requests.exceptions.RequestException as re_e: + SKIP += lines + logging.error("WIGLE: Got an exception while uploading %s", re_e) + except OSError as os_e: + SKIP += lines + logging.error("WIGLE: Got the following error: %s", os_e) From a9ef098d3291860f48ce27e075b6957c1121f039 Mon Sep 17 00:00:00 2001 From: SecurityWaffle Date: Sun, 6 Oct 2019 13:32:17 -0500 Subject: [PATCH 33/69] Fixed bug with defaults.yml path --- bin/pwnagotchi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pwnagotchi b/bin/pwnagotchi index 4733105..9b0b561 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -16,7 +16,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-C', '--config', action='store', dest='config', - default=os.path.join(os.path.abspath(os.path.dirname(pwnagotchi.__file__)), '/defaults.yml'), + default=os.path.join(os.path.abspath(os.path.dirname(pwnagotchi.__file__)), 'defaults.yml'), help='Main configuration file.') parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.yml', help='If this file exists, configuration will be merged and this will override default values.') From 90f0d6dc4ea7ac021813216edf6904eafe2b9504 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 20:32:43 +0200 Subject: [PATCH 34/69] update defaults --- pwnagotchi/defaults.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index cceac71..813b97f 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -39,6 +39,9 @@ main: wpa-sec: enabled: false api_key: ~ + wigle: + enabled: false + api_key: ~ # monitor interface to use iface: mon0 From 27255bd8ec696cc463d79abd893f9e478b8d5b86 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 20:37:28 +0200 Subject: [PATCH 35/69] Fix bug with gps.json files --- pwnagotchi/plugins/default/wpa-sec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py index 68e4171..cfe35da 100644 --- a/pwnagotchi/plugins/default/wpa-sec.py +++ b/pwnagotchi/plugins/default/wpa-sec.py @@ -61,7 +61,7 @@ def on_internet_available(display, config, log): if READY: handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) - handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames] + handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] handshake_new = set(handshake_paths) - set(ALREADY_UPLOADED) if handshake_new: From cb943ae4e168ae93ce3357bfe9a311e6622c54d1 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 20:39:16 +0200 Subject: [PATCH 36/69] Fix bug with gps.json files --- pwnagotchi/plugins/default/onlinehashcrack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/onlinehashcrack.py b/pwnagotchi/plugins/default/onlinehashcrack.py index 249ea19..2319d96 100644 --- a/pwnagotchi/plugins/default/onlinehashcrack.py +++ b/pwnagotchi/plugins/default/onlinehashcrack.py @@ -62,7 +62,7 @@ def on_internet_available(display, config, log): if READY: handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) - handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames] + handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] handshake_new = set(handshake_paths) - set(ALREADY_UPLOADED) if handshake_new: From 1cf45138b8c8793cd7e1dbc8b8e0962a5ab5214a Mon Sep 17 00:00:00 2001 From: SecurityWaffle Date: Sun, 6 Oct 2019 13:40:16 -0500 Subject: [PATCH 37/69] Fixes path to defaults.yml --- bin/pwnagotchi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pwnagotchi b/bin/pwnagotchi index 9b0b561..56300af 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -16,7 +16,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-C', '--config', action='store', dest='config', - default=os.path.join(os.path.abspath(os.path.dirname(pwnagotchi.__file__)), 'defaults.yml'), + default=os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.yml'), help='Main configuration file.') parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.yml', help='If this file exists, configuration will be merged and this will override default values.') From a3052c3b990558db9d3c60c5ed55fa1de72efc02 Mon Sep 17 00:00:00 2001 From: SecurityWaffle Date: Sun, 6 Oct 2019 13:41:55 -0500 Subject: [PATCH 38/69] fixes issue where defaults.yml is not included in the install --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 41d7475..98ee45c 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,8 @@ setup(name='pwnagotchi', license='GPL', install_requires=required, scripts=['bin/pwnagotchi'], - package_data={'pwnagotchi': ('pwnagotchi/defaults.yml',)}, + package_data={'pwnagotchi': ['defaults.yml', 'pwnagotchi/defaults.yml']}, + include_package_data=True, packages=find_packages(), classifiers=[ 'Programming Language :: Python :: 3', From 5b1bf6dd4afe62354e8499048b09e4d8a88288a4 Mon Sep 17 00:00:00 2001 From: waxwing Date: Sun, 6 Oct 2019 15:23:37 -0400 Subject: [PATCH 39/69] added UI anatomy diagram --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4fb4153..02b9506 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ [Pwnagotchi](https://twitter.com/pwnagotchi) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding WiFi environment in order to maximize the crackable WPA key material it captures (either passively, or by performing deauthentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/), full and half WPA handshakes. -![handshake](https://i.imgur.com/pdA4vCZ.png) +![ui](https://i.imgur.com/c7xh4hN.png) Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54) over time to **get better at pwning WiFi things** in the environments you expose it to. From 8d8e4b037651f708c24a314aacb49e4fef0d3fdd Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 22:30:43 +0200 Subject: [PATCH 40/69] Rename some vars and fix some bugs --- pwnagotchi/plugins/default/wigle.py | 50 ++++++++++++++++++----------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py index b87e71b..cba5745 100644 --- a/pwnagotchi/plugins/default/wigle.py +++ b/pwnagotchi/plugins/default/wigle.py @@ -126,11 +126,10 @@ def _extract_gps_data(path): with open(path, 'r') as json_file: return json.load(json_file) except OSError as os_err: - logging.error("WIGLE: %s", os_err) + raise os_err except json.JSONDecodeError as json_err: - logging.error("WIGLE: %s", json_err) + raise json_err - return None def _format_auth(data): out = "" @@ -203,16 +202,18 @@ def on_internet_available(display, config, log): if READY: handshake_dir = config['bettercap']['handshakes'] all_files = os.listdir(handshake_dir) - gps_files = [os.path.join(handshake_dir, filename) + all_gps_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.gps.json')] - gps_new = set(gps_files) - set(ALREADY_UPLOADED) - set(SKIP) + new_gps_files = set(all_gps_files) - set(ALREADY_UPLOADED) - set(SKIP) - if gps_new: + if new_gps_files: logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net") - lines = list() - for gps_file in gps_new: + csv_entries = list() + no_err_entries = list() + + for gps_file in new_gps_files: pcap_filename = gps_file.replace('.gps.json', '.pcap') if not os.path.exists(pcap_filename): @@ -220,7 +221,17 @@ def on_internet_available(display, config, log): SKIP.append(gps_file) continue - gps_data = _extract_gps_data(gps_file) + try: + gps_data = _extract_gps_data(gps_file) + except OSError as os_err: + logging.error("WIGLE: %s", os_err) + SKIP.append(gps_file) + continue + except json.JSONDecodeError as json_err: + logging.error("WIGLE: %s", json_err) + SKIP.append(gps_file) + continue + try: pcap_data = _analyze_pcap(pcap_filename) except Scapy_Exception as sc_e: @@ -228,34 +239,37 @@ def on_internet_available(display, config, log): SKIP.append(gps_file) continue + # encrypption-key is only there if privacy-cap was set if 'encryption' in pcap_data: if not pcap_data['encryption']: pcap_data['encryption'].add('WEP') else: + # no encryption, nothing to eat :( pcap_data['encryption'] = set() pcap_data['encryption'].add('OPN') if len(pcap_data) < 5: - # not enough data + # not enough data; try next time SKIP.append(gps_file) continue new_entry = _transform_wigle_entry(gps_data, pcap_data) - lines.append(new_entry) + csv_entries.append(new_entry) + no_err_entries.append(gps_file) - if lines: + if csv_entries: display.set('status', "Uploading gps-data to wigle.net ...") display.update(force=True) try: - _send_to_wigle(lines, OPTIONS['api_key']) - ALREADY_UPLOADED += gps_new + _send_to_wigle(csv_entries, OPTIONS['api_key']) + ALREADY_UPLOADED += no_err_entries with open('/root/.wigle_uploads', 'a') as up_file: - for gps in gps_new: + for gps in no_err_entries: up_file.write(gps + "\n") - logging.info("WIGLE: Successfuly uploaded %d files", len(gps_new)) + logging.info("WIGLE: Successfuly uploaded %d files", len(no_err_entries)) except requests.exceptions.RequestException as re_e: - SKIP += lines + SKIP += no_err_entries logging.error("WIGLE: Got an exception while uploading %s", re_e) except OSError as os_e: - SKIP += lines + SKIP += no_err_entries logging.error("WIGLE: Got the following error: %s", os_e) From f6a80aae7823ba3ad70df15eb5fcf58b2d16ab1e Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 23:00:31 +0200 Subject: [PATCH 41/69] Fix 404 url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02b9506..670ed47 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ full and half WPA handshakes. ![ui](https://i.imgur.com/c7xh4hN.png) -Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54) over time to **get better at pwning WiFi things** in the environments you expose it to. +Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.yml#L73) over time to **get better at pwning WiFi things** in the environments you expose it to. More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) doc.) From 1c251fc09381d0ac28940bd5fd3dd066fa1996c4 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Sun, 6 Oct 2019 23:25:02 +0200 Subject: [PATCH 42/69] new: fixed rsa identity generation and implemented api enrollment plugin --- bin/pwnagotchi | 8 +-- pwnagotchi/agent.py | 4 +- pwnagotchi/defaults.yml | 2 + pwnagotchi/identity.py | 47 +++++++++++++++++ pwnagotchi/mesh/__init__.py | 10 ---- pwnagotchi/mesh/utils.py | 7 ++- pwnagotchi/plugins/default/api.py | 51 +++++++++++++++++++ pwnagotchi/plugins/default/auto-backup.py | 2 +- pwnagotchi/plugins/default/auto-update.py | 2 +- pwnagotchi/plugins/default/example.py | 2 +- pwnagotchi/plugins/default/onlinehashcrack.py | 2 +- pwnagotchi/plugins/default/twitter.py | 2 +- pwnagotchi/plugins/default/wigle.py | 9 +++- pwnagotchi/plugins/default/wpa-sec.py | 2 +- pwnagotchi/utils.py | 3 ++ setup.py | 1 + 16 files changed, 127 insertions(+), 27 deletions(-) create mode 100644 pwnagotchi/identity.py create mode 100644 pwnagotchi/plugins/default/api.py diff --git a/bin/pwnagotchi b/bin/pwnagotchi index 56300af..f72d84c 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -10,6 +10,7 @@ if __name__ == '__main__': import pwnagotchi.plugins as plugins from pwnagotchi.log import SessionParser + from pwnagotchi.identity import KeyPair from pwnagotchi.agent import Agent from pwnagotchi.ui.display import Display @@ -34,10 +35,11 @@ if __name__ == '__main__': plugins.load(config) + keypair = KeyPair() display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()}) - agent = Agent(view=display, config=config) + agent = Agent(view=display, config=config, keypair=keypair) - logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, pwnagotchi.version)) + logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._keypair.fingerprint, pwnagotchi.version)) for _, plugin in plugins.loaded.items(): logging.debug("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__)) @@ -64,7 +66,7 @@ if __name__ == '__main__': time.sleep(1) if Agent.is_connected(): - plugins.on('internet_available', display, config, log) + plugins.on('internet_available', display, keypair, config, log) else: logging.info("entering auto mode ...") diff --git a/pwnagotchi/agent.py b/pwnagotchi/agent.py index 98f8aab..0e89763 100644 --- a/pwnagotchi/agent.py +++ b/pwnagotchi/agent.py @@ -17,13 +17,13 @@ RECOVERY_DATA_FILE = '/root/.pwnagotchi-recovery' class Agent(Client, AsyncAdvertiser, AsyncTrainer): - def __init__(self, view, config): + def __init__(self, view, config, keypair): Client.__init__(self, config['bettercap']['hostname'], config['bettercap']['scheme'], config['bettercap']['port'], config['bettercap']['username'], config['bettercap']['password']) - AsyncAdvertiser.__init__(self, config, view) + AsyncAdvertiser.__init__(self, config, view, keypair) AsyncTrainer.__init__(self, config) self._started_at = time.time() diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 813b97f..dff6b3b 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -6,6 +6,8 @@ main: custom_plugins: # which plugins to load and enable plugins: + api: + enabled: false auto-update: enabled: false interval: 1 # every day diff --git a/pwnagotchi/identity.py b/pwnagotchi/identity.py new file mode 100644 index 0000000..ae12490 --- /dev/null +++ b/pwnagotchi/identity.py @@ -0,0 +1,47 @@ +from Crypto.Signature import PKCS1_PSS +from Crypto.PublicKey import RSA +import Crypto.Hash.SHA256 as SHA256 +import base64 +import hashlib +import os +import logging + +DefaultPath = "/etc/pwnagotchi/" + + +class KeyPair(object): + def __init__(self, path=DefaultPath): + self.path = path + self.priv_path = os.path.join(path, "id_rsa") + self.priv_key = None + self.pub_path = "%s.pub" % self.priv_path + self.pub_key = None + + if not os.path.exists(self.path): + os.makedirs(self.path) + + if not os.path.exists(self.priv_path) or not os.path.exists(self.pub_path): + logging.info("generating %s ..." % self.priv_path) + os.system("/usr/bin/ssh-keygen -t rsa -m PEM -b 4096 -N '' -f '%s'" % self.priv_path) + + with open(self.priv_path) as fp: + self.priv_key = RSA.importKey(fp.read()) + + with open(self.pub_path) as fp: + self.pub_key = RSA.importKey(fp.read()) + self.pub_key_pem = self.pub_key.exportKey('PEM').decode("ascii") + # python is special + if 'RSA PUBLIC KEY' not in self.pub_key_pem: + self.pub_key_pem = self.pub_key_pem.replace('PUBLIC KEY', 'RSA PUBLIC KEY') + + pem = self.pub_key_pem.encode("ascii") + + self.pub_key_pem_b64 = base64.b64encode(pem).decode("ascii") + self.fingerprint = hashlib.sha256(pem).hexdigest() + + def sign(self, message): + hasher = SHA256.new(message.encode("ascii")) + signer = PKCS1_PSS.new(self.priv_key, saltLen=16) + signature = signer.sign(hasher) + signature_b64 = base64.b64encode(signature).decode("ascii") + return signature, signature_b64 \ No newline at end of file diff --git a/pwnagotchi/mesh/__init__.py b/pwnagotchi/mesh/__init__.py index 9e68dc8..e1c033f 100644 --- a/pwnagotchi/mesh/__init__.py +++ b/pwnagotchi/mesh/__init__.py @@ -1,14 +1,4 @@ import os -from Crypto.PublicKey import RSA -import hashlib - def new_session_id(): return ':'.join(['%02x' % b for b in os.urandom(6)]) - - -def get_identity(config): - pubkey = None - with open(config['main']['pubkey']) as fp: - pubkey = RSA.importKey(fp.read()) - return pubkey, hashlib.sha256(pubkey.exportKey('DER')).hexdigest() diff --git a/pwnagotchi/mesh/utils.py b/pwnagotchi/mesh/utils.py index 3b63d09..a775ad4 100644 --- a/pwnagotchi/mesh/utils.py +++ b/pwnagotchi/mesh/utils.py @@ -3,14 +3,13 @@ import logging import pwnagotchi import pwnagotchi.plugins as plugins -from pwnagotchi.mesh import get_identity class AsyncAdvertiser(object): - def __init__(self, config, view): + def __init__(self, config, view, keypair): self._config = config self._view = view - self._public_key, self._identity = get_identity(config) + self._keypair = keypair self._advertiser = None def start_advertising(self): @@ -24,7 +23,7 @@ class AsyncAdvertiser(object): self._config['main']['iface'], pwnagotchi.name(), pwnagotchi.version, - self._identity, + self._keypair.fingerprint, period=0.3, data=self._config['personality']) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py new file mode 100644 index 0000000..afd882c --- /dev/null +++ b/pwnagotchi/plugins/default/api.py @@ -0,0 +1,51 @@ +__author__ = 'evilsocket@gmail.com' +__version__ = '1.0.0' +__name__ = 'api' +__license__ = 'GPL3' +__description__ = 'This plugin signals the unit cryptographic identity to api.pwnagotchi.ai' + +import logging +import json +import requests +import pwnagotchi +from pwnagotchi.utils import StatusFile + +OPTIONS = dict() +READY = False +STATUS = StatusFile('/root/.api-enrollment.json') + + +def on_loaded(): + logging.info("api plugin loaded.") + + +def on_internet_available(ui, keypair, config, log): + global STATUS + + if STATUS.newer_then_minutes(10): + return + + try: + logging.info("api: signign enrollment request ...") + identity = "%s@%s" % (pwnagotchi.name(), keypair.fingerprint) + _, signature_b64 = keypair.sign(identity) + + api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' + enroll = { + 'identity': identity, + 'public_key': keypair.pub_key_pem_b64, + 'signature': signature_b64 + } + + logging.info("api: enrolling unit to %s ..." % api_address) + + r = requests.post(api_address, json=enroll) + if r.status_code == 200: + token = r.json() + logging.info("api: enrolled") + STATUS.update(data=json.dumps(token)) + else: + logging.error("error %d: %s" % (r.status_code, r.json())) + + except Exception as e: + logging.exception("error while enrolling the unit") diff --git a/pwnagotchi/plugins/default/auto-backup.py b/pwnagotchi/plugins/default/auto-backup.py index 731a40f..48d8597 100644 --- a/pwnagotchi/plugins/default/auto-backup.py +++ b/pwnagotchi/plugins/default/auto-backup.py @@ -33,7 +33,7 @@ def on_loaded(): logging.info("AUTO-BACKUP: Successfuly loaded.") -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): global STATUS if READY: diff --git a/pwnagotchi/plugins/default/auto-update.py b/pwnagotchi/plugins/default/auto-update.py index 4a1f135..dab44b3 100644 --- a/pwnagotchi/plugins/default/auto-update.py +++ b/pwnagotchi/plugins/default/auto-update.py @@ -23,7 +23,7 @@ def on_loaded(): READY = True -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): global STATUS if READY: diff --git a/pwnagotchi/plugins/default/example.py b/pwnagotchi/plugins/default/example.py index c18177c..2a82a18 100644 --- a/pwnagotchi/plugins/default/example.py +++ b/pwnagotchi/plugins/default/example.py @@ -20,7 +20,7 @@ def on_loaded(): # called in manual mode when there's internet connectivity -def on_internet_available(ui, config, log): +def on_internet_available(ui, keypair, config, log): pass diff --git a/pwnagotchi/plugins/default/onlinehashcrack.py b/pwnagotchi/plugins/default/onlinehashcrack.py index 2319d96..9771b88 100644 --- a/pwnagotchi/plugins/default/onlinehashcrack.py +++ b/pwnagotchi/plugins/default/onlinehashcrack.py @@ -55,7 +55,7 @@ def _upload_to_ohc(path, timeout=30): raise e -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): """ Called in manual mode when there's internet connectivity """ diff --git a/pwnagotchi/plugins/default/twitter.py b/pwnagotchi/plugins/default/twitter.py index 560903a..8f21f25 100644 --- a/pwnagotchi/plugins/default/twitter.py +++ b/pwnagotchi/plugins/default/twitter.py @@ -14,7 +14,7 @@ def on_loaded(): # called in manual mode when there's internet connectivity -def on_internet_available(ui, config, log): +def on_internet_available(ui, keypair, config, log): if log.is_new() and log.handshakes > 0: try: import tweepy diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py index cba5745..da57f94 100644 --- a/pwnagotchi/plugins/default/wigle.py +++ b/pwnagotchi/plugins/default/wigle.py @@ -12,7 +12,6 @@ import csv from datetime import datetime import requests from pwnagotchi.mesh.wifi import freq_to_channel -from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA READY = False ALREADY_UPLOADED = None @@ -26,6 +25,8 @@ AKMSUITE_TYPES = { } def _handle_packet(packet, result): + from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ + Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA """ Analyze each packet and extract the data from Dot11 layers """ @@ -76,6 +77,8 @@ def _handle_packet(packet, result): def _analyze_pcap(pcap): + from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ + Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA """ Iterate over the packets and extract data """ @@ -192,7 +195,9 @@ def _send_to_wigle(lines, api_key, timeout=30): raise re_e -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): + from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ + Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA """ Called in manual mode when there's internet connectivity """ diff --git a/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py index cfe35da..7d61b7b 100644 --- a/pwnagotchi/plugins/default/wpa-sec.py +++ b/pwnagotchi/plugins/default/wpa-sec.py @@ -54,7 +54,7 @@ def _upload_to_wpasec(path, timeout=30): raise e -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): """ Called in manual mode when there's internet connectivity """ diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py index d1b2ba0..2834664 100644 --- a/pwnagotchi/utils.py +++ b/pwnagotchi/utils.py @@ -89,6 +89,9 @@ class StatusFile(object): if os.path.exists(path): self._updated = datetime.fromtimestamp(os.path.getmtime(path)) + def newer_then_minutes(self, minutes): + return self._updated is not None and ((datetime.now() - self._updated).seconds / 60) < minutes + def newer_then_days(self, days): return self._updated is not None and (datetime.now() - self._updated).days < days diff --git a/setup.py b/setup.py index 98ee45c..f8e22b3 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- from setuptools import setup, find_packages import pwnagotchi From 967ba3a7a510e650e9b098a72d45629ca8e8407e Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 00:10:00 +0200 Subject: [PATCH 43/69] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index afd882c..6747d95 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -22,7 +22,7 @@ def on_loaded(): def on_internet_available(ui, keypair, config, log): global STATUS - if STATUS.newer_then_minutes(10): + if STATUS.newer_then_minutes(25): return try: From 8ef1f0f377771e6300a38d1aca66cbd29c385bce Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 00:27:22 +0200 Subject: [PATCH 44/69] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 1 - scripts/pypi_upload.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 6747d95..480f07c 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -38,7 +38,6 @@ def on_internet_available(ui, keypair, config, log): } logging.info("api: enrolling unit to %s ..." % api_address) - r = requests.post(api_address, json=enroll) if r.status_code == 200: token = r.json() diff --git a/scripts/pypi_upload.sh b/scripts/pypi_upload.sh index 4a71125..265a56a 100755 --- a/scripts/pypi_upload.sh +++ b/scripts/pypi_upload.sh @@ -1,6 +1,6 @@ #!/bin/bash -rm -rf build dist ergo_nn.egg-info && +rm -rf build dist pwnagotchi.egg-info && python3 setup.py sdist bdist_wheel && clear && twine upload dist/* From b26d238f4091cfd671586380fcffa64f30b89ace Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 00:30:05 +0200 Subject: [PATCH 45/69] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/__init__.py b/pwnagotchi/__init__.py index 9905b0f..ff43494 100644 --- a/pwnagotchi/__init__.py +++ b/pwnagotchi/__init__.py @@ -1,6 +1,6 @@ import subprocess -version = '1.0.0plz4' +version = '1.0.0a' _name = None From 61da16ed915877f8cdb60cc4b1d00de175305fbe Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 01:31:37 +0200 Subject: [PATCH 46/69] fix: when the AI is ready the mode label reports AI --- pwnagotchi/ai/__init__.py | 2 +- pwnagotchi/ui/view.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pwnagotchi/ai/__init__.py b/pwnagotchi/ai/__init__.py index 359a38c..190025f 100644 --- a/pwnagotchi/ai/__init__.py +++ b/pwnagotchi/ai/__init__.py @@ -39,4 +39,4 @@ def load(config, agent, epoch, from_disk=True): for key, value in config['params'].items(): logging.info(" %s: %s" % (key, value)) - return a2c + return a2c \ No newline at end of file diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index 78e75b6..5847662 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -153,7 +153,7 @@ class View(object): self.set('face', faces.AWAKE) def on_ai_ready(self): - self.set('mode', '') + self.set('mode', ' AI') self.set('face', faces.HAPPY) self.set('status', self._voice.on_ai_ready()) self.update() From 6e9bb866c79506b310b3d05374d0d2ec2f87e6d1 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 02:00:08 +0200 Subject: [PATCH 47/69] fix: fixed epochs log parser --- pwnagotchi/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/log.py b/pwnagotchi/log.py index 416ae4f..dab58e4 100644 --- a/pwnagotchi/log.py +++ b/pwnagotchi/log.py @@ -13,7 +13,7 @@ LAST_SESSION_FILE = '/root/.pwnagotchi-last-session' class SessionParser(object): EPOCH_TOKEN = '[epoch ' - EPOCH_PARSER = re.compile(r'^\s*\[epoch (\d+)\] (.+)') + EPOCH_PARSER = re.compile(r'^.+\[epoch (\d+)\] (.+)') EPOCH_DATA_PARSER = re.compile(r'([a-z_]+)=([^\s]+)') TRAINING_TOKEN = ' training epoch ' START_TOKEN = 'connecting to http' From 00582be3746c4adc23aed00797040a2902ffc9aa Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 10:25:46 +0200 Subject: [PATCH 48/69] new: reporting session data to the api --- pwnagotchi/plugins/default/api.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 480f07c..5828085 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -7,6 +7,7 @@ __description__ = 'This plugin signals the unit cryptographic identity to api.pw import logging import json import requests +import subprocess import pwnagotchi from pwnagotchi.utils import StatusFile @@ -27,6 +28,7 @@ def on_internet_available(ui, keypair, config, log): try: logging.info("api: signign enrollment request ...") + identity = "%s@%s" % (pwnagotchi.name(), keypair.fingerprint) _, signature_b64 = keypair.sign(identity) @@ -34,7 +36,16 @@ def on_internet_available(ui, keypair, config, log): enroll = { 'identity': identity, 'public_key': keypair.pub_key_pem_b64, - 'signature': signature_b64 + 'signature': signature_b64, + 'data': { + 'duration': log.duration, + 'epochs': log.epochs, + 'train_epochs': log.train_epochs, + 'avg_reward': log.avg_reward, + 'min_reward': log.min_reward, + 'max_reward': log.max_reward, + 'uname': subprocess.getoutput("uname -a") + } } logging.info("api: enrolling unit to %s ..." % api_address) From da52bcd705d2dfdfbd72bd3743246d1646fd5018 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 11:13:28 +0200 Subject: [PATCH 49/69] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 5828085..60792af 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -30,6 +30,7 @@ def on_internet_available(ui, keypair, config, log): logging.info("api: signign enrollment request ...") identity = "%s@%s" % (pwnagotchi.name(), keypair.fingerprint) + # sign the identity string to prove we own both keys _, signature_b64 = keypair.sign(identity) api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' @@ -44,6 +45,10 @@ def on_internet_available(ui, keypair, config, log): 'avg_reward': log.avg_reward, 'min_reward': log.min_reward, 'max_reward': log.max_reward, + 'deauthed': log.deauthed, + 'associated': log.associated, + 'handshakes': log.handshakes, + 'peers': log.peers, 'uname': subprocess.getoutput("uname -a") } } From d6efc0b70d215907f3a54ddc7a8a2c58b7efc9bf Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 13:06:29 +0200 Subject: [PATCH 50/69] new: api plugin will report pwned access points --- pwnagotchi/defaults.yml | 1 + pwnagotchi/plugins/default/api.py | 168 +++++++++++++++++++++++------- pwnagotchi/utils.py | 25 ++++- 3 files changed, 154 insertions(+), 40 deletions(-) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index dff6b3b..e9b45b6 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -8,6 +8,7 @@ main: plugins: api: enabled: false + report: true # report pwned networks auto-update: enabled: false interval: 1 # every day diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 60792af..eae9343 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -4,63 +4,155 @@ __name__ = 'api' __license__ = 'GPL3' __description__ = 'This plugin signals the unit cryptographic identity to api.pwnagotchi.ai' +import os import logging -import json import requests +import glob import subprocess import pwnagotchi -from pwnagotchi.utils import StatusFile +import pwnagotchi.utils as utils OPTIONS = dict() -READY = False -STATUS = StatusFile('/root/.api-enrollment.json') +AUTH = utils.StatusFile('/root/.api-enrollment.json', data_format='json') +REPORT = utils.StatusFile('/root/.api-report.json', data_format='json') def on_loaded(): logging.info("api plugin loaded.") -def on_internet_available(ui, keypair, config, log): - global STATUS +def get_api_token(log, keys): + global AUTH - if STATUS.newer_then_minutes(25): - return + if AUTH.newer_then_minutes(25) and AUTH.data is not None and 'token' in AUTH.data: + return AUTH.data['token'] + + if AUTH.data is None: + logging.info("api: enrolling unit ...") + else: + logging.info("api: refreshing token ...") + + identity = "%s@%s" % (pwnagotchi.name(), keys.fingerprint) + # sign the identity string to prove we own both keys + _, signature_b64 = keys.sign(identity) + + api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' + enrollment = { + 'identity': identity, + 'public_key': keys.pub_key_pem_b64, + 'signature': signature_b64, + 'data': { + 'duration': log.duration, + 'epochs': log.epochs, + 'train_epochs': log.train_epochs, + 'avg_reward': log.avg_reward, + 'min_reward': log.min_reward, + 'max_reward': log.max_reward, + 'deauthed': log.deauthed, + 'associated': log.associated, + 'handshakes': log.handshakes, + 'peers': log.peers, + 'uname': subprocess.getoutput("uname -a") + } + } + + r = requests.post(api_address, json=enrollment) + if r.status_code != 200: + raise Exception("(status %d) %s" % (r.status_code, r.json())) + + AUTH.update(data=r.json()) + + logging.info("api: done") + + return AUTH.data["token"] + + +def parse_packet(packet, info): + from scapy.all import Dot11Elt, Dot11Beacon, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq + + if packet.haslayer(Dot11Beacon): + if packet.haslayer(Dot11Beacon) \ + or packet.haslayer(Dot11ProbeResp) \ + or packet.haslayer(Dot11AssoReq) \ + or packet.haslayer(Dot11ReassoReq): + if 'bssid' not in info and hasattr(packet[Dot11], 'addr3'): + info['bssid'] = packet[Dot11].addr3 + if 'essid' not in info and hasattr(packet[Dot11Elt], 'info'): + info['essid'] = packet[Dot11Elt].info.decode('utf-8') + + return info + + +def parse_pcap(filename): + logging.info("api: parsing %s ..." % filename) + + essid = bssid = None try: - logging.info("api: signign enrollment request ...") + from scapy.all import rdpcap - identity = "%s@%s" % (pwnagotchi.name(), keypair.fingerprint) - # sign the identity string to prove we own both keys - _, signature_b64 = keypair.sign(identity) + info = {} - api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' - enroll = { - 'identity': identity, - 'public_key': keypair.pub_key_pem_b64, - 'signature': signature_b64, - 'data': { - 'duration': log.duration, - 'epochs': log.epochs, - 'train_epochs': log.train_epochs, - 'avg_reward': log.avg_reward, - 'min_reward': log.min_reward, - 'max_reward': log.max_reward, - 'deauthed': log.deauthed, - 'associated': log.associated, - 'handshakes': log.handshakes, - 'peers': log.peers, - 'uname': subprocess.getoutput("uname -a") - } + for pkt in rdpcap(filename): + info = parse_packet(pkt, info) + + bssid = info['bssid'] if 'bssid' in info else None + essid = info['essid'] if 'essid' in info else None + + except Exception as e: + bssid = None + logging.error("api: %s" % e) + + return essid, bssid + + +def api_report_ap(token, essid, bssid): + logging.info("api: reporting %s (%s)" % (essid, bssid)) + + try: + api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap' + headers = {'Authorization': 'access_token %s' % token} + report = { + 'essid': essid, + 'bssid': bssid, } + r = requests.post(api_address, headers=headers, json=report) + if r.status_code != 200: + raise Exception("(status %d) %s" % (r.status_code, r.text)) + except Exception as e: + logging.error("api: %s" % e) + return False - logging.info("api: enrolling unit to %s ..." % api_address) - r = requests.post(api_address, json=enroll) - if r.status_code == 200: - token = r.json() - logging.info("api: enrolled") - STATUS.update(data=json.dumps(token)) - else: - logging.error("error %d: %s" % (r.status_code, r.json())) + return True + + +def on_internet_available(ui, keys, config, log): + global REPORT + + try: + + pcap_files = glob.glob(os.path.join(config['bettercap']['handshakes'], "*.pcap")) + num_networks = len(pcap_files) + reported = REPORT.data_field_or('reported', default=[]) + num_reported = len(reported) + num_new = num_networks - num_reported + + if num_new > 0: + logging.info("api: %d new networks to report" % num_new) + token = get_api_token(log, keys) + + if OPTIONS['report']: + for pcap_file in pcap_files: + net_id = os.path.basename(pcap_file).replace('.pcap', '') + if net_id not in reported: + essid, bssid = parse_pcap(pcap_file) + if bssid: + if api_report_ap(token, essid, bssid): + reported.append(net_id) + + REPORT.update(data={'reported': reported}) + else: + logging.info("api: reporting disabled") except Exception as e: logging.exception("error while enrolling the unit") diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py index 2834664..e6e15f5 100644 --- a/pwnagotchi/utils.py +++ b/pwnagotchi/utils.py @@ -5,6 +5,7 @@ import os import time import subprocess import yaml +import json # https://stackoverflow.com/questions/823196/yaml-merge-in-python @@ -82,12 +83,24 @@ def blink(times=1, delay=0.3): class StatusFile(object): - def __init__(self, path): + def __init__(self, path, data_format='raw'): self._path = path self._updated = None + self._format = data_format + self.data = None if os.path.exists(path): self._updated = datetime.fromtimestamp(os.path.getmtime(path)) + with open(path) as fp: + if data_format == 'json': + self.data = json.load(fp) + else: + self.data = fp.read() + + def data_field_or(self, name, default=""): + if self.data is not None and name in self.data: + return self.data[name] + return default def newer_then_minutes(self, minutes): return self._updated is not None and ((datetime.now() - self._updated).seconds / 60) < minutes @@ -97,5 +110,13 @@ class StatusFile(object): def update(self, data=None): self._updated = datetime.now() + self.data = data with open(self._path, 'w') as fp: - fp.write(str(self._updated) if data is None else data) + if data is None: + fp.write(str(self._updated)) + + elif self._format == 'json': + json.dump(self.data, fp) + + else: + fp.write(data) From 4827dc65edf5251275e1336ec58f7640f8e3898e Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 14:12:22 +0200 Subject: [PATCH 51/69] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index eae9343..b912bb8 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -95,6 +95,8 @@ def parse_pcap(filename): for pkt in rdpcap(filename): info = parse_packet(pkt, info) + if 'essid' in info and info['essid'] is not None and 'bssid' in info and info['bssid'] is not None: + break bssid = info['bssid'] if 'bssid' in info else None essid = info['essid'] if 'essid' in info else None From 60f0f6c29eda2b2a761618af711a529d7bb00ccd Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 14:32:42 +0200 Subject: [PATCH 52/69] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index b912bb8..0b2f1be 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -151,8 +151,7 @@ def on_internet_available(ui, keys, config, log): if bssid: if api_report_ap(token, essid, bssid): reported.append(net_id) - - REPORT.update(data={'reported': reported}) + REPORT.update(data={'reported': reported}) else: logging.info("api: reporting disabled") From 90b0e10e81a5a0e03753715ec1034278bdd5f600 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 15:32:44 +0200 Subject: [PATCH 53/69] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 81 +++++++++++++++++-------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 0b2f1be..71d78e3 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -69,63 +69,70 @@ def get_api_token(log, keys): def parse_packet(packet, info): from scapy.all import Dot11Elt, Dot11Beacon, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq - if packet.haslayer(Dot11Beacon): - if packet.haslayer(Dot11Beacon) \ - or packet.haslayer(Dot11ProbeResp) \ - or packet.haslayer(Dot11AssoReq) \ - or packet.haslayer(Dot11ReassoReq): - if 'bssid' not in info and hasattr(packet[Dot11], 'addr3'): + if packet.haslayer(Dot11ProbeResp) or packet.haslayer(Dot11AssoReq) or packet.haslayer(Dot11ReassoReq): + if hasattr(packet[Dot11], 'addr3'): info['bssid'] = packet[Dot11].addr3 - if 'essid' not in info and hasattr(packet[Dot11Elt], 'info'): + if hasattr(packet[Dot11Elt], 'info'): info['essid'] = packet[Dot11Elt].info.decode('utf-8') - return info def parse_pcap(filename): logging.info("api: parsing %s ..." % filename) - essid = bssid = None + net_id = os.path.basename(filename).replace('.pcap', '') + + if '_' in net_id: + # /root/handshakes/ESSID_BSSID.pcap + essid, bssid = net_id.split('_') + else: + # /root/handshakes/BSSID.pcap + essid, bssid = '', net_id + + it = iter(bssid) + bssid = ':'.join([a + b for a, b in zip(it, it)]) + + info = { + 'essid': essid, + 'bssid': bssid + } try: from scapy.all import rdpcap - info = {} - for pkt in rdpcap(filename): info = parse_packet(pkt, info) - if 'essid' in info and info['essid'] is not None and 'bssid' in info and info['bssid'] is not None: - break - - bssid = info['bssid'] if 'bssid' in info else None - essid = info['essid'] if 'essid' in info else None except Exception as e: - bssid = None logging.error("api: %s" % e) - return essid, bssid + return info['essid'], info['bssid'] -def api_report_ap(token, essid, bssid): - logging.info("api: reporting %s (%s)" % (essid, bssid)) - - try: - api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap' - headers = {'Authorization': 'access_token %s' % token} - report = { - 'essid': essid, - 'bssid': bssid, - } - r = requests.post(api_address, headers=headers, json=report) - if r.status_code != 200: - raise Exception("(status %d) %s" % (r.status_code, r.text)) - except Exception as e: - logging.error("api: %s" % e) - return False - - return True +def api_report_ap(log, keys, token, essid, bssid): + while True: + logging.info("api: reporting %s (%s)" % (essid, bssid)) + try: + api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap' + headers = {'Authorization': 'access_token %s' % token} + report = { + 'essid': essid, + 'bssid': bssid, + } + r = requests.post(api_address, headers=headers, json=report) + if r.status_code != 200: + if r.status_code == 401: + logging.warning("token expired") + token = get_api_token(log, keys) + continue + else: + raise Exception("(status %d) %s" % (r.status_code, r.text)) + else: + return True + except Exception as e: + logging.error("api: %s" % e) + return False def on_internet_available(ui, keys, config, log): @@ -149,7 +156,7 @@ def on_internet_available(ui, keys, config, log): if net_id not in reported: essid, bssid = parse_pcap(pcap_file) if bssid: - if api_report_ap(token, essid, bssid): + if api_report_ap(log, keys, token, essid, bssid): reported.append(net_id) REPORT.update(data={'reported': reported}) else: From db3b7f577ab490e051e653141773a4ab7561df81 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 15:45:11 +0200 Subject: [PATCH 54/69] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 71d78e3..f906b06 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -112,6 +112,7 @@ def parse_pcap(filename): def api_report_ap(log, keys, token, essid, bssid): while True: + token = AUTH.data['token'] logging.info("api: reporting %s (%s)" % (essid, bssid)) try: api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap' From a787e78f937b68d7788bc1f8ca7f25a333ac6830 Mon Sep 17 00:00:00 2001 From: Andrea Draghetti Date: Mon, 7 Oct 2019 16:00:46 +0200 Subject: [PATCH 55/69] Proper management of the IF cycle --- scripts/create_sibling.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/create_sibling.sh b/scripts/create_sibling.sh index 650d40c..c0d7558 100755 --- a/scripts/create_sibling.sh +++ b/scripts/create_sibling.sh @@ -94,7 +94,7 @@ function provide_raspbian() { function setup_raspbian(){ # Detect the ability to create sparse files if [ "${OPT_SPARSE}" -eq 0 ]; then - if ! type "bmaptool" > /dev/null; then + if ! type "bmaptool" >/dev/null 2>&1; then echo "[!] bmaptool not available, not creating a sparse image" else echo "[+] Defaulting to sparse image generation as bmaptool is available" From 7a24a1a0bca6630942a656707734834d5935849b Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 16:23:38 +0200 Subject: [PATCH 56/69] fix: api plugin is now called grid and opt-out --- pwnagotchi/defaults.yml | 4 ++-- pwnagotchi/plugins/default/{api.py => grid.py} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename pwnagotchi/plugins/default/{api.py => grid.py} (98%) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index e9b45b6..3cda0cb 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -6,8 +6,8 @@ main: custom_plugins: # which plugins to load and enable plugins: - api: - enabled: false + grid: + enabled: true report: true # report pwned networks auto-update: enabled: false diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/grid.py similarity index 98% rename from pwnagotchi/plugins/default/api.py rename to pwnagotchi/plugins/default/grid.py index f906b06..09ee08a 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/grid.py @@ -1,8 +1,8 @@ __author__ = 'evilsocket@gmail.com' __version__ = '1.0.0' -__name__ = 'api' +__name__ = 'grid' __license__ = 'GPL3' -__description__ = 'This plugin signals the unit cryptographic identity to api.pwnagotchi.ai' +__description__ = 'This plugin signals the unit cryptographic identity and list of pwned networks and list of pwned networks to api.pwnagotchi.ai' import os import logging From 6bd1d5f764caea62ea1a37be5b4ef778814760a2 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 16:24:44 +0200 Subject: [PATCH 57/69] fix: updated auto-backup.files --- pwnagotchi/defaults.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 3cda0cb..3f2d871 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -18,9 +18,8 @@ main: files: - /root/brain.nn - /root/brain.json - - /root/custom.yml - - /root/handshakes - - /etc/ssh + - /root/handshakes/ + - /etc/pwnagotchi/ - /etc/hostname - /etc/hosts - /etc/motd From 8bc037abb720c1bee3a6871473d6c26cbde2748b Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 16:27:01 +0200 Subject: [PATCH 58/69] fix: fixed paths in doc --- docs/configure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configure.md b/docs/configure.md index b232e3e..a3fc7c2 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -33,7 +33,7 @@ ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/)! -Create the `/root/custom.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. +Open the `/etc/pwnagotchi/config.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.yml) with your custom values. ## Choose your Pwnagotchi's language From 6cf74dd282b46d5bdcff4dfd920a671fdd6a5076 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 16:35:25 +0200 Subject: [PATCH 59/69] docs: pwngrid docs --- docs/configure.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/configure.md b/docs/configure.md index a3fc7c2..11d204d 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -51,6 +51,32 @@ But if you want, you can change `main.lang` to one of the supported languages: - Russian - Swedish +## PwnGRID + +By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its +presence to the PwnGRID server and periodically send a list of the networks that it has pwned. None of the captured cryptographic material is sent to this server, +just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far. + +If you want to partially opt-out from this feature and have your unit only signal its presence without sending the list of networks, you can put this in your `/etc/pwnagotchi/config.yml` file: + +```yaml +main: + plugins: + grid: + enabled: true + report: false # partial opt-out +``` + +If you prefer to completely opt-out and also disable signaling: + +```yaml +main: + plugins: + grid: + enabled: false # full opt-out + report: false +``` + ## Display Selection **Set the type of display you want to use via `ui.display.type`.** From 2b50609752a5c8f8df48ae9a3c2d9a7df9ebe588 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 16:42:56 +0200 Subject: [PATCH 60/69] fix: phrasing --- docs/configure.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 11d204d..8c9c6a9 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -55,7 +55,13 @@ But if you want, you can change `main.lang` to one of the supported languages: By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its presence to the PwnGRID server and periodically send a list of the networks that it has pwned. None of the captured cryptographic material is sent to this server, -just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far. +just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far, namely: + +- The cryptographic identity of the unit, generated at first boot and used for authentication. +- The output of the `uname -a` command on the unit used to determine the type of hardware. +- The list of networks that the unit collected handshakes of, made of their `BSSID` and `ESSID`. + +Other than for easy unit identification and debugging, this data is collected in order to build rankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** If you want to partially opt-out from this feature and have your unit only signal its presence without sending the list of networks, you can put this in your `/etc/pwnagotchi/config.yml` file: @@ -67,7 +73,7 @@ main: report: false # partial opt-out ``` -If you prefer to completely opt-out and also disable signaling: +If you prefer to completely opt-out by disabling signaling: ```yaml main: From 1a13e744b539a9ff49ad411b4c00f463799666e7 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 16:52:23 +0200 Subject: [PATCH 61/69] fix: grid plugin is half opt-in --- docs/configure.md | 14 ++++++++------ pwnagotchi/defaults.yml | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 8c9c6a9..935197f 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -53,27 +53,29 @@ But if you want, you can change `main.lang` to one of the supported languages: ## PwnGRID -By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its -presence to the PwnGRID server and periodically send a list of the networks that it has pwned. None of the captured cryptographic material is sent to this server, +By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is **only partially** enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its +presence to the PwnGRID server without sending any data. + +It is possible to fully opt-in and also enable the unit to send basic information about the pwned networks. None of the captured cryptographic material is sent to this server, just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far, namely: - The cryptographic identity of the unit, generated at first boot and used for authentication. - The output of the `uname -a` command on the unit used to determine the type of hardware. - The list of networks that the unit collected handshakes of, made of their `BSSID` and `ESSID`. -Other than for easy unit identification and debugging, this data is collected in order to build rankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** +Other than for easy unit identification and debugging, this data is collected in order to build drankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** -If you want to partially opt-out from this feature and have your unit only signal its presence without sending the list of networks, you can put this in your `/etc/pwnagotchi/config.yml` file: +In order to fully opt-in, you can put this in your `/etc/pwnagotchi/config.yml` file: ```yaml main: plugins: grid: enabled: true - report: false # partial opt-out + report: true # full-opt in ``` -If you prefer to completely opt-out by disabling signaling: +If you prefer to completely opt-out by also disabling signaling: ```yaml main: diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 3f2d871..7d4d92b 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -8,7 +8,7 @@ main: plugins: grid: enabled: true - report: true # report pwned networks + report: false # don't report pwned networks by default! auto-update: enabled: false interval: 1 # every day From f7bf12a0a05dc331ad9cd137a1f062d0df730b59 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 16:55:55 +0200 Subject: [PATCH 62/69] misc: small fix or general refactoring i did not bother commenting --- docs/configure.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 935197f..47fe042 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -54,13 +54,14 @@ But if you want, you can change `main.lang` to one of the supported languages: ## PwnGRID By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is **only partially** enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its -presence to the PwnGRID server without sending any data. +presence to the PwnGRID server without sending any data other than: + +- The cryptographic identity of the unit, generated at first boot and used for authentication. +- The output of the `uname -a` command on the unit used to determine the type of hardware. It is possible to fully opt-in and also enable the unit to send basic information about the pwned networks. None of the captured cryptographic material is sent to this server, just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far, namely: -- The cryptographic identity of the unit, generated at first boot and used for authentication. -- The output of the `uname -a` command on the unit used to determine the type of hardware. - The list of networks that the unit collected handshakes of, made of their `BSSID` and `ESSID`. Other than for easy unit identification and debugging, this data is collected in order to build drankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** From 52af8391317bfb655b0c934988b6c2a2afbe548b Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 17:04:06 +0200 Subject: [PATCH 63/69] new: exclude option for the grid plugin --- docs/configure.md | 15 ++++++++++++++- pwnagotchi/defaults.yml | 2 ++ pwnagotchi/plugins/default/grid.py | 10 +++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 47fe042..dfc33ae 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -76,7 +76,20 @@ main: report: true # full-opt in ``` -If you prefer to completely opt-out by also disabling signaling: +Even if fully opted-in, you can still disable reporting for specific networks, for instance if you don't want your home network to be in the system: + +```yaml +main: + plugins: + grid: + enabled: true + report: true + exclude: + - MyHomeNetwork + - de:ad:be:ef:de:ad # both ESSIDs and BSSIDs are supported +``` + +If instead you prefer to completely opt-out by also disabling signaling: ```yaml main: diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 7d4d92b..330218f 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -9,6 +9,8 @@ main: grid: enabled: true report: false # don't report pwned networks by default! + exclude: # do not report the following networks (accepts both ESSIDs and BSSIDs) + - YourHomeNetworkHere auto-update: enabled: false interval: 1 # every day diff --git a/pwnagotchi/plugins/default/grid.py b/pwnagotchi/plugins/default/grid.py index 09ee08a..6c151b3 100644 --- a/pwnagotchi/plugins/default/grid.py +++ b/pwnagotchi/plugins/default/grid.py @@ -154,7 +154,15 @@ def on_internet_available(ui, keys, config, log): if OPTIONS['report']: for pcap_file in pcap_files: net_id = os.path.basename(pcap_file).replace('.pcap', '') - if net_id not in reported: + do_skip = False + for skip in OPTIONS['exclude']: + skip = skip.lower() + net = net_id.lower() + if skip in net or skip.replace(':', '') in net: + do_skip = True + break + + if net_id not in reported and not do_skip: essid, bssid = parse_pcap(pcap_file) if bssid: if api_report_ap(log, keys, token, essid, bssid): From 7f880698a40a01680928bdd5ded0d214a67672c3 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 18:25:59 +0200 Subject: [PATCH 64/69] docs: moved docs to www.pwnagotchi.ai repo --- README.md | 10 +-- docs/about.md | 44 ---------- docs/configure.md | 126 ---------------------------- docs/dev.md | 60 ------------- docs/faq.md | 208 ---------------------------------------------- docs/hacks.md | 102 ----------------------- docs/index.md | 23 ----- docs/install.md | 60 ------------- docs/plugins.md | 56 ------------- docs/usage.md | 149 --------------------------------- 10 files changed, 1 insertion(+), 837 deletions(-) delete mode 100644 docs/about.md delete mode 100644 docs/configure.md delete mode 100644 docs/dev.md delete mode 100644 docs/faq.md delete mode 100644 docs/hacks.md delete mode 100644 docs/index.md delete mode 100644 docs/install.md delete mode 100644 docs/plugins.md delete mode 100644 docs/usage.md diff --git a/README.md b/README.md index 670ed47..363d7ec 100644 --- a/README.md +++ b/README.md @@ -36,15 +36,7 @@ For hackers to learn reinforcement learning, WiFi networking, and have an excuse **IMPORTANT NOTE:** If you'd like to alphatest Pwnagotchi and are trying to get yours up and running while the project is still very unstable, please understand that the documentation here may not reflect what is currently implemented. If you have questions, ask the community of alphatesters in the [official Pwnagotchi Slack](https://pwnagotchi.herokuapp.com). The Pwnagotchi dev team is entirely focused on the v1.0 release and will NOT be providing support for alphatesters trying to get their Pwnagotchis working in the meantime. All technical support during this period of development is being provided by your fellow alphatesters in the Slack (thanks, everybody! :heart:). ---- -- [About the Project](https://github.com/evilsocket/pwnagotchi/blob/master/docs/about.md) -- [How to Install](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md) -- [Configuration](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md) -- [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md) -- [Plugins](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) -- [Development](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) -- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) -- [Community Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) +https://www.pwnagotchi.ai ## Links diff --git a/docs/about.md b/docs/about.md deleted file mode 100644 index 4777c21..0000000 --- a/docs/about.md +++ /dev/null @@ -1,44 +0,0 @@ -# About the Project - -[Pwnagotchi](https://twitter.com/pwnagotchi) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding WiFi environment in order to maximize the WPA key material it captures (either passively, or by performing deauthentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/), -full and half WPA handshakes. - -![handshake](https://i.imgur.com/pdA4vCZ.png) - -Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54) over time to **get better at pwning WiFi things** in the environments you expose it to. - -**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi actually learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but **definitely listen to your pwnagotchi when it tells you it's bored!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) - -Multiple units within close physical proximity can "talk" to each other, advertising their own presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard. Over time, two or more units trained together will learn to cooperate upon detecting each other's presence by dividing the available channels among them for optimal pwnage. - -![peers](https://i.imgur.com/Ywr5aqx.png) - -[Depending on the status of the unit](), several states and states transitions are configurable and represented on the display as different moods, expressions and sentences. Pwnagotchi speaks [many languages](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md#configuration), too! - -Of course, it is possible to run your Pwnagotchi with the AI disabled (configurable in `config.yml`). Why might you want to do this? Perhaps you simply want to use your own fixed parameters (instead of letting the AI decide for you), or maybe you want to save battery and CPU cycles, or maybe it's just you have strong concerns about aiding and abetting baby Skynet. Whatever your particular reasons may be: an AI-disabled Pwnagotchi is still a simple and very effective automated deauther, WPA handshake sniffer, and portable [bettercap](https://www.bettercap.org/) + [webui](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#bettercaps-web-ui) dedicated hardware. - -## WiFi Handshakes 101 - -In order to understand why it's valuable to have an AI that wants to eat handshakes, it's helpful to understand a little bit about how handshakes are used in the WPA/WPA2 wireless protocol. - -Before a client device that's connecting to a wireless access point—say, for instance, your phone connecting to your home WiFi network—is able to securely transmit to and receive data from that access point, a process called the **4-Way Handshake** needs to happen in order for the WPA encryption keys to be generated. This process consists of the exchange of four packets (hence the "4" in "4-Way") between the client device and the AP; these are used to derive session keys from the access point's WiFi password. Once the packets are successfully exchanged and the keys have been generated, the client device is authenticated and can start sending and receiving data packets to and from the wireless AP that are secured by encryption. - -

- -
-image taken from wifi-professionals.com -

- -So...what's the catch? Well, these four packets can easily be "sniffed" by an attacker monitoring nearby (say, with a Pwnagotchi :innocent:). And once recorded, that attacker can use [dictionary and/or bruteforce attacks](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2) to crack the handshakes and recover the original WiFi key. In fact, **successful recovery of the WiFi key doesn't necessarily even need all four packets!** A half-handshake (containing only two of the four packets) can be cracked, too—and in some *(most)* cases, just [a single packet is enough](https://hashcat.net/forum/thread-7717-post-41447.html), *even without clients.* - -In order to ~~eat~~ collect as many of these crackable handshake packets as possible, Pwnagotchi uses two strategies: - -- **Deauthenticating the client stations it detects.** A deauthenticated device must reauthenticate to its access point by re-performing the 4-Way Handshake with the AP, thereby giving Pwnagotchi another chance to sniff the handshake packets and collect more crackable material. -- **Send association frames directly to the access points themselves** -to try to force them to [leak the PMKID](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/). - -All the handshakes captured this way are saved into `.pcap` files on Pwnagotchi's filesystem. Each PCAP file that Pwnagotchi generates is organized according to access point; one PCAP will contain all the handshakes that Pwnagotchi has ever captured for that particular AP. These handshakes can later be [cracked with proper hardware and software](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2). - -## License - -`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It's released under the GPL3 license. diff --git a/docs/configure.md b/docs/configure.md deleted file mode 100644 index dfc33ae..0000000 --- a/docs/configure.md +++ /dev/null @@ -1,126 +0,0 @@ -# Configuration - -Once you've [written the image file onto the SD card](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md#flashing-an-image), there're a few steps you'll have to follow in order to configure your new Pwnagotchi properly. - -## Connect to your Pwnagotchi - -1. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. -2. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. -3. You'll need to configure it with a static IP address: - - IP: `10.0.0.1` - - Netmask: `255.255.255.0` - - Gateway: `10.0.0.1` - - DNS (if required): `8.8.8.8` (or whatever) - -4. If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` - * If you have already customized the hostname of your Pwnagotchi, `pwnagotchi.local` won't work. Instead, try *your unit's hostname* + `.local`. - -5. **Congratulations!** You can now connect to your unit using SSH: - -```bash -ssh pi@10.0.0.2 -``` -##### About your SSH connection -The default password is `raspberry`; you should change it as soon as you log in for the first time by issuing the `passwd` command and selecting a new and more complex passphrase. - -If you want to login directly without entering a password (recommended!), copy your SSH public key to the unit's authorized keys: - -```bash -ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 -``` - -## Give your Pwnagotchi a name - -You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/)! - -Open the `/etc/pwnagotchi/config.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.yml) with your custom values. - -## Choose your Pwnagotchi's language - -Pwnagotchi displays its UI in English by default, but it can speak several other languages! If you're fine with English, you don't need to do anything special. - -But if you want, you can change `main.lang` to one of the supported languages: - -- **English** *(default)* -- German -- Dutch -- Greek -- Macedonian -- Italian -- French -- Russian -- Swedish - -## PwnGRID - -By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is **only partially** enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its -presence to the PwnGRID server without sending any data other than: - -- The cryptographic identity of the unit, generated at first boot and used for authentication. -- The output of the `uname -a` command on the unit used to determine the type of hardware. - -It is possible to fully opt-in and also enable the unit to send basic information about the pwned networks. None of the captured cryptographic material is sent to this server, -just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far, namely: - -- The list of networks that the unit collected handshakes of, made of their `BSSID` and `ESSID`. - -Other than for easy unit identification and debugging, this data is collected in order to build drankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** - -In order to fully opt-in, you can put this in your `/etc/pwnagotchi/config.yml` file: - -```yaml -main: - plugins: - grid: - enabled: true - report: true # full-opt in -``` - -Even if fully opted-in, you can still disable reporting for specific networks, for instance if you don't want your home network to be in the system: - -```yaml -main: - plugins: - grid: - enabled: true - report: true - exclude: - - MyHomeNetwork - - de:ad:be:ef:de:ad # both ESSIDs and BSSIDs are supported -``` - -If instead you prefer to completely opt-out by also disabling signaling: - -```yaml -main: - plugins: - grid: - enabled: false # full opt-out - report: false -``` - -## Display Selection - -**Set the type of display you want to use via `ui.display.type`.** -If your display does not work after changing this setting, you might need to completely remove power from the Raspberry Pi and make a clean boot. - -**You can configure the refresh interval of the display via `ui.fps`.** We recommend using a slow refresh rate to avoid shortening the lifetime of your e-ink display. The default value is `0`, which will *only* refresh when changes are made to the screen. - -## Host Connection Share - -Want to be able to update your Pwnagotchi and access things from the internet on it? *Sure you do!* - -1. Connect to the Pwnagotchi unit via `usb0` (A.K.A., using the data port). -2. Run the appropriate connection sharing script to bring the interface up on your end and share internet connectivity from another interface: - -OS | Script Location -------|--------------------------- -Linux | `scripts/linux_connection_share.sh` -Mac OS X | `scripts/macos_connection_share.sh` -Windows | `scripts/win_connection_share.ps1` - -## Troubleshooting - -##### If your network connection keeps flapping on your device connecting to your Pwnagotchi. -* Check if `usb0` (or equivalent) device is being controlled by NetworkManager. -* You can check this via `nmcli dev status`. diff --git a/docs/dev.md b/docs/dev.md deleted file mode 100644 index 534b398..0000000 --- a/docs/dev.md +++ /dev/null @@ -1,60 +0,0 @@ -# Software - -- Raspbian + [nexmon patches](https://re4son-kernel.com/re4son-pi-kernel/) for monitor mode, or any Linux with a monitor mode enabled interface (if you tune config.yml). - -**Do not try with Kali on the Raspberry Pi 0 W, it is compiled without hardware floating point support and TensorFlow is simply not available for it, use Raspbian.** - -## Creating an Image - -You can use the `scripts/create_sibling.sh` script to create an - ready to flash - rasbian image with pwnagotchi. - -```shell -usage: ./scripts/create_sibling.sh [OPTIONS] - - Options: - -n # Name of the pwnagotchi (default: pwnagotchi) - -i # Provide the path of an already downloaded raspbian image - -o # Name of the img-file (default: pwnagotchi.img) - -s # Size which should be added to second partition (in Gigabyte) (default: 4) - -v # Version of raspbian (Supported: latest; default: latest) - -p # Only run provisioning (assumes the image is already mounted) - -d # Only run dependencies checks - -h # Show this help -``` - -#### Known Issues - -`GLib-ERROR **: 20:50:46.361: getauxval () failed: No such file or directory` - -- Affected DEB & Versions: QEMU <= 2.11 -- Fix: Upgrade QEMU to >= 3.1 -- Bug Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923289 - -## Adding a Language - -If you want to add a language use the `language.sh` script. If you want to add for example the language **italian** you would type: - -```shell -./scripts/language.sh add it -# Now make your changes to the file -# sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po -./scripts/language.sh compile it -# DONE -``` - -If you changed the `voice.py`- File, the translations need an update. Do it like this: - -```shell -./scripts/language.sh update it -# Now make your changes to the file (changed lines are marked with "fuzzy") -# sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po -./scripts/language.sh compile it -# DONE -``` - -Now you can use the `preview.py`-script to preview the changes: - -```shell -./scripts/preview.py --lang it --display ws1 ws2 inky --output preview.png -# Now open preview.png -``` diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 348e039..0000000 --- a/docs/faq.md +++ /dev/null @@ -1,208 +0,0 @@ -# FAQ - - -[**What can Pwnagotchi actually do?**](#what-can-pwnagotchi-actually-do) - -* [Why does Pwnagotchi eat handshakes?](#why-does-pwnagotchi-eat-handshakes) -* [What kinds of handshakes does Pwnagotchi eat?](#what-kinds-of-handshakes-does-pwnagotchi-eat) -* [Does Pwnagotchi support both 2.4 GHz and 5.0 GHz?](#does-pwnagotchi-support-both-24-ghz-and-50-ghz) -* [Just how politely *does* Pwnagotchi deauth?](#just-how-politely-does-pwnagotchi-deauth) -* [Hey, I want to learn more about how Pwnagotchi actually works.](#hey-i-want-to-learn-more-about-how-pwnagotchi-actually-works) -* [How is Pwnagotchi using bettercap?](#how-is-pwnagotchi-using-bettercap) -* [What happens if I run a Pwnagotchi without the AI enabled?](#what-happens-if-i-run-a-pwnagotchi-without-the-ai-enabled) -* [How easy is it to hack Pwnagotchi to add additional functionality?](#how-easy-is-it-to-hack-pwnagotchi-to-add-additional-functionality) - -[**Building Your Pwnagotchi**](#building-your-pwnagotchi) - -* [What hardware do I need to create my very own Pwnagotchi?](#what-hardware-do-i-need-to-create-my-very-own-pwnagotchi) -* [Is there any way to see my Pwnagotchi's face even if I don't have a display?](#is-there-any-way-to-see-my-pwnagotchis-face-even-if-i-dont-have-a-display) -* [How do I attach the screen to the Raspberry Pi?](#how-do-i-attach-the-screen-to-the-raspberry-pi) -* [I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case?](#i-love-my-new-pwnagotchi-but-it-kinda-looks-like-a-bomb-where-can-i-find-a-decent-case) -* [Why does everybody use e-ink screens for their Pwnagotchis?](#why-does-everybody-use-e-ink-screens-for-their-pwnagotchis) -* [How do I connect to my Pwnagotchi?](#how-do-i-connect-to-my-pwnagotchi) - -[**Customizing Your Pwnagotchi**](#customizing-your-pwnagotchi) - -* [How do I change my Pwnagotchi's name?](#how-do-i-change-my-pwnagotchis-name) -* [I want to change the faces. What do I hack?](#i-want-to-change-the-faces-what-do-i-hack) -* [I want my Pwnagotchi to speak a different language. Can it?](#i-want-my-pwnagotchi-to-speak-a-different-language-can-it) -* [I have a great idea for something cool I wish Pwnagotchi could do!](#i-have-a-great-idea-for-something-cool-i-wish-pwnagotchi-could-do) -* [Are there any unofficial community "hacks" for further customizing my Pwnagotchi?](#are-there-any-unofficial-community-hacks-for-further-customizing-my-pwnagotchi) - -[**Getting to Know Your Pwnagotchi**](#getting-to-know-your-pwnagotchi) - -* [What does everything on the screen mean?](#what-does-everything-on-the-screen-mean) -* [How do I whitelist my home network so Pwnagotchi stops pwning me?](#how-do-i-whitelist-my-home-network-so-pwnagotchi-stops-pwning-me) -* [What is MANU mode? What is AUTO mode?](#what-is-manu-mode-what-is-auto-mode) -* [Why does the AI take 30 minutes to load?](#why-does-the-ai-take-30-minutes-to-load) -* [What is Pwnagotchi doing while it's waiting for the AI to load?](#what-is-pwnagotchi-doing-while-its-waiting-for-the-ai-to-load) -* [How do I know when the AI is running?](#how-do-i-know-when-the-ai-is-running) -* [Where does Pwnagotchi store all the handshakes it's eaten?](#where-does-pwnagotchi-store-all-the-handshakes-its-eaten) -* [What happens when my Pwnagotchi meets another Pwnagotchi?](#what-happens-when-my-pwnagotchi-meets-another-pwnagotchi) - -[**Caring for Your Pwnagotchi**](#caring-for-your-pwnagotchi) - -* [What do all my Pwnagotchi's faces mean?](#what-do-all-my-pwnagotchis-faces-mean) -* [How do I feed my Pwnagotchi?](#how-do-i-feed-my-pwnagotchi) -* [Oh no, my Pwnagotchi is sad and bored! How do I entertain it?!](#oh-no-my-pwnagotchi-is-sad-and-bored-how-do-i-entertain-it) -* [How do I update my Pwnagotchi?](#how-do-i-update-my-pwnagotchi) -* [I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain?](#im-extremely-emotionally-attached-to-my-pwnagotchi-how-can-i-back-up-its-brain) -* [How do I turn off my Pwnagotchi?](#how-do-i-turn-off-my-pwnagotchi) -* [Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating?](#uh-so-what-do-i-do-with-all-these-handshakes-my-pwnagotchi-has-been-eating) - -[**Known Quirks**](#known-quirks) - -* [My Pwnagotchi's log timestamps seem...unreliable. Huh?](#my-pwnagotchis-log-timestamps-seemunreliable-huh) -* [Help! My Pwnagotchi's SD card got corrupted. What gives?](#help-my-pwnagotchis-sd-card-got-corrupted-what-gives) - ---- - -## **What can Pwnagotchi actually do?** -### Why does Pwnagotchi eat handshakes? -lorem ipsum dolor sit amet - ---- -### What kinds of handshakes does Pwnagotchi eat? -lorem ipsum dolor sit amet - ---- -### Does Pwnagotchi support both 2.4 GHz and 5.0 GHz? -lorem ipsum dolor sit amet - ---- -### Just how politely *does* Pwnagotchi deauth? -lorem ipsum dolor sit amet - ---- -### Hey, I want to learn more about how Pwnagotchi actually works. -lorem ipsum dolor sit amet - ---- -### How is Pwnagotchi using bettercap? -lorem ipsum dolor sit amet - ---- -### What happens if I run a Pwnagotchi without the AI enabled? -lorem ipsum dolor sit amet - ---- -### How easy is it to hack Pwnagotchi to add additional functionality? -lorem ipsum dolor sit amet - ---- - -## **Building Your Pwnagotchi** -### What hardware do I need to create my very own Pwnagotchi? -lorem ipsum dolor sit amet - ---- -### Is there any way to see my Pwnagotchi's face even if I don't have a display? -lorem ipsum dolor sit amet - ---- -### How do I attach the screen to the Raspberry Pi? -lorem ipsum dolor sit amet - ---- -### I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case? -lorem ipsum dolor sit amet - ---- -### Why does everybody use e-ink screens for their Pwnagotchis? -lorem ipsum dolor sit amet - ---- -### How do I connect to my Pwnagotchi? -lorem ipsum dolor sit amet - ---------------------------------------------------------------------------------------------------------------- -## **Customizing Your Pwnagotchi** -### How do I change my Pwnagotchi's name? -lorem ipsum dolor sit amet - ---- -### I want to change the faces. What do I hack? -lorem ipsum dolor sit amet - ---- -### I want my Pwnagotchi to speak a different language. Can it? -lorem ipsum dolor sit amet - ---- -### I have a great idea for something cool I wish Pwnagotchi could do! -lorem ipsum dolor sit amet - ---- -### Are there any unofficial community "hacks" for further customizing my Pwnagotchi? -lorem ipsum dolor sit amet - ---------------------------------------------------------------------------------------------------------------- -## **Getting to Know Your Pwnagotchi** -### What does everything on the screen mean? -lorem ipsum dolor sit amet - ---- -### How do I whitelist my home network so Pwnagotchi stops pwning me? -lorem ipsum dolor sit amet - ---- -### What is MANU mode? What is AUTO mode? -lorem ipsum dolor sit amet - ---- -### Why does the AI take 30 minutes to load? -lorem ipsum dolor sit amet - ---- -### What is Pwnagotchi doing while it's waiting for the AI to load? -lorem ipsum dolor sit amet - ---- -### How do I know when the AI is running? -lorem ipsum dolor sit amet - ---- -### Where does Pwnagotchi store all the handshakes it's eaten? -lorem ipsum dolor sit amet - ---- -### What happens when my Pwnagotchi meets another Pwnagotchi? -lorem ipsum dolor sit amet - ---------------------------------------------------------------------------------------------------------------- -## **Caring for Your Pwnagotchi** -### What do all my Pwnagotchi's faces mean? -lorem ipsum dolor sit amet - ---- -### How do I feed my Pwnagotchi? -lorem ipsum dolor sit amet - ---- -### Oh no, my Pwnagotchi is sad and bored! How do I entertain it?! -lorem ipsum dolor sit amet - ---- -### How do I update my Pwnagotchi? -lorem ipsum dolor sit amet - ---- -### I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain? -lorem ipsum dolor sit amet - ---- -### How do I turn off my Pwnagotchi? -lorem ipsum dolor sit amet - ---- -### Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating? -lorem ipsum dolor sit amet - ---------------------------------------------------------------------------------------------------------------- -## **Known Quirks** -### My Pwnagotchi's log timestamps seem...unreliable. Huh? -lorem ipsum dolor sit amet - ---- -### Help! My Pwnagotchi's SD card got corrupted. What gives? -lorem ipsum dolor sit amet diff --git a/docs/hacks.md b/docs/hacks.md deleted file mode 100644 index 6f58c3f..0000000 --- a/docs/hacks.md +++ /dev/null @@ -1,102 +0,0 @@ -# Unofficial Hacks ---- -**IMPORTANT DISCLAIMER:** The information provided on this page is NOT officially supported by the Pwnagotchi development team. These are unofficial "hacks" that users have worked out while customizing their units and decided to document for anybody else who might want to do something similar. - -- **Please do NOT open issues if you cannot get something described in this document to work.** -- It (almost) goes without saying, but obviously: **we are NOT responsible if you break your hardware by following any instructions documented here. Use this information at your own risk.** - ---- -If you test one of these hacks yourself and it still works, it's extra nice if you update the **Last Tested On** table and note any minor adjustments you may have had to make to the instructions to make it work with your particular Pwnagotchi setup. :heart: - - -## Screens -### Waveshare 3.5" SPI TFT screen - -Last tested on | Pwnagotchi version | Working? | Reference ----------------|--------------------|----------|-----------| -2019 October 3 | Unknown | :white_check_mark: | ([link](https://github.com/evilsocket/pwnagotchi/issues/124#issue-502346040)) - -Some of this guide will work with other framebuffer-based displays. - -- First: SSH into your Pwnagotchi, and give it some internet! - - Don't forget to check your default gateway and `apt-get update`. -- Follow the guide here: [www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)#Method_1._Driver_installation](https://www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)#Method_1._Driver_installation) - - At the step with `./LCD35-show`, add `lite` to the command prompt (e.g., `./LCD35-show lite`). -- Reboot. -- As root, make three symlinks: - - `cd ~` - - `ln -s pwnagotchi.png pwnagotchi_1.png` - - `ln -s pwnagotchi.png pwnagotchi_2.png` - - `ln -s pwnagotchi.png pwnagotchi_3.png` -- `apt install fbi` -- Change display type to `inky` in `config.yml` -- Add `modules-load=dwc2,g_ether` to your kernel command line (`/boot/cmdline.txt`) or it will break! -- Also must add `dtoverlay=dwc2` to the bottom of (`/boot/config.txt`) -- Edit `/etc/rc.local` and add: `fbi -T 1 -a -noverbose -t 15 -cachemem 0 /root/pwnagotchi_1.png /root/pwnagotchi_2.png /root/pwnagotchi_3.png &` -- Reboot. - -And you should be good! - ---- -### Pwnagotchi face via Bluetooth -Last tested on | Pwnagotchi version | Working? | Reference ----------------|--------------------|----------|-----------| -2019 October 6 | Unknown | :white_check_mark: | on Android -2019 October 6 | Unknown | :white_check_mark: | on iPad iOS 9.3.5 - -A way to view your Pwnagotchi's ~~face~~ UI wirelessly via Bluetooth on a separate device. Refresh rate is the same as the e-ink display (every few seconds). This is NOT Bluetooth tethering; this is only Bluetooth as a server on pi side; you connect the Bluetooth and get a DHCP IP address and that's it. This hack cannot leverage the data connection. - -Contributed by Systemic in the Slack. - -##### 1. First Step -- Comment out the Bluetooth disable line from `/boot/config.txt` : `#dtoverlay=pi3-disable-bt` -- Change `/root/pwnagotchi/config.yml` to have `0.0.0.0` instead of `10.0.0.2` to listen as well on Bluetooth. -- Then launch the following commands: - -##### 2. Install required packages. - -```sudo apt-get install bluez bluez-tools bridge-utils dnsmasq``` - -##### 3. Configure Bluetooth and start it. -```sudo modprobe bnep -sudo brctl addbr pan0 -sudo brctl setfd pan0 0 -sudo brctl stp pan0 off -sudo ifconfig pan0 172.26.0.1 netmask 255.255.255.0 -sudo ip link set pan0 up -``` - -```cat <<- EOF > /tmp/dnsmasq_bt.conf``` - -```bind-interfaces -port=0 -interface=pan0 -listen-address=172.26.0.1 -dhcp-range=172.26.0.2,172.26.0.100,255.255.255.0,5m -dhcp-leasefile=/tmp/dnsmasq_bt.leases -dhcp-authoritative -log-dhcp -``` - -```EOF``` - -```sudo dnsmasq -C /tmp/dnsmasq_bt.conf -sudo bt-agent -c NoInputNoOutput& -sudo bt-adapter -a hci0 --set Discoverable 1 -sudo bt-adapter -a hci0 --set DiscoverableTimeout 0 -sudo bt-adapter -a hci0 --set Pairable 1 -sudo bt-adapter -a hci0 --set PairableTimeout 0 -sudo bt-network -a hci0 -s nap pan0 & -``` - -##### 4. Finally: on your phone, you have to disable all existing interfaces: - -- Shutdown WiFi. -- Shutdown mobile data. -- Connect to the newly available Bluetooth device (which has the name of your Pwnagotchi). - - Once connected, you can test: `http://172.26.0.1:8080` -- You can also install bettercap's UI (`sudo bettercap` then `ui.update`) - - You'll need to change the http caplets to change `127.0.0.1` to `0.0.0.0`. -- You can connect to the shell with a terminal emulator ... - -Happy tweaking. diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index b998af0..0000000 --- a/docs/index.md +++ /dev/null @@ -1,23 +0,0 @@ -# Documentation - -- [About the Project](https://github.com/evilsocket/pwnagotchi/blob/master/docs/about.md) -- [How to Install](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md) -- [Configuration](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md) -- [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md) -- [Plugins](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) -- [Development](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) -- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) -- [Community Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) - -## Links - -  | Official Links ----------|------- -Slack | [pwnagotchi.slack.com](https://pwnagotchi.herokuapp.com) -Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) -Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) -Website | [pwnagotchi.ai](https://pwnagotchi.ai/) - -## License - -`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It's released under the GPL3 license. diff --git a/docs/install.md b/docs/install.md deleted file mode 100644 index 9f143e8..0000000 --- a/docs/install.md +++ /dev/null @@ -1,60 +0,0 @@ -# Installation - -The project has been developed to run on a Raspberry Pi 0 W configured as an [USB Ethernet gadget](https://learn.adafruit.com/turning-your-raspberry-pi-zero-into-a-usb-gadget/ethernet-gadget) device in order to connect to it via USB. However, given the proper configuration tweaks, any GNU/Linux computer with a WiFi interface that supports monitor mode could be used. - -**An important note about the AI:** a network trained with a specific WiFi interface will ONLY work with another interface if it supports the *exact same* WiFi channels of the first one. For instance, you CANNOT use a neural network trained on a Raspberry Pi Zero W (that only supports 2.4Ghz channels) with a 5Ghz antenna; you will need to train one from scratch for those channels. - -## Required Hardware - -- [Raspberry Pi Zero W](https://www.raspberrypi.org/products/raspberry-pi-zero-w/).† -- A micro SD card, 8GB recommended, **preferably of good quality and speed**. -- A decent power bank (with 1500 mAh you get ~2 hours with AI on). -- One of the supported displays (optional). - -† Many users have gotten Pwnagotchi running on other types of Raspberry Pi, but the RPi0W is the "vanilla" hardware config for Pwnagotchi. - -### Display - -The display is an optional component as the UI is also rendered via a web interface available via the USB cable. If you connect to `usb0` (by using the data port on the unit) and point your browser to the web ui (see `config.yml`), your unit can work in "headless mode". - -If, instead, you want to fully enjoy walking around and literally looking at your unit's face, the supported display models are: - -- [Waveshare eInk Display (both V1 and V2)](https://www.waveshare.com/2.13inch-e-paper-hat.htm) - - [Product comparison](https://www.waveshare.com/4.3inch-e-paper.htm) (scroll down to `Selection Guide`) - - [GitHub](https://github.com/waveshare/e-Paper/tree/master/RaspberryPi%26JetsonNano/python) -- [Pimoroni Inky pHAT](https://shop.pimoroni.com/products/inky-phat) - - [Product page](https://shop.pimoroni.com/products/inky-phat) - - [GitHub](https://github.com/pimoroni/inky) -- [PaPiRus eInk Screen](https://uk.pi-supply.com/products/papirus-zero-epaper-screen-phat-pi-zero) - -Needless to say, we are always happy to receive pull requests adding support for new models. - -**One thing to note:** Not all displays are created equally! TFT displays, for example, work similar to an HDMI display, and they are NOT supported. Currently, all the officially-supported displays are I2C displays. If you are still interested in using unsupported displays, you may be able to find a community-submitted hack in the [Screens](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md#screens) section of the [Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) page. We are not responsible for anything you break by trying to use any display that is not officially supported by the development team! - -#### Color vs. Black & White displays - -Some of the supported displays support both **Black & White** and **Colored** versions. One common question whether there are meaningful differences between the two. There are: -- Color displays have a much slower refresh rate. In some cases, it can take up to 15 seconds; if slow refresh rates are something that you want to avoid, we recommend you use B&W displays. -- The 3-color 2.13" Waveshare displays have a slightly smaller pixel layout (104x212) compared to their B&W counterparts (122x250). - -#### Recommendations -- Avoid the Waveshare eInk **3-color** display. The refresh time is 15 seconds. -- Avoid the Pimoroni Inky pHAT **v1.** They're discontinued due to a faulty hardware part source used in manufacturing that resulted in high failure rates. -- Many users seem to prefer the Inky pHATs. There are two primary reasons: - - The Inkys feature better documentation and SDK support. - - Many Waveshare resellers do not disclose the version of the Waveshare boards they are selling (v1 vs v2), and the type they are selling can be fairly unclear (i.e., Waveshare 2.13 vs 2.13 B vs. 2.13C, and so on.) - -## Flashing an Image - -The easiest way to create a new Pwnagotchi is downloading the latest stable image from [our release page](https://github.com/evilsocket/pwnagotchi/releases) and write it to your SD card. You will need to use an image writing tool to install the image you have downloaded on your SD card. - -[balenaEtcher](https://www.balena.io/etcher/) is a graphical SD card writing tool that works on Mac OS, Linux and Windows, and is the easiest option for most users. balenaEtcher also supports writing images directly from the zip file, without any unzipping required. To write your image with balenaEtcher: - -- Download the latest [Pwnagotchi .img file](https://github.com/evilsocket/pwnagotchi/releases). -- Download [balenaEtcher](https://www.balena.io/etcher/) and install it. -- Connect an SD card reader with the SD card inside. -- Open balenaEtcher and select from your hard drive the Raspberry Pi .img or .zip file you wish to write to the SD card. -- Select the SD card you wish to write your image to. -- Review your selections and click 'Flash!' to begin writing data to the SD card. - -Your SD card is now ready for the first boot! diff --git a/docs/plugins.md b/docs/plugins.md deleted file mode 100644 index 0038df9..0000000 --- a/docs/plugins.md +++ /dev/null @@ -1,56 +0,0 @@ -# Plugins - -Pwnagotchi has a simple plugins system that you can use to customize your unit and its behaviour. You can place your plugins anywhere -as python files and then edit the `config.yml` file (`main.plugins` value) to point to their containing folder. Check the [plugins folder](https://github.com/evilsocket/pwnagotchi/tree/master/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/) for a list of default plugins and all the callbacks that you can define for your own customizations. - -Here's as an example the GPS plugin: - -```python -__author__ = 'evilsocket@gmail.com' -__version__ = '1.0.0' -__name__ = 'gps' -__license__ = 'GPL3' -__description__ = 'Save GPS coordinates whenever an handshake is captured.' -__enabled__ = True # set to false if you just don't use GPS - -import core -import json -import os - -device = '/dev/ttyUSB0' -speed = 19200 -running = False - - -def on_loaded(): - logging.info("GPS plugin loaded for %s" % device) - - -def on_ready(agent): - global running - - if os.path.exists(device): - logging.info("enabling GPS bettercap's module for %s" % device) - try: - agent.run('gps off') - except: - pass - - agent.run('set gps.device %s' % device) - agent.run('set gps.speed %d' % speed) - agent.run('gps on') - running = True - else: - logging.info("no GPS detected") - - -def on_handshake(agent, filename, access_point, client_station): - if running: - info = agent.session() - gps = info['gps'] - gps_filename = filename.replace('.pcap', '.gps.json') - - logging.info("saving GPS to %s (%s)" % (gps_filename, gps)) - with open(gps_filename, 'w+t') as fp: - json.dump(gps, fp) -``` diff --git a/docs/usage.md b/docs/usage.md deleted file mode 100644 index fff0fb1..0000000 --- a/docs/usage.md +++ /dev/null @@ -1,149 +0,0 @@ -# Usage - -## User Interface - -The UI is available either via display if installed, or via http://pwnagotchi.local:8080/ if you connect to the unit via `usb0` and set a static address on the network interface (change `pwnagotchi` with the hostname of your unit). - -![ui](https://i.imgur.com/c7xh4hN.png) - -* **CH**: Current channel the unit is operating on or `*` when hopping on all channels. -* **APS**: Number of access points on the current channel and total visible access points. -* **UP**: Time since the unit has been activated. -* **PWND**: Number of handshakes captured in this session and number of unique networks we own at least one handshake of, from the beginning. -* **MODE**: - * **AUTO:** This indicates that the Pwnagotchi algorithm is running in AUTOMATIC mode, with AI disabled (or still loading); it disappears once the AI dependencies have been bootstrapped and the neural network has finished loading. - * **MANU:** This appears when the unit is running in MANUAL mode. -* **FRIEND:** If another unit is nearby, its presence will be indicated here. If more than one unit is nearby, only one—whichever has the stronger signal strength—will be displayed. - -## Training the AI - -At its core Pwnagotchi is a very simple creature: we could summarize its main algorithm as: - -```python -# main loop -while True: - # ask bettercap for all visible access points and their clients - aps = get_all_visible_access_points() - # loop each AP - for ap in aps: - # send an association frame in order to grab the PMKID - send_assoc(ap) - # loop each client station of the AP - for client in ap.clients: - # deauthenticate the client to get its half or full handshake - deauthenticate(client) - - wait_for_loot() -``` - -Despite its simplicity, this logic is controlled by several parameters that regulate the wait times, the timeouts, on which channels to hop and so on. - -From `config.yml`: - -```yaml -personality: - # advertise our presence - advertise: true - # perform a deauthentication attack to client stations in order to get full or half handshakes - deauth: true - # send association frames to APs in order to get the PMKID - associate: true - # list of channels to recon on, or empty for all channels - channels: [] - # minimum WiFi signal strength in dBm - min_rssi: -200 - # number of seconds for wifi.ap.ttl - ap_ttl: 120 - # number of seconds for wifi.sta.ttl - sta_ttl: 300 - # time in seconds to wait during channel recon - recon_time: 30 - # number of inactive epochs after which recon_time gets multiplied by recon_inactive_multiplier - max_inactive_scale: 2 - # if more than max_inactive_scale epochs are inactive, recon_time *= recon_inactive_multiplier - recon_inactive_multiplier: 2 - # time in seconds to wait during channel hopping if activity has been performed - hop_recon_time: 10 - # time in seconds to wait during channel hopping if no activity has been performed - min_recon_time: 5 - # maximum amount of deauths/associations per BSSID per session - max_interactions: 3 - # maximum amount of misses before considering the data stale and triggering a new recon - max_misses_for_recon: 5 - # number of active epochs that triggers the excited state - excited_num_epochs: 10 - # number of inactive epochs that triggers the bored state - bored_num_epochs: 15 - # number of inactive epochs that triggers the sad state - sad_num_epochs: 25 -``` - -There is no optimal set of parameters for every situation: when the unit is moving (during a walk for instance) smaller timeouts and RSSI thresholds might be preferred in order to quickly remove routers that are not in range anymore, while when stationary in high density areas (like an office) other parameters might be better. The role of the AI is to observe what's going on at the WiFi level, and adjust those parameters in order to maximize the cumulative reward of that loop / epoch. - -## Reward Function - -After each iteration of the main loop (an `epoch`), the reward, a score that represents how well the parameters performed, is computed as (an excerpt from `pwnagotchi/ai/reward.py`): - -```python -# state contains the information of the last epoch -# epoch_n is the number of the last epoch -tot_epochs = epoch_n + 1e-20 # 1e-20 is added to avoid a division by 0 -tot_interactions = max(state['num_deauths'] + state['num_associations'], state['num_handshakes']) + 1e-20 -tot_channels = wifi.NumChannels - -# ideally, for each interaction we would have an handshake -h = state['num_handshakes'] / tot_interactions -# small positive rewards the more active epochs we have -a = .2 * (state['active_for_epochs'] / tot_epochs) -# make sure we keep hopping on the widest channel spectrum -c = .1 * (state['num_hops'] / tot_channels) -# small negative reward if we don't see aps for a while -b = -.3 * (state['blind_for_epochs'] / tot_epochs) -# small negative reward if we interact with things that are not in range anymore -m = -.3 * (state['missed_interactions'] / tot_interactions) -# small negative reward for inactive epochs -i = -.2 * (state['inactive_for_epochs'] / tot_epochs) - -reward = h + a + c + b + i + m -``` - -By maximizing this reward value, the AI learns over time to find the set of parameters that better perform with the current environmental conditions. - -## BetterCAP's Web UI - -Moreover, given that the unit is running bettercap with API and Web UI, you'll be able to use the unit as a WiFi penetration testing portable station by accessing `http://pwnagotchi.local/`. - -![webui](https://raw.githubusercontent.com/bettercap/media/master/ui-events.png) - -## Update your Pwnagotchi - -You can use the `scripts/update_pwnagotchi.sh` script to update to the most recent version of pwnagotchi. - -```shell -usage: ./update_pwnagitchi.sh [OPTIONS] - - Options: - -v # Version to update to, can be a branch or commit. (default: master) - -u # Url to clone from. (default: https://github.com/evilsocket/pwnagotchi) - -m # Mode to restart to. (Supported: auto manual; default: auto) - -b # Backup the current pwnagotchi config. - -r # Restore the current pwnagotchi config. -b will be enabled. - -h # Shows this help. Shows this help. - -``` - -## Backup your Pwnagotchi - -You can use the `scripts/backup.sh` script to backup the important files of your unit. - -```shell -usage: ./scripts/backup.sh HOSTNAME backup.zip -``` - -## Random Info - -* **On a rpi0w, it'll take approximately 30 minutes to load the AI**. -* `/var/log/pwnagotchi.log` is your friend. -* if connected to a laptop via usb data port, with internet connectivity shared, magic things will happen. -* checkout the `ui.video` section of the `config.yml` - if you don't want to use a display, you can connect to it with the browser and a cable. -* If you get `[FAILED] Failed to start Remount Root and Kernel File Systems.` while booting pwnagotchi, make sure the `PARTUUID`s for `rootfs` and `boot` partitions are the same in `/etc/fstab`. Use `sudo blkid` to find those values when you are using `create_sibling.sh`. From 0577972867a06c51a1aee032fcf42f8d8c581c72 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 18:42:57 +0200 Subject: [PATCH 65/69] new: if more than one peer are present, display their number (closes #201) --- pwnagotchi/agent.py | 3 ++- pwnagotchi/ui/view.py | 10 ++++++++-- scripts/preview.py | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pwnagotchi/agent.py b/pwnagotchi/agent.py index 0e89763..71e87f5 100644 --- a/pwnagotchi/agent.py +++ b/pwnagotchi/agent.py @@ -296,7 +296,8 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer): def _update_peers(self): peer = self._advertiser.closest_peer() - self._view.set_closest_peer(peer) + tot = self._advertiser.num_peers() + self._view.set_closest_peer(peer, tot) def _save_recovery_data(self): logging.warning("writing recovery data to %s ..." % RECOVERY_DATA_FILE) diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index 5847662..3289eb1 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -168,7 +168,7 @@ class View(object): self.set('aps', "%d" % log.associated) self.set('shakes', '%d (%s)' % (log.handshakes, \ utils.total_unique_handshakes(self._config['bettercap']['handshakes']))) - self.set_closest_peer(log.last_peer) + self.set_closest_peer(log.last_peer, log.peers) def is_normal(self): return self._state.get('face') not in ( @@ -188,7 +188,7 @@ class View(object): self.set('status', self._voice.on_normal()) self.update() - def set_closest_peer(self, peer): + def set_closest_peer(self, peer, num_total): if peer is None: self.set('friend_face', None) self.set('friend_name', None) @@ -207,6 +207,12 @@ class View(object): name += '│' * (4 - num_bars) name += ' %s %d (%d)' % (peer.name(), peer.pwnd_run(), peer.pwnd_total()) + if num_total > 1: + if num_total > 9000: + name += ' of over 9000' + else: + name += ' of %d' % num_total + self.set('friend_face', peer.face()) self.set('friend_name', name) self.update() diff --git a/scripts/preview.py b/scripts/preview.py index 06be3c6..e0ef7c4 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -122,7 +122,7 @@ def main(): for display in list_of_displays: emotions = list() if args.showpeer: - display.set_closest_peer(DummyPeer()) + display.set_closest_peer(DummyPeer(), 10) display.on_starting() display.update() emotions.append(display.get_image()) From 4f694ddb83244f094a6a0c5a0aa40c3b329ea50d Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 7 Oct 2019 19:42:29 +0200 Subject: [PATCH 66/69] new: added clean shutdown button to the web ui (closes #161) --- pwnagotchi/__init__.py | 14 ++++++++++++++ pwnagotchi/ui/display.py | 27 ++++++++++++++++++++++++--- pwnagotchi/ui/view.py | 16 +++++++++++++++- pwnagotchi/voice.py | 5 +++++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/pwnagotchi/__init__.py b/pwnagotchi/__init__.py index ff43494..c343bb3 100644 --- a/pwnagotchi/__init__.py +++ b/pwnagotchi/__init__.py @@ -1,4 +1,8 @@ import subprocess +import os +import logging +import time +import pwnagotchi.ui.view as view version = '1.0.0a' @@ -46,3 +50,13 @@ def temperature(celsius=True): temp = int(fp.read().strip()) c = int(temp / 1000) return c if celsius else ((c * (9 / 5)) + 32) + + +def shutdown(): + logging.warning("shutting down ...") + if view.ROOT: + view.ROOT.on_shutdown() + # give it some time to refresh the ui + time.sleep(5) + os.system("sync") + os.system("halt") diff --git a/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py index 76e9a24..ffefa18 100644 --- a/pwnagotchi/ui/display.py +++ b/pwnagotchi/ui/display.py @@ -15,11 +15,29 @@ class VideoHandler(BaseHTTPRequestHandler): _lock = Lock() _index = """ - %s + %s + - - +
+ +
+
+
+ +
+
+