commit
9992d748f1
@ -3,6 +3,7 @@ maintainers:
|
||||
- caquino
|
||||
- dadav
|
||||
- justin-p
|
||||
- hexwaxwing
|
||||
|
||||
features:
|
||||
- comments
|
||||
|
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at pwnagotchi@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
@ -5,6 +5,7 @@
|
||||
<a href="https://github.com/evilsocket/pwnagotchi/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/evilsocket/pwnagotchi.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/evilsocket/pwnagotchi/blob/master/LICENSE.md"><img alt="Software License" src="https://img.shields.io/badge/license-GPL3-brightgreen.svg?style=flat-square"></a>
|
||||
<a href="https://travis-ci.org/evilsocket/pwnagotchi"><img alt="Travis" src="https://img.shields.io/travis/evilsocket/pwnagotchi/master.svg?style=flat-square"></a>
|
||||
<a href="https://pwnagotchi.herokuapp.com/"><img alt="Slack" src="https://pwnagotchi.herokuapp.com/badge.svg"></a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
@ -40,7 +41,4 @@ For hackers to learn reinforcement learning, WiFi networking and have an excuse
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
|
||||
`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.
|
@ -3,28 +3,135 @@
|
||||
- 127.0.0.1
|
||||
become: yes
|
||||
vars:
|
||||
pwn_hostname: "pwnagotchi"
|
||||
pwn_version: "master"
|
||||
pwnagotchi:
|
||||
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
||||
version: "{{ lookup('env', 'PWN_VERSION') | default('master', true) }} "
|
||||
system:
|
||||
boot_options:
|
||||
- "dtoverlay=dwc2"
|
||||
- "dtparam=spi=on"
|
||||
- "dtoverlay=spi1-3cs"
|
||||
- "dtoverlay=pi3-disable-bt"
|
||||
- "dtparam=audio=off"
|
||||
services:
|
||||
enable:
|
||||
- dphys-swapfile.service
|
||||
- getty@ttyGS0.service
|
||||
disable:
|
||||
- apt-daily.timer
|
||||
- apt-daily.service
|
||||
- apt-daily-upgrade.timer
|
||||
- apt-daily-upgrade.service
|
||||
- wpa_supplicant.service
|
||||
- bluetooth.service
|
||||
- triggerhappy.service
|
||||
- ifup@wlan0.service
|
||||
packages:
|
||||
pip:
|
||||
install:
|
||||
- inky
|
||||
- smbus2
|
||||
- absl-py>=0.1.6
|
||||
- enum34
|
||||
- gast==0.2.2
|
||||
- google_pasta
|
||||
- opt_einsum
|
||||
- scapy
|
||||
- gym
|
||||
- keras_applications>=1.0.6
|
||||
- keras_preprocessing>=1.0.5
|
||||
- stable-baselines
|
||||
- file_read_backwards
|
||||
- tensorflow_estimator>=1.14.0,<1.15.0
|
||||
- tensorboard>=1.13.0,<1.14.0
|
||||
apt:
|
||||
remove:
|
||||
- rasberrypi-net-mods
|
||||
- dhcpcd5
|
||||
- triggerhappy
|
||||
- wpa_supplicant
|
||||
- nfs-common
|
||||
install:
|
||||
- vim
|
||||
- screen
|
||||
- golang
|
||||
- git
|
||||
- build-essential
|
||||
- python3-pip
|
||||
- unzip
|
||||
- gawk
|
||||
- libopenmpi-dev
|
||||
- libatlas-base-dev
|
||||
- libjasper-dev
|
||||
- libqtgui4
|
||||
- libqt4-test
|
||||
- libopenjp2-7
|
||||
- tcpdump
|
||||
- lsof
|
||||
- libilmbase23
|
||||
- libopenexr23
|
||||
- libgstreamer1.0-0
|
||||
- libavcodec58
|
||||
- libavformat58
|
||||
- libswscale5
|
||||
- libpcap-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libnetfilter-queue-dev
|
||||
- dphys-swapfile
|
||||
- kalipi-kernel
|
||||
- kalipi-bootloader
|
||||
- kalipi-re4son-firmware
|
||||
- kalipi-kernel-headers
|
||||
- libraspberrypi0
|
||||
- libraspberrypi-dev
|
||||
- libraspberrypi-doc
|
||||
- libraspberrypi-bin
|
||||
- fonts-dejavu
|
||||
- fonts-dejavu-core
|
||||
- fonts-dejavu-extra
|
||||
- python3-crypto
|
||||
- python3-requests
|
||||
- python3-yaml
|
||||
- python3-smbus
|
||||
- python3-inkyphat
|
||||
- python3-numpy
|
||||
- python3-pil
|
||||
- python3-tweepy
|
||||
- python3-opencv
|
||||
- python3-termcolor
|
||||
- python3-astor
|
||||
- python3-backports.weakref
|
||||
- python3-h5py
|
||||
- python3-six
|
||||
- python3-protobuf
|
||||
- python3-wrapt
|
||||
- python3-wheel
|
||||
- python3-mock
|
||||
- python3-scipy
|
||||
- python3-cloudpickle
|
||||
|
||||
bettercap:
|
||||
query: "assets[?contains(name, 'armv6l')].browser_download_url"
|
||||
|
||||
tasks:
|
||||
|
||||
- name: selected hostname
|
||||
debug:
|
||||
msg: "{{ pwn_hostname }}"
|
||||
msg: "{{ pwnagotchi.hostname }}"
|
||||
|
||||
- name: build version
|
||||
debug:
|
||||
msg: "{{ pwn_version }}"
|
||||
msg: "{{ pwnagotchi.version }}"
|
||||
|
||||
- name: change hostname
|
||||
hostname:
|
||||
name: "{{pwn_hostname}}"
|
||||
name: "{{pwnagotchi.hostname}}"
|
||||
|
||||
- name: add hostname to /etc/hosts
|
||||
lineinfile:
|
||||
dest: /etc/hosts
|
||||
regexp: '^127\.0\.0\.1[ \t]+localhost'
|
||||
line: '127.0.0.1 localhost {{pwn_hostname}} {{pwn_hostname}}.local'
|
||||
line: '127.0.0.1 localhost {{pwnagotchi.hostname}} {{pwnagotchi.hostname}}.local'
|
||||
state: present
|
||||
|
||||
- name: Add re4son-kernel repo key
|
||||
@ -41,88 +148,55 @@
|
||||
apt:
|
||||
update_cache: yes
|
||||
|
||||
- name: remove unecessary apt packages
|
||||
apt:
|
||||
name: "{{ packages.apt.remove }}"
|
||||
state: absent
|
||||
purge: yes
|
||||
|
||||
- name: upgrade apt distro
|
||||
apt:
|
||||
upgrade: dist
|
||||
|
||||
- name: install packages
|
||||
apt:
|
||||
name: "{{ packages }}"
|
||||
name: "{{ packages.apt.install }}"
|
||||
state: present
|
||||
vars:
|
||||
packages:
|
||||
- vim
|
||||
- screen
|
||||
- golang
|
||||
- git
|
||||
- build-essential
|
||||
- python3-pip
|
||||
- gawk
|
||||
- libopenmpi-dev
|
||||
- libatlas-base-dev
|
||||
- libjasper-dev
|
||||
- libqtgui4
|
||||
- libqt4-test
|
||||
- libopenjp2-7
|
||||
- tcpdump
|
||||
- lsof
|
||||
- libilmbase23
|
||||
- libopenexr23
|
||||
- libgstreamer1.0-0
|
||||
- libavcodec58
|
||||
- libavformat58
|
||||
- libswscale5
|
||||
- libpcap-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libnetfilter-queue-dev
|
||||
- dphys-swapfile
|
||||
- kalipi-kernel
|
||||
- kalipi-bootloader
|
||||
- kalipi-re4son-firmware
|
||||
- kalipi-kernel-headers
|
||||
- libraspberrypi0
|
||||
- libraspberrypi-dev
|
||||
- libraspberrypi-doc
|
||||
- libraspberrypi-bin
|
||||
- fonts-dejavu
|
||||
- fonts-dejavu-core
|
||||
- fonts-dejavu-extra
|
||||
|
||||
- name: configure dphys-swapfile
|
||||
file:
|
||||
path: /etc/dphys-swapfile
|
||||
content: "CONF_SWAPSIZE=1024"
|
||||
|
||||
- name: disable unecessary services
|
||||
systemd:
|
||||
name: "{{services}}"
|
||||
state: stopped
|
||||
enabled: no
|
||||
vars:
|
||||
services:
|
||||
- apt-daily.timer
|
||||
- apt-daily.service
|
||||
- apt-daily-upgrade.timer
|
||||
- apt-daily-upgrade.service
|
||||
- bluetooth.service
|
||||
- triggerhappy.service
|
||||
- name: acquire python3 pip target
|
||||
command: "python3 -c 'import sys;print(sys.path.pop())'"
|
||||
register: pip_target
|
||||
|
||||
- name: enable dphys-swapfile service
|
||||
systemd:
|
||||
name: dphys-swapfile.service
|
||||
state: started
|
||||
enabled: yes
|
||||
- name: install pip packages
|
||||
pip:
|
||||
name: "{{packages.pip.install}}"
|
||||
extra_args: "--no-deps --extra-index-url=https://www.piwheels.hostedpi.com/simple/ --prefer-binary --no-cache-dir --platform=armv6l --target={{ pip_target.stdout }}"
|
||||
|
||||
- name: build bettercap
|
||||
command: go get -u github.com/bettercap/bettercap
|
||||
environment:
|
||||
GOPATH: /root/go
|
||||
GOROOT: /usr/lib/go
|
||||
- name: install grpcio
|
||||
command: "pip3 install --no-deps --extra-index-url=https://www.piwheels.hostedpi.com/simple/ --no-cache-dir --prefer-binary --platform=armv6l --only-binary=:all: --target={{ pip_target.stdout }} https://www.piwheels.hostedpi.com/simple/grpcio/grpcio-1.24.1-cp37-cp37m-linux_armv6l.whl"
|
||||
|
||||
- name: install bettercap
|
||||
copy:
|
||||
src: /root/go/bin/bettercap
|
||||
dest: /usr/bin/bettercap
|
||||
- name: install tensorflow
|
||||
command: "pip3 install --no-deps --extra-index-url=https://www.piwheels.hostedpi.com/simple/ --no-cache-dir --prefer-binary --platform=armv6l --only-binary=:all: --target={{ pip_target.stdout }} https://www.piwheels.org/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl"
|
||||
|
||||
- name: fetch bettercap release information
|
||||
uri:
|
||||
url: https://api.github.com/repos/bettercap/bettercap/releases/latest
|
||||
return_content: yes
|
||||
register: bettercap_release
|
||||
|
||||
- name: download and install bettercap
|
||||
unarchive:
|
||||
src: "{{ bettercap_release.content | from_json | json_query(bettercap.query) | first }}"
|
||||
dest: /usr/bin
|
||||
remote_src: yes
|
||||
exclude:
|
||||
- README.md
|
||||
- LICENSE.md
|
||||
mode: 0755
|
||||
|
||||
- name: clone bettercap caplets
|
||||
@ -151,10 +225,6 @@
|
||||
path: /tmp/pwnagotchi
|
||||
state: absent
|
||||
|
||||
- name: install python modules
|
||||
pip:
|
||||
requirements: /root/pwnagotchi/scripts/requirements.txt
|
||||
|
||||
- name: create cpuusage script
|
||||
copy:
|
||||
dest: /usr/bin/cpuusage
|
||||
@ -249,11 +319,7 @@
|
||||
insertafter: EOF
|
||||
line: '{{ item }}'
|
||||
with_items:
|
||||
- "dtoverlay=dwc2"
|
||||
- "dtparam=spi=on"
|
||||
- "dtoverlay=spi1-3cs"
|
||||
- "dtoverlay=pi3-disable-bt"
|
||||
- "dtparam=audio=off"
|
||||
- "{{system.boot_options}}"
|
||||
|
||||
- name: change root partition
|
||||
replace:
|
||||
@ -269,7 +335,7 @@
|
||||
state: present
|
||||
backup: no
|
||||
regexp: '(.*)$'
|
||||
line: '\1 modules-load=dwc2,g_ether'
|
||||
line: '\1 modules-load=dwc2,g_cdc'
|
||||
|
||||
- name: configure ssh
|
||||
lineinfile:
|
||||
@ -281,7 +347,7 @@
|
||||
- name: configure motd
|
||||
copy:
|
||||
dest: /etc/motd
|
||||
content: "(◕‿‿◕) {{pwn_hostname}} (pwnagotchi-{{pwn_version}})"
|
||||
content: "(◕‿‿◕) {{pwnagotchi.hostname}} (pwnagotchi-{{pwnagotchi.version}})"
|
||||
|
||||
- name: clean apt cache
|
||||
apt:
|
||||
@ -291,16 +357,21 @@
|
||||
apt:
|
||||
autoremove: yes
|
||||
|
||||
- name: enable services
|
||||
systemd:
|
||||
name: "{{services.enable}}"
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
- name: disable unecessary services
|
||||
systemd:
|
||||
name: "{{services.disable}}"
|
||||
state: stopped
|
||||
enabled: no
|
||||
|
||||
- name: remove ssh keys
|
||||
file:
|
||||
state: absent
|
||||
path: "{{item}}"
|
||||
with_items:
|
||||
- /etc/ssh/ssh_host_rsa_key
|
||||
- /etc/ssh/ssh_host_rsa_key.pub
|
||||
- /etc/ssh/ssh_host_dsa_key
|
||||
- /etc/ssh/ssh_host_dsa_key.pub
|
||||
- /etc/ssh/ssh_host/ecdsa_key
|
||||
- /etc/ssh/ssh_host/ecdsa_key.pub
|
||||
- /etc/ssh/ssh_host_ed25519_key
|
||||
- /etc/ssh/ssh_host_ed25519_key.pub
|
||||
with_fileglob:
|
||||
- "/etc/ssh/ssh_host*_key*"
|
||||
|
@ -1,4 +1,4 @@
|
||||
## About the Project
|
||||
# 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.
|
||||
|
@ -1,11 +1,10 @@
|
||||
### Connecting to your Pwnagotchi
|
||||
# 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:
|
||||
|
||||
- IP: `10.0.0.2`
|
||||
- IP: `10.0.0.1`
|
||||
- Netmask: `255.255.255.0`
|
||||
- Gateway: `10.0.0.1`
|
||||
- DNS (if required): `8.8.8.8` (or whatever)
|
||||
@ -26,26 +25,34 @@ Moreover, it is recommended that you copy your SSH public key among the unit's a
|
||||
ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2
|
||||
```
|
||||
|
||||
### Configuration
|
||||
## 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 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.
|
||||
|
||||
## Language Selection
|
||||
|
||||
For instance, you can change `main.lang` to one of the supported languages:
|
||||
|
||||
* **english** (default)
|
||||
* german
|
||||
* dutch
|
||||
* greek
|
||||
* macedonian
|
||||
* italian
|
||||
* french
|
||||
- **english** (default)
|
||||
- german
|
||||
- dutch
|
||||
- greek
|
||||
- macedonian
|
||||
- italian
|
||||
- french
|
||||
- russian
|
||||
- swedish
|
||||
|
||||
The 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 complete remove power from the Raspberry and make a clean boot).
|
||||
## 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.
|
||||
|
||||
### Host Connection Share
|
||||
## 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` or
|
||||
`scripts/macos_connection_share.sh` 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.
|
||||
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.
|
||||
|
||||
## 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`.
|
||||
|
11
docs/dev.md
11
docs/dev.md
@ -1,4 +1,4 @@
|
||||
## Software
|
||||
# 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).
|
||||
|
||||
@ -21,6 +21,15 @@ usage: ./scripts/create_sibling.sh [OPTIONS]
|
||||
-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:
|
||||
|
@ -10,4 +10,4 @@ Because Python sucks and TF is huge.
|
||||
|
||||
## Why ...?
|
||||
|
||||
Because!
|
||||
Because!
|
||||
|
38
docs/hacks.md
Normal file
38
docs/hacks.md
Normal file
@ -0,0 +1,38 @@
|
||||
# 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!
|
@ -12,8 +12,9 @@
|
||||
|
||||
- [Project Slack](https://join.slack.com/t/pwnagotchi/shared_invite/enQtNzc4NzY3MDE2OTAzLTg5NmNmNDJiMDM3ZWFkMWUwN2Y5NDk0Y2JlZWZjODlhMmRhNDZiOGMwYjJhM2UzNzA3YjA5NjJmZGY5NGI5NmI)
|
||||
- [Project Twitter](https://twitter.com/pwnagotchi)
|
||||
- [Project Subreddit](https://www.reddit.com/r/pwnagotchi/)
|
||||
- [Project Website](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.
|
||||
`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.
|
||||
|
@ -1,7 +1,9 @@
|
||||
# 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.
|
||||
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.
|
||||
|
||||
## Required Hardware
|
||||
|
||||
@ -34,9 +36,7 @@ Color displays have a much slower refresh rate, in some cases it can take up to
|
||||
|
||||
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:
|
||||
[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.
|
||||
@ -45,4 +45,4 @@ without any unzipping required. To write your image with balenaEtcher:
|
||||
- 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!
|
||||
Your SD card is now ready for the first boot!
|
||||
|
@ -1,8 +1,7 @@
|
||||
# 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.
|
||||
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:
|
||||
|
||||
|
121
docs/usage.md
121
docs/usage.md
@ -1,4 +1,6 @@
|
||||
### UI
|
||||
# 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).
|
||||
|
||||
@ -10,14 +12,107 @@ The UI is available either via display if installed, or via http://pwnagotchi.lo
|
||||
* **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.
|
||||
|
||||
### BetterCAP's Web UI
|
||||
## Training the AI
|
||||
|
||||
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/`.
|
||||
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/`.
|
||||
|
||||

|
||||
|
||||
### Update your Pwnagotchi
|
||||
## Update your Pwnagotchi
|
||||
|
||||
You can use the `scripts/update_pwnagotchi.sh` script to update to the most recent version of pwnagotchi.
|
||||
|
||||
@ -34,7 +129,7 @@ usage: ./update_pwnagitchi.sh [OPTIONS]
|
||||
|
||||
```
|
||||
|
||||
### Backup your Pwnagotchi
|
||||
## Backup your Pwnagotchi
|
||||
|
||||
You can use the `scripts/backup.sh` script to backup the important files of your unit.
|
||||
|
||||
@ -42,12 +137,10 @@ You can use the `scripts/backup.sh` script to backup the important files of your
|
||||
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`.
|
||||
## 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`.
|
||||
|
@ -5,7 +5,7 @@
|
||||
set -eu
|
||||
|
||||
REQUIREMENTS=( wget gunzip git dd e2fsck resize2fs parted losetup qemu-system-x86_64 )
|
||||
DEBREQUIREMENTS=( wget gzip git parted qemu-system-x86 qemu-user-static bmap-tools )
|
||||
DEBREQUIREMENTS=( wget gzip git parted qemu-system-x86 qemu-user-static )
|
||||
REPO_DIR="$(dirname "$(dirname "$(realpath "$0")")")"
|
||||
TMP_DIR="${REPO_DIR}/tmp"
|
||||
MNT_DIR="${TMP_DIR}/mnt"
|
||||
@ -93,15 +93,13 @@ function provide_raspbian() {
|
||||
|
||||
function setup_raspbian(){
|
||||
# Detect the ability to create sparse files
|
||||
if [ "${OPT_SPARSE}" -eq 0 ];
|
||||
then
|
||||
which bmaptool >/dev/null 2>&1
|
||||
if [ $? -eq 0 ];
|
||||
then
|
||||
if [ "${OPT_SPARSE}" -eq 0 ]; then
|
||||
if [ which bmaptool -eq 0 ]; then
|
||||
echo "[!] bmaptool not available, not creating a sparse image"
|
||||
|
||||
else
|
||||
echo "[+] Defaulting to sparse image generation as bmaptool is available"
|
||||
OPT_SPARSE=1
|
||||
else
|
||||
echo "[!] bmaptool not available, not creating a sparse image"
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -335,7 +333,13 @@ fi
|
||||
setup_raspbian
|
||||
provision_raspbian
|
||||
|
||||
echo -e "[+] Congratz, it's a boy (⌐■_■)!"
|
||||
#Make a baby with a random gender, maybe do something fun with this later!
|
||||
gender[0]="boy"
|
||||
gender[1]="girl"
|
||||
|
||||
rand=$[ $RANDOM % 2 ]
|
||||
|
||||
echo -e "[+] Congratz, it's a ${gender[$rand]} (⌐■_■)!"
|
||||
echo -e "[+] One more step: dd if=../${PWNI_OUTPUT} of=<PATH_TO_SDCARD> bs=4M status=progress"
|
||||
|
||||
if [ "${OPT_SPARSE}" -eq 1 ];
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
# nothing to see here, just a utility i use to create new releases ^_^
|
||||
|
||||
VERSION_FILE=$(dirname "${BASH_SOURCE[0]}")/../sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/version.py
|
||||
VERSION_FILE=$(dirname "${BASH_SOURCE[0]}")/../sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py
|
||||
echo "version file is $VERSION_FILE"
|
||||
CURRENT_VERSION=$(cat $VERSION_FILE | grep version | cut -d"'" -f2)
|
||||
TO_UPDATE=(
|
||||
|
@ -89,7 +89,7 @@ fi
|
||||
|
||||
if [ $BACKUPCONFIG -eq 1 ]; then
|
||||
echo "[+] Creating backup of config.yml and hostname references"
|
||||
mv /root/pwnagotchi/config.yml /root/config.bak -f
|
||||
mv /root/pwnagotchi/config.yml /root/config.yml.bak -f
|
||||
mv /etc/hosts /root/hosts.bak -f
|
||||
mv /etc/hostname /root/hostname.bak -f
|
||||
mv /etc/motd /etc/motd.bak -f
|
||||
|
290
scripts/win_connection_share.ps1
Normal file
290
scripts/win_connection_share.ps1
Normal file
@ -0,0 +1,290 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A script that setups Internet Connection Sharing for Pwnagotchi.
|
||||
|
||||
.DESCRIPTION
|
||||
A script that setups Internet Connection Sharing for Pwnagotchi.
|
||||
|
||||
Note: Internet Connection Sharing on Windows can be a bit unstable on between reboots.
|
||||
You might need to run this script occasionally to disable and re-enable Internet Connection Sharing.
|
||||
|
||||
.PARAMETER EnableInternetConnectionSharing
|
||||
Enable Internet Connection Sharing
|
||||
|
||||
.PARAMETER DisableInternetConnectionSharing
|
||||
Disable Internet Connection Sharing
|
||||
|
||||
.PARAMETER SetPwnagotchiSubnet
|
||||
Change the Internet Connection Sharing subnet to the Pwnagotchi subnet. The USB Gadget Interface IP will default to 10.0.0.1.
|
||||
|
||||
.PARAMETER ScopeAddress
|
||||
Custom ScopeAddress (The IP Address of the USB Gadget Interface.)
|
||||
|
||||
.EXAMPLE
|
||||
# Enable Internet Connection Sharing
|
||||
PS C:\> .\win_connection_share -EnableInternetConnectionSharing
|
||||
|
||||
.EXAMPLE
|
||||
# Disable Internet Connection Sharing
|
||||
PS C:\> .\win_connection_share -DisableInternetConnectionSharing
|
||||
|
||||
.EXAMPLE
|
||||
# Change the regkeys of Internet Connection Sharing to the Pwnagotchi Subnet
|
||||
PS C:\> .\win_connection_share -SetPwnagotchiSubnet
|
||||
|
||||
.EXAMPLE
|
||||
# Change the regkeys of Internet Connection Sharing to the Pwnagotchi Subnet with a custom ScopeAddress (The IP Address of the USB Gadget Interface.)
|
||||
PS C:\> .\win_connection_share -SetPwnagotchiSubnet -ScopeAddress 10.0.0.10
|
||||
#>
|
||||
|
||||
#Requires -Version 5
|
||||
#Requires -RunAsAdministrator
|
||||
[Cmdletbinding()]
|
||||
Param (
|
||||
[switch]$EnableInternetConnectionSharing,
|
||||
[switch]$DisableInternetConnectionSharing,
|
||||
[switch]$SetPwnagotchiSubnet,
|
||||
[ipaddress]$ScopeAddress = '10.0.0.1'
|
||||
)
|
||||
|
||||
# Load helper functions
|
||||
Function Create-HNetObjects {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A helper function that does the heavy lifiting with NetCfg.HNetShare
|
||||
|
||||
.DESCRIPTION
|
||||
A helper function that does the heavy lifiting with NetCfg.HNetShare. This returns a PSObject containing the `INetSharingConfigurationForINetConnection` info of 2 Adapters.
|
||||
|
||||
.PARAMETER InternetAdaptor
|
||||
The output of Get-NetAdaptor filtered down to the 'main' uplink interface.
|
||||
|
||||
.PARAMETER RNDISGadget
|
||||
The output of Get-NetAdaptor filtered down to the 'USB Ethernet/RNDIS Gadget' interface.
|
||||
|
||||
.EXAMPLE
|
||||
PS> $HNetObject = Create-HNetObjects
|
||||
PS> $HNetObject
|
||||
RNDISIntConfig InternetIntConfig
|
||||
-------------- -----------------
|
||||
System.__ComObject System.__ComObject
|
||||
#>
|
||||
[Cmdletbinding()]
|
||||
Param (
|
||||
$InternetAdaptor = $(Select-NetAdaptor -Message "Please select your main a ethernet adaptor with internet access that will be used for internet sharing."),
|
||||
$RNDISGadget = $(Select-NetAdaptor -Message "Please select your 'USB Ethernet/RNDIS Gadget' adaptor")
|
||||
)
|
||||
Begin {
|
||||
regsvr32.exe /s hnetcfg.dll
|
||||
$HNetShare = New-Object -ComObject HNetCfg.HNetShare
|
||||
}
|
||||
Process {
|
||||
if ($HNetShare.EnumEveryConnection -ne $null) {
|
||||
$InternetInt = $HNetShare.EnumEveryConnection | Where-Object { $HNetShare.NetConnectionProps.Invoke($_).Name -eq ($InternetAdaptor).Name }
|
||||
$InternetIntConfig = $HNetShare.INetSharingConfigurationForINetConnection.Invoke($InternetInt)
|
||||
$RNDISInt = $HNetShare.EnumEveryConnection | Where-Object { $HNetShare.NetConnectionProps.Invoke($_).Name -eq ($RNDISGadget).Name }
|
||||
$RNDISIntConfig = $HNetShare.INetSharingConfigurationForINetConnection.Invoke($RNDISInt)
|
||||
}
|
||||
}
|
||||
End {
|
||||
Return $(New-Object -TypeName PSObject -Property @{InternetIntConfig=$InternetIntConfig;RNDISIntConfig=$RNDISIntConfig;})
|
||||
}
|
||||
}
|
||||
Function Enable-InternetConnectionSharing {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Enables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface.
|
||||
|
||||
.DESCRIPTION
|
||||
Enables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface.
|
||||
|
||||
.EXAMPLE
|
||||
PS> Enable-InternetConnectionSharing
|
||||
|
||||
#>
|
||||
[Cmdletbinding()]
|
||||
$HNetObject = Create-HNetObjects
|
||||
$HNetObject.InternetIntConfig.EnableSharing(0)
|
||||
$HNetObject.RNDISIntConfig.EnableSharing(1)
|
||||
Write-Output "[x] Enabled Internet Connection Sharing."
|
||||
}
|
||||
Function Disable-InternetConnectionSharing {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Disables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface.
|
||||
|
||||
.DESCRIPTION
|
||||
Disables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface.
|
||||
|
||||
.EXAMPLE
|
||||
PS> Disable-InternetConnectionSharing
|
||||
|
||||
#>
|
||||
[Cmdletbinding()]
|
||||
$HNetObject = $(Create-HNetObjects)
|
||||
$HNetObject.InternetIntConfig.DisableSharing()
|
||||
$HNetObject.RNDISIntConfig.DisableSharing()
|
||||
Write-Output "[x] Disabled Internet Connection Sharing."
|
||||
}
|
||||
Function Test-PwnagotchiSubnet {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Tests the registry for the correct ScopeAddress.
|
||||
|
||||
.DESCRIPTION
|
||||
Tests the registry for the correct ScopeAddress. By default windows uses a 192.168.137.x subnet for Internet Connection Sharing. This value can be changed
|
||||
in the registry.
|
||||
|
||||
.EXAMPLE
|
||||
PS> Test-PwnagotchiSubnet
|
||||
[!] By default Internet Connection Sharing uses a 192.168.137.x subnet. Run Set-PwnagotchiSubnet to ensure you and your little friend are on the same subnet.
|
||||
#>
|
||||
[Cmdletbinding()]
|
||||
$RegKeys = Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters -ErrorAction Stop
|
||||
If ($RegKeys.ScopeAddress -notmatch '10.0.0.') {
|
||||
Write-Error "By default Internet Connection Sharing uses a 192.168.137.x subnet. Run Set-PwnagotchiSubnet to ensure you and your little friend are on the same subnet." -ErrorAction Stop
|
||||
}
|
||||
If ($RegKeys.ScopeAddressBackup -notmatch '10.0.0.') {
|
||||
Write-Error "By default Internet Connection Sharing uses a 192.168.137.x subnet. Run Set-PwnagotchiSubnet to ensure you and your little friend are on the same subnet." -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
Function Set-PwnagotchiSubnet {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Set the registry for the correct ScopeAddress.
|
||||
|
||||
.DESCRIPTION
|
||||
Set the registry for the correct ScopeAddress. By default windows uses a 192.168.137.x subnet for Internet Connection Sharing. This value can be changed
|
||||
in the registry. By default it will be changed to 10.0.0.1
|
||||
|
||||
.PARAMETER ScopeAddress
|
||||
The IP address the USB Gadget interface should use.
|
||||
|
||||
.EXAMPLE
|
||||
Set-PwnagotchiSubnet
|
||||
|
||||
#>
|
||||
[Cmdletbinding()]
|
||||
Param (
|
||||
$ScopeAddress = '10.0.0.1'
|
||||
)
|
||||
Try {
|
||||
[void]([ipaddress]$ScopeAddress)
|
||||
[void]([byte[]] $ScopeAddress.split('.'))
|
||||
} Catch {
|
||||
Write-Error "$ScopeAddress is not a valid IP."
|
||||
}
|
||||
Try {
|
||||
Set-ItemProperty -Name ScopeAddress -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\" -Value $ScopeAddress -ErrorAction Stop
|
||||
Set-ItemProperty -Name ScopeAddressBackup -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\" -Value $ScopeAddress -ErrorAction Stop
|
||||
Write-Warning "The Internet Connection Sharing subnet has been updated. A reboot of windows is required !"
|
||||
} Catch {
|
||||
$PSCmdlet.ThrowTerminatingError($PSItem)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Main Function
|
||||
Function Setup-PwnagotchiNetwork {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Function to setup networking.
|
||||
|
||||
.DESCRIPTION
|
||||
Function to setup networking. Main function calls helpers functions.
|
||||
|
||||
.PARAMETER EnableInternetConnectionSharing
|
||||
Enable Internet Connection Sharing
|
||||
|
||||
.PARAMETER DisableInternetConnectionSharing
|
||||
Disable Internet Connection Sharing
|
||||
|
||||
.PARAMETER SetPwnagotchiSubnet
|
||||
Change the Internet Connection Sharing subnet to the Pwnagotchi. Defaults to 10.0.0.1.
|
||||
|
||||
.PARAMETER ScopeAddress
|
||||
Custom ScopeAddress (the ICS ip address)
|
||||
|
||||
.EXAMPLE
|
||||
PS> Setup-PwnagotchiNetwork -EnableInternetConnectionSharing
|
||||
|
||||
#>
|
||||
|
||||
Param (
|
||||
[switch]$EnableInternetConnectionSharing,
|
||||
[switch]$DisableInternetConnectionSharing,
|
||||
[switch]$SetPwnagotchiSubnet,
|
||||
$ScopeAddress = '10.0.0.1'
|
||||
)
|
||||
Begin {
|
||||
Try {
|
||||
Write-Debug "Begin"
|
||||
$ErrorSplat=@{ErrorAction="stop"}
|
||||
Write-Debug "Testing subnet"
|
||||
Try {
|
||||
Test-PwnagotchiSubnet @ErrorSplat
|
||||
} Catch {
|
||||
If ($SetPwnagotchiSubnet) {
|
||||
Write-Debug "Setting subnet"
|
||||
Set-PwnagotchiSubnet -ScopeAddress $ScopeAddress @ErrorSplat
|
||||
} Else {
|
||||
Write-Error "By default Internet Connection Sharing uses a 192.168.137.x subnet. Run this script with the -SetPwnagotchiSubnet to setup the network." -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
} Catch {
|
||||
$PSCmdlet.ThrowTerminatingError($PSItem)
|
||||
}
|
||||
}
|
||||
Process {
|
||||
Write-Debug "Process"
|
||||
Try {
|
||||
If ($EnableInternetConnectionSharing) {
|
||||
Write-Debug "Enable network Sharing"
|
||||
Enable-InternetConnectionSharing @ErrorSplat
|
||||
} ElseIf ($DisableInternetConnectionSharing) {
|
||||
Write-Debug "Disable network Sharing"
|
||||
Disable-InternetConnectionSharing @ErrorSplat
|
||||
}
|
||||
} Catch {
|
||||
$PSCmdlet.ThrowTerminatingError($PSItem)
|
||||
}
|
||||
}
|
||||
End {
|
||||
Write-Debug "End"
|
||||
Try {
|
||||
# Nothing to return.
|
||||
} Catch {
|
||||
$PSCmdlet.ThrowTerminatingError($PSItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
Function Select-NetAdaptor {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A menu function to select the correct network adaptors.
|
||||
|
||||
.DESCRIPTION
|
||||
A menu function to select the correct network adaptors.
|
||||
|
||||
.PARAMETER Message
|
||||
Message that will be displayed during the question.
|
||||
|
||||
#>
|
||||
|
||||
Param (
|
||||
$Message
|
||||
)
|
||||
$Adaptors = Get-NetAdapter | Where-Object {$_.MediaConnectionState -eq 'Connected'} | Sort-Object LinkSpeed -Descending
|
||||
do {
|
||||
Write-Host $Message
|
||||
$index = 1
|
||||
foreach ($Adaptor in $Adaptors) {
|
||||
Write-Host "[$index] $($Adaptor.Name), $($Adaptor.InterfaceDescription)"
|
||||
$index++
|
||||
}
|
||||
$Selection = Read-Host "Number"
|
||||
} until ($Adaptors[$selection-1])
|
||||
Return $Adaptors[$selection-1]
|
||||
}
|
||||
# Dynamically create params for Setup-PwnagotchiNetwork function based of param input of script.
|
||||
Setup-PwnagotchiNetwork @psBoundParameters
|
@ -1,9 +1,45 @@
|
||||
# main algorithm configuration
|
||||
main:
|
||||
# currently implemented: en (default), de, nl, it
|
||||
# currently implemented: en (default), de, el, fr, it, mk, nl, ru, se
|
||||
lang: en
|
||||
# custom plugins path, if null only default plugins with be loaded
|
||||
plugins: null
|
||||
custom_plugins:
|
||||
# which plugins to load and enable
|
||||
plugins:
|
||||
auto-update:
|
||||
enabled: false
|
||||
interval: 1 # every day
|
||||
auto-backup:
|
||||
enabled: false
|
||||
interval: 1 # every day
|
||||
files:
|
||||
- /root/brain.nn
|
||||
- /root/brain.json
|
||||
- /root/custom.yml
|
||||
- /root/handshakes
|
||||
- /etc/ssh
|
||||
- /etc/hostname
|
||||
- /etc/hosts
|
||||
- /etc/motd
|
||||
- /var/log/pwnagotchi.log
|
||||
commands:
|
||||
- 'tar czf /tmp/backup.tar.gz {files}'
|
||||
- 'scp /tmp/backup.tar.gz pwnagotchi@10.0.0.1:/home/pwnagotchi/backups/backup-$(date +%s).tar.gz'
|
||||
gps:
|
||||
enabled: false
|
||||
twitter:
|
||||
enabled: false
|
||||
consumer_key: aaa
|
||||
consumer_secret: aaa
|
||||
access_token_key: aaa
|
||||
access_token_secret: aaa
|
||||
onlinehashcrack:
|
||||
enabled: false
|
||||
email: ~
|
||||
wpa-sec:
|
||||
enabled: false
|
||||
api_key: ~
|
||||
|
||||
# monitor interface to use
|
||||
iface: mon0
|
||||
# command to run to bring the mon interface up in case it's not up already
|
||||
@ -15,7 +51,9 @@ main:
|
||||
# if true, will not restart the wifi module
|
||||
no_restart: false
|
||||
# access points to ignore
|
||||
whitelist: []
|
||||
whitelist:
|
||||
- EXAMPLE_NETWORK
|
||||
- ANOTHER_EXAMPLE_NETWORK
|
||||
# if not null, filter access points by this regular expression
|
||||
filter: null
|
||||
# cryptographic key for identity
|
||||
@ -108,13 +146,6 @@ ui:
|
||||
address: '10.0.0.2'
|
||||
port: 8080
|
||||
|
||||
# twitter bot data
|
||||
twitter:
|
||||
enabled: false
|
||||
consumer_key: aaa
|
||||
consumer_secret: aaa
|
||||
access_token_key: aaa
|
||||
access_token_secret: aaa
|
||||
|
||||
# bettercap rest api configuration
|
||||
bettercap:
|
||||
@ -143,6 +174,3 @@ bettercap:
|
||||
- wifi.ap.new
|
||||
- wifi.ap.lost
|
||||
- mod.started
|
||||
|
||||
|
||||
|
||||
|
@ -1,48 +0,0 @@
|
||||
import glob
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
|
||||
def secs_to_hhmmss(secs):
|
||||
mins, secs = divmod(secs, 60)
|
||||
hours, mins = divmod(mins, 60)
|
||||
return '%02d:%02d:%02d' % (hours, mins, secs)
|
||||
|
||||
|
||||
def total_unique_handshakes(path):
|
||||
expr = os.path.join(path, "*.pcap")
|
||||
return len(glob.glob(expr))
|
||||
|
||||
|
||||
def iface_address(ifname):
|
||||
output = subprocess.getoutput("/usr/sbin/ifconfig %s" % ifname)
|
||||
for line in output.split("\n"):
|
||||
line = line.strip()
|
||||
if line.startswith("inet "):
|
||||
return line.split(' ')[1].strip()
|
||||
return None
|
||||
|
||||
|
||||
def iface_channels(ifname):
|
||||
channels = []
|
||||
output = subprocess.getoutput("/sbin/iwlist %s freq" % ifname)
|
||||
for line in output.split("\n"):
|
||||
line = line.strip()
|
||||
if line.startswith("Channel "):
|
||||
channels.append(int(line.split()[1]))
|
||||
return channels
|
||||
|
||||
|
||||
def led(on=True):
|
||||
with open('/sys/class/leds/led0/brightness', 'w+t') as fp:
|
||||
fp.write("%d" % (0 if on is True else 1))
|
||||
|
||||
|
||||
def blink(times=1, delay=0.3):
|
||||
for t in range(0, times):
|
||||
led(True)
|
||||
time.sleep(delay)
|
||||
led(False)
|
||||
time.sleep(delay)
|
||||
led(True)
|
@ -1,18 +1,13 @@
|
||||
#!/usr/bin/python3
|
||||
import os
|
||||
import argparse
|
||||
import time
|
||||
import logging
|
||||
|
||||
import yaml
|
||||
|
||||
import pwnagotchi
|
||||
import pwnagotchi.utils as utils
|
||||
import pwnagotchi.version as version
|
||||
import pwnagotchi.plugins as plugins
|
||||
|
||||
from pwnagotchi.log import SessionParser
|
||||
from pwnagotchi.voice import Voice
|
||||
from pwnagotchi.agent import Agent
|
||||
from pwnagotchi.ui.display import Display
|
||||
|
||||
@ -34,119 +29,75 @@ args = parser.parse_args()
|
||||
config = utils.load_config(args)
|
||||
utils.setup_logging(args, config)
|
||||
|
||||
if args.do_clear:
|
||||
print("clearing the display ...")
|
||||
cleardisplay = config['ui']['display']['type']
|
||||
if cleardisplay in ('inkyphat', 'inky'):
|
||||
print("inky display")
|
||||
from inky import InkyPHAT
|
||||
|
||||
epd = InkyPHAT(config['ui']['display']['color'])
|
||||
epd.set_border(InkyPHAT.BLACK)
|
||||
self._render_cb = self._inky_render
|
||||
elif cleardisplay in ('papirus', 'papi'):
|
||||
print("papirus display")
|
||||
from pwnagotchi.ui.papirus.epd import EPD
|
||||
|
||||
os.environ['EPD_SIZE'] = '2.0'
|
||||
epd = EPD()
|
||||
epd.clear()
|
||||
elif cleardisplay in ('waveshare_1', 'ws_1', 'waveshare1', 'ws1'):
|
||||
print("waveshare v1 display")
|
||||
from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
|
||||
|
||||
epd = EPD()
|
||||
epd.init(epd.lut_full_update)
|
||||
epd.Clear(0xFF)
|
||||
elif cleardisplay in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2'):
|
||||
print("waveshare v2 display")
|
||||
from pwnagotchi.ui.waveshare.v2.waveshare import EPD
|
||||
|
||||
epd = EPD()
|
||||
epd.init(epd.FULL_UPDATE)
|
||||
epd.Clear(0xff)
|
||||
else:
|
||||
print("unknown display type %s" % cleardisplay)
|
||||
quit()
|
||||
|
||||
plugins.load_from_path(plugins.default_path)
|
||||
if 'plugins' in config['main'] and config['main']['plugins'] is not None:
|
||||
plugins.load_from_path(config['main']['plugins'])
|
||||
|
||||
plugins.on('loaded')
|
||||
plugins.load(config)
|
||||
|
||||
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
|
||||
agent = Agent(view=display, config=config)
|
||||
|
||||
logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, version.version))
|
||||
# for key, value in config['personality'].items():
|
||||
# logging.info(" %s: %s" % (key, value))
|
||||
logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, pwnagotchi.version))
|
||||
|
||||
for _, plugin in plugins.loaded.items():
|
||||
logging.info("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__))
|
||||
logging.debug("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__))
|
||||
|
||||
if args.do_manual:
|
||||
if args.do_clear:
|
||||
logging.info("clearing the display ...")
|
||||
display.clear()
|
||||
|
||||
elif args.do_manual:
|
||||
logging.info("entering manual mode ...")
|
||||
|
||||
log = SessionParser(config['main']['log'])
|
||||
logging.info("the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
||||
log.duration_human,
|
||||
log.epochs,
|
||||
log.train_epochs,
|
||||
log.avg_reward,
|
||||
log.min_reward,
|
||||
log.max_reward))
|
||||
log = SessionParser(config)
|
||||
logging.info(
|
||||
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
||||
log.duration_human,
|
||||
log.epochs,
|
||||
log.train_epochs,
|
||||
log.avg_reward,
|
||||
log.min_reward,
|
||||
log.max_reward))
|
||||
|
||||
while True:
|
||||
display.on_manual_mode(log)
|
||||
time.sleep(1)
|
||||
|
||||
if Agent.is_connected():
|
||||
plugins.on('internet_available', config, log)
|
||||
plugins.on('internet_available', display, config, log)
|
||||
|
||||
quit()
|
||||
else:
|
||||
logging.info("entering auto mode ...")
|
||||
|
||||
agent.start_ai()
|
||||
agent.setup_events()
|
||||
agent.set_starting()
|
||||
agent.start_monitor_mode()
|
||||
agent.start_event_polling()
|
||||
agent.start()
|
||||
|
||||
# print initial stats
|
||||
agent.next_epoch()
|
||||
while True:
|
||||
try:
|
||||
# recon on all channels
|
||||
agent.recon()
|
||||
# get nearby access points grouped by channel
|
||||
channels = agent.get_access_points_by_channel()
|
||||
# check for free channels to use
|
||||
agent.check_channels(channels)
|
||||
# for each channel
|
||||
for ch, aps in channels:
|
||||
agent.set_channel(ch)
|
||||
|
||||
agent.set_ready()
|
||||
if not agent.is_stale() and agent.any_activity():
|
||||
logging.info("%d access points on channel %d" % (len(aps), ch))
|
||||
|
||||
while True:
|
||||
try:
|
||||
# recon on all channels
|
||||
agent.recon()
|
||||
# get nearby access points grouped by channel
|
||||
channels = agent.get_access_points_by_channel()
|
||||
# check for free channels to use
|
||||
agent.check_channels(channels)
|
||||
# for each channel
|
||||
for ch, aps in channels:
|
||||
agent.set_channel(ch)
|
||||
# for each ap on this channel
|
||||
for ap in aps:
|
||||
# send an association frame in order to get for a PMKID
|
||||
agent.associate(ap)
|
||||
# deauth all client stations in order to get a full handshake
|
||||
for sta in ap['clients']:
|
||||
agent.deauth(ap, sta)
|
||||
|
||||
if not agent.is_stale() and agent.any_activity():
|
||||
logging.info("%d access points on channel %d" % (len(aps), ch))
|
||||
|
||||
# for each ap on this channel
|
||||
for ap in aps:
|
||||
# send an association frame in order to get for a PMKID
|
||||
agent.associate(ap)
|
||||
# deauth all client stations in order to get a full handshake
|
||||
for sta in ap['clients']:
|
||||
agent.deauth(ap, sta)
|
||||
|
||||
# An interesting effect of this:
|
||||
#
|
||||
# From Pwnagotchi's perspective, the more new access points
|
||||
# and / or client stations nearby, the longer one epoch of
|
||||
# its relative time will take ... basically, in Pwnagotchi's universe,
|
||||
# WiFi electromagnetic fields affect time like gravitational fields
|
||||
# affect ours ... neat ^_^
|
||||
agent.next_epoch()
|
||||
except Exception as e:
|
||||
logging.exception("main loop exception")
|
||||
# An interesting effect of this:
|
||||
#
|
||||
# From Pwnagotchi's perspective, the more new access points
|
||||
# and / or client stations nearby, the longer one epoch of
|
||||
# its relative time will take ... basically, in Pwnagotchi's universe,
|
||||
# WiFi electromagnetic fields affect time like gravitational fields
|
||||
# affect ours ... neat ^_^
|
||||
agent.next_epoch()
|
||||
except Exception as e:
|
||||
logging.exception("main loop exception")
|
||||
|
@ -1,5 +1,7 @@
|
||||
import subprocess
|
||||
|
||||
version = '1.0.0plz3'
|
||||
|
||||
_name = None
|
||||
|
||||
|
||||
|
@ -7,10 +7,9 @@ from datetime import datetime
|
||||
import logging
|
||||
import _thread
|
||||
|
||||
import core
|
||||
|
||||
import pwnagotchi.utils as utils
|
||||
import pwnagotchi.plugins as plugins
|
||||
from bettercap.client import Client
|
||||
from pwnagotchi.bettercap import Client
|
||||
from pwnagotchi.mesh.utils import AsyncAdvertiser
|
||||
from pwnagotchi.ai.train import AsyncTrainer
|
||||
|
||||
@ -30,7 +29,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
|
||||
self._started_at = time.time()
|
||||
self._filter = None if config['main']['filter'] is None else re.compile(config['main']['filter'])
|
||||
self._current_channel = 0
|
||||
self._supported_channels = core.iface_channels(config['main']['iface'])
|
||||
self._supported_channels = utils.iface_channels(config['main']['iface'])
|
||||
self._view = view
|
||||
self._access_points = []
|
||||
self._last_pwnd = None
|
||||
@ -130,14 +129,24 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
|
||||
wifi_running = self.is_module_running('wifi')
|
||||
if wifi_running and restart:
|
||||
logging.debug("restarting wifi module ...")
|
||||
self.restart('wifi.recon')
|
||||
self.restart_module('wifi.recon')
|
||||
self.run('wifi.clear')
|
||||
elif not wifi_running:
|
||||
logging.debug("starting wifi module ...")
|
||||
self.start('wifi.recon')
|
||||
self.start_module('wifi.recon')
|
||||
|
||||
self.start_advertising()
|
||||
|
||||
def start(self):
|
||||
self.start_ai()
|
||||
self.setup_events()
|
||||
self.set_starting()
|
||||
self.start_monitor_mode()
|
||||
self.start_event_polling()
|
||||
# print initial stats
|
||||
self.next_epoch()
|
||||
self.set_ready()
|
||||
|
||||
def wait_for(self, t, sleeping=True):
|
||||
plugins.on('sleep' if sleeping else 'wait', self, t)
|
||||
self._view.wait(t, sleeping)
|
||||
@ -242,7 +251,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
def _update_uptime(self, s):
|
||||
secs = time.time() - self._started_at
|
||||
self._view.set('uptime', core.secs_to_hhmmss(secs))
|
||||
self._view.set('uptime', utils.secs_to_hhmmss(secs))
|
||||
self._view.set('epoch', '%04d' % self._epoch.epoch)
|
||||
|
||||
def _update_counters(self):
|
||||
@ -262,7 +271,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
|
||||
if new_shakes > 0:
|
||||
self._epoch.track(handshake=True, inc=new_shakes)
|
||||
|
||||
tot = core.total_unique_handshakes(self._config['bettercap']['handshakes'])
|
||||
tot = utils.total_unique_handshakes(self._config['bettercap']['handshakes'])
|
||||
txt = '%d (%d)' % (len(self._handshakes), tot)
|
||||
|
||||
if self._last_pwnd is not None:
|
||||
@ -275,7 +284,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
def _update_advertisement(self, s):
|
||||
run_handshakes = len(self._handshakes)
|
||||
tot_handshakes = core.total_unique_handshakes(self._config['bettercap']['handshakes'])
|
||||
tot_handshakes = utils.total_unique_handshakes(self._config['bettercap']['handshakes'])
|
||||
started = s['started_at'].split('.')[0]
|
||||
started = datetime.strptime(started, '%Y-%m-%dT%H:%M:%S')
|
||||
started = time.mktime(started.timetuple())
|
||||
@ -379,10 +388,10 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
|
||||
return m['running']
|
||||
return False
|
||||
|
||||
def start(self, module):
|
||||
def start_module(self, module):
|
||||
self.run('%s on' % module)
|
||||
|
||||
def restart(self, module):
|
||||
def restart_module(self, module):
|
||||
self.run('%s off; %s on' % (module, module))
|
||||
|
||||
def _has_handshake(self, bssid):
|
||||
|
@ -2,8 +2,8 @@ import time
|
||||
import threading
|
||||
import logging
|
||||
|
||||
import core
|
||||
import pwnagotchi
|
||||
import pwnagotchi.utils as utils
|
||||
import pwnagotchi.mesh.wifi as wifi
|
||||
|
||||
from pwnagotchi.ai.reward import RewardFunction
|
||||
@ -174,22 +174,22 @@ class Epoch(object):
|
||||
self._epoch_data_ready.set()
|
||||
|
||||
logging.info("[epoch %d] duration=%s slept_for=%s blind=%d inactive=%d active=%d hops=%d missed=%d "
|
||||
"deauths=%d assocs=%d handshakes=%d cpu=%d%% mem=%d%% temperature=%dC reward=%s" % (
|
||||
self.epoch,
|
||||
core.secs_to_hhmmss(self.epoch_duration),
|
||||
core.secs_to_hhmmss(self.num_slept),
|
||||
self.blind_for,
|
||||
self.inactive_for,
|
||||
self.active_for,
|
||||
self.num_hops,
|
||||
self.num_missed,
|
||||
self.num_deauths,
|
||||
self.num_assocs,
|
||||
self.num_shakes,
|
||||
cpu * 100,
|
||||
mem * 100,
|
||||
temp,
|
||||
self._epoch_data['reward']))
|
||||
"deauths=%d assocs=%d handshakes=%d cpu=%d%% mem=%d%% temperature=%dC reward=%s" % (
|
||||
self.epoch,
|
||||
utils.secs_to_hhmmss(self.epoch_duration),
|
||||
utils.secs_to_hhmmss(self.num_slept),
|
||||
self.blind_for,
|
||||
self.inactive_for,
|
||||
self.active_for,
|
||||
self.num_hops,
|
||||
self.num_missed,
|
||||
self.num_deauths,
|
||||
self.num_assocs,
|
||||
self.num_shakes,
|
||||
cpu * 100,
|
||||
mem * 100,
|
||||
temp,
|
||||
self._epoch_data['reward']))
|
||||
|
||||
self.epoch += 1
|
||||
self.epoch_started = now
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-03 16:42+0200\n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"PO-Revision-Date: 2019-09-29 14:00+0200\n"
|
||||
"Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n"
|
||||
"Language-Team: DE <33197631+dadav@users.noreply.github.com>\n"
|
||||
@ -188,3 +188,21 @@ msgstr ""
|
||||
"Ich war {duration} am Pwnen und habe {deauthed} Clients gekickt! Außerdem "
|
||||
"habe ich {associated} neue Freunde getroffen und {handshakes} Handshakes "
|
||||
"gefressen! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "Stunden"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "Minuten"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "Sekunden"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "Stunde"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "Minute"
|
||||
|
||||
msgid "second"
|
||||
msgstr "Sekunde"
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-03 16:44+0200\n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"PO-Revision-Date: 2019-10-03 08:00+0000\n"
|
||||
"Last-Translator: Periklis Fregkos <fregkos@gmail.com>\n"
|
||||
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
|
||||
@ -189,3 +189,21 @@ msgstr ""
|
||||
"Pwnαρα για {duration} και έριξα {deauthed} πελάτες! Επίσης γνώρισα "
|
||||
"{associated} νέους φίλους και καταβρόχθισα {handshakes} χειραψίες! "
|
||||
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr ""
|
||||
|
||||
msgid "minutes"
|
||||
msgstr ""
|
||||
|
||||
msgid "seconds"
|
||||
msgstr ""
|
||||
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-03 16:47+0200\n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"PO-Revision-Date: 2019-10-03 10:34+0200\n"
|
||||
"Last-Translator: quantumsheep <7271496+quantumsheep@users.noreply.github."
|
||||
"com>\n"
|
||||
@ -190,3 +190,21 @@ msgstr ""
|
||||
"J'ai pwn durant {duration} et kick {deauthed} clients! J'ai aussi rencontré "
|
||||
"{associated} nouveaux amis and mangé {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr ""
|
||||
|
||||
msgid "minutes"
|
||||
msgstr ""
|
||||
|
||||
msgid "seconds"
|
||||
msgstr ""
|
||||
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-03 16:43+0200\n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"PO-Revision-Date: 2019-10-02 17:20+0000\n"
|
||||
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
|
||||
"Language: italian\n"
|
||||
@ -187,3 +187,21 @@ msgstr ""
|
||||
"Ho lavorato per {duration} e preso a calci {deauthed} clients! Ho anche "
|
||||
"incontrato {associate} nuovi amici e ho mangiato {handshakes} handshakes! "
|
||||
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "ore"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minuti"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "secondi"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "ora"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minuto"
|
||||
|
||||
msgid "second"
|
||||
msgstr "secondo"
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-03 16:35+0200\n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"PO-Revision-Date: 2019-09-30 23:53+0200\n"
|
||||
"Last-Translator: kovach <2214005+kovachwt@users.noreply.github.com>\n"
|
||||
"Language-Team: \n"
|
||||
@ -189,3 +189,21 @@ msgstr ""
|
||||
"Си газам веќе {duration} и избацив {deauthed} клиенти! Запознав {associated} "
|
||||
"нови другарчиња и лапнав {handshakes} ракувања! #pwnagotchi #pwnlog #pwnlife "
|
||||
"#hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr ""
|
||||
|
||||
msgid "minutes"
|
||||
msgstr ""
|
||||
|
||||
msgid "seconds"
|
||||
msgstr ""
|
||||
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
|
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-03 16:43+0200\n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"PO-Revision-Date: 2019-09-29 14:00+0200\n"
|
||||
"Last-Translator: Justin-P <justin-p@users.noreply.github.com>\n"
|
||||
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
|
||||
@ -188,3 +188,21 @@ msgstr ""
|
||||
"Ik heb gepwned voor {duration} and heb {deauthed} clients gekicked! Ik heb "
|
||||
"ook {associated} nieuwe vrienden gevonden en heb {handshakes} handshakes "
|
||||
"gegeten! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr ""
|
||||
|
||||
msgid "minutes"
|
||||
msgstr ""
|
||||
|
||||
msgid "seconds"
|
||||
msgstr ""
|
||||
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
|
Binary file not shown.
@ -0,0 +1,205 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <25989971+adolfaka@users.noreply.github.com>, 2019.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-03 16:47+0200\n"
|
||||
"PO-Revision-Date: 2019-10-05 18:50+0300\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.2.4\n"
|
||||
"Last-Translator: Elliot Manson\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
|
||||
"Language: ru_RU\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Привет, я Pwnagotchi! Поехали …"
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Новый день, новая охота, новые взломы!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Взломаем всю планету!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "Искусственный интеллект готов."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Нейронная сеть готова."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Эй, канал {channel} свободен! Ваша точка доступа скажет спасибо."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Мне скучно …"
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Пойдем прогуляемся!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Это лучший день в моей жизни!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Дерьмовый день :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Мне очень скучно …"
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Мне очень грустно …"
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Мне грустно"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Я живу своей жизнью!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Я взламываю, поэтому я существую."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Так, много сетей!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Мне так весело!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Моё преступление - это любопытство …"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you. {name}"
|
||||
msgstr "Привет, {name}! Приятно познакомиться. {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby! {name}"
|
||||
msgstr "Цель {name} близко! {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Хм … до свидания {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} исчезла …"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Упс … {name} исчезла."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} упустил!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Промахнулся!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Никто не хочет играть со мной …"
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Мне так одиноко …"
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Где все?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Дремлет {secs}с …"
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}c)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Ждем {secs}c …"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Оглядываюсь вокруг ({secs}с)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Эй, {what} давай дружить!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Связываюсь с {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Йоy {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Просто решил, что {mac} не нужен WiFi! Кхе-кхе)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Деаутентификация {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Кикаю {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Круто, мы получили {num} новое рукопожатие!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ой, что-то пошло не так … Перезагружаюсь …"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Кикнул {num} станцию\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Заимел {num} новых друзей\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Получил {num} рукопожатие\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Встретился один знакомый"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Встретились {num} приятелей"
|
||||
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
|
||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
msgstr ""
|
||||
"Я взламывал {duration} и кикнул {deauthed} клиентов! Я также встретил "
|
||||
"{associated} новых друзей и съел {handshakes} рукопожатий! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "часов"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "час"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "минут"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "минуту"
|
Binary file not shown.
@ -0,0 +1,202 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-03 16:47+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Mike Eriksson <mike@swedishmike.org>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: swedish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hej, jag är Pwnagotchi! Startar ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Ny dag, ny jakt, nya pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hacka planeten!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI klar."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Det neurala nätverket är klart."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Du, kanal {channel} är ledig! Din AP will gilla detta."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Jag har det så tråkigt..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Dags för en promenad!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Det här är den bästa dagen i mitt liv!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Idag suger :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Jag är extremt uttråkad ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Jag är jätteledsen ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Jag är ledsen"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Nu leker livet!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Jag pwnar därför är jag."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Så många nätverk!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Fan vad skoj jag har!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Mitt brott är att vara nyfiken ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you. {name}"
|
||||
msgstr "Hejsan {name}! Trevligt att träffas {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby! {name}"
|
||||
msgstr "Enheten {name} är nära! {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ... farväl {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} är borta ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Hoppsan ... {name} är borta."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} missade!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Bom!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Ingen vill leka med mig ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Jag är så ensam ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Var är alla?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Sover för {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Väntar {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Tittar omkring mig ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Hejsan {what} låt oss vara vänner"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Ansluter till {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Jag bestämde just att {mac} inte behöver WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Lysande, vi har {num} ny handskakningar{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "Hoppsan, någpt gick fel ... Startar om ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Sparkade {num} stationer\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Har {num} nya vänner\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Har {num} handskakningar\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Mötte 1 jämlike"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Mötte {num} jämlikar"
|
||||
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
|
||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
msgstr "Jag har pwnat för {duration} och sparkat ut {deauthed} klienter, Jag "
|
||||
"har också träffat {associated} nya vänner och har skakat {handshakes} händer! "
|
||||
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "timmar"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "timme"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minuter"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minut"
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-03 16:47+0200\n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -186,3 +186,21 @@ msgid ""
|
||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
msgstr ""
|
||||
|
||||
msgid "hours"
|
||||
msgstr ""
|
||||
|
||||
msgid "minutes"
|
||||
msgstr ""
|
||||
|
||||
msgid "seconds"
|
||||
msgstr ""
|
||||
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
|
@ -1,10 +1,10 @@
|
||||
import os
|
||||
import hashlib
|
||||
import time
|
||||
import re
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from pwnagotchi.voice import Voice
|
||||
from pwnagotchi.mesh.peer import Peer
|
||||
from file_read_backwards import FileReadBackwards
|
||||
|
||||
@ -125,17 +125,19 @@ class SessionParser(object):
|
||||
self.duration = '%02d:%02d:%02d' % (hours, mins, secs)
|
||||
self.duration_human = []
|
||||
if hours > 0:
|
||||
self.duration_human.append('%d hours' % hours)
|
||||
self.duration_human.append('%d %s' % (hours, self.voice.hhmmss(hours, 'h')))
|
||||
if mins > 0:
|
||||
self.duration_human.append('%d minutes' % mins)
|
||||
self.duration_human.append('%d %s' % (mins, self.voice.hhmmss(mins, 'm')))
|
||||
if secs > 0:
|
||||
self.duration_human.append('%d seconds' % secs)
|
||||
self.duration_human.append('%d %s' % (secs, self.voice.hhmmss(secs, 's')))
|
||||
|
||||
self.duration_human = ', '.join(self.duration_human)
|
||||
self.avg_reward /= (self.epochs if self.epochs else 1)
|
||||
|
||||
def __init__(self, path='/var/log/pwnagotchi.log'):
|
||||
self.path = path
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.voice = Voice(lang=config['main']['lang'])
|
||||
self.path = config['main']['log']
|
||||
self.last_session = None
|
||||
self.last_session_id = ''
|
||||
self.last_saved_session_id = ''
|
||||
|
@ -86,7 +86,9 @@ class Advertiser(object):
|
||||
logging.info("started advertiser thread (period:%s sid:%s) ..." % (str(self._period), self._me.session_id))
|
||||
while self._running:
|
||||
try:
|
||||
sendp(self._frame, iface=self._iface, verbose=False, count=5, inter=self._period)
|
||||
sendp(self._frame, iface=self._iface, verbose=False, count=1, inter=self._period)
|
||||
except OSError as ose:
|
||||
logging.warning("non critical issue while sending advertising packet: %s" % ose)
|
||||
except Exception as e:
|
||||
logging.exception("error")
|
||||
time.sleep(self._period)
|
||||
|
@ -2,7 +2,6 @@ import _thread
|
||||
import logging
|
||||
|
||||
import pwnagotchi
|
||||
import pwnagotchi.version as version
|
||||
import pwnagotchi.plugins as plugins
|
||||
from pwnagotchi.mesh import get_identity
|
||||
|
||||
@ -24,7 +23,7 @@ class AsyncAdvertiser(object):
|
||||
self._advertiser = Advertiser(
|
||||
self._config['main']['iface'],
|
||||
pwnagotchi.name(),
|
||||
version.version,
|
||||
pwnagotchi.version,
|
||||
self._identity,
|
||||
period=0.3,
|
||||
data=self._config['personality'])
|
||||
|
@ -27,17 +27,34 @@ def load_from_file(filename):
|
||||
return plugin_name, instance
|
||||
|
||||
|
||||
def load_from_path(path):
|
||||
def load_from_path(path, enabled=()):
|
||||
global loaded
|
||||
|
||||
for filename in glob.glob(os.path.join(path, "*.py")):
|
||||
name, plugin = load_from_file(filename)
|
||||
if name in loaded:
|
||||
raise Exception("plugin %s already loaded from %s" % (name, plugin.__file__))
|
||||
elif not plugin.__enabled__:
|
||||
elif name not in enabled:
|
||||
# print("plugin %s is not enabled" % name)
|
||||
pass
|
||||
else:
|
||||
loaded[name] = plugin
|
||||
|
||||
return loaded
|
||||
|
||||
|
||||
def load(config):
|
||||
enabled = [name for name, options in config['main']['plugins'].items() if 'enabled' in options and options['enabled']]
|
||||
custom_path = config['main']['custom_plugins'] if 'custom_plugins' in config['main'] else None
|
||||
# load default plugins
|
||||
loaded = load_from_path(default_path, enabled=enabled)
|
||||
# set the options
|
||||
for name, plugin in loaded.items():
|
||||
plugin.__dict__['OPTIONS'] = config['main']['plugins'][name]
|
||||
# load custom ones
|
||||
if custom_path is not None:
|
||||
loaded = load_from_path(custom_path, enabled=enabled)
|
||||
# set the options
|
||||
for name, plugin in loaded.items():
|
||||
plugin.__dict__['OPTIONS'] = config['main']['plugins'][name]
|
||||
|
||||
on('loaded')
|
||||
|
@ -0,0 +1,63 @@
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '1.0.0'
|
||||
__name__ = 'auto-backup'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin backups files when internet is availaible.'
|
||||
|
||||
from pwnagotchi.utils import StatusFile
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
OPTIONS = dict()
|
||||
READY = False
|
||||
STATUS = StatusFile('/root/.auto-backup')
|
||||
|
||||
|
||||
def on_loaded():
|
||||
global READY
|
||||
|
||||
if 'files' not in OPTIONS or ('files' in OPTIONS and OPTIONS['files'] is None):
|
||||
logging.error("AUTO-BACKUP: No files to backup.")
|
||||
return
|
||||
|
||||
if 'interval' not in OPTIONS or ('interval' in OPTIONS and OPTIONS['interval'] is None):
|
||||
logging.error("AUTO-BACKUP: Interval is not set.")
|
||||
return
|
||||
|
||||
if 'commands' not in OPTIONS or ('commands' in OPTIONS and OPTIONS['commands'] is None):
|
||||
logging.error("AUTO-BACKUP: No commands given.")
|
||||
return
|
||||
|
||||
READY = True
|
||||
logging.info("AUTO-BACKUP: Successfuly loaded.")
|
||||
|
||||
|
||||
def on_internet_available(display, config, log):
|
||||
global STATUS
|
||||
|
||||
if READY:
|
||||
if STATUS.newer_then_days(OPTIONS['interval']):
|
||||
return
|
||||
|
||||
files_to_backup = " ".join(OPTIONS['files'])
|
||||
try:
|
||||
logging.info("AUTO-BACKUP: Backing up ...")
|
||||
display.set('status', 'Backing up ...')
|
||||
display.update()
|
||||
|
||||
for cmd in OPTIONS['commands']:
|
||||
logging.info(f"AUTO-BACKUP: Running {cmd.format(files=files_to_backup)}")
|
||||
process = subprocess.Popen(cmd.format(files=files_to_backup), shell=True, stdin=None,
|
||||
stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash")
|
||||
process.wait()
|
||||
if process.returncode > 0:
|
||||
raise OSError(f"Command failed (rc: {process.returncode})")
|
||||
|
||||
logging.info("AUTO-BACKUP: backup done")
|
||||
STATUS.update()
|
||||
except OSError as os_e:
|
||||
logging.info(f"AUTO-BACKUP: Error: {os_e}")
|
||||
|
||||
display.set('status', 'Backup done!')
|
||||
display.update()
|
@ -0,0 +1,56 @@
|
||||
__author__ = 'evilsocket@gmail.com'
|
||||
__version__ = '1.0.0'
|
||||
__name__ = 'auto-update'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin performs an "apt update && apt upgrade" when internet is availaible.'
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
from pwnagotchi.utils import StatusFile
|
||||
|
||||
OPTIONS = dict()
|
||||
READY = False
|
||||
STATUS = StatusFile('/root/.auto-update')
|
||||
|
||||
|
||||
def on_loaded():
|
||||
global READY
|
||||
|
||||
if 'interval' not in OPTIONS or ('interval' in OPTIONS and OPTIONS['interval'] is None):
|
||||
logging.error("AUTO-UPDATE: Interval is not set.")
|
||||
return
|
||||
|
||||
READY = True
|
||||
|
||||
|
||||
def on_internet_available(display, config, log):
|
||||
global STATUS
|
||||
|
||||
if READY:
|
||||
if STATUS.newer_then_days(OPTIONS['interval']):
|
||||
return
|
||||
|
||||
try:
|
||||
display.set('status', 'Updating ...')
|
||||
display.update()
|
||||
|
||||
logging.info("AUTO-UPDATE: updating packages index ...")
|
||||
|
||||
update = subprocess.Popen('apt update -y', shell=True, stdin=None,
|
||||
stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash")
|
||||
update.wait()
|
||||
|
||||
logging.info("AUTO-UPDATE: updating packages ...")
|
||||
|
||||
upgrade = subprocess.Popen('apt upgrade -y', shell=True, stdin=None,
|
||||
stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash")
|
||||
upgrade.wait()
|
||||
|
||||
logging.info("AUTO-UPDATE: complete.")
|
||||
|
||||
STATUS.update()
|
||||
except Exception as e:
|
||||
logging.exception("AUTO-UPDATE ERROR")
|
||||
|
||||
display.set('status', 'Updated!')
|
||||
display.update()
|
@ -3,7 +3,6 @@ __version__ = '1.0.0'
|
||||
__name__ = 'hello_world'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'An example plugin for pwnagotchi that implements all the available callbacks.'
|
||||
__enabled__ = False # IMPORTANT: set this to True to enable your plugin.
|
||||
|
||||
import logging
|
||||
|
||||
@ -12,13 +11,16 @@ from pwnagotchi.ui.view import BLACK
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
|
||||
|
||||
# Will be set with the options in config.yml config['main']['plugins'][__name__]
|
||||
OPTIONS = dict()
|
||||
|
||||
# called when the plugin is loaded
|
||||
def on_loaded():
|
||||
logging.warning("WARNING: plugin %s should be disabled!" % __name__)
|
||||
|
||||
|
||||
# called in manual mode when there's internet connectivity
|
||||
def on_internet_available(config, log):
|
||||
def on_internet_available(ui, config, log):
|
||||
pass
|
||||
|
||||
|
||||
@ -82,7 +84,7 @@ def on_ai_best_reward(agent, reward):
|
||||
pass
|
||||
|
||||
|
||||
# called when the AI got the best reward so far
|
||||
# called when the AI got the worst reward so far
|
||||
def on_ai_worst_reward(agent, reward):
|
||||
pass
|
||||
|
||||
|
@ -3,7 +3,6 @@ __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 logging
|
||||
import json
|
||||
@ -15,14 +14,14 @@ running = False
|
||||
|
||||
|
||||
def on_loaded():
|
||||
logging.info("GPS plugin loaded for %s" % device)
|
||||
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)
|
||||
logging.info("enabling gps bettercap's module for %s" % device)
|
||||
try:
|
||||
agent.run('gps off')
|
||||
except:
|
||||
|
@ -7,7 +7,6 @@ __version__ = '1.0.0'
|
||||
__name__ = 'memtemp'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'A plugin that will add a memory and temperature indicator'
|
||||
__enabled__ = False
|
||||
|
||||
import struct
|
||||
|
||||
|
@ -0,0 +1,84 @@
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '1.0.0'
|
||||
__name__ = 'onlinehashcrack'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin automatically uploades handshakes to https://onlinehashcrack.com'
|
||||
|
||||
import os
|
||||
import logging
|
||||
import requests
|
||||
|
||||
READY = False
|
||||
ALREADY_UPLOADED = None
|
||||
OPTIONS = dict()
|
||||
|
||||
|
||||
def on_loaded():
|
||||
"""
|
||||
Gets called when the plugin gets loaded
|
||||
"""
|
||||
global READY
|
||||
global EMAIL
|
||||
global ALREADY_UPLOADED
|
||||
|
||||
if not 'email' in OPTIONS or ('email' in OPTIONS and OPTIONS['email'] is None):
|
||||
logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com")
|
||||
return
|
||||
|
||||
try:
|
||||
with open('/root/.ohc_uploads', 'r') as f:
|
||||
ALREADY_UPLOADED = f.read().splitlines()
|
||||
except OSError:
|
||||
logging.warning('OHC: No upload-file found.')
|
||||
ALREADY_UPLOADED = []
|
||||
|
||||
READY = True
|
||||
|
||||
|
||||
def _upload_to_ohc(path, timeout=30):
|
||||
"""
|
||||
Uploads the file to onlinehashcrack.com
|
||||
"""
|
||||
with open(path, 'rb') as file_to_upload:
|
||||
data = {'email': OPTIONS['email']}
|
||||
payload = {'file': file_to_upload}
|
||||
|
||||
try:
|
||||
result = requests.post('https://api.onlinehashcrack.com',
|
||||
data=data,
|
||||
files=payload,
|
||||
timeout=timeout)
|
||||
if 'already been sent' in result.text:
|
||||
logging.warning(f"{path} was already uploaded.")
|
||||
except requests.exceptions.RequestException as e:
|
||||
logging.error(f"OHC: Got an exception while uploading {path} -> {e}")
|
||||
raise e
|
||||
|
||||
|
||||
def on_internet_available(display, config, log):
|
||||
"""
|
||||
Called in manual mode when there's internet connectivity
|
||||
"""
|
||||
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_new = set(handshake_paths) - set(ALREADY_UPLOADED)
|
||||
|
||||
if handshake_new:
|
||||
logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onelinehashcrack.com")
|
||||
|
||||
for idx, handshake in enumerate(handshake_new):
|
||||
display.set('status', f"Uploading handshake to onlinehashcrack.com ({idx + 1}/{len(handshake_new)})")
|
||||
display.update(force=True)
|
||||
try:
|
||||
_upload_to_ohc(handshake)
|
||||
ALREADY_UPLOADED.append(handshake)
|
||||
with open('/root/.ohc_uploads', 'a') as f:
|
||||
f.write(handshake + "\n")
|
||||
logging.info(f"OHC: Successfuly uploaded {handshake}")
|
||||
except requests.exceptions.RequestException:
|
||||
pass
|
||||
except OSError as os_e:
|
||||
logging.error(f"OHC: Got the following error: {os_e}")
|
||||
|
@ -3,21 +3,19 @@ __version__ = '1.0.0'
|
||||
__name__ = 'twitter'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin creates tweets about the recent activity of pwnagotchi'
|
||||
__enabled__ = True
|
||||
|
||||
import logging
|
||||
from pwnagotchi.voice import Voice
|
||||
|
||||
UI = None
|
||||
|
||||
OPTIONS = dict()
|
||||
|
||||
def on_loaded():
|
||||
logging.info("Twitter plugin loaded.")
|
||||
logging.info("twitter plugin loaded.")
|
||||
|
||||
|
||||
# called in manual mode when there's internet connectivity
|
||||
def on_internet_available(config, log):
|
||||
if config['twitter']['enabled'] and log.is_new() and log.handshakes > 0 and UI:
|
||||
def on_internet_available(ui, config, log):
|
||||
if log.is_new() and log.handshakes > 0:
|
||||
try:
|
||||
import tweepy
|
||||
except ImportError:
|
||||
@ -28,15 +26,15 @@ def on_internet_available(config, log):
|
||||
|
||||
picture = '/dev/shm/pwnagotchi.png'
|
||||
|
||||
UI.on_manual_mode(log)
|
||||
UI.update(force=True)
|
||||
UI.image().save(picture, 'png')
|
||||
UI.set('status', 'Tweeting...')
|
||||
UI.update(force=True)
|
||||
ui.on_manual_mode(log)
|
||||
ui.update(force=True)
|
||||
ui.image().save(picture, 'png')
|
||||
ui.set('status', 'Tweeting...')
|
||||
ui.update(force=True)
|
||||
|
||||
try:
|
||||
auth = tweepy.OAuthHandler(config['twitter']['consumer_key'], config['twitter']['consumer_secret'])
|
||||
auth.set_access_token(config['twitter']['access_token_key'], config['twitter']['access_token_secret'])
|
||||
auth = tweepy.OAuthHandler(OPTIONS['consumer_key'], OPTIONS['consumer_secret'])
|
||||
auth.set_access_token(OPTIONS['access_token_key'], OPTIONS['access_token_secret'])
|
||||
api = tweepy.API(auth)
|
||||
|
||||
tweet = Voice(lang=config['main']['lang']).on_log_tweet(log)
|
||||
@ -46,9 +44,3 @@ def on_internet_available(config, log):
|
||||
logging.info("tweeted: %s" % tweet)
|
||||
except Exception as e:
|
||||
logging.exception("error while tweeting")
|
||||
|
||||
|
||||
def on_ui_setup(ui):
|
||||
# need that object
|
||||
global UI
|
||||
UI = ui
|
||||
|
@ -12,7 +12,6 @@ __version__ = '1.0.0'
|
||||
__name__ = 'ups_lite'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'A plugin that will add a voltage indicator for the UPS Lite v1.1'
|
||||
__enabled__ = False
|
||||
|
||||
import struct
|
||||
|
||||
|
@ -0,0 +1,83 @@
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '1.0.0'
|
||||
__name__ = 'wpa-sec'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin automatically uploades handshakes to https://wpa-sec.stanev.org'
|
||||
|
||||
import os
|
||||
import logging
|
||||
import requests
|
||||
|
||||
READY = False
|
||||
ALREADY_UPLOADED = None
|
||||
|
||||
|
||||
def on_loaded():
|
||||
"""
|
||||
Gets called when the plugin gets loaded
|
||||
"""
|
||||
global READY
|
||||
global API_KEY
|
||||
global ALREADY_UPLOADED
|
||||
|
||||
if not 'api_key' in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None):
|
||||
logging.error("WPA_SEC: API-KEY isn't set. Can't upload to wpa-sec.stanev.org")
|
||||
return
|
||||
|
||||
try:
|
||||
with open('/root/.wpa_sec_uploads', 'r') as f:
|
||||
ALREADY_UPLOADED = f.read().splitlines()
|
||||
except OSError:
|
||||
logging.warning('WPA_SEC: No upload-file found.')
|
||||
ALREADY_UPLOADED = []
|
||||
|
||||
READY = True
|
||||
|
||||
|
||||
def _upload_to_wpasec(path, timeout=30):
|
||||
"""
|
||||
Uploads the file to wpa-sec.stanev.org
|
||||
"""
|
||||
with open(path, 'rb') as file_to_upload:
|
||||
headers = {'key': OPTIONS['api_key']}
|
||||
payload = {'file': file_to_upload}
|
||||
|
||||
try:
|
||||
result = requests.post('https://wpa-sec.stanev.org/?submit',
|
||||
headers=headers,
|
||||
files=payload,
|
||||
timeout=timeout)
|
||||
if ' already submitted' in result.text:
|
||||
logging.warning(f"{path} was already submitted.")
|
||||
except requests.exceptions.RequestException as e:
|
||||
logging.error(f"WPA_SEC: Got an exception while uploading {path} -> {e}")
|
||||
raise e
|
||||
|
||||
|
||||
def on_internet_available(display, config, log):
|
||||
"""
|
||||
Called in manual mode when there's internet connectivity
|
||||
"""
|
||||
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_new = set(handshake_paths) - set(ALREADY_UPLOADED)
|
||||
|
||||
if handshake_new:
|
||||
logging.info("WPA_SEC: Internet connectivity detected.\
|
||||
Uploading new handshakes to wpa-sec.stanev.org")
|
||||
|
||||
for idx, handshake in enumerate(handshake_new):
|
||||
display.set('status', f"Uploading handshake to wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})")
|
||||
display.update(force=True)
|
||||
try:
|
||||
_upload_to_wpasec(handshake)
|
||||
ALREADY_UPLOADED.append(handshake)
|
||||
with open('/root/.wpa_sec_uploads', 'a') as f:
|
||||
f.write(handshake + "\n")
|
||||
logging.info(f"WPA_SEC: Successfuly uploaded {handshake}")
|
||||
except requests.exceptions.RequestException:
|
||||
pass
|
||||
except OSError as os_e:
|
||||
logging.error(f"WPA_SEC: Got the following error: {os_e}")
|
@ -102,12 +102,15 @@ class Display(View):
|
||||
def _is_papirus(self):
|
||||
return self._display_type in ('papirus', 'papi')
|
||||
|
||||
def _is_waveshare1(self):
|
||||
def _is_waveshare_v1(self):
|
||||
return self._display_type in ('waveshare_1', 'ws_1', 'waveshare1', 'ws1')
|
||||
|
||||
def _is_waveshare2(self):
|
||||
def _is_waveshare_v2(self):
|
||||
return self._display_type in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2')
|
||||
|
||||
def _is_waveshare(self):
|
||||
return self._is_waveshare_v1() or self._is_waveshare_v2()
|
||||
|
||||
def _init_display(self):
|
||||
if self._is_inky():
|
||||
logging.info("initializing inky display")
|
||||
@ -124,7 +127,7 @@ class Display(View):
|
||||
self._display.clear()
|
||||
self._render_cb = self._papirus_render
|
||||
|
||||
elif self._is_waveshare1():
|
||||
elif self._is_waveshare_v1():
|
||||
logging.info("initializing waveshare v1 display")
|
||||
from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
|
||||
self._display = EPD()
|
||||
@ -133,7 +136,7 @@ class Display(View):
|
||||
self._display.init(self._display.lut_partial_update)
|
||||
self._render_cb = self._waveshare_render
|
||||
|
||||
elif self._is_waveshare2():
|
||||
elif self._is_waveshare_v2():
|
||||
logging.info("initializing waveshare v2 display")
|
||||
from pwnagotchi.ui.waveshare.v2.waveshare import EPD
|
||||
self._display = EPD()
|
||||
@ -149,6 +152,18 @@ class Display(View):
|
||||
|
||||
self.on_render(self._on_view_rendered)
|
||||
|
||||
def clear(self):
|
||||
if self._display is None:
|
||||
logging.error("no display object created")
|
||||
elif self._is_inky():
|
||||
self._display.Clear()
|
||||
elif self._is_papirus():
|
||||
self._display.clear()
|
||||
elif self._is_waveshare():
|
||||
self._display.Clear(WHITE)
|
||||
else:
|
||||
logging.critical("unknown display type %s" % self._display_type)
|
||||
|
||||
def _inky_render(self):
|
||||
if self._display_color != 'mono':
|
||||
display_colors = 3
|
||||
@ -175,7 +190,10 @@ class Display(View):
|
||||
])
|
||||
|
||||
self._display.set_image(img_buffer)
|
||||
self._display.show()
|
||||
try:
|
||||
self._display.show()
|
||||
except:
|
||||
print("")
|
||||
|
||||
def _papirus_render(self):
|
||||
self._display.display(self._canvas)
|
||||
@ -183,9 +201,9 @@ class Display(View):
|
||||
|
||||
def _waveshare_render(self):
|
||||
buf = self._display.getbuffer(self._canvas)
|
||||
if self._is_waveshare1():
|
||||
if self._is_waveshare_v1():
|
||||
self._display.display(buf)
|
||||
elif self._is_waveshare2():
|
||||
elif self._is_waveshare_v2():
|
||||
self._display.displayPartial(buf)
|
||||
|
||||
def image(self):
|
||||
|
@ -4,7 +4,7 @@ import time
|
||||
import logging
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
import core
|
||||
import pwnagotchi.utils as utils
|
||||
import pwnagotchi.plugins as plugins
|
||||
from pwnagotchi.voice import Voice
|
||||
|
||||
@ -86,8 +86,9 @@ class View(object):
|
||||
|
||||
'face': Text(value=faces.SLEEP, position=face_pos, color=BLACK, font=fonts.Huge),
|
||||
|
||||
'friend_face': Text(value=None, position=(0, 90), font=fonts.Bold, color=BLACK),
|
||||
'friend_name': Text(value=None, position=(40, 93), font=fonts.BoldSmall, color=BLACK),
|
||||
'friend_face': Text(value=None, position=(0, (self._height * 0.88) - 15), font=fonts.Bold, color=BLACK),
|
||||
'friend_name': Text(value=None, position=(40, (self._height * 0.88) - 13), font=fonts.BoldSmall,
|
||||
color=BLACK),
|
||||
|
||||
'name': Text(value='%s>' % 'pwnagotchi', position=name_pos, color=BLACK, font=fonts.Bold),
|
||||
|
||||
@ -166,7 +167,7 @@ class View(object):
|
||||
self.set('channel', '-')
|
||||
self.set('aps', "%d" % log.associated)
|
||||
self.set('shakes', '%d (%s)' % (log.handshakes, \
|
||||
core.total_unique_handshakes(self._config['bettercap']['handshakes'])))
|
||||
utils.total_unique_handshakes(self._config['bettercap']['handshakes'])))
|
||||
self.set_closest_peer(log.last_peer)
|
||||
|
||||
def is_normal(self):
|
||||
|
@ -1,6 +1,10 @@
|
||||
import yaml
|
||||
import os
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import glob
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
import yaml
|
||||
|
||||
|
||||
# https://stackoverflow.com/questions/823196/yaml-merge-in-python
|
||||
@ -40,3 +44,55 @@ def setup_logging(args, config):
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setFormatter(formatter)
|
||||
root.addHandler(console_handler)
|
||||
|
||||
|
||||
def secs_to_hhmmss(secs):
|
||||
mins, secs = divmod(secs, 60)
|
||||
hours, mins = divmod(mins, 60)
|
||||
return '%02d:%02d:%02d' % (hours, mins, secs)
|
||||
|
||||
|
||||
def total_unique_handshakes(path):
|
||||
expr = os.path.join(path, "*.pcap")
|
||||
return len(glob.glob(expr))
|
||||
|
||||
|
||||
def iface_channels(ifname):
|
||||
channels = []
|
||||
output = subprocess.getoutput("/sbin/iwlist %s freq" % ifname)
|
||||
for line in output.split("\n"):
|
||||
line = line.strip()
|
||||
if line.startswith("Channel "):
|
||||
channels.append(int(line.split()[1]))
|
||||
return channels
|
||||
|
||||
|
||||
def led(on=True):
|
||||
with open('/sys/class/leds/led0/brightness', 'w+t') as fp:
|
||||
fp.write("%d" % (0 if on is True else 1))
|
||||
|
||||
|
||||
def blink(times=1, delay=0.3):
|
||||
for t in range(0, times):
|
||||
led(True)
|
||||
time.sleep(delay)
|
||||
led(False)
|
||||
time.sleep(delay)
|
||||
led(True)
|
||||
|
||||
|
||||
class StatusFile(object):
|
||||
def __init__(self, path):
|
||||
self._path = path
|
||||
self._updated = None
|
||||
|
||||
if os.path.exists(path):
|
||||
self._updated = datetime.fromtimestamp(os.path.getmtime(path))
|
||||
|
||||
def newer_then_days(self, days):
|
||||
return self._updated is not None and (datetime.now() - self._updated).days < days
|
||||
|
||||
def update(self, data=None):
|
||||
self._updated = datetime.now()
|
||||
with open(self._path, 'w') as fp:
|
||||
fp.write(str(self._updated) if data is None else data)
|
||||
|
@ -1 +0,0 @@
|
||||
version = '1.0.0plz2'
|
@ -138,5 +138,21 @@ class Voice:
|
||||
associated=log.associated,
|
||||
handshakes=log.handshakes)
|
||||
|
||||
def custom(self, text):
|
||||
return self._(text)
|
||||
def hhmmss(self, count, fmt):
|
||||
if count > 1:
|
||||
# plural
|
||||
if fmt == "h":
|
||||
return self._("hours")
|
||||
if fmt == "m":
|
||||
return self._("minutes")
|
||||
if fmt == "s":
|
||||
return self._("seconds")
|
||||
else:
|
||||
# sing
|
||||
if fmt == "h":
|
||||
return self._("hour")
|
||||
if fmt == "m":
|
||||
return self._("minute")
|
||||
if fmt == "s":
|
||||
return self._("second")
|
||||
return fmt
|
||||
|
Loading…
x
Reference in New Issue
Block a user