diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index bc534052..00000000 --- a/.editorconfig +++ /dev/null @@ -1,31 +0,0 @@ -# EditorConfig helps developers define and maintain consistent -# coding styles between different editors and IDEs -# editorconfig.org - -root = true - -[*] -indent_style = space -indent_size = 2 -charset = utf-8 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true - -[Makefile] -indent_style = tab - -[*.py] -indent_style = space -indent_size = 4 - -[*.json] -insert_final_newline = ignore - -[*.js] -indent_style = ignore -insert_final_newline = ignore - -[*.{md,txt}] -indent_size = 4 -trim_trailing_whitespace = false diff --git a/README.md b/README.md index 88d92cc3..04365250 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,153 @@ -# Pwnagotchi +# Pwnagotchi Torch installation +I assume you have a new image of Raspberry Pi OS lite 64-bit flashed to a micro sd-card. -

- Release - Software License - Contributors - Travis - Slack - Forum - follow on Twitter -

+# APT hold packages +``` +sudo apt-mark hold raspberrypi-kernel +sudo apt install raspberrypi-kernel-headers +sudo apt-mark hold raspberrypi-kernel-headers +sudo apt -y update +sudo apt -y upgrade +``` +# Downgrade libpcap +``` +cd ~ +wget http://ports.ubuntu.com/pool/main/libp/libpcap/libpcap0.8_1.9.1-3_arm64.deb +wget http://ports.ubuntu.com/pool/main/libp/libpcap/libpcap0.8-dev_1.9.1-3_arm64.deb +wget http://ports.ubuntu.com/pool/main/libp/libpcap/libpcap-dev_1.9.1-3_arm64.deb +sudo apt -y install ./libpcap*.deb --allow-downgrades +sudo apt-mark hold libpcap-dev libpcap0.8 libpcap0.8-dev +``` -[Pwnagotchi](https://pwnagotchi.ai/) 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 Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication 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. +# Set-up dependencies +``` +cat > /tmp/dependencies << EOF +time +rsync +vim +wget +screen +git +build-essential +dkms +python3-pip +python3-smbus +unzip +gawk +libopenmpi-dev +libatlas-base-dev +libelf-dev +libopenjp2-7 +libtiff5 +tcpdump +lsof +libgstreamer1.0-0 +libavcodec58 +libavformat58 +libswscale5 +libusb-1.0-0-dev +libnetfilter-queue-dev +libopenmpi3 +dphys-swapfile +libdbus-1-dev +libdbus-glib-1-dev +liblapack-dev +libhdf5-dev +libc-ares-dev +libeigen3-dev +fonts-dejavu +fonts-dejavu-core +fonts-dejavu-extra +python3-pil +python3-smbus +libfuse-dev +libatlas-base-dev +libopenblas-dev +libblas-dev +bc +libgl1-mesa-glx +libncursesw5-dev +libssl-dev +libsqlite3-dev +tk-dev +libgdbm-dev +libc6-dev +libbz2-dev +libffi-dev +zlib1g-dev +fonts-freefont-ttf +fbi +python3-flask +python3-flask-cors +python3-flaskext.wtf +EOF -![ui](https://i.imgur.com/X68GXrn.png) +cat /tmp/dependencies | xargs -n5 sudo apt install -y +``` -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 parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.toml) over time to **get better at pwning Wi-Fi things to** in the environments you expose it to. +# Install Bettercap +``` +cd ~ +git clone https://github.com/jayofelony/bettercap.git +cd bettercap +make all +sudo make install +sudo bettercap -eval "caplets.update; ui.update; quit" +``` -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://www.pwnagotchi.ai/usage/#training-the-ai) doc.) +# Install PwnGrid +``` +cd ~ +git clone https://github.com/jayofelony/pwngrid.git +cd bettercap +make +sudo make install +sudo pwngrid -generate -keys /etc/pwnagotchi +``` -**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi 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://www.pwnagotchi.ai/usage/#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but ** listen to your Pwnagotchi when it tells you it's boring!** Bring it into novel Wi-Fi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) +# Install Pwnagotchi-Torch +``` +cd ~ +git clone -b pwnagotchi-torch https://github.com/jayofelony/pwnagotchi.git +cd pwnagotchi +for i in $(grep -v ^# requirements.txt | cut -d \> -f 1); do sudo apt -y install python3-$i; done +sudo pip install -r requirements.txt +sudo pip install --upgrade numpy +sudo ln -s `pwd`/bin/pwnagotchi /usr/local/bin +sudo ln -s `pwd`/pwnagotchi /usr/local/lib/python3.9/dist-packages/pwnagotchi +sudo mkdir -p /usr/local/share/pwnagotchi/custom-plugins -Multiple units within close physical proximity can "talk" to each other, advertising their 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. -## Documentation +sudo bash -c 'cat > /etc/pwnagotchi/config.toml' << EOF +main.name = "new_ai_CHANGEME" +main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins" -https://www.pwnagotchi.ai +main.plugins.led.enabled = false -## Links +personality.deauth = false -|   | Official Links | -|-----------|-------------------------------------------------------------| -| Website | [pwnagotchi.ai](https://pwnagotchi.ai/) | -| Forum | [community.pwnagotchi.ai](https://community.pwnagotchi.ai/) | -| Slack | [pwnagotchi.slack.com](https://invite.pwnagotchi.ai/) | -| Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) | -| Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) | +ui.display.enabled = false +ui.web.username = "pwny" +ui.web.password = "pwny1234" +EOF -## License +for file in `find builder/data -type f`; do + dest=${file#builder/data} + if [ -s $dest ]; then + echo File $dest exists. Skipping + else + echo Copying $file to $dest + sudo cp -p $file $dest + fi +done +``` -`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It is released under the GPL3 license. +# Enable all services and reboot +``` +sudo systemctl enable bettercap +sudo systemctl enable pwngrid-peer +sudo systemctl enable pwnagotchi + +sudo sync +sudo reboot +``` \ No newline at end of file diff --git a/pwnagotchi/ai/__init__.py b/pwnagotchi/ai/__init__.py index 54933423..420daf6d 100644 --- a/pwnagotchi/ai/__init__.py +++ b/pwnagotchi/ai/__init__.py @@ -18,45 +18,67 @@ def load(config, agent, epoch, from_disk=True): logging.info("[ai] bootstrapping dependencies ...") start = time.time() - from stable_baselines import A2C - logging.debug("[ai] A2C imported in %.2fs" % (time.time() - start)) + SB_BACKEND = "stable_baselines3"; - start = time.time() - from stable_baselines.common.policies import MlpLstmPolicy - logging.debug("[ai] MlpLstmPolicy imported in %.2fs" % (time.time() - start)) + try: + from stable_baselines3 import A2C + logging.debug("[ai] A2C imported in %.2fs" % (time.time() - start)) - start = time.time() - from stable_baselines.common.vec_env import DummyVecEnv - logging.debug("[ai] DummyVecEnv imported in %.2fs" % (time.time() - start)) - - start = time.time() - import pwnagotchi.ai.gym as wrappers - logging.debug("[ai] gym wrapper imported in %.2fs" % (time.time() - start)) - - env = wrappers.Environment(agent, epoch) - env = DummyVecEnv([lambda: env]) - - logging.info("[ai] creating model ...") - - start = time.time() - a2c = A2C(MlpLstmPolicy, env, **config['params']) - logging.debug("[ai] A2C created in %.2fs" % (time.time() - start)) - - if from_disk and os.path.exists(config['path']): - logging.info("[ai] loading %s ..." % config['path']) start = time.time() - a2c.load(config['path'], env) - logging.debug("[ai] A2C loaded in %.2fs" % (time.time() - start)) - else: - logging.info("[ai] model created:") - for key, value in config['params'].items(): - logging.info(" %s: %s" % (key, value)) + from stable_baselines3.a2c import MlpPolicy + logging.debug("[ai] MlpPolicy imported in %.2fs" % (time.time() - start)) + SB_A2C_POLICY = MlpPolicy - logging.debug("[ai] total loading time is %.2fs" % (time.time() - begin)) + start = time.time() + from stable_baselines3.common.vec_env import DummyVecEnv + logging.debug("[ai] DummyVecEnv imported in %.2fs" % (time.time() - start)) - return a2c - except Exception as e: - logging.exception("error while starting AI (%s)", e) + except Exception as e: + logging.debug("[ai] stable_baselines3 not accessible. Trying stable_baselines") - logging.warning("[ai] AI not loaded!") - return False + from stable_baselines import A2C + logging.debug("[ai] A2C imported in %.2fs" % (time.time() - start)) + SB_BACKEND = "stable_baselines" + + start = time.time() + from stable_baselines.common.policies import MlpLstmPolicy + logging.debug("[ai] MlpLstmPolicy imported in %.2fs" % (time.time() - start)) + SB_A2C_POLICY = MlpLstmPolicy + + start = time.time() + from stable_baselines.common.vec_env import DummyVecEnv + logging.debug("[ai] DummyVecEnv imported in %.2fs" % (time.time() - start)) + + +start = time.time() +import pwnagotchi.ai.gym as wrappers + +logging.debug("[ai] gym wrapper imported in %.2fs" % (time.time() - start)) + +env = wrappers.Environment(agent, epoch) +env = DummyVecEnv([lambda: env]) + +logging.info("[ai] creating model ...") + +start = time.time() +a2c = A2C(SB_A2C_POLICY, env, **config['params']) +logging.debug("[ai] A2C created in %.2fs" % (time.time() - start)) + +if from_disk and os.path.exists(config['path']): + logging.info("[ai] loading %s ..." % config['path']) + start = time.time() + a2c.load(config['path'], env) + logging.debug("[ai] A2C loaded in %.2fs" % (time.time() - start)) +else: + logging.info("[ai] model created:") + for key, value in config['params'].items(): + logging.info(" %s: %s" % (key, value)) + +logging.debug("[ai] total loading time is %.2fs" % (time.time() - begin)) + +return a2c +except Exception as e: +logging.exception("error while starting AI (%s)", e) + +logging.warning("[ai] AI not loaded!") +return False \ No newline at end of file diff --git a/pwnagotchi/ai/gym.py b/pwnagotchi/ai/gym.py index de5c94ce..23eb1c5c 100644 --- a/pwnagotchi/ai/gym.py +++ b/pwnagotchi/ai/gym.py @@ -140,10 +140,11 @@ class Environment(gym.Env): logging.info("[ai] --- training epoch %d/%d ---" % (self._epoch_num, self._agent.training_epochs())) logging.info("[ai] REWARD: %f" % self.last['reward']) - logging.debug("[ai] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items())) + logging.debug( + "[ai] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items())) logging.info("[ai] observation:") for name, value in self.last['state'].items(): if 'histogram' in name: logging.info(" %s" % name.replace('_histogram', '')) - self._render_histogram(value) + self._render_histogram(value) \ No newline at end of file diff --git a/pwnagotchi/defaults.toml b/pwnagotchi/defaults.toml index c554968d..95eaa36d 100644 --- a/pwnagotchi/defaults.toml +++ b/pwnagotchi/defaults.toml @@ -137,10 +137,7 @@ ai.params.vf_coef = 0.25 ai.params.ent_coef = 0.01 ai.params.max_grad_norm = 0.5 ai.params.learning_rate = 0.001 -ai.params.alpha = 0.99 -ai.params.epsilon = 0.00001 ai.params.verbose = 1 -ai.params.lr_schedule = "constant" personality.advertise = true personality.deauth = true diff --git a/requirements.txt b/requirements.txt index 33a43795..600a4e08 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,28 +1,21 @@ -pycryptodome==3.9.4 -requests==2.21.0 -PyYAML==5.3.1 -scapy==2.4.3 -gym==0.14.0 -scipy==1.3.1 -stable-baselines==2.7.0 -tensorflow==1.15.0 -tensorflow-estimator==1.15.1 -tweepy==3.7.0 -file-read-backwards==2.0.0 -numpy==1.20.2 -inky==1.2.0 -smbus2==0.3.0 -Pillow==5.4.1 -spidev==3.4 -gast==0.2.2 -itsdangerous==1.1.0 -markupsafe==1.1.1 -Werkzeug==0.14.1 -flask==1.0.2 -flask-cors==3.0.7 -flask-wtf==0.14.3 -dbus-python==1.2.12 -toml==0.10.0 -python-dateutil==2.8.1 -websockets==8.1 -RPi.GPIO +pycryptodome>=3.9.4 +requests>=2.21.0 +PyYAML>=5.3.1 +scapy>=2.4.3 +tweepy>=3.7.0 +file-read-backwards>=2.0.0 +inky>=1.2.0 +smbus2>=0.3.0 +Pillow>=5.4.1 +spidev>=3.4 +gast>=0.2.2 +flask>=1.0.2 +flask-cors>=3.0.7 +flask-wtf>=0.14.3 +dbus-python>=1.2.12 +toml>=0.10.0 +python-dateutil>=2.8.1 +websockets>=8.1 +torch>=2.0.1 +torchvision>=0.15.2 +stable-baselines3>=1.4.0 \ No newline at end of file