From 50dd651fab8174c97017bd8a0e17771d15b9a0f0 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Fri, 4 Oct 2019 23:15:47 +0100 Subject: [PATCH 001/150] move dependencies to binary or deb packages --- builder/pwnagotchi.yml | 79 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 515ee70..e091dab 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -3,8 +3,9 @@ - 127.0.0.1 become: yes vars: - pwn_hostname: "pwnagotchi" - pwn_version: "master" + pwn_hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}" + pwn_version: "{{ lookup('env', 'PWN_VERSION') | default('master', true) }} " + bettercap_query: "assets[?contains(name, 'armv6l')].browser_download_url" tasks: @@ -57,6 +58,7 @@ - git - build-essential - python3-pip + - unzip - gawk - libopenmpi-dev - libatlas-base-dev @@ -87,6 +89,25 @@ - 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-keras-applications + - python3-keras-preprocessing + - python3-six + - python3-protobuf + - python3-wrapt + - python3-wheel + - python3-mock - name: configure dphys-swapfile file: @@ -113,16 +134,48 @@ state: started enabled: yes - - name: build bettercap - command: go get -u github.com/bettercap/bettercap - environment: - GOPATH: /root/go - GOROOT: /usr/lib/go + - name: acquire python3 pip target + command: "python3 -c 'import sys;print(sys.path.pop())'" + register: pip_target - - name: install bettercap - copy: - src: /root/go/bin/bettercap - dest: /usr/bin/bettercap + - name: install pip packages that are not architecture tied + pip: + name: "{{ packages }}" + extra_args: "--no-deps --extra-index-url=https://www.piwheels.hostedpi.com/simple/ --prefer-binary --no-cache-dir --platform=armv6l --target={{ pip_target.stdout }}" + vars: + packages: + - absl-py + - enum34 + - gast + - google_pasta + - opt_einsum + - tensorboard + - scapy + - gym + - stable-baselines + - file_read_backwards + - tensorflow_estimator + + - 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 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://storage.googleapis.com/tensorflow/raspberrypi/tensorflow-1.14.0-cp34-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 +204,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 From 695499314d094b346d26164aaf8a5f1ad8aa2761 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sat, 5 Oct 2019 17:57:15 +0200 Subject: [PATCH 002/150] Fix split-bug in script --- sdcard/rootfs/root/pwnagotchi/config.yml | 4 ++-- .../scripts/pwnagotchi/plugins/default/auto-backup.py | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/sdcard/rootfs/root/pwnagotchi/config.yml b/sdcard/rootfs/root/pwnagotchi/config.yml index bbc93b8..ae470ad 100644 --- a/sdcard/rootfs/root/pwnagotchi/config.yml +++ b/sdcard/rootfs/root/pwnagotchi/config.yml @@ -15,7 +15,7 @@ main: files: - /root/brain.nn - /root/brain.json - - /root/custom.yaml + - /root/custom.yml - /root/handshakes - /etc/ssh - /etc/hostname @@ -24,7 +24,7 @@ main: - /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).tar.gz' + - 'scp /tmp/backup.tar.gz pwnagotchi@10.0.0.1:/home/pwnagotchi/backups/backup-$(date +%s).tar.gz' gps: enabled: false twitter: diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-backup.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-backup.py index 7312201..731a40f 100644 --- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-backup.py +++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-backup.py @@ -30,6 +30,7 @@ def on_loaded(): return READY = True + logging.info("AUTO-BACKUP: Successfuly loaded.") def on_internet_available(display, config, log): @@ -41,11 +42,17 @@ def on_internet_available(display, config, log): 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']: - subprocess.call(cmd.format(files=files_to_backup).split(), stdout=open(os.devnull, 'wb')) + 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() From 419208ee5d5d26307b7e273f46704a29e27dd61f Mon Sep 17 00:00:00 2001 From: Elliot Manson <adolfaka@gmail.com> Date: Sat, 5 Oct 2019 19:02:59 +0300 Subject: [PATCH 003/150] Translated into Russian --- .../pwnagotchi/locale/ru/LC_MESSAGES/voice.mo | Bin 0 -> 5038 bytes .../pwnagotchi/locale/ru/LC_MESSAGES/voice.po | 205 ++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo create mode 100644 sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.po diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo new file mode 100644 index 0000000000000000000000000000000000000000..08fae17ce786f4525b5a7211b10217638ba6dea1 GIT binary patch literal 5038 zcmbW4+ix6K9mh{0P;d*SrBJTzS<)sk@oskAM5J!rD$Tvrt(!QQ_5n2BJ@yXXoiQ`B zaW}4M;*cgK#u05Hw9r-*QOZLU<HofU#|{$w0W(4fApsA4K|=7vMM6S?&+p9a+G)xQ z<IH|%&YbhRe1Dg7^2b}&eOGa9<NgfyudY*SFIamoKU}BQDfL0{S@2`v^I#|VGw|c! zue0$#gCF5ty-%qPz|Ek@+YW9A4}lt-0Y3_!2Swg5!4HAI18)QWl;MBCTY10j{YqUA zc7Y;)D|iXq4Q>GC<1N5OQ0(aeW!+=oC%|D)<US2{fM>y*z}G<jsX2af;3ZJz{T>v% z{|;*K<_{>f8SDnd&aZ%Ce+`s*M?np~42u30@U!4=K#})1Q0CtNiJt=R0L6|T@Mdr~ z_+fAm#AP)MLZzx@?_URTRgHs~q`m=)zt4cO?nThMPN_G*dw9R?2BmHX9|Io($3Wp} z3G4#j1~-B?-I&Jt3!uC|4c5S)f?L2_aaM!7!23W0ia)P`p93#}p9cQ`-U<E-lz85b zQS!bO{3rN0D0cKiy7>D8P}ZLVWnK&xz<+?7!8-}E*wYVg0iOg#?;GGw@E4%yU5Bt; zpa#Wn1NMQhg5v*QK@HvrS)E`W+zJkYcY)KO=$`{6Uw#W>g8B=X1OE-;lDeJXAxd?D zxT5X{B~E>y#N{9;@q7Y=I%Tr=0Q@BHlc40qS&%SU-z6R*OYFLXTh@!O@4QND2E+!@ z+s!TcRpb`?<q|z|y@y+KOJ2%_N_*jo<>d1qX`<G1lLm^^v_1%*!ofOj$x*pBaZ5af zdnmV;#7Sb*y0-BkoQmJ#qr^?PZC%nrgv-0Lf$KrxxQko3l}l<gFAqER>%cgbW<l); zL%-}gk?W1<$k!9&RVSKMJAJ>_rN@mP@gqIqHR_YvGvm7Ic$Ki~jGBqDS|f03lR34^ zIE|=kyvQx5LMG}?c~b3duWPRfJJdd>JgS+k`)iJ8qMX`iYBiL4PTfrA^doNBh!J&T zBHi>Gh3pH{nwe#~W}=Ou9x{43a1GXojAoZEqsB8eJ&`?3YBywxF**IX-w5=Mem(Bi zYC3e97~puLVL|P4yY&8zb=^PiIrv$wx;cF?astamp-@o!^{_!^=xe9udq$^YW8<Fq z5SU><FnYgs>dZvU5U-VV4sYhkC@^(XYrbQ=>KqoTky&q)t9rQM<?QO3YsZD0nmcUL z!O*GLkB3dr#PpW*!v+G3r?Et2VTYcm5Rb_9y-7`s^r%}N#h8go8lB3dF4v$d3UheO za;GK+!19EX#osVTLY&cRr8tq%6RYu?T#Y|+)}uY6%|6%skrc6Budfnmd>ONG*6YL3 zCh=AeHNr?&Oxdkq9AQAeWDAN-CDfj;2i-kxPJI#Uhlr;*XD6gX;*d?nFtV<aP!HQ| z81XF!jvjYvqdE1k?~lq-C-56^v1uYS<#00WRDDhbhuYbkx!R%nAflwlj0x;xIzwJV z)#rwxspM2&Q<np`ZX~U&;r_5LHv-oW-Kc4U{)jU+){aK1F&&50ibIOp0%~|s`UHFA z?j!z?Uuj}Dp^?moFW%g!N^tDzN5{f0l7&!JMff-|)Z>BABgv=_m?7V9r|ZG08(|E} zbk7!bkN`Vg6SdK}AB={%Tuu#;U7nH3Z4HY*Stl5%@@(Cc^pZd!2si7^EF%w9>sITC z?^lMJ!laa~I^?<1Bjj0@d&UWdnz`23LC3WQ+9X{=%b-*cm24!pg@gxkYOw12W1-ct zDsP0!E2zg?ttqFTIC6x4N7lquUZ0R3`#~lgIijkRz5NMQ-Srxg$skl$%eFLUL-j1$ z{ek~AQ;zccEBP;x1(a2f?l@q^{2<Erg(Gey|6pSz%n$es_dl9HU=F)7DZh(~>CxTA z(mnZ7F<;!KOWS+46*m{}E*3i;cD#{>Gh*@s#;LQeZ-3wJRq3Tdv16z2MZ`Bh&>TZb zL<iYIKDge!(%pj4M7@Uw_T;y(&I=qb95z9Iw^#Nnq(_h5KIBFnU(W9}o}nH1L67eD zO@;V$7rF~uu^~jfEUTqoJNj)O&+qYrdf21Aw5^A|ci&4YN}lv?^6n}XdwWZI!v-yn z#awTxq#w{l&2--H@9l1Vy03SuJlYk-`%2yVsi)E<_m{SIvZTb4?#_GLO!VlW@zn!| zI#m4q<XC(uIi4KjZz`UP=i}vgIXN9))bSz@Qw%KeH_xOg9lsf`FtifS6;%AAcrG~; zFEDp5o=s+0KdqBvt!*rRDLIN*CU@!ha&lV7S6H{i&`AU@#fxOA>}{w+9QVAI1@R>w zW#(JRoMPsDPQ@>ve%1=ZIvGvA$(KuvJ;U>vv{9+}RhG3CWa8glCVoE`FJQ$=d^w&) z`eltX$C4Sba*2`Ucs`j*j-&h4<S4q}C7J5V3M;46(c}!aV1DY_0*|xS{L2_Ml}sb= zOnV!R&&3y6Z?||IUtlJr3CS0%g>R)x&}Zj~Z&uvWnq8GZ2~R)gzJSTgnDHDGoQh|) z&<wAv64@&fN~pXdUx+TU-V*!eg)x2?MG7Ij$~lPaZ|^JOBZ}se6RehKy?yj`75`9( z5p6iCtsUeDNYK&==i$*3J1a34ACnp4Pe7Azi;;{T*C@im<H@s#w@JtI4Ax}Q(|kNB zg4b%b9!mTr%WTSCGo@viMC%w75nmX^Cg>%;Xih1d7vg1%m=UKLCZ)RLH*M02QD|?E z3b(1|t0(*~pJT$ddMJQA2$FnVCMfc$eKKGSZmn!vRj__OqgO(fCgl_vNc5P;+O=l} zOZ{AOIyuowqXk4s;R?Us5(2J1L%eG-T>Sse6E=5jrV%D<;VH4OOD88K(Ant%6Y)a) z#+`UMJ!EKl!-;r4PY{7l6~AQJAyryOj%+03%k~MIDHMe*`56jW((|IN1d8_x7OW5) z_@yUN&rCl<SYM%I$;gFPGBQ3bIZDo2Hr{OQWyvuNIZI+M(h@9lYueK_97_Wci&jG; z3Td;FQc@i@)#;+w%%jl7gsl0A-E=S*lWZk;X-(R;1UZ%fk!&+`CHYS3sVy5@;Id{J zljn6_rxm90cg8k}D_A`xMvA*>s=grh;vdUs2kGIAmThbMWlScSY)=xY$uxy4#VtjA zhHA$iY5udVVdhGPw)$vt8Y5qpNZR<@$lE2<j%*!^QagyEGfExOH7{H77gP1GNPkRo zC!Y$Crg2JYSj=d59c-1>G;cjwfNEQ^q*rSTvd*w$5Z-B2ywfQ;pT)09i0~t|fK5#- zk{Tlda#+I;o=~xZYT}9i=>$oh_8u6|(zMQ{#@c-ca~Id}2RWjGF1n`Q#EXdk2^)@e zgk~+0=U69u0-;1zdLmNs>nvQdxnd7^1XY^Ff+Sb_j2C||D}gka<&@0lWzcqyc%H*u z{9_8lhB=c>G)N3-YO)E68!yHyt^em;bIdDu+H>7C$2<hEnWz+mXoa_OXkgn<j<0!9 W*%|L@t=81+izGA4Bw1-9IrU%QkI=RN literal 0 HcmV?d00001 diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.po new file mode 100644 index 0000000..796bf17 --- /dev/null +++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.po @@ -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 "минуту" From 09c175e2ebd0eda5e264bf34d4ed99e1bd1a725d Mon Sep 17 00:00:00 2001 From: Elliot Manson <adolfaka@gmail.com> Date: Sat, 5 Oct 2019 19:07:21 +0300 Subject: [PATCH 004/150] Corrected the documentation --- docs/configure.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configure.md b/docs/configure.md index 733abc9..d69bc9a 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -40,6 +40,7 @@ For instance, you can change `main.lang` to one of the supported languages: - macedonian - italian - french +- russian ## Display Selection From cc651c3290ff3b2538694b592f1df1ff9556a85e Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Sat, 5 Oct 2019 17:31:14 +0100 Subject: [PATCH 005/150] many builder fixes and support for serial gadget --- builder/pwnagotchi.yml | 44 +++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index e091dab..d2263a8 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -42,6 +42,19 @@ apt: update_cache: yes + - name: remove unecessary apt packages + apt: + name: "{{ packages }}" + state: absent + purge: yes + var: + packages: + - rasberrypi-net-mods + - dhcpcd5 + - triggerhappy + - wpa_supplicant + - nfs-common + - name: upgrade apt distro apt: upgrade: dist @@ -101,13 +114,14 @@ - python3-termcolor - python3-astor - python3-backports.weakref - - python3-keras-applications - - python3-keras-preprocessing + - python3-h5py - python3-six - python3-protobuf - python3-wrapt - python3-wheel - python3-mock + - python3-scipy + - python3-cloudpickle - name: configure dphys-swapfile file: @@ -125,8 +139,10 @@ - apt-daily.service - apt-daily-upgrade.timer - apt-daily-upgrade.service + - wpa_supplicant.service - bluetooth.service - triggerhappy.service + - ifup@wlan0.service - name: enable dphys-swapfile service systemd: @@ -134,33 +150,43 @@ state: started enabled: yes + - name: enable gadget serial port service + systemd: + name: getty@ttyGS0.service + state: started + enabled: yes + - name: acquire python3 pip target command: "python3 -c 'import sys;print(sys.path.pop())'" register: pip_target - - name: install pip packages that are not architecture tied + - name: install pip packages pip: name: "{{ packages }}" extra_args: "--no-deps --extra-index-url=https://www.piwheels.hostedpi.com/simple/ --prefer-binary --no-cache-dir --platform=armv6l --target={{ pip_target.stdout }}" vars: packages: - - absl-py + - inky + - smbus2 + - absl-py>=0.1.6 - enum34 - - gast + - gast==0.2.2 - google_pasta - opt_einsum - - tensorboard - scapy - gym + - keras_applications>=1.0.6 + - keras_preprocessing>=1.0.5 - stable-baselines - file_read_backwards - - tensorflow_estimator + - tensorflow_estimator>=1.14.0,<1.15.0 + - tensorboard>=1.13.0,<1.14.0 - 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 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://storage.googleapis.com/tensorflow/raspberrypi/tensorflow-1.14.0-cp34-none-linux_armv6l.whl" + 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: @@ -318,7 +344,7 @@ state: present backup: no regexp: '(.*)$' - line: '\1 modules-load=dwc2,g_ether' + line: '\1 modules-load=dwc2,g_cdc' - name: configure ssh lineinfile: From 74a23b3c069945f51b74f210b35e17fb3901ee0b Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Sat, 5 Oct 2019 17:41:13 +0100 Subject: [PATCH 006/150] fix typo on purge package blocks --- builder/pwnagotchi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index d2263a8..77a5cdf 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -47,7 +47,7 @@ name: "{{ packages }}" state: absent purge: yes - var: + vars: packages: - rasberrypi-net-mods - dhcpcd5 From 6ce522a2619caa4882bc11e76f7731df2a5e45d7 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 18:49:57 +0200 Subject: [PATCH 007/150] releasing v1.0.0plz3 --- sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py index c894d45..dd6c38a 100644 --- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py +++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py @@ -1,6 +1,6 @@ import subprocess -version = '1.0.0plz2' +version = '1.0.0plz3' _name = None From 7cc82bccb856b8d7773c16373d70c8f16afd32b5 Mon Sep 17 00:00:00 2001 From: swedishmike <mike@swedishmike.org> Date: Sat, 5 Oct 2019 18:11:05 +0100 Subject: [PATCH 008/150] More documentation updates for languages --- docs/configure.md | 1 + sdcard/rootfs/root/pwnagotchi/config.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/configure.md b/docs/configure.md index d69bc9a..d2bb0f5 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -41,6 +41,7 @@ For instance, you can change `main.lang` to one of the supported languages: - italian - french - russian +- swedish ## Display Selection diff --git a/sdcard/rootfs/root/pwnagotchi/config.yml b/sdcard/rootfs/root/pwnagotchi/config.yml index ae470ad..a93a7e8 100644 --- a/sdcard/rootfs/root/pwnagotchi/config.yml +++ b/sdcard/rootfs/root/pwnagotchi/config.yml @@ -1,6 +1,6 @@ # main algorithm configuration main: - # currently implemented: en (default), de, el, fr, it, mk, nl, se + # 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 custom_plugins: From 75b1e78e399ec641e3b36a3a1ee39a5c91731336 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sat, 5 Oct 2019 18:51:20 +0200 Subject: [PATCH 009/150] Refracture ansible playbook --- builder/pwnagotchi.yml | 270 ++++++++++++++++++++--------------------- 1 file changed, 133 insertions(+), 137 deletions(-) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 77a5cdf..a8dca14 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -3,29 +3,135 @@ - 127.0.0.1 become: yes vars: - pwn_hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}" - pwn_version: "{{ lookup('env', 'PWN_VERSION') | default('master', true) }} " - bettercap_query: "assets[?contains(name, 'armv6l')].browser_download_url" + 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 @@ -44,16 +150,9 @@ - name: remove unecessary apt packages apt: - name: "{{ packages }}" + name: "{{ packages.apt.remove }}" state: absent purge: yes - vars: - packages: - - rasberrypi-net-mods - - dhcpcd5 - - triggerhappy - - wpa_supplicant - - nfs-common - name: upgrade apt distro apt: @@ -61,126 +160,22 @@ - name: install packages apt: - name: "{{ packages }}" + name: "{{ packages.apt.install }}" state: present - vars: - packages: - - 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 - 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 - - wpa_supplicant.service - - bluetooth.service - - triggerhappy.service - - ifup@wlan0.service - - - name: enable dphys-swapfile service - systemd: - name: dphys-swapfile.service - state: started - enabled: yes - - - name: enable gadget serial port service - systemd: - name: getty@ttyGS0.service - state: started - enabled: yes - - name: acquire python3 pip target command: "python3 -c 'import sys;print(sys.path.pop())'" register: pip_target - name: install pip packages pip: - name: "{{ packages }}" + 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 }}" - vars: - packages: - - 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 - 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" @@ -196,7 +191,7 @@ - name: download and install bettercap unarchive: - src: "{{ bettercap_release.content | from_json | json_query(bettercap_query) | first }}" + src: "{{ bettercap_release.content | from_json | json_query(bettercap.query) | first }}" dest: /usr/bin remote_src: yes exclude: @@ -324,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: @@ -356,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: @@ -366,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*" From 006c6f30bc482a45e7357c38e32269fefb12247c Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 20:14:18 +0200 Subject: [PATCH 010/150] docs: documented NN wifi card portability (fixes #145) --- docs/install.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/install.md b/docs/install.md index 6902979..203a38f 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,6 +2,9 @@ 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 - [Raspberry Pi Zero W](https://www.raspberrypi.org/products/raspberry-pi-zero-w/). From 6a1a16556e7d065d46cbd7bd8d0b368101058489 Mon Sep 17 00:00:00 2001 From: Justin-p <justin-p@users.noreply.github.com> Date: Sat, 5 Oct 2019 20:15:12 +0200 Subject: [PATCH 011/150] Script will now ask which adaptors it should use instead of trying to find the correct ones by default --- scripts/win_connection_share.ps1 | 57 +++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/scripts/win_connection_share.ps1 b/scripts/win_connection_share.ps1 index ecbed3b..0be60d0 100644 --- a/scripts/win_connection_share.ps1 +++ b/scripts/win_connection_share.ps1 @@ -5,7 +5,7 @@ .DESCRIPTION A script that setups Internet Connection Sharing for Pwnagotchi. - Note: Internet Connection Sharing on Windows can be a bit unstable on windows between reboots. + 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 @@ -15,7 +15,7 @@ Disable Internet Connection Sharing .PARAMETER SetPwnagotchiSubnet - Change the Internet Connection Sharing subnet to the Pwnagotchi. Defaults to 10.0.0.1. + Change the Internet Connection Sharing subnet to the Pwnagotchi subnet. The USB Gadget Interface IP ip default to 10.0.0.1. .PARAMETER ScopeAddress Custom ScopeAddress (The IP Address of the USB Gadget Interface.) @@ -55,14 +55,12 @@ Function Create-HNetObjects { .DESCRIPTION A helper function that does the heavy lifiting with NetCfg.HNetShare. This returns a PSObject containing the `INetSharingConfigurationForINetConnection` info of 2 Adapters. - By default it tries to get the correct interfaces. This method might not be foolproof for every setup, but should work in most default senarios, if this causes issues these - could be passed as a param, they would need to be implemented in Setup-PwnagotchiNetwork and the Param block of this file. .PARAMETER InternetAdaptor - The output of Get-NetAdaptor filtered down to the 'main' uplink interface. Should default to the correct value. + 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. Should default to the correct value. + The output of Get-NetAdaptor filtered down to the 'USB Ethernet/RNDIS Gadget' interface. .EXAMPLE PS> $HNetObject = Create-HNetObjects @@ -73,8 +71,8 @@ Function Create-HNetObjects { #> [Cmdletbinding()] Param ( - $InternetAdaptor = $(Get-NetAdapter | Where-Object {$_.MediaConnectionState -eq 'Connected' -and $_.PhysicalMediaType -ne 'Unspecified'} | Sort-Object LinkSpeed -Descending), - $RNDISGadget = $(Get-NetAdapter | Where-Object {$_.MediaConnectionState -eq 'Connected' -and $_.InterfaceDescription -eq "USB Ethernet/RNDIS Gadget"}) + $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 @@ -190,25 +188,25 @@ Function Set-PwnagotchiSubnet { Function Setup-PwnagotchiNetwork { <# .SYNOPSIS - Function to setup networking. + Function to setup networking. .DESCRIPTION - Function to setup networking. Main function calls helpers functions. + Function to setup networking. Main function calls helpers functions. .PARAMETER EnableInternetConnectionSharing - Enable Internet Connection Sharing + Enable Internet Connection Sharing .PARAMETER DisableInternetConnectionSharing - Disable Internet Connection Sharing + Disable Internet Connection Sharing .PARAMETER SetPwnagotchiSubnet - Change the Internet Connection Sharing subnet to the Pwnagotchi. Defaults to 10.0.0.1. + Change the Internet Connection Sharing subnet to the Pwnagotchi. Defaults to 10.0.0.1. .PARAMETER ScopeAddress - Custom ScopeAddress (the ICS ip address) + Custom ScopeAddress (the ICS ip address) .EXAMPLE - PS> Setup-PwnagotchiNetwork -EnableInternetConnectionSharing + PS> Setup-PwnagotchiNetwork -EnableInternetConnectionSharing #> @@ -260,6 +258,33 @@ Function Setup-PwnagotchiNetwork { } } } - +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 \ No newline at end of file From eee5df4413145a7922dc323df7a65a4e40593b89 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 20:16:22 +0200 Subject: [PATCH 012/150] misc: small fix or general refactoring i did not bother commenting --- docs/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.md b/docs/install.md index 203a38f..b1ee1d7 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,7 +2,7 @@ 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 +**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 From 106c87847dfc06e98127959e6c478966d92bf2a2 Mon Sep 17 00:00:00 2001 From: Justin Perdok <justin-p@users.noreply.github.com> Date: Sat, 5 Oct 2019 20:23:05 +0200 Subject: [PATCH 013/150] typo --- scripts/win_connection_share.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/win_connection_share.ps1 b/scripts/win_connection_share.ps1 index 0be60d0..c7c4ece 100644 --- a/scripts/win_connection_share.ps1 +++ b/scripts/win_connection_share.ps1 @@ -15,7 +15,7 @@ Disable Internet Connection Sharing .PARAMETER SetPwnagotchiSubnet - Change the Internet Connection Sharing subnet to the Pwnagotchi subnet. The USB Gadget Interface IP ip default to 10.0.0.1. + 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.) @@ -287,4 +287,4 @@ Function Select-NetAdaptor { Return $Adaptors[$selection-1] } # Dynamically create params for Setup-PwnagotchiNetwork function based of param input of script. -Setup-PwnagotchiNetwork @psBoundParameters \ No newline at end of file +Setup-PwnagotchiNetwork @psBoundParameters From 3b9260a9be17439f06801bd48f79a698347eea24 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sat, 5 Oct 2019 15:22:18 -0400 Subject: [PATCH 014/150] Create hacks.md page for adding user mods||customizations that are not officially supported by the development team but deserve to be documented for posterity. --- docs/hacks.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 docs/hacks.md diff --git a/docs/hacks.md b/docs/hacks.md new file mode 100644 index 0000000..184cff0 --- /dev/null +++ b/docs/hacks.md @@ -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! From 52ae7fa198fe29e21bc94e02c79b817ab487ad3d Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sat, 5 Oct 2019 15:37:49 -0400 Subject: [PATCH 015/150] added Hacks.md; fixed typo; put official links into a table #124 --- docs/index.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/index.md b/docs/index.md index 806c648..b998af0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,15 +5,18 @@ - [Configuration](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md) - [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md) - [Plugins](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) -- [Developement](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) +- [Development](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) - [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) +- [Community Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) ## Links -- [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/) + | Official Links +---------|------- +Slack | [pwnagotchi.slack.com](https://pwnagotchi.herokuapp.com) +Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) +Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) +Website | [pwnagotchi.ai](https://pwnagotchi.ai/) ## License From 26297d536cfb0e0a8a56981f7695d318380c0ad6 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Sat, 5 Oct 2019 20:42:42 +0100 Subject: [PATCH 016/150] disable serial --- builder/pwnagotchi.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index a8dca14..561c64f 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -16,7 +16,6 @@ services: enable: - dphys-swapfile.service - - getty@ttyGS0.service disable: - apt-daily.timer - apt-daily.service @@ -335,7 +334,7 @@ state: present backup: no regexp: '(.*)$' - line: '\1 modules-load=dwc2,g_cdc' + line: '\1 modules-load=dwc2,g_ether' - name: configure ssh lineinfile: From 6b31726ca1a6479feda3f20b9be3c758481484bb Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 21:47:19 +0200 Subject: [PATCH 017/150] releasing v1.0.0plz4 --- sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py index dd6c38a..9905b0f 100644 --- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py +++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py @@ -1,6 +1,6 @@ import subprocess -version = '1.0.0plz3' +version = '1.0.0plz4' _name = None From b38a3cfb39dadb7a0fcd9eeeafbd7f80882c39ae Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sat, 5 Oct 2019 16:17:54 -0400 Subject: [PATCH 018/150] updated main text; added alpha disclaimer to Docs section; added hacks.md to TOC [x] updated text of README.md to match readability of about.md text [x] fixed typo in TOC [x] added hacks.md to TOC [x] official links are now in tabular form [x] added subreddit link [x] added link to the **Training the AI** section of usage.md --- README.md | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8ed5369..b2e30f8 100644 --- a/README.md +++ b/README.md @@ -9,36 +9,49 @@ </p> </p> -[Pwnagotchi](https://twitter.com/pwnagotchi) is an "AI" that learns from the WiFi environment and instruments bettercap in order to maximize the WPA key material (any form of handshake that is crackable, 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) captured. +[Pwnagotchi](https://twitter.com/pwnagotchi) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding WiFi environment in order to maximize the crackable WPA key material it captures (either passively, or by performing deauthentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/), +full and half WPA handshakes.  -Specifically, it's using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html), here is [a very good intro](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) on the subject. +Specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) doc.) -Instead of playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced), pwnagotchi will tune over time [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54), effectively learning to get better at pwning WiFi things. **Keep in mind:** unlike the usual RL simulations, pwnagotchi learns over time (where a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible), do not expect it to perform amazingly well at the beginning, as it'll be exploring several combinations of parameters ... but listen to it when it's bored, bring it with you and have it observe new networks and capture new handshakes and you'll see :) -Multiple units can talk to each other, advertising their own presence using a parasite protocol I've built on top of the existing dot11 standard, by broadcasting custom information elements. Over time, two or more units learn to cooperate if they detect each other's presence, by dividing the available channels among them. +Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54) over time to **get better at pwning WiFi things** in the environments you expose it to. -## Why +**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi actually learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but **definitely listen to your pwnagotchi when it tells you it's bored!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) -For hackers to learn reinforcement learning, WiFi networking and have an excuse to take a walk more often. And **it's cute as f---**. +Multiple units within close physical proximity can "talk" to each other, advertising their own presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard. Over time, two or more units trained together will learn to cooperate upon detecting each other's presence by dividing the available channels among them for optimal pwnage. + +## Why does Pwnagotchi exist? + +For hackers to learn reinforcement learning, WiFi networking, and have an excuse to get out for more walks. Also? **It's cute as f---**. ## Documentation +--- +:warning: **THE FOLLOWING DOCUMENTATION IS BEING PREPARED FOR THE v1.0 RELEASE OF PWNAGOTCHI. Since this effort is an active (and unstable) work-in-progress, the docs displayed here are in various stages of [in]completion. There will be dead links and placeholders throughout as we are still building things out in preparation for the v1.0 release.** :warning: +**IMPORTANT NOTE:** If you'd like to alphatest Pwnagotchi and are trying to get yours up and running while the project is still very unstable, please understand that the documentation here may not reflect what is currently implemented. If you have questions, ask the community of alphatesters in the [official Pwnagotchi Slack](https://pwnagotchi.herokuapp.com). The Pwnagotchi dev team is entirely focused on the v1.0 release and will NOT be providing support for alphatesters trying to get their Pwnagotchis working in the meantime. All technical support during this period of development is being provided by your fellow alphatesters in the Slack (thanks, everybody! :heart:). + +--- - [About the Project](https://github.com/evilsocket/pwnagotchi/blob/master/docs/about.md) +- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) - [How to Install](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md) - [Configuration](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md) - [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md) - [Plugins](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) -- [Developement](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) -- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) +- [Development](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) +- [Community Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) ## Links -- [Project Slack](https://join.slack.com/t/pwnagotchi/shared_invite/enQtNzc4NzY3MDE2OTAzLTg5NmNmNDJiMDM3ZWFkMWUwN2Y5NDk0Y2JlZWZjODlhMmRhNDZiOGMwYjJhM2UzNzA3YjA5NjJmZGY5NGI5NmI) -- [Project Twitter](https://twitter.com/pwnagotchi) -- [Project Website](https://pwnagotchi.ai/) + | Official Links +---------|------- +Slack | [pwnagotchi.slack.com](https://pwnagotchi.herokuapp.com) +Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) +Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) +Website | [pwnagotchi.ai](https://pwnagotchi.ai/) ## License -`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It's released under the GPL3 license. \ No newline at end of file +`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It is released under the GPL3 license. From 01271acb92dd83e7f4892e389687537deeee4545 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 22:23:58 +0200 Subject: [PATCH 019/150] removed unused files --- sdcard/boot/cmdline.txt | 1 - sdcard/boot/config.txt | 1202 ----------------- sdcard/boot/ssh | 0 sdcard/rootfs/etc/hostname | 1 - sdcard/rootfs/etc/hosts | 6 - sdcard/rootfs/etc/motd | 1 - sdcard/rootfs/etc/network/interfaces | 17 - sdcard/rootfs/etc/rc.local | 19 - sdcard/rootfs/etc/ssh/sshd_config | 121 -- .../pwnagotchi/data/images/face_happy.bmp | Bin 11398 -> 0 bytes sdcard/rootfs/usr/bin/cpuusage | 51 - sdcard/rootfs/usr/bin/memusage | 1 - sdcard/rootfs/usr/bin/monstart | 2 - sdcard/rootfs/usr/bin/monstop | 2 - 14 files changed, 1424 deletions(-) delete mode 100755 sdcard/boot/cmdline.txt delete mode 100755 sdcard/boot/config.txt delete mode 100644 sdcard/boot/ssh delete mode 100644 sdcard/rootfs/etc/hostname delete mode 100644 sdcard/rootfs/etc/hosts delete mode 100644 sdcard/rootfs/etc/motd delete mode 100644 sdcard/rootfs/etc/network/interfaces delete mode 100755 sdcard/rootfs/etc/rc.local delete mode 100644 sdcard/rootfs/etc/ssh/sshd_config delete mode 100644 sdcard/rootfs/root/pwnagotchi/data/images/face_happy.bmp delete mode 100755 sdcard/rootfs/usr/bin/cpuusage delete mode 100755 sdcard/rootfs/usr/bin/memusage delete mode 100755 sdcard/rootfs/usr/bin/monstart delete mode 100755 sdcard/rootfs/usr/bin/monstop diff --git a/sdcard/boot/cmdline.txt b/sdcard/boot/cmdline.txt deleted file mode 100755 index 6c44ae3..0000000 --- a/sdcard/boot/cmdline.txt +++ /dev/null @@ -1 +0,0 @@ -dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait modules-load=dwc2,g_ether diff --git a/sdcard/boot/config.txt b/sdcard/boot/config.txt deleted file mode 100755 index 4d69af8..0000000 --- a/sdcard/boot/config.txt +++ /dev/null @@ -1,1202 +0,0 @@ -################################################################################ -## Raspberry Pi Configuration Settings -## -## Revision 17, 2018/04/22 -## -## Details taken from the eLinux wiki -## For up-to-date information please refer to wiki page. -## -## Wiki Location : http://elinux.org/RPiconfig -## -## -## Description: -## Details of each setting are described with each section that begins with -## a double hashed comment ('##') -## It is up to the user to remove the single hashed comment ('#') from each -## option they want to enable, and to set the specific value of that option. -## -## Overclock settings will be disabled at runtime if the SoC reaches temp_limit -## -## Originally based off https://github.com/Evilpaul/RPi-config/ with minor -## update because display_rotate is deprecated. -## -################################################################################ - -################################################################################ -## Standard Definition Video Settings -################################################################################ - -## sdtv_mode -## defines the TV standard for composite output -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Normal NTSC (Default) -## 1 Japanese version of NTSC - no pedestal -## 2 Normal PAL -## 3 Brazilian version of PAL - 525/60 rather than 625/50, different -## subcarrier -## -#sdtv_mode=0 - -## sdtv_aspect -## defines the aspect ratio for composite output -## -## Value Description -## ------------------------------------------------------------------------- -## 1 4:3 (Default) -## 2 14:9 -## 3 16:9 -## -#sdtv_aspect=1 - -## sdtv_disable_colourburst -## Disables colour burst on composite output. The picture will be -## monochrome, but possibly sharper -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Colour burst is enabled (Default) -## 1 Colour burst is disabled -## -#sdtv_disable_colourburst=1 - -################################################################################ -## High Definition Video Settings -################################################################################ - -## hdmi_safe -## Use "safe mode" settings to try to boot with maximum hdmi compatibility. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Disabled (Default) -## 1 Enabled (this does: hdmi_force_hotplug=1, -## hdmi_ignore_edid=0xa5000080, -## config_hdmi_boost=4, hdmi_group=2, -## hdmi_mode=4, disable_overscan=0, -## overscan_left=24, overscan_right=24, -## overscan_top=24, overscan_bottom=24) -## -#hdmi_safe=1 - -## hdmi_force_hotplug -## Pretends HDMI hotplug signal is asserted so it appears a HDMI display -## is attached -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Disabled (Default) -## 1 Use HDMI mode even if no HDMI monitor is detected -## -#hdmi_force_hotplug=1 - -## hdmi_ignore_hotplug -## Pretends HDMI hotplug signal is not asserted so it appears a HDMI -## display is not attached -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Disabled (Default) -## 1 Use composite mode even if HDMI monitor is detected -## -#hdmi_ignore_hotplug=1 - -## hdmi_drive -## chooses between HDMI and DVI modes -## -## Value Description -## ------------------------------------------------------------------------- -## 1 Normal DVI mode (No sound) -## 2 Normal HDMI mode (Sound will be sent if supported and enabled) -## -#hdmi_drive=2 - -## hdmi_ignore_edid -## Enables the ignoring of EDID/display data -## -#hdmi_ignore_edid=0xa5000080 - -## hdmi_edid_file -## Read the EDID data from the edid.dat file instead of from the attached -## device -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Read EDID data from attached device (Default) -## 1 Read EDID data from edid.txt file -## -#hdmi_edid_file=1 - -## hdmi_ignore_edid_audio -## Pretends all audio formats are unsupported by display. This means ALSA -## will default to analogue. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Use EDID provided values (Default) -## 1 Pretend all audio formats are unsupported -## -#hdmi_ignore_edid_audio=1 - -## hdmi_force_edid_audio -## Pretends all audio formats are supported by display, allowing -## passthrough of DTS/AC3 even when not reported as supported. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Use EDID provided values (Default) -## 1 Pretend all audio formats are supported -## -#hdmi_force_edid_audio=1 - -## hdmi_force_edid_3d -## Pretends all CEA modes support 3D even when edid doesn't indicate -## support for them. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Use EDID provided values (Default) -## 1 Pretend 3D mode is supported -## -#hdmi_force_edid_3d=1 - -## avoid_edid_fuzzy_match -## Avoid fuzzy matching of modes described in edid. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Use fuzzy matching (Default) -## 1 Avoid fuzzy matching -## -#avoid_edid_fuzzy_match=1 - -## hdmi_pixel_encoding -## Force the pixel encoding mode. -## By default it will use the mode requested from edid so shouldn't -## need changing. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Use EDID provided values (Default) -## 1 RGB limited (16-235) -## 2 RGB full ( 0-255) -## 3 YCbCr limited (16-235) -## 4 YCbCr limited ( 0-255) -## -#hdmi_pixel_encoding=1 - -## hdmi_group -## Defines the HDMI type -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Use the preferred group reported by the edid (Default) -## 1 CEA -## 2 DMT -## -#hdmi_group=1 - -## hdmi_mode -## defines screen resolution in CEA or DMT format -## -## H means 16:9 variant (of a normally 4:3 mode). -## 2x means pixel doubled (i.e. higher clock rate, with each pixel repeated -## twice) -## 4x means pixel quadrupled (i.e. higher clock rate, with each pixel -## repeated four times) -## reduced blanking means fewer bytes are used for blanking within the data -## stream (i.e. lower clock rate, with fewer wasted bytes) -## -## Value hdmi_group=CEA hdmi_group=DMT -## ------------------------------------------------------------------------- -## 1 VGA 640x350 85Hz -## 2 480p 60Hz 640x400 85Hz -## 3 480p 60Hz H 720x400 85Hz -## 4 720p 60Hz 640x480 60Hz -## 5 1080i 60Hz 640x480 72Hz -## 6 480i 60Hz 640x480 75Hz -## 7 480i 60Hz H 640x480 85Hz -## 8 240p 60Hz 800x600 56Hz -## 9 240p 60Hz H 800x600 60Hz -## 10 480i 60Hz 4x 800x600 72Hz -## 11 480i 60Hz 4x H 800x600 75Hz -## 12 240p 60Hz 4x 800x600 85Hz -## 13 240p 60Hz 4x H 800x600 120Hz -## 14 480p 60Hz 2x 848x480 60Hz -## 15 480p 60Hz 2x H 1024x768 43Hz DO NOT USE -## 16 1080p 60Hz 1024x768 60Hz -## 17 576p 50Hz 1024x768 70Hz -## 18 576p 50Hz H 1024x768 75Hz -## 19 720p 50Hz 1024x768 85Hz -## 20 1080i 50Hz 1024x768 120Hz -## 21 576i 50Hz 1152x864 75Hz -## 22 576i 50Hz H 1280x768 reduced blanking -## 23 288p 50Hz 1280x768 60Hz -## 24 288p 50Hz H 1280x768 75Hz -## 25 576i 50Hz 4x 1280x768 85Hz -## 26 576i 50Hz 4x H 1280x768 120Hz reduced blanking -## 27 288p 50Hz 4x 1280x800 reduced blanking -## 28 288p 50Hz 4x H 1280x800 60Hz -## 29 576p 50Hz 2x 1280x800 75Hz -## 30 576p 50Hz 2x H 1280x800 85Hz -## 31 1080p 50Hz 1280x800 120Hz reduced blanking -## 32 1080p 24Hz 1280x960 60Hz -## 33 1080p 25Hz 1280x960 85Hz -## 34 1080p 30Hz 1280x960 120Hz reduced blanking -## 35 480p 60Hz 4x 1280x1024 60Hz -## 36 480p 60Hz 4x H 1280x1024 75Hz -## 37 576p 50Hz 4x 1280x1024 85Hz -## 38 576p 50Hz 4x H 1280x1024 120Hz reduced blanking -## 39 1080i 50Hz reduced blanking 1360x768 60Hz -## 40 1080i 100Hz 1360x768 120Hz reduced blanking -## 41 720p 100Hz 1400x1050 reduced blanking -## 42 576p 100Hz 1400x1050 60Hz -## 43 576p 100Hz H 1400x1050 75Hz -## 44 576i 100Hz 1400x1050 85Hz -## 45 576i 100Hz H 1400x1050 120Hz reduced blanking -## 46 1080i 120Hz 1440x900 reduced blanking -## 47 720p 120Hz 1440x900 60Hz -## 48 480p 120Hz 1440x900 75Hz -## 49 480p 120Hz H 1440x900 85Hz -## 50 480i 120Hz 1440x900 120Hz reduced blanking -## 51 480i 120Hz H 1600x1200 60Hz -## 52 576p 200Hz 1600x1200 65Hz -## 53 576p 200Hz H 1600x1200 70Hz -## 54 576i 200Hz 1600x1200 75Hz -## 55 576i 200Hz H 1600x1200 85Hz -## 56 480p 240Hz 1600x1200 120Hz reduced blanking -## 57 480p 240Hz H 1680x1050 reduced blanking -## 58 480i 240Hz 1680x1050 60Hz -## 59 480i 240Hz H 1680x1050 75Hz -## 60 1680x1050 85Hz -## 61 1680x1050 120Hz reduced blanking -## 62 1792x1344 60Hz -## 63 1792x1344 75Hz -## 64 1792x1344 120Hz reduced blanking -## 65 1856x1392 60Hz -## 66 1856x1392 75Hz -## 67 1856x1392 120Hz reduced blanking -## 68 1920x1200 reduced blanking -## 69 1920x1200 60Hz -## 70 1920x1200 75Hz -## 71 1920x1200 85Hz -## 72 1920x1200 120Hz reduced blanking -## 73 1920x1440 60Hz -## 74 1920x1440 75Hz -## 75 1920x1440 120Hz reduced blanking -## 76 2560x1600 reduced blanking -## 77 2560x1600 60Hz -## 78 2560x1600 75Hz -## 79 2560x1600 85Hz -## 80 2560x1600 120Hz reduced blanking -## 81 1366x768 60Hz -## 82 1080p 60Hz -## 83 1600x900 reduced blanking -## 84 2048x1152 reduced blanking -## 85 720p 60Hz -## 86 1366x768 reduced blanking -## -#hdmi_mode=1 - -## config_hdmi_boost -## configure the signal strength of the HDMI interface. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 (Default) -## 1 -## 2 -## 3 -## 4 Try if you have interference issues with HDMI -## 5 -## 6 -## 7 Maximum -## -#config_hdmi_boost=0 - -## hdmi_ignore_cec_init -## Doesn't sent initial active source message. Avoids bringing -## (CEC enabled) TV out of standby and channel switch when rebooting. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Normal behaviour (Default) -## 1 Doesn't sent initial active source message -## -#hdmi_ignore_cec_init=1 - -## hdmi_ignore_cec -## Pretends CEC is not supported at all by TV. -## No CEC functions will be supported. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Normal behaviour (Default) -## 1 Pretend CEC is not supported by TV -## -#hdmi_ignore_cec=1 - -################################################################################ -## Overscan Video Settings -################################################################################ - -## overscan_left -## Number of pixels to skip on left -## -#overscan_left=0 - -## overscan_right -## Number of pixels to skip on right -## -#overscan_right=0 - -## overscan_top -## Number of pixels to skip on top -## -#overscan_top=0 - -## overscan_bottom -## Number of pixels to skip on bottom -## -#overscan_bottom=0 - -## disable_overscan -## Set to 1 to disable overscan -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Overscan Enabled (Default) -## 1 Overscan Disabled -## -#disable_overscan=1 - -################################################################################ -## Framebuffer Video Settings -################################################################################ - -## framebuffer_width -## Console framebuffer width in pixels. Default is display width minus -## overscan. -## -#framebuffer_width=0 - -## framebuffer_height -## Console framebuffer height in pixels. Default is display height minus -## overscan. -## -#framebuffer_height=0 - -## framebuffer_depth -## Console framebuffer depth in bits per pixel. -## -## Value Description -## ------------------------------------------------------------------------- -## 8 Valid, but default RGB palette makes an unreadable screen -## 16 (Default) -## 24 Looks better but has corruption issues as of 2012/06/15 -## 32 Has no corruption issues but needs framebuffer_ignore_alpha=1 -## and shows the wrong colors as of 2012/06/15 -## -#framebuffer_depth=16 - -## framebuffer_ignore_alpha -## Set to 1 to disable alpha channel. Helps with 32bit. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Enable Alpha Channel (Default) -## 1 Disable Alpha Channel -## -#framebuffer_ignore_alpha=0 - -################################################################################ -## General Video Settings -################################################################################ - -## display_rotate -## Rotate the display clockwise or flip the display. -## The 90 and 270 degrees rotation options require additional memory on GPU, -## so won't work with the 16M GPU split. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 0 degrees (Default) -## 1 90 degrees -## 2 180 degrees -## 3 270 degrees -## 0x10000 Horizontal flip -## 0x20000 Vertical flip -## -## Note: latest firmware deprecates display_rotate so use these instead. -#display_hdmi_rotate=0 -#display_lcd_rotate=0 - -## dispmanx_offline -## Set to "1" to enable offline compositing -## -## Default 0 -## -#dispmanx_offline=0 - -################################################################################ -## Licensed Codecs -## -## Hardware decoding of additional codecs can be enabled by purchasing a -## license that is locked to the CPU serial number of your Raspberry Pi. -## -## Up to 8 licenses per CODEC can be specified as a comma seperated list. -## -################################################################################ - -## decode_MPG2 -## License key to allow hardware MPEG-2 decoding. -## -#decode_MPG2=0x12345678 - -## decode_WVC1 -## License key to allow hardware VC-1 decoding. -## -#decode_WVC1=0x12345678 - -################################################################################ -## Camera Settings -################################################################################ - -## start_x -## Set to "1" to enable the camera module. -## -## Enabling the camera requires gpu_mem option to be specified with a value -## of at least 128. -## -## Default 0 -## -#start_x=0 - -## disable_camera_led -## Turn off the red camera led when recording video or taking a still -## picture. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 LED enabled (Default) -## 1 LED disabled -## -#disable_camera_led=1 - -################################################################################ -## Test Settings -################################################################################ - -## test_mode -## Enable test sound/image during boot for manufacturing test. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Disable Test Mod (Default) -## 1 Enable Test Mode -## -#test_mode=0 - -################################################################################ -## Memory Settings -################################################################################ - -## disable_l2cache -## Disable arm access to GPU's L2 cache. Needs corresponding L2 disabled -## kernel. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Enable L2 Cache (Default) -## 1 Disable L2 cache -## -#disable_l2cache=0 - -## gpu_mem -## GPU memory allocation in MB for all board revisions. -## -## Default 64 -## -#gpu_mem=128 - -## gpu_mem_256 -## GPU memory allocation in MB for 256MB board revision. -## This option overrides gpu_mem. -## -#gpu_mem_256=192 - -## gpu_mem_512 -## GPU memory allocation in MB for 512MB board revision. -## This option overrides gpu_mem. -## -#gpu_mem_512=448 - -## gpu_mem_1024 -## GPU memory allocation in MB for 1024MB board revision. -## This option overrides gpu_mem. -## -#gpu_mem_1024=944 - -## disable_pvt -## Disable adjusting the refresh rate of RAM every 500ms -## (measuring RAM temparature). -## -#disable_pvt=1 - -################################################################################ -## CMA - Dynamic Memory Split -## -## CMA enables dynamic management of the ARM and GPU memory split at runtime. -## -## The following options need to be in cmdline.txt for CMA to work: -## coherent_pool=6M smsc95xx.turbo_mode=N -## -################################################################################ - -## cma_lwm -## When GPU has less than cma_lwm (low water mark) memory available it -## will request some from ARM. -## -#cma_lwm=16 - -## cma_hwm -## When GPU has more than cma_hwm (high water mark) memory available it -## will release some to ARM. -## -#cma_hwm=32 - -################################################################################ -## Boot Option Settings -################################################################################ - -## disable_commandline_tags -## Stop start.elf from filling in ATAGS (memory from 0x100) before -## launching kernel -## -#disable_commandline_tags=0 - -## cmdline (string) -## Command line parameters. Can be used instead of cmdline.txt file -## -#cmdline="" - -## kernel (string) -## Alternative name to use when loading kernel. -## -#kernel="" - -## kernel_address -## Address to load kernel.img file at -## -#kernel_address=0x00000000 - -## kernel_old -## Support loading old kernels -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Disabled (Default) -## 1 Load kernel at address 0x00000000 -## -#kernel_old=1 - -## ramfsfile (string) -## ramfs file to load -## -#ramfsfile="" - -## ramfsaddr -## Address to load ramfs file at -## -#ramfsaddr=0x00000000 - -## initramfs (string address) -## ramfs file and address to load it at (it's like ramfsfile+ramfsaddr in -## one option). -## -## NOTE: this option uses different syntax than all other options - you -## should not use "=" character here. -## -#initramfs initramf.gz 0x00800000 - -## device_tree_address -## Address to load device_tree at -## -#device_tree_address=0x00000000 - -## init_uart_baud -## Initial uart baud rate. -## -## Default 115200 -## -#init_uart_baud=115200 - -## init_uart_clock -## Initial uart clock. -## -## Default 3000000 (3MHz) -## -#init_uart_clock=3000000 - -## init_emmc_clock -## Initial emmc clock, increasing this can speedup your SD-card. -## -## Default 100000000 (100mhz) -## -#init_emmc_clock=100000000 - -## boot_delay -## Wait for a given number of seconds in start.elf before loading -## kernel.img. -## -## delay = (1000 * boot_delay) + boot_delay_ms -## -## Default 1 -## -#boot_delay=0 - -## boot_delay_ms -## Wait for a given number of milliseconds in start.elf before loading -## kernel.img. -## -## delay = (1000 * boot_delay) + boot_delay_ms -## -## Default 0 -## -#boot_delay_ms=0 - -## avoid_safe_mode -## Adding a jumper between pins 5 & 6 of P1 enables a recovery Safe Mode. -## If pins 5 & 6 are used for connecting to external devices (e.g. GPIO), -## then this setting can be used to ensure Safe Mode is not triggered. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Respect Safe Mode input (Default) -## 1 Ignore Safe Mode input -## -#avoid_safe_mode=1 - -## disable_splash -## Avoids the rainbow splash screen on boot. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Splash screen enabled (Default) -## 1 Splash screen disabled -## -#disable_splash=1 - -################################################################################ -## Overclocking Settings -## -## ARM, SDRAM and GPU each have their own PLLs and can have unrelated -## frequencies. -## -## The GPU core, h264, v3d and isp share a PLL, so need to have related -## frequencies. -## pll_freq = floor(2400 / (2 * core_freq)) * (2 * core_freq) -## gpu_freq = pll_freq / [even number] -## -## The effective gpu_freq is automatically rounded to nearest even integer, so -## asking for core_freq = 500 and gpu_freq = 300 will result in divisor of -## 2000/300 = 6.666 => 6 and so 333.33MHz. -## -## -## Standard Profiles: -## arm_freq core_freq sdram_freq over_voltage -## ------------------------------------------------------------------------- -## None 700 250 400 0 -## Modest 800 300 400 0 -## Medium 900 333 450 2 -## High 950 450 450 6 -## Turbo 1000 500 500 6 -## -################################################################################ - -## force_turbo -## Control the kernel "ondemand" governor. It has no effect if no overclock -## settings are specified. -## May set warrany bit. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Enable dynamic clocks and voltage for the ARM core, GPU core and -## SDRAM (Default). -## Overclocking of h264_freq, v3d_freq and isp_freq is ignored. -## 1 Disable dynamic clocks and voltage for the ARM core, GPU core -## and SDRAM. -## Overclocking of h264_freq, v3d_freq and isp_freq is allowed. -## -#force_turbo=0 - -## initial_turbo -## Enables turbo mode from boot for the given value in seconds (up to 60) -## or until cpufreq sets a frequency. Can help with sdcard corruption if -## overclocked. -## -## Default 0 -## -#initial_turbo=0 - -## temp_limit -## Overheat protection. Sets clocks and voltages to default when the SoC -## reaches this Celsius value. -## Setting this higher than default voids warranty. -## -## Default 85 -## -#temp_limit=85 - -## arm_freq -## Frequency of ARM in MHz. -## -## Default 700. -## -#arm_freq=700 - -## arm_freq_min -## Minimum frequency of ARM in MHz (used for dynamic clocking). -## -## Default 700. -## -#arm_freq_min=700 - -## gpu_freq -## Sets core_freq, h264_freq, isp_freq, v3d_freq together. -## -## Default 250. -## -#gpu_freq=250 - -## core_freq -## Frequency of GPU processor core in MHz. It has an impact on ARM -## performance since it drives L2 cache. -## -## Default 250. -## -#core_freq=250 - -## core_freq_min -## Minimum frequency of GPU processor core in MHz (used for dynamic -## clocking). It has an impact on ARM performance since it drives L2 cache. -## -## Default 250. -## -#core_freq_min=250 - -## h264_freq -## Frequency of hardware video block in MHz. -## -## Default 250. -## -#h264_freq=250 - -## isp_freq -## Frequency of image sensor pipeline block in MHz. -## -## Default 250. -## -#isp_freq=250 - -## v3d_freq -## Frequency of 3D block in MHz. -## -## Default 250. -## -#v3d_freq=250 - -## sdram_freq -## Frequency of SDRAM in MHz. -## -## Default 400. -## -#sdram_freq=400 - -## sdram_freq_min -## Minimum frequency of SDRAM in MHz (used for dynamic clocking). -## -## Default 400. -## -#sdram_freq_min=400 - -## avoid_pwm_pll -## Don't dedicate a pll to PWM audio. This will reduce analogue audio -## quality slightly. The spare PLL allows the core_freq to be set -## independently from the rest of the gpu allowing more control over -## overclocking. -## -## Value Description -## ------------------------------------------------------------------------- -## 0 Linked core_freq (Default) -## 1 Unlinked core_freq -## -#avoid_pwm_pll=1 - -################################################################################ -## Voltage Settings -################################################################################ - -## current_limit_override -## Disables SMPS current limit protection. Can help if you are currently -## hitting a reboot failure when overclocking too high. -## May set warrany bit. -## -#current_limit_override=0x5A000020 - -## over_voltage -## ARM/GPU core voltage adjust. -## May set warrany bit. -## -## Value Description -## ------------------------------------------------------------------------- -## -16 0.8 V -## -15 0.825 V -## -14 0.85 V -## -13 0.875 V -## -12 0.9 V -## -11 0.925 V -## -10 0.95 V -## -9 0.975 V -## -8 1.0 V -## -7 1.025 V -## -6 1.05 V -## -5 1.075 V -## -4 1.1 V -## -3 1.125 V -## -2 1.15 V -## -1 1.175 V -## 0 1.2 V (Default) -## 1 1.225 V -## 2 1.25 V -## 3 1.275 V -## 4 1.3 V -## 5 1.325 V -## 6 1.35 V -## 7 1.375 V (requires force_turbo=1 or current_limit_override) -## 8 1.4 V (requires force_turbo=1 or current_limit_override) -## -#over_voltage=0 - -## over_voltage_min -## Minimum ARM/GPU core voltage adjust (used for dynamic clocking). -## -## Value Description -## ------------------------------------------------------------------------- -## -16 0.8 V -## -15 0.825 V -## -14 0.85 V -## -13 0.875 V -## -12 0.9 V -## -11 0.925 V -## -10 0.95 V -## -9 0.975 V -## -8 1.0 V -## -7 1.025 V -## -6 1.05 V -## -5 1.075 V -## -4 1.1 V -## -3 1.125 V -## -2 1.15 V -## -1 1.175 V -## 0 1.2 V (Default) -## 1 1.225 V -## 2 1.25 V -## 3 1.275 V -## 4 1.3 V -## 5 1.325 V -## 6 1.35 V -## 7 1.375 V (requires force_turbo=1) -## 8 1.4 V (requires force_turbo=1) -## -#over_voltage_min=0 - -## over_voltage_sdram -## Sets over_voltage_sdram_c, over_voltage_sdram_i, over_voltage_sdram_p -## together -## -## Value Description -## ------------------------------------------------------------------------- -## -16 0.8 V -## -15 0.825 V -## -14 0.85 V -## -13 0.875 V -## -12 0.9 V -## -11 0.925 V -## -10 0.95 V -## -9 0.975 V -## -8 1.0 V -## -7 1.025 V -## -6 1.05 V -## -5 1.075 V -## -4 1.1 V -## -3 1.125 V -## -2 1.15 V -## -1 1.175 V -## 0 1.2 V (Default) -## 1 1.225 V -## 2 1.25 V -## 3 1.275 V -## 4 1.3 V -## 5 1.325 V -## 6 1.35 V -## 7 1.375 V -## 8 1.4 V -## -#over_voltage_sdram=0 - -## over_voltage_sdram_c -## SDRAM controller voltage adjust. -## -## Value Description -## ------------------------------------------------------------------------- -## -16 0.8 V -## -15 0.825 V -## -14 0.85 V -## -13 0.875 V -## -12 0.9 V -## -11 0.925 V -## -10 0.95 V -## -9 0.975 V -## -8 1.0 V -## -7 1.025 V -## -6 1.05 V -## -5 1.075 V -## -4 1.1 V -## -3 1.125 V -## -2 1.15 V -## -1 1.175 V -## 0 1.2 V (Default) -## 1 1.225 V -## 2 1.25 V -## 3 1.275 V -## 4 1.3 V -## 5 1.325 V -## 6 1.35 V -## 7 1.375 V -## 8 1.4 V -## -#over_voltage_sdram_c=0 - -## over_voltage_sdram_i -## SDRAM I/O voltage adjust. -## -## Value Description -## ------------------------------------------------------------------------- -## -16 0.8 V -## -15 0.825 V -## -14 0.85 V -## -13 0.875 V -## -12 0.9 V -## -11 0.925 V -## -10 0.95 V -## -9 0.975 V -## -8 1.0 V -## -7 1.025 V -## -6 1.05 V -## -5 1.075 V -## -4 1.1 V -## -3 1.125 V -## -2 1.15 V -## -1 1.175 V -## 0 1.2 V (Default) -## 1 1.225 V -## 2 1.25 V -## 3 1.275 V -## 4 1.3 V -## 5 1.325 V -## 6 1.35 V -## 7 1.375 V -## 8 1.4 V -## -#over_voltage_sdram_i=0 - -## over_voltage_sdram_p -## SDRAM phy voltage adjust. -## -## Value Description -## ------------------------------------------------------------------------- -## -16 0.8 V -## -15 0.825 V -## -14 0.85 V -## -13 0.875 V -## -12 0.9 V -## -11 0.925 V -## -10 0.95 V -## -9 0.975 V -## -8 1.0 V -## -7 1.025 V -## -6 1.05 V -## -5 1.075 V -## -4 1.1 V -## -3 1.125 V -## -2 1.15 V -## -1 1.175 V -## 0 1.2 V (Default) -## 1 1.225 V -## 2 1.25 V -## 3 1.275 V -## 4 1.3 V -## 5 1.325 V -## 6 1.35 V -## 7 1.375 V -## 8 1.4 V -## -#over_voltage_sdram_p=0 - -################################################################################ -## USB Power -################################################################################ - -## max_usb_current -## When set to 1, change the output current limit (for all 4 USB -## ports combined) from 600mA to double that, 1200mA. -## -## This option is not available for Model A/B boards. -## -## Default 0. -## -#max_usb_current=0 - -################################################################################ -## Base Device Tree Parameters -################################################################################ - -## audio -## Enable the onboard ALSA audio -## -## Default off. -## -#dtparam=audio=off - -## i2c_arm -## Enable the ARM's i2c interface -## -## Default off. -## -#dtparam=i2c_arm=off - -## i2c_vc -## Enable the i2c interface -## -## Usually reserved for the VideoCore processor -## -## Default off. -## -#dtparam=i2c_vc=off - -## i2c_arm_baudrate -## Set the baudrate of the ARM's i2c interface -## -## Default 100000. -## -#dtparam=i2c_arm_baudrate=100000 - -## i2c_vc_baudrate -## Set the baudrate of the VideoCore i2c interface -## -## Default 100000. -## -#dtparam=i2c_vc_baudrate=100000 - -## i2s -## Set to "on" to enable the i2s interface -## -## Default off. -## -#dtparam=i2s=off - -## spi -## Set to "on" to enable the spi interfaces -## -## Default off. -## -#dtparam=spi=off - -## random -## Set to "on" to enable the hardware random -## -## Default off. -## -#dtparam=random=off - -## uart0 -## Set to "off" to disable uart0 -## -## Default on. -## -#dtparam=uart0=on - -## watchdog -## Set to "on" to enable the hardware watchdog -## -## Default off. -## -#dtparam=watchdog=off - -## act_led_trigger -## Choose which activity the LED tracks. -## -## Use "heartbeat" for a nice load indicator. -## -## Default mmc. -## -#dtparam=act_led_trigger=mmc - -## act_led_activelow -## Set to "on" to invert the sense of the LED -## -## Default off. -## -#dtparam=act_led_activelow=off - -## act_led_gpio -## Set which GPIO to use for the activity LED -## -## In case you want to connect it to an external device -## -## Default 16 on a non-Plus board, 47 on a Plus or Pi 2. -## -#dtparam=act_led_gpio=47 - -## pwr_led_trigger -## Choose which activity the LED tracks. -## -## Use "heartbeat" for a nice load indicator. -## -## Not available on Model A/B boards. -## -## Default mmc. -## -#dtparam=pwr_led_trigger=mmc - -## pwr_led_activelow -## Set to "on" to invert the sense of the LED -## -## Not available on Model A/B boards. -## -## Default off. -## -#dtparam=pwr_led_activelow=off - -## pwr_led_gpio -## Set which GPIO to use for the PWR LED -## -## In case you want to connect it to an external device -## -## Not available on Model A/B boards. -## -## Default 35. -## -#dtparam=pwr_led_gpio=35 - -dtoverlay=dwc2 -dtparam=spi=on -dtoverlay=spi1-3cs - -# Disable bluetooth -dtoverlay=pi3-disable-bt -# Disable audio -dtparam=audio=off - diff --git a/sdcard/boot/ssh b/sdcard/boot/ssh deleted file mode 100644 index e69de29..0000000 diff --git a/sdcard/rootfs/etc/hostname b/sdcard/rootfs/etc/hostname deleted file mode 100644 index 4a58007..0000000 --- a/sdcard/rootfs/etc/hostname +++ /dev/null @@ -1 +0,0 @@ -alpha diff --git a/sdcard/rootfs/etc/hosts b/sdcard/rootfs/etc/hosts deleted file mode 100644 index 02498b0..0000000 --- a/sdcard/rootfs/etc/hosts +++ /dev/null @@ -1,6 +0,0 @@ -127.0.0.1 localhost alpha alpha.local -::1 localhost ip6-localhost ip6-loopback -fe00::0 ip6-localnet -ff00::0 ip6-mcastprefix -ff02::1 ip6-allnodes -ff02::2 ip6-allrouters diff --git a/sdcard/rootfs/etc/motd b/sdcard/rootfs/etc/motd deleted file mode 100644 index 6c86778..0000000 --- a/sdcard/rootfs/etc/motd +++ /dev/null @@ -1 +0,0 @@ -(◕‿‿◕) alpha (pwnagotchi) diff --git a/sdcard/rootfs/etc/network/interfaces b/sdcard/rootfs/etc/network/interfaces deleted file mode 100644 index 2c84f3c..0000000 --- a/sdcard/rootfs/etc/network/interfaces +++ /dev/null @@ -1,17 +0,0 @@ -auto lo - -iface lo inet loopback - -allow-hotplug wlan0 -iface wlan0 inet static - -allow-hotplug eth0 -iface eth0 inet dhcp - -allow-hotplug usb0 -iface usb0 inet static - address 10.0.0.2 - netmask 255.255.255.0 - network 10.0.0.0 - broadcast 10.0.0.255 - gateway 10.0.0.1 diff --git a/sdcard/rootfs/etc/rc.local b/sdcard/rootfs/etc/rc.local deleted file mode 100755 index bab03e6..0000000 --- a/sdcard/rootfs/etc/rc.local +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -e -# -# rc.local -# -# This script is executed at the end of each multiuser runlevel. -# Make sure that the script will "exit 0" on success or any other -# value on error. -# -# In order to enable or disable this script just change the execution -# bits. -# -# By default this script does nothing. -# Powersave (Disable HDMI) ~30ma -sleep 10 -if ! /opt/vc/bin/tvservice -s | grep HDMI; then - /opt/vc/bin/tvservice -o -fi -/root/pwnagotchi/scripts/startup.sh & -exit 0 diff --git a/sdcard/rootfs/etc/ssh/sshd_config b/sdcard/rootfs/etc/ssh/sshd_config deleted file mode 100644 index e2f34f6..0000000 --- a/sdcard/rootfs/etc/ssh/sshd_config +++ /dev/null @@ -1,121 +0,0 @@ -# $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $ - -# This is the sshd server system-wide configuration file. See -# sshd_config(5) for more information. - -# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin - -# The strategy used for options in the default sshd_config shipped with -# OpenSSH is to specify options with their default value where -# possible, but leave them commented. Uncommented options override the -# default value. - -#Port 22 -#AddressFamily any -#ListenAddress 0.0.0.0 -#ListenAddress :: - -#HostKey /etc/ssh/ssh_host_rsa_key -#HostKey /etc/ssh/ssh_host_ecdsa_key -#HostKey /etc/ssh/ssh_host_ed25519_key - -# Ciphers and keying -#RekeyLimit default none - -# Logging -#SyslogFacility AUTH -#LogLevel INFO - -# Authentication: - -#LoginGraceTime 2m -PermitRootLogin yes -#StrictModes yes -#MaxAuthTries 6 -#MaxSessions 10 - -#PubkeyAuthentication yes - -# Expect .ssh/authorized_keys2 to be disregarded by default in future. -#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2 - -#AuthorizedPrincipalsFile none - -#AuthorizedKeysCommand none -#AuthorizedKeysCommandUser nobody - -# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts -#HostbasedAuthentication no -# Change to yes if you don't trust ~/.ssh/known_hosts for -# HostbasedAuthentication -#IgnoreUserKnownHosts no -# Don't read the user's ~/.rhosts and ~/.shosts files -#IgnoreRhosts yes - -# To disable tunneled clear text passwords, change to no here! -#PasswordAuthentication yes -#PermitEmptyPasswords no - -# Change to yes to enable challenge-response passwords (beware issues with -# some PAM modules and threads) -ChallengeResponseAuthentication no - -# Kerberos options -#KerberosAuthentication no -#KerberosOrLocalPasswd yes -#KerberosTicketCleanup yes -#KerberosGetAFSToken no - -# GSSAPI options -#GSSAPIAuthentication no -#GSSAPICleanupCredentials yes -#GSSAPIStrictAcceptorCheck yes -#GSSAPIKeyExchange no - -# Set this to 'yes' to enable PAM authentication, account processing, -# and session processing. If this is enabled, PAM authentication will -# be allowed through the ChallengeResponseAuthentication and -# PasswordAuthentication. Depending on your PAM configuration, -# PAM authentication via ChallengeResponseAuthentication may bypass -# the setting of "PermitRootLogin without-password". -# If you just want the PAM account and session checks to run without -# PAM authentication, then enable this but set PasswordAuthentication -# and ChallengeResponseAuthentication to 'no'. -UsePAM yes - -#AllowAgentForwarding yes -#AllowTcpForwarding yes -#GatewayPorts no -X11Forwarding yes -#X11DisplayOffset 10 -#X11UseLocalhost yes -#PermitTTY yes -PrintMotd no -#PrintLastLog yes -#TCPKeepAlive yes -#PermitUserEnvironment no -#Compression delayed -#ClientAliveInterval 0 -#ClientAliveCountMax 3 -#UseDNS no -#PidFile /var/run/sshd.pid -#MaxStartups 10:30:100 -#PermitTunnel no -#ChrootDirectory none -#VersionAddendum none - -# no default banner path -#Banner none - -# Allow client to pass locale environment variables -AcceptEnv LANG LC_* - -# override default of no subsystems -Subsystem sftp /usr/lib/openssh/sftp-server - -# Example of overriding settings on a per-user basis -#Match User anoncvs -# X11Forwarding no -# AllowTcpForwarding no -# PermitTTY no -# ForceCommand cvs server diff --git a/sdcard/rootfs/root/pwnagotchi/data/images/face_happy.bmp b/sdcard/rootfs/root/pwnagotchi/data/images/face_happy.bmp deleted file mode 100644 index 8f3e857b7bcaff9b73030daf36a2b74b654f29c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11398 zcmeI0W3(j67KL})wr$(CZQHhO+qP}nwr$(plaZMb8Oo~L)A!B$)3sK0oH%Fi{Y7Ne ztu<@%mKc~E<}0IrAhA((G~N}Bw;u#H`=yO$*kIP%Xp({X^5e%3{QC6^fByWzzkmM- z5Fh{o1`LQmfdV0L;J^qHBnW~A4T@mFf+2YD;0O^S1VV-kiBO?JA#~`_2ooj@!iEit zaN)useE9H)5Fr90MvRC^ks={-<j9B;B?_WOjf!Z|q9J<p=!g*`24cpHiCD2>A$IK8 zh!ZCc;>L}Oc=6&Pe*E}IkRSmPCQOJ#i4q}k;>1XjBngrxO^Rg6k|BBW<VcYs1yZI= ziBzdlA$981NRuWF(xy#|bm`I|efspskRbyyX3U68nKB`B=FG^FB@41<&5CT<vLSo+ z?8uQL2Xf}jiCnpIA$RWF$de}z^5)HpeEISrfByU^P@n(`7A%NDg$ki?;le0VqzH-@ zEsA2rilKP%;wVv~1WJ}HiBhFXp>*leC{v~k%9bsQa^=dQeEIUIP@w`UR;-9hl`5ff z<;tj1r3$K6t%_>Zs-b%I>Znnp25Q!<iCVR4p?2-ss8gp7>ej7`diCm|e*OAr(4YYt zHf)GSjT)hG<Hl&xqzRffZHi{inxT2~=4jEP1zNUjiB_#zp>^xlXw#+*+O}<rcJ11s zef#$4(4hl5cI=2wojRd&=g#QTr3<=t?TT*Qx}kgb?@2YUAGiC(>Wp?B}z=+mbU z`u6RMe*OBPfB*g%Fkk=%4jhO<g9c&n;K3L&WC(^19g1PYhGF>d;TSPu1V)Y=iBY3Q zVf5(H7&B%J#*Q6}apT5e{P^*hFku2FPMnBIlO|#E<jI&aWeTQFor-DGreXT@>6kHN z24>EjiCME|VfO6Vm@{V%=FXjqdGqFB{`~n^uwVfeE?kI3ixy$=;>B39WC@loU5aJP zmSOqw<yf&|1y-(HiB+psVfE_OShHpg)~;QPb?eq){rdITuwer>Zrq4Xn>Jzd=FQl$ zWec`$-HL77wqg7B?bxwn2X^k<iCw#PVfXIc*t2I3_U_$_ef#!d|Ni|raNqzA9z2Lc zhYsQJ;lns`<Oq%)J&I$;j^X(6<2Z5R1WukjiBqRe;q>X#ICJI<&YnGsbLY<C{Q2{^ zaNz<jUc87)moDM*<;%Eo<qED|y^3qsuHpLi>$q{_25#QGiCedB;r8v@xO3+Y?%utN zd-v|){{8!S@ZbR+K75Erj~?Oi<HvaN<O!ZWeTrw#p5giP=Xmkr1zx^<iC3>);q~j+ zc=P5B-oAZ{ckkZe{rmU$@ZkeKe*B0}pFZL9=g;`^<qN)k{fckjzTx}#@A&cK2Y&wi ziC@2dk@x@Mg}>bY<f;FC&i~0{{`L1c>z=du9~ZiTk9x!{3)POSAbXSRGpS;?ymH%T zVTQ`I%Wfa+U5@Nyu6=1Aka&rGT9u-9cpI&46-l8jbG0v{EL3C;jE!W|cBqG6DUar` zPh24?4KwCXxhju89jiIG)%&+x@~x7X4B}qccthYEgsRn((21}*Mp6kQPaDx@?rgaa zL+k)g43`;2n`2}1J6y}_6703j!zfns4OeqatkAX&;sgpqX~V8aK4v!KO4bt#4^#^; z%02A(b9&{HY$?m>kfA)4#LiZbdb4SRg#;(qb0wO*aJ5+L@6j_wnUW;#(eSmRwSF$t zw*5n(8@Toi5YOUk#Uq?hnu^DlhWafZ=ZWhj+KW}gwjE^oeX}M{X^|6iA10mKO)p%n zl|<+wX|%NxtcGhj&5?G@%wihddGOd0ldAy}q;(?q3~NqV{OUfTDnp?cvHri|+DE1k zbv4tq$?Q1VshQ+HAzaNvbod^*mdrK4W3(5r8`tW4zLiI=mm9lq`L>7qI?s(&j+$h! zwp{Q4Ze}{jKgL^in<ke(QVwRzxLBuj;hF+#CUb3vTPWY>YX`|?>z{{h6YgvFh}=Ds zpBS|S^tNRH;l8BHoLtFzq;RZGTi0`@T;&M4c%JI=`I-@ynHlL;v@0-nBg@F_<RY9{ zco2M_)D*?nW=|G!T9z(xC9Fp-<q5GaV0uQDFLGI%9+{nwHnc03YL&7|5ST-Ot6;s} z*8&K!Wb=DmtwD~^?Y(iOIf>x!E1uI9>o`(RiX~f7Aqh;ZDKUkjoNC1c?T$*hRn&0< zQkq;-!Pl0>8k4^jF+#T^S<*|CsY5%KEXL4L(+ypniLR1z6_Ye~SQeLyWM^OnH&jX~ zSMf-5`mM57LCrSG*%heKkaC%sX$664utr2FsM*FxTm{U4b|G<DsLW*p={Ry8-3z(w z%yeq3yujR9GnLCc+!0t$Q;5N;fX$7O6RC2Y@3x~-)A$Ze=6if?&dr9CD0BG_+fnf; ze^7IQp19m&`2!ed^~@C>w6kG;&dqv0DhwlwQ|`+@XmVkuWYJttGi5TRAYsg)<8mlY z>EwuRr|-EUQO3&_sa)|><)lt5v$Ggy=2lrjf>U<0A<3}xVjAlSN>L;2Yzs4J?n_vl zi#Yf_8PS$H;f#Dq4J_J)D}abeXz*Y@-L-ZCXp+U1QptVz3}O>I=8ft9tKg-&TCm$e zW}K91c(oYKSyZP2wy5U6WagPm$4-iEcN*4}g|l!&yJRlAuoh!z-&eI83MqzJY2R$n zjb^nCY{Cav=?cTp8Y<?X1#{X%oyOEULTY8J<)K`ap#>XmC(?VxU`k<MN^>{;jcI^a zCKbA@jH|h@+l^hKeydF{YCUz?N~d!F&-W}@3)VLO9BL#mE?05E&X{ZZM(G4=@8*ns z1IA@=!8$NE=1jg&^1&)t?f3%HjNrM--_8FHD}9?pkht@$n_4--eXSos8(>g%za_qe em$M0ty?lwGi;=n9@4im%Ydw|gYtbHqfPVn-dL-!p diff --git a/sdcard/rootfs/usr/bin/cpuusage b/sdcard/rootfs/usr/bin/cpuusage deleted file mode 100755 index ea9e63a..0000000 --- a/sdcard/rootfs/usr/bin/cpuusage +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/gawk -f -#------------------------------------------------------------------------------- -# ~/bin/cpustat : display cpu utilization -# -# usage : normally used as a GNU/screen backtick -# -# notes : 1. Works on the assumption that /proc/stat's first line -# : has the total "jiffies" since boot up used by the -# : different types of tasks in the system. See the -# : filesystems/proc.txt document in kernel source tree -# : -# : 2. Displays a total CPU% (user+system+nice) as well as -# : user CPU% system CPU% and nice CPU% -#------------------------------------------------------------------------------- -BEGIN { - file = "/proc/stat" - while (getline < file) { # read first line - # extract jiffies: - user=$2-user_saved; # . user - nice=$3-nice_saved; # . nice user - syst=$4-syst_saved; # . system - idle=$5-idle_saved; # . idle - wait=$6-wait_saved; # . iowait - irqs=$7-irqs_saved; # . irq - sirq=$8-sirq_saved; # . softirq - - cact=user+syst+nice; # what counts - ctot=user+nice+syst+idle+wait+irqs+sirq; # total activity - - tcpu=cact/ctot*100; # total % cpu utilization - ucpu=user/ctot*100; # user % cpu utilization - scpu=syst/ctot*100; # system % cpu utilization - ncpu=nice/ctot*100; # nice % cpu utilization - - printf "%.1f %%\n",tcpu - - - user_saved=$2; # save the current jiffies - nice_saved=$3; # values for the next loop - syst_saved=$4; - idle_saved=$5; - wait_saved=$6; - irqs_saved=$7; - sirq_saved=$8; - - close(file) # re-read file - - system("sleep 3") - } -} - diff --git a/sdcard/rootfs/usr/bin/memusage b/sdcard/rootfs/usr/bin/memusage deleted file mode 100755 index 14efdb1..0000000 --- a/sdcard/rootfs/usr/bin/memusage +++ /dev/null @@ -1 +0,0 @@ -free -m | grep Mem | awk {'printf( "%.1f %", $3 / $2 * 100 )'} diff --git a/sdcard/rootfs/usr/bin/monstart b/sdcard/rootfs/usr/bin/monstart deleted file mode 100755 index 112c65e..0000000 --- a/sdcard/rootfs/usr/bin/monstart +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -iw phy phy0 interface add mon0 type monitor && ifconfig mon0 up diff --git a/sdcard/rootfs/usr/bin/monstop b/sdcard/rootfs/usr/bin/monstop deleted file mode 100755 index 8ca7f89..0000000 --- a/sdcard/rootfs/usr/bin/monstop +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -ifconfig mon0 down && iw dev mon0 del From 23e62ec68a79cdfd80bea092f7966bbdf8afbef3 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 22:30:23 +0200 Subject: [PATCH 020/150] misc: blink.sh becomes /usr/bin/bootblink in ansible build --- builder/pwnagotchi.yml | 16 ++++++++++++++++ sdcard/rootfs/root/pwnagotchi/scripts/blink.sh | 12 ------------ sdcard/rootfs/root/pwnagotchi/scripts/startup.sh | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) delete mode 100755 sdcard/rootfs/root/pwnagotchi/scripts/blink.sh diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 561c64f..ecd6251 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -244,6 +244,22 @@ #!/usr/bin/env bash free -m | awk '/Mem/ { printf( "%d %", $3 / $2 * 100 + 0.5 ) }' + - name: create bootblink script + copy: + dest: /usr/bin/bootblink + mode: 0755 + content: | + #!/usr/bin/env bash + for i in $(seq 1 "$1"); + do + echo 0 >/sys/class/leds/led0/brightness + sleep 0.3 + echo 1 >/sys/class/leds/led0/brightness + sleep 0.3 + done + echo 0 >/sys/class/leds/led0/brightness + sleep 0.3 + - name: create monstart script copy: dest: /usr/bin/monstart diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/blink.sh b/sdcard/rootfs/root/pwnagotchi/scripts/blink.sh deleted file mode 100755 index 6ad62a3..0000000 --- a/sdcard/rootfs/root/pwnagotchi/scripts/blink.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -for i in $(seq 1 "$1"); -do - echo 0 >/sys/class/leds/led0/brightness - sleep 0.3 - echo 1 >/sys/class/leds/led0/brightness - sleep 0.3 -done - -echo 0 >/sys/class/leds/led0/brightness -sleep 0.3 \ No newline at end of file diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/startup.sh b/sdcard/rootfs/root/pwnagotchi/scripts/startup.sh index 58b681c..4d8f959 100755 --- a/sdcard/rootfs/root/pwnagotchi/scripts/startup.sh +++ b/sdcard/rootfs/root/pwnagotchi/scripts/startup.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # blink 10 times to signal ready state -/root/pwnagotchi/scripts/blink.sh 10 & +/usr/bin/bootblink 10 & # start a detached screen session with bettercap if ifconfig | grep usb0 | grep RUNNING; then From f2f73e13cb51245fe10ebc5d4ee931d287412ef7 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sat, 5 Oct 2019 17:07:22 -0400 Subject: [PATCH 021/150] added @dadav's tamagotchi name explainer --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b2e30f8..58c5a52 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,11 @@ full and half WPA handshakes.  -Specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) doc.) - - Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54) over time to **get better at pwning WiFi things** in the environments you expose it to. -**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi actually learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but **definitely listen to your pwnagotchi when it tells you it's bored!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) +More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) doc.) + +**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi actually learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but **definitely listen to your Pwnagotchi when it tells you it's bored!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) Multiple units within close physical proximity can "talk" to each other, advertising their own presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard. Over time, two or more units trained together will learn to cooperate upon detecting each other's presence by dividing the available channels among them for optimal pwnage. @@ -27,6 +26,8 @@ Multiple units within close physical proximity can "talk" to each other, adverti For hackers to learn reinforcement learning, WiFi networking, and have an excuse to get out for more walks. Also? **It's cute as f---**. +**In case you're curious about the name:** *Pwnagotchi* is a portmanteau of *pwn* (which we shouldn't have to explain if you are interested in this project :kissing_heart:) and *-gotchi*. It is a nostalgic reference made in homage to a very popular children's toy from the 1990s called the [Tamagotchi](https://en.wikipedia.org/wiki/Tamagotchi). The Tamagotchi (たまごっち, derived from *tamago* (たまご) "egg" + *uotchi* (ウオッチ) "watch") is a cultural touchstone for many Millennial hackers as a formative electronic toy from our collective childhoods. Were you lucky enough to possess a Tamagotchi as a kid? Well, with your Pwnagotchi, you too can enjoy the nostalgic delight of being strangely emotionally attached to a handheld automata *yet again!* Except, this time around...you get to #HackThePlanet. >:D + ## Documentation --- :warning: **THE FOLLOWING DOCUMENTATION IS BEING PREPARED FOR THE v1.0 RELEASE OF PWNAGOTCHI. Since this effort is an active (and unstable) work-in-progress, the docs displayed here are in various stages of [in]completion. There will be dead links and placeholders throughout as we are still building things out in preparation for the v1.0 release.** :warning: From 97283ba49a7efc9dfdf4d5f3e5ac253d7101ee68 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sat, 5 Oct 2019 17:21:50 -0400 Subject: [PATCH 022/150] minor copyediting --- docs/configure.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index d2bb0f5..a7c251f 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -1,6 +1,6 @@ # Connecting to your Pwnagotchi -Once you wrote the image file on the SD card, there're a few steps you'll have to follow in order to configure your unit properly, first, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. After a few seconds the board will boot and you will see a new Ethernet interface on your host computer. +Once you wrote the image file on the SD card, there're a few steps you'll have to follow in order to configure your unit properly. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. You'll need to configure it with a static IP address: @@ -17,9 +17,9 @@ You can now connect to your unit using SSH: ssh pi@10.0.0.2 ``` -The default password is `raspberry`, you should change it as soon as you log in for the first time by issuing the `passwd`command and selecting a new and more complex passphrase. +The default password is `raspberry`; you should change it as soon as you log in for the first time by issuing the `passwd` command and selecting a new and more complex passphrase. -Moreover, it is recommended that you copy your SSH public key among the unit's authorized ones, so you can directly log in without entering a password: +If you want to login directly without entering a password (recommended!), copy your SSH public key to the unit's authorized keys: ```bash ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 @@ -27,27 +27,27 @@ ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 ## Configuration -You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/). Create the `/root/custom.yml` file (either via SSH or by direclty editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. +You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/). Create the `/root/custom.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. ## Language Selection -For instance, you can change `main.lang` to one of the supported languages: +Pwnagotchi displays its UI in English by default, but it can speak several other languages! You can change `main.lang` to one of the supported languages: -- **english** (default) -- german -- dutch -- greek -- macedonian -- italian -- french -- russian -- swedish +- **English** *(default)* +- German +- Dutch +- Greek +- Macedonian +- Italian +- French +- Russian +- Swedish ## Display Selection Set the type of display you want to use via `ui.display.type` (if your display does not work after changing this setting, you might need to completely remove power from the Raspberry and make a clean boot). -You can configure the refresh interval of the display via `ui.fps`, we advise to use a slow refresh to not shorten the lifetime of your display. The default value is 0, which will only refresh when changes are made to the screen. +You can configure the refresh interval of the display via `ui.fps`. We recommend using a slow refresh rate to avoid shortening the lifetime of your e-ink display. The default value is `0`, which will *only* refresh when changes are made to the screen. ## Host Connection Share @@ -55,4 +55,4 @@ If you connect to the unit via `usb0` (thus using the data port), you might want ## Troubleshooting -If your network connection keeps flapping on your device connecting to your pwnagotchi, check if `usb0` (or equivalent) device is being controlled by NetworkManager. You can check this via `nmcli dev status`. +If your network connection keeps flapping on your device connecting to your Pwnagotchi, check if `usb0` (or equivalent) device is being controlled by NetworkManager. You can check this via `nmcli dev status`. From cbf7511e1754a41a7f3144d1813603b0bb8b9cef Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 23:23:31 +0200 Subject: [PATCH 023/150] misc: refactored code to work as a setup.py based package Signed-off-by: Simone Margaritelli <evilsocket@gmail.com> --- .gitignore | 4 + bin/pwnagotchi | 106 ++++++++++++++++++ builder/pwnagotchi.yml | 22 ++++ .../pwnagotchi => pwnagotchi}/__init__.py | 0 .../pwnagotchi => pwnagotchi}/agent.py | 0 .../pwnagotchi => pwnagotchi}/ai/__init__.py | 0 .../pwnagotchi => pwnagotchi}/ai/epoch.py | 0 .../ai/featurizer.py | 0 .../pwnagotchi => pwnagotchi}/ai/gym.py | 0 .../pwnagotchi => pwnagotchi}/ai/parameter.py | 0 .../pwnagotchi => pwnagotchi}/ai/reward.py | 0 .../pwnagotchi => pwnagotchi}/ai/train.py | 0 .../pwnagotchi => pwnagotchi}/ai/utils.py | 0 .../pwnagotchi => pwnagotchi}/bettercap.py | 0 .../config.yml => pwnagotchi/defaults.yml | 4 +- .../locale/de/LC_MESSAGES/voice.mo | Bin .../locale/de/LC_MESSAGES/voice.po | 0 .../locale/el/LC_MESSAGES/voice.mo | Bin .../locale/el/LC_MESSAGES/voice.po | 0 .../locale/fr/LC_MESSAGES/voice.mo | Bin .../locale/fr/LC_MESSAGES/voice.po | 0 .../locale/it/LC_MESSAGES/voice.mo | Bin .../locale/it/LC_MESSAGES/voice.po | 0 .../locale/mk/LC_MESSAGES/voice.mo | Bin .../locale/mk/LC_MESSAGES/voice.po | 0 .../locale/nl/LC_MESSAGES/voice.mo | Bin .../locale/nl/LC_MESSAGES/voice.po | 0 .../locale/ru/LC_MESSAGES/voice.mo | Bin .../locale/ru/LC_MESSAGES/voice.po | 0 .../locale/se/LC_MESSAGES/voice.mo | Bin .../locale/se/LC_MESSAGES/voice.po | 0 .../locale/voice.pot | 0 .../scripts/pwnagotchi => pwnagotchi}/log.py | 0 .../mesh/__init__.py | 0 .../mesh/advertise.py | 0 .../pwnagotchi => pwnagotchi}/mesh/peer.py | 0 .../pwnagotchi => pwnagotchi}/mesh/utils.py | 0 .../pwnagotchi => pwnagotchi}/mesh/wifi.py | 0 .../plugins/__init__.py | 0 .../plugins/default/auto-backup.py | 0 .../plugins/default/auto-update.py | 0 .../plugins/default/example.py | 0 .../plugins/default/gps.py | 0 .../plugins/default/memtemp.py | 0 .../plugins/default/onlinehashcrack.py | 0 .../plugins/default/twitter.py | 0 .../plugins/default/ups_lite.py | 0 .../plugins/default/wpa-sec.py | 0 .../pwnagotchi => pwnagotchi}/ui/__init__.py | 0 .../ui/components.py | 0 .../pwnagotchi => pwnagotchi}/ui/display.py | 0 .../pwnagotchi => pwnagotchi}/ui/faces.py | 0 .../pwnagotchi => pwnagotchi}/ui/fonts.py | 0 .../ui/papirus/__init__.py | 0 .../ui/papirus/epd.py | 0 .../ui/papirus/lm75b.py | 0 .../pwnagotchi => pwnagotchi}/ui/state.py | 0 .../pwnagotchi => pwnagotchi}/ui/view.py | 0 .../ui/waveshare/__init__.py | 0 .../ui/waveshare/v1/__init__.py | 0 .../ui/waveshare/v1/epd2in13.py | 0 .../ui/waveshare/v1/epdconfig.py | 0 .../ui/waveshare/v2/__init__.py | 0 .../ui/waveshare/v2/waveshare.py | 0 .../pwnagotchi => pwnagotchi}/utils.py | 0 .../pwnagotchi => pwnagotchi}/voice.py | 0 .../requirements.txt => requirements.txt | 0 .../rootfs/root/pwnagotchi/data/screenrc.auto | 49 -------- .../root/pwnagotchi/data/screenrc.manual | 49 -------- sdcard/rootfs/root/pwnagotchi/scripts/main.py | 103 ----------------- .../rootfs/root/pwnagotchi/scripts/startup.sh | 11 -- setup.py | 26 +++++ 72 files changed, 160 insertions(+), 214 deletions(-) create mode 100755 bin/pwnagotchi rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/__init__.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/agent.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ai/__init__.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ai/epoch.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ai/featurizer.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ai/gym.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ai/parameter.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ai/reward.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ai/train.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ai/utils.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/bettercap.py (100%) rename sdcard/rootfs/root/pwnagotchi/config.yml => pwnagotchi/defaults.yml (99%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/de/LC_MESSAGES/voice.mo (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/de/LC_MESSAGES/voice.po (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/el/LC_MESSAGES/voice.mo (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/el/LC_MESSAGES/voice.po (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/fr/LC_MESSAGES/voice.mo (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/fr/LC_MESSAGES/voice.po (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/it/LC_MESSAGES/voice.mo (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/it/LC_MESSAGES/voice.po (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/mk/LC_MESSAGES/voice.mo (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/mk/LC_MESSAGES/voice.po (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/nl/LC_MESSAGES/voice.mo (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/nl/LC_MESSAGES/voice.po (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/ru/LC_MESSAGES/voice.mo (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/ru/LC_MESSAGES/voice.po (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/se/LC_MESSAGES/voice.mo (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/se/LC_MESSAGES/voice.po (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/locale/voice.pot (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/log.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/mesh/__init__.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/mesh/advertise.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/mesh/peer.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/mesh/utils.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/mesh/wifi.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/__init__.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/default/auto-backup.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/default/auto-update.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/default/example.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/default/gps.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/default/memtemp.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/default/onlinehashcrack.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/default/twitter.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/default/ups_lite.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/plugins/default/wpa-sec.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/__init__.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/components.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/display.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/faces.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/fonts.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/papirus/__init__.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/papirus/epd.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/papirus/lm75b.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/state.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/view.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/waveshare/__init__.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/waveshare/v1/__init__.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/waveshare/v1/epd2in13.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/waveshare/v1/epdconfig.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/waveshare/v2/__init__.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/ui/waveshare/v2/waveshare.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/utils.py (100%) rename {sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi => pwnagotchi}/voice.py (100%) rename sdcard/rootfs/root/pwnagotchi/scripts/requirements.txt => requirements.txt (100%) delete mode 100644 sdcard/rootfs/root/pwnagotchi/data/screenrc.auto delete mode 100644 sdcard/rootfs/root/pwnagotchi/data/screenrc.manual delete mode 100755 sdcard/rootfs/root/pwnagotchi/scripts/main.py delete mode 100755 sdcard/rootfs/root/pwnagotchi/scripts/startup.sh create mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 3833e98..fc54ebf 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ config.laptop.yml .idea packer_cache output-pwnagotchi +.DS_Store +build +dist +pwnagotchi.egg-info diff --git a/bin/pwnagotchi b/bin/pwnagotchi new file mode 100755 index 0000000..4733105 --- /dev/null +++ b/bin/pwnagotchi @@ -0,0 +1,106 @@ +#!/usr/bin/python3 +if __name__ == '__main__': + import argparse + import time + import os + import logging + + import pwnagotchi + import pwnagotchi.utils as utils + import pwnagotchi.plugins as plugins + + from pwnagotchi.log import SessionParser + from pwnagotchi.agent import Agent + from pwnagotchi.ui.display import Display + + parser = argparse.ArgumentParser() + + parser.add_argument('-C', '--config', action='store', dest='config', + default=os.path.join(os.path.abspath(os.path.dirname(pwnagotchi.__file__)), '/defaults.yml'), + help='Main configuration file.') + parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.yml', + help='If this file exists, configuration will be merged and this will override default values.') + + parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.") + parser.add_argument('--clear', dest="do_clear", action="store_true", default=False, + help="Clear the ePaper display and exit.") + + parser.add_argument('--debug', dest="debug", action="store_true", default=False, + help="Enable debug logs.") + + args = parser.parse_args() + config = utils.load_config(args) + utils.setup_logging(args, config) + + 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, pwnagotchi.version)) + + for _, plugin in plugins.loaded.items(): + logging.debug("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__)) + + if args.do_clear: + logging.info("clearing the display ...") + display.clear() + + elif args.do_manual: + logging.info("entering manual mode ...") + + 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', display, config, log) + + else: + logging.info("entering auto mode ...") + + agent.start() + + 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) + + 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") diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index ecd6251..fab9c2e 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -260,6 +260,21 @@ echo 0 >/sys/class/leds/led0/brightness sleep 0.3 + - name: create pwnagotchi-launcher script + copy: + dest: /usr/bin/pwnagotchi-launcher + mode: 0755 + content: | + #!/usr/bin/env bash + # blink 10 times to signal ready state + /usr/bin/bootblink 10 & + # start a detached screen session with bettercap + if ifconfig | grep usb0 | grep RUNNING; then + /usr/bin/pwnagotchi --manual + else + /usr/bin/pwnagotchi + fi + - name: create monstart script copy: dest: /usr/bin/monstart @@ -286,6 +301,13 @@ fi /root/pwnagotchi/scripts/startup.sh & + - name: create /etc/pwnagotchi/config.yml + blockinfile: + path: /etc/pwnagotchi/config.yml + create: yes + block: | + # put here your custom configuration overrides + - name: configure lo interface blockinfile: path: /etc/network/interfaces.d/lo-cfg diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py b/pwnagotchi/__init__.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py rename to pwnagotchi/__init__.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/agent.py b/pwnagotchi/agent.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/agent.py rename to pwnagotchi/agent.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/__init__.py b/pwnagotchi/ai/__init__.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/__init__.py rename to pwnagotchi/ai/__init__.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/epoch.py b/pwnagotchi/ai/epoch.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/epoch.py rename to pwnagotchi/ai/epoch.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/featurizer.py b/pwnagotchi/ai/featurizer.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/featurizer.py rename to pwnagotchi/ai/featurizer.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/gym.py b/pwnagotchi/ai/gym.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/gym.py rename to pwnagotchi/ai/gym.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/parameter.py b/pwnagotchi/ai/parameter.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/parameter.py rename to pwnagotchi/ai/parameter.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/reward.py b/pwnagotchi/ai/reward.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/reward.py rename to pwnagotchi/ai/reward.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/train.py b/pwnagotchi/ai/train.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/train.py rename to pwnagotchi/ai/train.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/utils.py b/pwnagotchi/ai/utils.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/utils.py rename to pwnagotchi/ai/utils.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/bettercap.py b/pwnagotchi/bettercap.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/bettercap.py rename to pwnagotchi/bettercap.py diff --git a/sdcard/rootfs/root/pwnagotchi/config.yml b/pwnagotchi/defaults.yml similarity index 99% rename from sdcard/rootfs/root/pwnagotchi/config.yml rename to pwnagotchi/defaults.yml index a93a7e8..cceac71 100644 --- a/sdcard/rootfs/root/pwnagotchi/config.yml +++ b/pwnagotchi/defaults.yml @@ -153,8 +153,8 @@ bettercap: scheme: http hostname: localhost port: 8081 - username: user - password: pass + username: pwnagotchi + password: pwnagotchi # folder where bettercap stores the WPA handshakes, given that # wifi.handshakes.aggregate will be set to false and individual # pcap files will be created in order to minimize the chances diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.mo b/pwnagotchi/locale/de/LC_MESSAGES/voice.mo similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.mo rename to pwnagotchi/locale/de/LC_MESSAGES/voice.mo diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.po b/pwnagotchi/locale/de/LC_MESSAGES/voice.po similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.po rename to pwnagotchi/locale/de/LC_MESSAGES/voice.po diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/el/LC_MESSAGES/voice.mo b/pwnagotchi/locale/el/LC_MESSAGES/voice.mo similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/el/LC_MESSAGES/voice.mo rename to pwnagotchi/locale/el/LC_MESSAGES/voice.mo diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/el/LC_MESSAGES/voice.po b/pwnagotchi/locale/el/LC_MESSAGES/voice.po similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/el/LC_MESSAGES/voice.po rename to pwnagotchi/locale/el/LC_MESSAGES/voice.po diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/fr/LC_MESSAGES/voice.mo b/pwnagotchi/locale/fr/LC_MESSAGES/voice.mo similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/fr/LC_MESSAGES/voice.mo rename to pwnagotchi/locale/fr/LC_MESSAGES/voice.mo diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/fr/LC_MESSAGES/voice.po b/pwnagotchi/locale/fr/LC_MESSAGES/voice.po similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/fr/LC_MESSAGES/voice.po rename to pwnagotchi/locale/fr/LC_MESSAGES/voice.po diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.mo b/pwnagotchi/locale/it/LC_MESSAGES/voice.mo similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.mo rename to pwnagotchi/locale/it/LC_MESSAGES/voice.mo diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po b/pwnagotchi/locale/it/LC_MESSAGES/voice.po similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po rename to pwnagotchi/locale/it/LC_MESSAGES/voice.po diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/mk/LC_MESSAGES/voice.mo b/pwnagotchi/locale/mk/LC_MESSAGES/voice.mo similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/mk/LC_MESSAGES/voice.mo rename to pwnagotchi/locale/mk/LC_MESSAGES/voice.mo diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/mk/LC_MESSAGES/voice.po b/pwnagotchi/locale/mk/LC_MESSAGES/voice.po similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/mk/LC_MESSAGES/voice.po rename to pwnagotchi/locale/mk/LC_MESSAGES/voice.po diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/nl/LC_MESSAGES/voice.mo b/pwnagotchi/locale/nl/LC_MESSAGES/voice.mo similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/nl/LC_MESSAGES/voice.mo rename to pwnagotchi/locale/nl/LC_MESSAGES/voice.mo diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/nl/LC_MESSAGES/voice.po b/pwnagotchi/locale/nl/LC_MESSAGES/voice.po similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/nl/LC_MESSAGES/voice.po rename to pwnagotchi/locale/nl/LC_MESSAGES/voice.po diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo rename to pwnagotchi/locale/ru/LC_MESSAGES/voice.mo diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.po b/pwnagotchi/locale/ru/LC_MESSAGES/voice.po similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.po rename to pwnagotchi/locale/ru/LC_MESSAGES/voice.po diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.mo b/pwnagotchi/locale/se/LC_MESSAGES/voice.mo similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.mo rename to pwnagotchi/locale/se/LC_MESSAGES/voice.mo diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.po b/pwnagotchi/locale/se/LC_MESSAGES/voice.po similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.po rename to pwnagotchi/locale/se/LC_MESSAGES/voice.po diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/voice.pot b/pwnagotchi/locale/voice.pot similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/voice.pot rename to pwnagotchi/locale/voice.pot diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/log.py b/pwnagotchi/log.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/log.py rename to pwnagotchi/log.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/__init__.py b/pwnagotchi/mesh/__init__.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/__init__.py rename to pwnagotchi/mesh/__init__.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/advertise.py b/pwnagotchi/mesh/advertise.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/advertise.py rename to pwnagotchi/mesh/advertise.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/peer.py b/pwnagotchi/mesh/peer.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/peer.py rename to pwnagotchi/mesh/peer.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/utils.py b/pwnagotchi/mesh/utils.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/utils.py rename to pwnagotchi/mesh/utils.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/wifi.py b/pwnagotchi/mesh/wifi.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/wifi.py rename to pwnagotchi/mesh/wifi.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/__init__.py b/pwnagotchi/plugins/__init__.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/__init__.py rename to pwnagotchi/plugins/__init__.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-backup.py b/pwnagotchi/plugins/default/auto-backup.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-backup.py rename to pwnagotchi/plugins/default/auto-backup.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-update.py b/pwnagotchi/plugins/default/auto-update.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-update.py rename to pwnagotchi/plugins/default/auto-update.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/example.py b/pwnagotchi/plugins/default/example.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/example.py rename to pwnagotchi/plugins/default/example.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/gps.py b/pwnagotchi/plugins/default/gps.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/gps.py rename to pwnagotchi/plugins/default/gps.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/memtemp.py b/pwnagotchi/plugins/default/memtemp.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/memtemp.py rename to pwnagotchi/plugins/default/memtemp.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/onlinehashcrack.py b/pwnagotchi/plugins/default/onlinehashcrack.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/onlinehashcrack.py rename to pwnagotchi/plugins/default/onlinehashcrack.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/twitter.py b/pwnagotchi/plugins/default/twitter.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/twitter.py rename to pwnagotchi/plugins/default/twitter.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/ups_lite.py b/pwnagotchi/plugins/default/ups_lite.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/ups_lite.py rename to pwnagotchi/plugins/default/ups_lite.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/wpa-sec.py rename to pwnagotchi/plugins/default/wpa-sec.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/__init__.py b/pwnagotchi/ui/__init__.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/__init__.py rename to pwnagotchi/ui/__init__.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/components.py b/pwnagotchi/ui/components.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/components.py rename to pwnagotchi/ui/components.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py rename to pwnagotchi/ui/display.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/faces.py b/pwnagotchi/ui/faces.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/faces.py rename to pwnagotchi/ui/faces.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/fonts.py b/pwnagotchi/ui/fonts.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/fonts.py rename to pwnagotchi/ui/fonts.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/papirus/__init__.py b/pwnagotchi/ui/papirus/__init__.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/papirus/__init__.py rename to pwnagotchi/ui/papirus/__init__.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/papirus/epd.py b/pwnagotchi/ui/papirus/epd.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/papirus/epd.py rename to pwnagotchi/ui/papirus/epd.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/papirus/lm75b.py b/pwnagotchi/ui/papirus/lm75b.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/papirus/lm75b.py rename to pwnagotchi/ui/papirus/lm75b.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/state.py b/pwnagotchi/ui/state.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/state.py rename to pwnagotchi/ui/state.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py rename to pwnagotchi/ui/view.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/__init__.py b/pwnagotchi/ui/waveshare/__init__.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/__init__.py rename to pwnagotchi/ui/waveshare/__init__.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/__init__.py b/pwnagotchi/ui/waveshare/v1/__init__.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/__init__.py rename to pwnagotchi/ui/waveshare/v1/__init__.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epd2in13.py b/pwnagotchi/ui/waveshare/v1/epd2in13.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epd2in13.py rename to pwnagotchi/ui/waveshare/v1/epd2in13.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epdconfig.py b/pwnagotchi/ui/waveshare/v1/epdconfig.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epdconfig.py rename to pwnagotchi/ui/waveshare/v1/epdconfig.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v2/__init__.py b/pwnagotchi/ui/waveshare/v2/__init__.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v2/__init__.py rename to pwnagotchi/ui/waveshare/v2/__init__.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v2/waveshare.py b/pwnagotchi/ui/waveshare/v2/waveshare.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v2/waveshare.py rename to pwnagotchi/ui/waveshare/v2/waveshare.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/utils.py b/pwnagotchi/utils.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/utils.py rename to pwnagotchi/utils.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py b/pwnagotchi/voice.py similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py rename to pwnagotchi/voice.py diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/requirements.txt b/requirements.txt similarity index 100% rename from sdcard/rootfs/root/pwnagotchi/scripts/requirements.txt rename to requirements.txt diff --git a/sdcard/rootfs/root/pwnagotchi/data/screenrc.auto b/sdcard/rootfs/root/pwnagotchi/data/screenrc.auto deleted file mode 100644 index 9319672..0000000 --- a/sdcard/rootfs/root/pwnagotchi/data/screenrc.auto +++ /dev/null @@ -1,49 +0,0 @@ -defutf8 on - -shell -${SHELL} -defscrollback 1024 -startup_message off -altscreen on -autodetach on -zombie kr - -activity "activity in %n (%t)" -bell_msg "bell in %n (%t)" - -vbell on -vbell_msg "WTF DUDE ??!!" -vbellwait 1 - -# set terminal emulator to xterm mode -term xterm -# Make the output buffer large for (fast) xterms. -termcapinfo xterm* ti@:te@ -# tell screen how to set colors -termcapinfo xterm 'Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm' -# allow bold colors -attrcolor b ".I" -# erase background with current bg color -defbce "on" - -# ctrl + { left, right } -bindkey ^[[1;5D prev -bindkey ^[[1;5C next -bindkey ^[[5D prev -bindkey ^[[5C next -bindkey ^[b prev -bindkey ^[f next -bindkey ^[[D prev -bindkey ^[[C next - -screen -t shell -screen -t auto-mode /bin/bash -c "/root/pwnagotchi/scripts/main.py" -screen -t bettercap /usr/bin/bettercap -caplet http-ui -eval "!/usr/bin/monstart; set wifi.interface mon0" - -select log - -backtick 1 0 0 cpuusage -backtick 2 5 5 memusage - -hardstatus alwayslastline -hardstatus string '%{= kG}[ %{G}%H %{g}][%= %{= kw}%?%-Lw%?%{r}(%{W}%n*%f%t%?(%u)%?%{r})%{w}%?%+Lw%?%?%= %{g}][cpu %1`][mem %2`][%{B} %m-%d %{W}%c %{g}]' - diff --git a/sdcard/rootfs/root/pwnagotchi/data/screenrc.manual b/sdcard/rootfs/root/pwnagotchi/data/screenrc.manual deleted file mode 100644 index 1d62528..0000000 --- a/sdcard/rootfs/root/pwnagotchi/data/screenrc.manual +++ /dev/null @@ -1,49 +0,0 @@ -defutf8 on - -shell -${SHELL} -defscrollback 1024 -startup_message off -altscreen on -autodetach on -zombie kr - -activity "activity in %n (%t)" -bell_msg "bell in %n (%t)" - -vbell on -vbell_msg "WTF DUDE ??!!" -vbellwait 1 - -# set terminal emulator to xterm mode -term xterm -# Make the output buffer large for (fast) xterms. -termcapinfo xterm* ti@:te@ -# tell screen how to set colors -termcapinfo xterm 'Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm' -# allow bold colors -attrcolor b ".I" -# erase background with current bg color -defbce "on" - -# ctrl + { left, right } -bindkey ^[[1;5D prev -bindkey ^[[1;5C next -bindkey ^[[5D prev -bindkey ^[[5C next -bindkey ^[b prev -bindkey ^[f next -bindkey ^[[D prev -bindkey ^[[C next - -screen -t shell -screen -t manual-mode /bin/bash -c "/root/pwnagotchi/scripts/main.py --manual" -screen -t bettercap /usr/bin/bettercap -caplet http-ui -eval "!/usr/bin/monstart; set wifi.interface mon0" - -select log - -backtick 1 0 0 cpuusage -backtick 2 5 5 memusage - -hardstatus alwayslastline -hardstatus string '%{= kG}[ %{G}%H %{g}][%= %{= kw}%?%-Lw%?%{r}(%{W}%n*%f%t%?(%u)%?%{r})%{w}%?%+Lw%?%?%= %{g}][cpu %1`][mem %2`][%{B} %m-%d %{W}%c %{g}]' - diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/main.py b/sdcard/rootfs/root/pwnagotchi/scripts/main.py deleted file mode 100755 index 60469e8..0000000 --- a/sdcard/rootfs/root/pwnagotchi/scripts/main.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/python3 -import argparse -import time -import logging - -import pwnagotchi -import pwnagotchi.utils as utils -import pwnagotchi.plugins as plugins - -from pwnagotchi.log import SessionParser -from pwnagotchi.agent import Agent -from pwnagotchi.ui.display import Display - -parser = argparse.ArgumentParser() - -parser.add_argument('-C', '--config', action='store', dest='config', default='/root/pwnagotchi/config.yml', - help='Main configuration file.') -parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/root/custom.yml', - help='If this file exists, configuration will be merged and this will override default values.') - -parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.") -parser.add_argument('--clear', dest="do_clear", action="store_true", default=False, - help="Clear the ePaper display and exit.") - -parser.add_argument('--debug', dest="debug", action="store_true", default=False, - help="Enable debug logs.") - -args = parser.parse_args() -config = utils.load_config(args) -utils.setup_logging(args, config) - -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, pwnagotchi.version)) - -for _, plugin in plugins.loaded.items(): - logging.debug("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__)) - -if args.do_clear: - logging.info("clearing the display ...") - display.clear() - -elif args.do_manual: - logging.info("entering manual mode ...") - - 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', display, config, log) - -else: - logging.info("entering auto mode ...") - - agent.start() - - 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) - - 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") diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/startup.sh b/sdcard/rootfs/root/pwnagotchi/scripts/startup.sh deleted file mode 100755 index 4d8f959..0000000 --- a/sdcard/rootfs/root/pwnagotchi/scripts/startup.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -# blink 10 times to signal ready state -/usr/bin/bootblink 10 & - -# start a detached screen session with bettercap -if ifconfig | grep usb0 | grep RUNNING; then - sudo -H -u root /usr/bin/screen -dmS pwnagotchi -c /root/pwnagotchi/data/screenrc.manual -else - sudo -H -u root /usr/bin/screen -dmS pwnagotchi -c /root/pwnagotchi/data/screenrc.auto -fi diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9761640 --- /dev/null +++ b/setup.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +from setuptools import setup, find_packages +import pwnagotchi + +required = [] +with open('requirements.txt') as fp: + for line in fp: + line = line.strip() + if line != "": + required.append(line) + +setup(name='pwnagotchi', + version=pwnagotchi.version, + description='Pwnagotchi is a cute AI that eats WPA handshakes.', + author='evilsocket && the dev team', + author_email='evilsocket@gmail.com', + url='https://pwnagotchi.ai/', + install_requires=required, + scripts=['bin/pwnagotchi'], + license='GPL', + classifiers=[ + 'Programming Language :: Python :: 3', + 'Development Status :: 5 - Production/Stable', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Environment :: Console', + ]) \ No newline at end of file From d6b1e86333e8edb03a60b5028ba6534b4dbdbc38 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 23:28:29 +0200 Subject: [PATCH 024/150] misc: small fix or general refactoring i did not bother commenting --- MANIFEST.in | 8 ++++++++ setup.py | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..55e0a5d --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,8 @@ +exclude *.pyc .DS_Store .gitignore MANIFEST.in +include setup.py +include distribute_setup.py +include README.md +include LICENSE +recursive-include bin * +recursive-include pwnagotchi *.py +recursive-include pwnagotchi *.yml diff --git a/setup.py b/setup.py index 9761640..3075614 100644 --- a/setup.py +++ b/setup.py @@ -15,9 +15,10 @@ setup(name='pwnagotchi', author='evilsocket && the dev team', author_email='evilsocket@gmail.com', url='https://pwnagotchi.ai/', + license='GPL', install_requires=required, scripts=['bin/pwnagotchi'], - license='GPL', + package_data={'pwnagotchi': ('pwnagotchi/defaults.yml',)}, classifiers=[ 'Programming Language :: Python :: 3', 'Development Status :: 5 - Production/Stable', From 2461bfbd110ca806cdce3a6ca844eaa646eb4831 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 23:30:06 +0200 Subject: [PATCH 025/150] misc: small fix or general refactoring i did not bother commenting --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3075614..a1e6e84 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ with open('requirements.txt') as fp: setup(name='pwnagotchi', version=pwnagotchi.version, - description='Pwnagotchi is a cute AI that eats WPA handshakes.', + description='(⌐■_■) - Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.', author='evilsocket && the dev team', author_email='evilsocket@gmail.com', url='https://pwnagotchi.ai/', From d6bad150822e8804b9042a0d0694624b4f2bd449 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 23:34:14 +0200 Subject: [PATCH 026/150] added contributors badge Signed-off-by: Simone Margaritelli <evilsocket@gmail.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b2e30f8..b44c5bf 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ <p align="center"> <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=""><img alt="Contributors" src="https://img.shields.io/github/contributors/evilsocket/pwnagotchi"/></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> From 25b13b5e6db18fc9fa9280110e10588e1d128138 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 23:36:21 +0200 Subject: [PATCH 027/150] added twitter button --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b44c5bf..5074aea 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ <a href=""><img alt="Contributors" src="https://img.shields.io/github/contributors/evilsocket/pwnagotchi"/></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> + <a href="https://twitter.com/intent/follow?screen_name=pwnagotchi"><img src="https://img.shields.io/twitter/follow/pwnagotchi?style=social&logo=twitter" alt="follow on Twitter"></a> </p> </p> From dac09fccf4d6f9cb2eb673bb1a230d21bdfc0bac Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sat, 5 Oct 2019 23:37:52 +0200 Subject: [PATCH 028/150] misc: small fix or general refactoring i did not bother commenting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5074aea..b6ea9aa 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ <p align="center"> <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=""><img alt="Contributors" src="https://img.shields.io/github/contributors/evilsocket/pwnagotchi"/></a> + <a href="https://github.com/evilsocket/pwnagotchi/graphs/contributors"><img alt="Contributors" src="https://img.shields.io/github/contributors/evilsocket/pwnagotchi"/></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> <a href="https://twitter.com/intent/follow?screen_name=pwnagotchi"><img src="https://img.shields.io/twitter/follow/pwnagotchi?style=social&logo=twitter" alt="follow on Twitter"></a> From b765a642ae0147929cea9836d528da9890ab3b4a Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sat, 5 Oct 2019 17:50:26 -0400 Subject: [PATCH 029/150] minor copyediting + details about screens --- docs/install.md | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/docs/install.md b/docs/install.md index b1ee1d7..9f143e8 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,35 +2,47 @@ The project has been developed to run on a Raspberry Pi 0 W configured as an [USB Ethernet gadget](https://learn.adafruit.com/turning-your-raspberry-pi-zero-into-a-usb-gadget/ethernet-gadget) device in order to connect to it via USB. However, given the proper configuration tweaks, any GNU/Linux computer with a WiFi interface that supports monitor mode could be used. -**An important note about the AI:** a network trained with a specific WiFi interface will only work with another interface if it supports -the same exact WiFi channels of the first one. For instance, you can not use a neural network trained on a Raspberry Pi Zero W (that only supports 2.4Ghz channels) with a 5Ghz antenna, but you'll need to train one from scratch for those channels. +**An important note about the AI:** a network trained with a specific WiFi interface will ONLY work with another interface if it supports the *exact same* WiFi channels of the first one. For instance, you CANNOT use a neural network trained on a Raspberry Pi Zero W (that only supports 2.4Ghz channels) with a 5Ghz antenna; you will need to train one from scratch for those channels. ## Required Hardware -- [Raspberry Pi Zero W](https://www.raspberrypi.org/products/raspberry-pi-zero-w/). -- A micro SD card, 8GB recomended, **preferably of good quality and speed**. +- [Raspberry Pi Zero W](https://www.raspberrypi.org/products/raspberry-pi-zero-w/).† +- A micro SD card, 8GB recommended, **preferably of good quality and speed**. - A decent power bank (with 1500 mAh you get ~2 hours with AI on). - One of the supported displays (optional). +† Many users have gotten Pwnagotchi running on other types of Raspberry Pi, but the RPi0W is the "vanilla" hardware config for Pwnagotchi. + ### Display -The display is an optional component as the UI is also rendered via a web interface available via the USB cable. If you connect to `usb0` (by using the data port on the unit) and point your browser to the web ui (see config.yml), your unit can work in "headless mode". +The display is an optional component as the UI is also rendered via a web interface available via the USB cable. If you connect to `usb0` (by using the data port on the unit) and point your browser to the web ui (see `config.yml`), your unit can work in "headless mode". -If instead you want to fully enjoy walking around and literally looking at your unit's face, the supported display models are: +If, instead, you want to fully enjoy walking around and literally looking at your unit's face, the supported display models are: - [Waveshare eInk Display (both V1 and V2)](https://www.waveshare.com/2.13inch-e-paper-hat.htm) + - [Product comparison](https://www.waveshare.com/4.3inch-e-paper.htm) (scroll down to `Selection Guide`) + - [GitHub](https://github.com/waveshare/e-Paper/tree/master/RaspberryPi%26JetsonNano/python) - [Pimoroni Inky pHAT](https://shop.pimoroni.com/products/inky-phat) + - [Product page](https://shop.pimoroni.com/products/inky-phat) + - [GitHub](https://github.com/pimoroni/inky) - [PaPiRus eInk Screen](https://uk.pi-supply.com/products/papirus-zero-epaper-screen-phat-pi-zero) Needless to say, we are always happy to receive pull requests adding support for new models. -One thing to note, not all displays are created equaly, TFT displays for example work similar to an HDMI display, and they are not supported, currently all the displays supported are I2C displays. +**One thing to note:** Not all displays are created equally! TFT displays, for example, work similar to an HDMI display, and they are NOT supported. Currently, all the officially-supported displays are I2C displays. If you are still interested in using unsupported displays, you may be able to find a community-submitted hack in the [Screens](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md#screens) section of the [Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) page. We are not responsible for anything you break by trying to use any display that is not officially supported by the development team! -#### Color and Black & White displays +#### Color vs. Black & White displays -Some of the supported displays support Black & White and Coloured versions, one common question is regarding refresh speed of said displays. +Some of the supported displays support both **Black & White** and **Colored** versions. One common question whether there are meaningful differences between the two. There are: +- Color displays have a much slower refresh rate. In some cases, it can take up to 15 seconds; if slow refresh rates are something that you want to avoid, we recommend you use B&W displays. +- The 3-color 2.13" Waveshare displays have a slightly smaller pixel layout (104x212) compared to their B&W counterparts (122x250). -Color displays have a much slower refresh rate, in some cases it can take up to 15 seconds, if slow refresh rates is something that you want to avoid we advise you to use Black & White displays +#### Recommendations +- Avoid the Waveshare eInk **3-color** display. The refresh time is 15 seconds. +- Avoid the Pimoroni Inky pHAT **v1.** They're discontinued due to a faulty hardware part source used in manufacturing that resulted in high failure rates. +- Many users seem to prefer the Inky pHATs. There are two primary reasons: + - The Inkys feature better documentation and SDK support. + - Many Waveshare resellers do not disclose the version of the Waveshare boards they are selling (v1 vs v2), and the type they are selling can be fairly unclear (i.e., Waveshare 2.13 vs 2.13 B vs. 2.13C, and so on.) ## Flashing an Image From 7f72e9ae3e7c1a5f366202c03e1a9dd0dda4ddb2 Mon Sep 17 00:00:00 2001 From: hisakiyo <34187647+hisakiyo@users.noreply.github.com> Date: Sun, 6 Oct 2019 00:13:06 +0200 Subject: [PATCH 030/150] Update voice.po --- pwnagotchi/locale/fr/LC_MESSAGES/voice.po | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pwnagotchi/locale/fr/LC_MESSAGES/voice.po b/pwnagotchi/locale/fr/LC_MESSAGES/voice.po index 1172f7f..1d061f7 100644 --- a/pwnagotchi/locale/fr/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/fr/LC_MESSAGES/voice.po @@ -25,20 +25,20 @@ msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Bonjour, je suis Pwnagotchi! Démarrage ..." msgid "New day, new hunt, new pwns!" -msgstr "Nouvelle journée, nouvelle chasse, nouveau pwns!" +msgstr "Nouveau jour, nouvelle chasse, nouveaux pwns !" msgid "Hack the Planet!" msgstr "Hack la planète!" msgid "AI ready." -msgstr "IA prête." +msgstr "L'IA est prête." msgid "The neural network is ready." msgstr "Le réseau neuronal est prêt." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." -msgstr "Hey, le channel {channel} est libre! Ton AP va dis merci." +msgstr "Hey, le channel {channel} est libre! Ton point d'accès va te remercier." msgid "I'm bored ..." msgstr "Je m'ennuie ..." @@ -68,17 +68,17 @@ msgid "I pwn therefore I am." msgstr "Je pwn donc je suis." msgid "So many networks!!!" -msgstr "Autant de réseaux!!!" +msgstr "Tellement de réseaux!!!" msgid "I'm having so much fun!" msgstr "Je m'amuse tellement!" msgid "My crime is that of curiosity ..." -msgstr "Mon crime est celui de la curiosité ..." +msgstr "Mon crime, c'est la curiosité ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" -msgstr "Bonjour {name}! Ravis de te rencontrer. {name}" +msgstr "Bonjour {name}! Ravi de te rencontrer. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" @@ -145,7 +145,7 @@ msgstr "" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" -msgstr "Décidé à l'instant que {mac} n'a pas besoin de WiFi!" +msgstr "Je viens de décider que {mac} n'a pas besoin de WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" @@ -153,11 +153,11 @@ msgstr "Désauthentification de {mac}" #, python-brace-format msgid "Kickbanning {mac}!" -msgstr "" +msgstr "Je kick et je bannis {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" -msgstr "Cool, nous avons {num} nouveaux handshake{plural}!" +msgstr "Cool, on a {num} nouveaux handshake{plural}!" msgid "Ops, something went wrong ... Rebooting ..." msgstr "Oups, quelque chose s'est mal passé ... Redémarrage ..." @@ -188,7 +188,7 @@ msgid "" "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "J'ai pwn durant {duration} et kick {deauthed} clients! J'ai aussi rencontré " -"{associated} nouveaux amis and mangé {handshakes} handshakes! #pwnagotchi " +"{associated} nouveaux amis et dévoré {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" From 6bc507412a7888feeb16073847dbd08a49f632e6 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sun, 6 Oct 2019 00:18:36 +0200 Subject: [PATCH 031/150] pypi upload script --- scripts/pypi_upload.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100755 scripts/pypi_upload.sh diff --git a/scripts/pypi_upload.sh b/scripts/pypi_upload.sh new file mode 100755 index 0000000..4a71125 --- /dev/null +++ b/scripts/pypi_upload.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +rm -rf build dist ergo_nn.egg-info && + python3 setup.py sdist bdist_wheel && + clear && + twine upload dist/* From 03318bdaef253dc54aecea7853daf363ac3edfb3 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 00:14:09 +0200 Subject: [PATCH 032/150] Change code --- scripts/preview.py | 226 ++++++++++++++++++++++----------------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/scripts/preview.py b/scripts/preview.py index 2b45933..1d29119 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -14,64 +14,26 @@ sys.path.insert(0, '../sdcard/rootfs/root/pwnagotchi/scripts/')) from pwnagotchi.ui.display import Display, VideoHandler - +from PIL import Image class CustomDisplay(Display): + def __init__(self, config, state): + self.last_image = None + super(CustomDisplay, self).__init__(config, state) + def _http_serve(self): - if self._video_address is not None: - self._httpd = HTTPServer((self._video_address, self._video_port), - CustomVideoHandler) - logging.info("ui available at http://%s:%d/" % (self._video_address, - self._video_port)) - self._httpd.serve_forever() - else: - logging.info("could not get ip of usb0, video server not starting") + # do nothing + pass def _on_view_rendered(self, img): - CustomVideoHandler.render(img) + self.last_image = img - if self._enabled: - self.canvas = (img if self._rotation == 0 else img.rotate(self._rotation)) - if self._render_cb is not None: - self._render_cb() - - -class CustomVideoHandler(VideoHandler): - - @staticmethod - def render(img): - with CustomVideoHandler._lock: - try: - img.save("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), format='PNG') - except BaseException: - logging.exception("could not write preview") - - def do_GET(self): - if self.path == '/': - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - try: - self.wfile.write( - bytes( - self._index % - ('localhost', 1000), "utf8")) - except BaseException: - pass - - elif self.path.startswith('/ui'): - with self._lock: - self.send_response(200) - self.send_header('Content-type', 'image/png') - self.end_headers() - try: - with open("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), 'rb') as fp: - shutil.copyfileobj(fp, self.wfile) - except BaseException: - logging.exception("could not open preview") - else: - self.send_response(404) + def get_image(self): + """ + Return the saved image + """ + return self.last_image class DummyPeer: @@ -79,21 +41,43 @@ class DummyPeer: def name(): return "beta" +def append_images(images, horizontal=True, xmargin=0, ymargin=0): + w, h = zip(*(i.size for i in images)) + + if horizontal: + t_w = sum(w) + t_h = max(h) + else: + t_w = max(w) + t_h = sum(h) + + result = Image.new('RGB', (t_w, t_h)) + + x_offset = 0 + y_offset = 0 + + for im in images: + result.paste(im, (x_offset,y_offset)) + if horizontal: + x_offset += im.size[0] + xmargin + else: + y_offset += im.size[1] + ymargin + + return result def main(): parser = argparse.ArgumentParser(description="This program emulates\ the pwnagotchi display") - parser.add_argument('--display', help="Which display to use.", + parser.add_argument('--displays', help="Which displays to use.", nargs="+", default="waveshare_2") - parser.add_argument('--port', help="Which port to use", - default=8080) - parser.add_argument('--sleep', type=int, help="Time between emotions", - default=2) parser.add_argument('--lang', help="Language to use", default="en") + parser.add_argument('--output', help="Path to output image (PNG)", default="preview.png") + parser.add_argument('--xmargin', type=int, default=5) + parser.add_argument('--ymargin', type=int, default=5) args = parser.parse_args() - CONFIG = yaml.load(''' + config_template = ''' main: lang: {lang} ui: @@ -107,65 +91,81 @@ def main(): video: enabled: true address: "0.0.0.0" - port: {port} - '''.format(display=args.display, - port=args.port, - lang=args.lang)) + port: 8080 + ''' - DISPLAY = CustomDisplay(config=CONFIG, state={'name': '%s>' % 'preview'}) + list_of_displays = list() + for display_type in args.displays: + config = yaml.safe_load(config_template.format(display=display_type, + lang=args.lang)) + display = CustomDisplay(config=config, state={'name': f"{display_type}>"}) + list_of_displays.append(display) - while True: - DISPLAY.on_starting() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_ai_ready() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_normal() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_new_peer(DummyPeer()) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_lost_peer(DummyPeer()) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_free_channel('6') - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.wait(args.sleep) - DISPLAY.update() - DISPLAY.on_bored() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_sad() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_motivated(1) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_demotivated(-1) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_excited() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_deauth({'mac': 'DE:AD:BE:EF:CA:FE'}) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_miss('test') - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_lonely() - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_handshakes(1) - DISPLAY.update() - time.sleep(args.sleep) - DISPLAY.on_rebooting() - DISPLAY.update() - time.sleep(args.sleep) + columns = list() + + for display in list_of_displays: + emotions = list() + # Starting + display.on_starting() + display.update() + emotions.append(display.get_image()) + display.on_ai_ready() + display.update() + emotions.append(display.get_image()) + display.on_normal() + display.update() + emotions.append(display.get_image()) + display.on_new_peer(DummyPeer()) + display.update() + emotions.append(display.get_image()) + display.on_lost_peer(DummyPeer()) + display.update() + emotions.append(display.get_image()) + display.on_free_channel('6') + display.update() + emotions.append(display.get_image()) + display.wait(2) + display.update() + emotions.append(display.get_image()) + display.on_bored() + display.update() + emotions.append(display.get_image()) + display.on_sad() + display.update() + emotions.append(display.get_image()) + display.on_motivated(1) + display.update() + emotions.append(display.get_image()) + display.on_demotivated(-1) + display.update() + emotions.append(display.get_image()) + display.on_excited() + display.update() + emotions.append(display.get_image()) + display.on_deauth({'mac': 'DE:AD:BE:EF:CA:FE'}) + display.update() + emotions.append(display.get_image()) + display.on_miss('test') + display.update() + emotions.append(display.get_image()) + display.on_lonely() + display.update() + emotions.append(display.get_image()) + display.on_handshakes(1) + display.update() + emotions.append(display.get_image()) + display.on_rebooting() + display.update() + emotions.append(display.get_image()) + + # append them all together (vertical) + columns.append(append_images(emotions, horizontal=False, xmargin=args.xmargin, ymargin=args.ymargin)) + + + # append columns side by side + final_image = append_images(columns, horizontal=True, xmargin=args.xmargin, ymargin=args.ymargin) + final_image.save(args.output, 'PNG') if __name__ == '__main__': SystemExit(main()) From b4a382e266c21b356189b3a22268e7e7f2f445c4 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 00:15:23 +0200 Subject: [PATCH 033/150] Update dev.md --- docs/dev.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/dev.md b/docs/dev.md index 75b7ea0..534b398 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -26,7 +26,7 @@ usage: ./scripts/create_sibling.sh [OPTIONS] `GLib-ERROR **: 20:50:46.361: getauxval () failed: No such file or directory` -- Affected DEB & Versions: QEMU <= 2.11 +- Affected DEB & Versions: QEMU <= 2.11 - Fix: Upgrade QEMU to >= 3.1 - Bug Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923289 @@ -55,7 +55,6 @@ If you changed the `voice.py`- File, the translations need an update. Do it like Now you can use the `preview.py`-script to preview the changes: ```shell -./scripts/preview.py --lang it --display ws2 --port 8080 & -./scripts/preview.py --lang it --display inky --port 8081 & -# Now open http://localhost:8080 and http://localhost:8081 +./scripts/preview.py --lang it --display ws1 ws2 inky --output preview.png +# Now open preview.png ``` From 0b07bf3621befdc890d467681d250944d83cd958 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sun, 6 Oct 2019 00:27:21 +0200 Subject: [PATCH 034/150] misc: small fix or general refactoring i did not bother commenting --- scripts/preview.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/scripts/preview.py b/scripts/preview.py index 1d29119..b298865 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -1,21 +1,17 @@ #!/usr/bin/env python3 - import sys import os -import time import argparse -from http.server import HTTPServer -import shutil -import logging import yaml sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), - '../sdcard/rootfs/root/pwnagotchi/scripts/')) + '../pwnagotchi/')) from pwnagotchi.ui.display import Display, VideoHandler from PIL import Image + class CustomDisplay(Display): def __init__(self, config, state): @@ -41,6 +37,7 @@ class DummyPeer: def name(): return "beta" + def append_images(images, horizontal=True, xmargin=0, ymargin=0): w, h = zip(*(i.size for i in images)) @@ -57,14 +54,15 @@ def append_images(images, horizontal=True, xmargin=0, ymargin=0): y_offset = 0 for im in images: - result.paste(im, (x_offset,y_offset)) - if horizontal: - x_offset += im.size[0] + xmargin - else: - y_offset += im.size[1] + ymargin + result.paste(im, (x_offset, y_offset)) + if horizontal: + x_offset += im.size[0] + xmargin + else: + y_offset += im.size[1] + ymargin return result + def main(): parser = argparse.ArgumentParser(description="This program emulates\ the pwnagotchi display") @@ -97,11 +95,10 @@ def main(): list_of_displays = list() for display_type in args.displays: config = yaml.safe_load(config_template.format(display=display_type, - lang=args.lang)) + lang=args.lang)) display = CustomDisplay(config=config, state={'name': f"{display_type}>"}) list_of_displays.append(display) - columns = list() for display in list_of_displays: @@ -162,10 +159,10 @@ def main(): # append them all together (vertical) columns.append(append_images(emotions, horizontal=False, xmargin=args.xmargin, ymargin=args.ymargin)) - # append columns side by side final_image = append_images(columns, horizontal=True, xmargin=args.xmargin, ymargin=args.ymargin) final_image.save(args.output, 'PNG') + if __name__ == '__main__': SystemExit(main()) From 7580b3c30b9d8bb7096499a0e547e56def95e353 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sat, 5 Oct 2019 18:42:49 -0400 Subject: [PATCH 035/150] added FAQ questions TOC structure will fill in the actual content tomorrow --- docs/faq.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index ff19087..fcc6782 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,13 +1,55 @@ # FAQ -## Why eINK? +[**What can Pwnagotchi actually do?**](#what-can-pwnagotchi-actually-do) +* Does Pwnagotchi support both 2.4 GHz and 5.0 GHz? +* Just how politely *does* Pwnagotchi deauth? +* What kinds of handshakes does Pwnagotchi eat? +* Hey, I want to learn more about how Pwnagotchi actually works. -Because! +[**Building Your Pwnagotchi**](#building-your-pwnagotchi) +* What hardware do I need to create my very own Pwnagotchi? +* Is there any way to see my Pwnagotchi's face even if I don't have a display? +* I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case? +* Why does everybody use e-ink screens for their Pwnagotchis? +* How do I connect to my Pwnagotchi? -## Why the AI takes 30 minutes to load? +[**Customizing Your Pwnagotchi**](#customizing-your-pwnagotchi) +* How do I change my Pwnagotchi's name? +* I want to change the faces. What do I hack? +* I want my Pwnagotchi to speak a different language. Can it? +* I have a great idea for something cool I wish Pwnagotchi could do! -Because Python sucks and TF is huge. +[**Getting to Know Your Pwnagotchi**](#getting-to-know-your-pwnagotchi) +* What is MANU mode? What is AUTO mode? +* Why does the AI take 30 minutes to load? +* What is Pwnagotchi doing while it's waiting for the AI to load? +* How do I whitelist my home network so Pwnagotchi stops pwning me? -## Why ...? +[**Caring for Your Pwnagotchi**](#caring-for-your-pwnagotchi) +* What do all my Pwnagotchi's faces mean? +* Oh no, my Pwnagotchi is sad and bored! How do I entertain it?! +* How do I turn off my Pwnagotchi? -Because! +[**Known Quirks**](#known-quirks) +* My Pwnagotchi's log timestamps seem...unreliable. Huh? +* Help! My Pwnagotchi's SD card got corrupted. What gives? + +--- + +## What can Pwnagotchi actually do? +lorem ipsum dolor sit amet + +## Building Your Pwnagotchi +lorem ipsum dolor sit amet + +## Customizing Your Pwnagotchi +lorem ipsum dolor sit amet + +## Getting to Know Your Pwnagotchi +lorem ipsum dolor sit amet + +## Caring for Your Pwnagotchi +lorem ipsum dolor sit amet + +## Known Quirks +lorem ipsum dolor sit amet From 705040e07520f5843dfda3194fe73e433bf679d6 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sun, 6 Oct 2019 00:44:24 +0200 Subject: [PATCH 036/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/mesh/advertise.py | 2 +- pwnagotchi/mesh/wifi.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pwnagotchi/mesh/advertise.py b/pwnagotchi/mesh/advertise.py index ff797a3..84286d2 100644 --- a/pwnagotchi/mesh/advertise.py +++ b/pwnagotchi/mesh/advertise.py @@ -152,7 +152,7 @@ class Advertiser(object): if self._is_broadcasted_advertisement(dot11): try: dot11elt = p.getlayer(Dot11Elt) - if dot11elt.ID == wifi.Dot11ElemID_Identity: + if dot11elt.ID == wifi.Dot11ElemID_Whisper: self._parse_identity(p[RadioTap], dot11, dot11elt) else: diff --git a/pwnagotchi/mesh/wifi.py b/pwnagotchi/mesh/wifi.py index 6a9a00a..6fe231e 100644 --- a/pwnagotchi/mesh/wifi.py +++ b/pwnagotchi/mesh/wifi.py @@ -1,6 +1,6 @@ SignatureAddress = 'de:ad:be:ef:de:ad' BroadcastAddress = 'ff:ff:ff:ff:ff:ff' -Dot11ElemID_Identity = 222 +Dot11ElemID_Whisper = 222 NumChannels = 140 def freq_to_channel(freq): @@ -30,7 +30,7 @@ def encapsulate(payload, addr_from, addr_to=BroadcastAddress): while data_left > 0: sz = min(chunk_size, data_left) chunk = payload[data_off: data_off + sz] - frame /= Dot11Elt(ID=Dot11ElemID_Identity, info=chunk, len=sz) + frame /= Dot11Elt(ID=Dot11ElemID_Whisper, info=chunk, len=sz) data_off += sz data_left -= sz From e369d596d689aff499944c6689feb19091ced163 Mon Sep 17 00:00:00 2001 From: SecurityWaffle <SecurityWaffle@users.noreply.github.com> Date: Sat, 5 Oct 2019 21:22:03 -0500 Subject: [PATCH 037/150] Fixes "No module named 'pwnagotchi'" error /usr/local/bin/pwnagotchi Traceback (most recent call last): File "/usr/local/bin/pwnagotchi", line 8, in <module> import pwnagotchi ModuleNotFoundError: No module named 'pwnagotchi' --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a1e6e84..41d7475 100644 --- a/setup.py +++ b/setup.py @@ -19,9 +19,10 @@ setup(name='pwnagotchi', install_requires=required, scripts=['bin/pwnagotchi'], package_data={'pwnagotchi': ('pwnagotchi/defaults.yml',)}, + packages=find_packages(), classifiers=[ 'Programming Language :: Python :: 3', 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Environment :: Console', - ]) \ No newline at end of file + ]) From 3bb42549f67762868be2dbd14cf340bdd02e9cae Mon Sep 17 00:00:00 2001 From: gh0stshell <gh0stshell@users.noreply.github.com> Date: Sat, 5 Oct 2019 22:25:10 -0700 Subject: [PATCH 038/150] BMAPTOOL Check #2 update switched second check with faster builtin tool, tested fix --- scripts/create_sibling.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/create_sibling.sh b/scripts/create_sibling.sh index 02537cd..1e07a12 100755 --- a/scripts/create_sibling.sh +++ b/scripts/create_sibling.sh @@ -94,7 +94,7 @@ function provide_raspbian() { function setup_raspbian(){ # Detect the ability to create sparse files if [ "${OPT_SPARSE}" -eq 0 ]; then - if [ which bmaptool -eq 0 ]; then + if ! type "bmaptool" > /dev/null; then echo "[!] bmaptool not available, not creating a sparse image" else From 2682a5487a52e6f4625d5eb82893115ab00d8c91 Mon Sep 17 00:00:00 2001 From: gh0stshell <gh0stshell@users.noreply.github.com> Date: Sat, 5 Oct 2019 22:26:03 -0700 Subject: [PATCH 039/150] Removed extra return --- scripts/create_sibling.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/create_sibling.sh b/scripts/create_sibling.sh index 1e07a12..650d40c 100755 --- a/scripts/create_sibling.sh +++ b/scripts/create_sibling.sh @@ -96,7 +96,6 @@ function setup_raspbian(){ if [ "${OPT_SPARSE}" -eq 0 ]; then if ! type "bmaptool" > /dev/null; then echo "[!] bmaptool not available, not creating a sparse image" - else echo "[+] Defaulting to sparse image generation as bmaptool is available" OPT_SPARSE=1 From eb3836ba1e23819e8c1d1b5206cd9742b1c8780f Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 08:55:51 +0200 Subject: [PATCH 040/150] Fix path to pwnagotchi --- .gitignore | 1 + scripts/preview.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fc54ebf..0cac4ae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.img.bmap *.pcap *.po~ +preview.png __pycache__ _backups _emulation diff --git a/scripts/preview.py b/scripts/preview.py index b298865..4f69a3d 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -6,7 +6,7 @@ import yaml sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), - '../pwnagotchi/')) + '../')) from pwnagotchi.ui.display import Display, VideoHandler from PIL import Image From c7b31ae45698477d8cf313f699a6d88bf1c9745f Mon Sep 17 00:00:00 2001 From: Panos Vasilopoulos <hello@alwayslivid.com> Date: Sun, 6 Oct 2019 07:59:33 +0000 Subject: [PATCH 041/150] fixed spacing error in greek translation --- pwnagotchi/locale/el/LC_MESSAGES/voice.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pwnagotchi/locale/el/LC_MESSAGES/voice.po b/pwnagotchi/locale/el/LC_MESSAGES/voice.po index f113ce1..b1cfa69 100644 --- a/pwnagotchi/locale/el/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/el/LC_MESSAGES/voice.po @@ -33,11 +33,11 @@ msgid "AI ready." msgstr "ΤΝ έτοιμη." msgid "The neural network is ready." -msgstr "Το νευρωνικό δίκτυοείναι έτοιμο." +msgstr "Το νευρωνικό δίκτυο είναι έτοιμο." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." -msgstr "Ε, το κανάλι {channel} είναιελεύθερο! Το AP σου θαείναι ευγνώμων." +msgstr "Ε, το κανάλι {channel} είναιελεύθερο! Το AP σου θα είναι ευγνώμων." msgid "I'm bored ..." msgstr "Βαριέμαι ..." From 2e6967bd1f92be95698bb3dbdd0da3a4e0855817 Mon Sep 17 00:00:00 2001 From: swedishmike <mike@swedishmike.org> Date: Sun, 6 Oct 2019 09:48:18 +0100 Subject: [PATCH 042/150] Added hdmion/hdmioff scripts --- builder/pwnagotchi.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index fab9c2e..2fe3ff9 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -291,6 +291,22 @@ #!/usr/bin/env bash ifconfig mon0 down && iw dev mon0 del + - name: create hdmion script + copy: + dest: /usr/bin/hdmion + mode: 0755 + content: | + #!/usr/bin/env bash + sudo /opt/vc/bin/tvservice -p + + - name: create hdmioff script + copy: + dest: /usr/bin/hdmioff + mode: 0755 + content: | + #!/usr/bin/env bash + sudo /opt/vc/bin/tvservice -o + - name: configure rc.local blockinfile: path: /etc/rc.local From acb09effce8a3358b2fee604d986c9d46dfe6369 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sun, 6 Oct 2019 12:49:25 +0200 Subject: [PATCH 043/150] fix: pinned requirements versions (fixes #168) --- requirements.txt | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8fa5e8a..57579fd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,15 @@ -Crypto -requests -pyyaml -scapy -gym -stable-baselines -tensorflow -tweepy -file_read_backwards -numpy -inky -smbus -pillow +crypto==1.4.1 +requests==2.21.0 +PyYAML==3.13 +scapy==2.4.3 +gym==0.14.0 +stable-baselines==2.7.0 +tensorflow==1.13.1 +tensorflow-estimator==1.14.0 +tweepy==3.6.0 +file-read-backwards==2.0.0 +numpy==1.17.2 +inky==0.0.5 +smbus2==0.3.0 +Pillow==5.4.1 +spidev==3.4 \ No newline at end of file From a7b43b6d0dd38cf674eaa4614058953a7c883f4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Oct 2019 10:53:50 +0000 Subject: [PATCH 044/150] Bump pyyaml from 3.13 to 5.1 Bumps [pyyaml](https://github.com/yaml/pyyaml) from 3.13 to 5.1. - [Release notes](https://github.com/yaml/pyyaml/releases) - [Changelog](https://github.com/yaml/pyyaml/blob/master/CHANGES) - [Commits](https://github.com/yaml/pyyaml/compare/3.13...5.1) Signed-off-by: dependabot[bot] <support@github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 57579fd..f364dbc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ crypto==1.4.1 requests==2.21.0 -PyYAML==3.13 +PyYAML==5.1 scapy==2.4.3 gym==0.14.0 stable-baselines==2.7.0 From ac1f1ce8f034f67a8fff3c17392aa9250a60d4ba Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 06:56:15 -0400 Subject: [PATCH 045/150] reordered docs list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58c5a52..f492955 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,12 @@ For hackers to learn reinforcement learning, WiFi networking, and have an excuse --- - [About the Project](https://github.com/evilsocket/pwnagotchi/blob/master/docs/about.md) -- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) - [How to Install](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md) - [Configuration](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md) - [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md) - [Plugins](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) - [Development](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) +- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) - [Community Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) ## Links From 5737460ebdc7725e488b37280e470945807e83fc Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sun, 6 Oct 2019 13:19:38 +0200 Subject: [PATCH 046/150] docs: WPA/WPA2 handshakes 101 (fixes #179) --- docs/about.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/about.md b/docs/about.md index eacbd73..f08fa43 100644 --- a/docs/about.md +++ b/docs/about.md @@ -17,6 +17,25 @@ Multiple units within close physical proximity can "talk" to each other, adverti Of course, it is possible to run your Pwnagotchi with the AI disabled (configurable in `config.yml`). Why might you want to do this? Perhaps you simply want to use your own fixed parameters (instead of letting the AI decide for you), or maybe you want to save battery and CPU cycles, or maybe it's just you have strong concerns about aiding and abetting baby Skynet. Whatever your particular reasons may be: an AI-disabled Pwnagotchi is still a simple and very effective automated deauther, WPA handshake sniffer, and portable [bettercap](https://www.bettercap.org/) + [webui](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#bettercaps-web-ui) dedicated hardware. +## WPA/WPA2 Handshakes 101 + +Before a device that's connecting to a wireless access point (say, your phone connecting to your home WiFi) is able to securely transmit and receive data, a process called *4-Way Handshake* needs to happen in order for WPA encryption keys to be generated. +This process consists in the exchange of four packets (therefore the "4" in the name) between the station and the AP that are used to derive session keys from the main access point WiFi password, once the packets are successfully +exchanged and the keys generated, the client station is authenticated and can start sending data packets that are secured by encryption. + +<p> +<img src="https://i.imgur.com/nI8IE6a.png"/> +<br/> +<small>image taken from <a target="_blank" href="https://www.wifi-professionals.com/2019/01/4-way-handshake">wifi-professionals.com</a></small> +</p> + +The catch here is that these four packets can be "sniffed" by an attacker and, through the use of dictionary and/or bruteforce attacks, the original WiFi key can be recovered from them. Technically speaking, the recovery of +the WiFi key doesn't necessarily need all four packets: an half-handshake (containing ony two of the four packets) can be cracked too, and in some (most) cases even just [a single packet is enough](https://hashcat.net/forum/thread-7717-post-41447.html), even without clients. + +In order to get these packets, Pwnagotchi will deauthenticate client stations it detects (thus forcing them to reauthenticate to their access point, resending the handshake packets) and send association frames to the access points +to try to force them to [leak the PMKID](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/). + +All the handshakes captured this way are saved into `.pcap` files (organized as one file per access point containing all the captured handshakes for that access point) that can later be [cracked with proper hardware and software](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2). ## License From 0d292cdd10cf1f84061b11b82d1629c58619697d Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 07:25:50 -0400 Subject: [PATCH 047/150] added hyperlinks and headers to all FAQ questions --- docs/faq.md | 202 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 174 insertions(+), 28 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index fcc6782..47305b4 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,55 +1,201 @@ # FAQ +<!-- IF YOU CHANGE ANY CHARACTERS IN AN FAQ QUESTION, YOU MUST ALSO CHANGE THE TABLE OF CONTENTS ENTRY &&&AND&&& THE ANCHOR LINK TEXT, OR ELSE THE LINKS WILL BREAK. --> [**What can Pwnagotchi actually do?**](#what-can-pwnagotchi-actually-do) -* Does Pwnagotchi support both 2.4 GHz and 5.0 GHz? -* Just how politely *does* Pwnagotchi deauth? -* What kinds of handshakes does Pwnagotchi eat? -* Hey, I want to learn more about how Pwnagotchi actually works. + +* [Why does Pwnagotchi eat handshakes?](#why-does-pwnagotchi-eat-handshakes) +* [What kinds of handshakes does Pwnagotchi eat?](#what-kinds-of-handshakes-does-pwnagotchi-eat) +* [Does Pwnagotchi support both 2.4 GHz and 5.0 GHz?](#does-pwnagotchi-support-both-24-ghz-and-50-ghz) +* [Just how politely *does* Pwnagotchi deauth?](#just-how-politely-does-pwnagotchi-deauth) +* [Hey, I want to learn more about how Pwnagotchi actually works.](#hey-i-want-to-learn-more-about-how-pwnagotchi-actually-works) +* [How is Pwnagotchi using bettercap?](#how-is-pwnagotchi-using-bettercap) +* [What happens if I run a Pwnagotchi without the AI enabled?](#what-happens-if-i-run-a-pwnagotchi-without-the-ai-enabled) +* [How easy is it to hack Pwnagotchi to add additional functionality?](#how-easy-is-it-to-hack-pwnagotchi-to-add-additional-functionality) [**Building Your Pwnagotchi**](#building-your-pwnagotchi) -* What hardware do I need to create my very own Pwnagotchi? -* Is there any way to see my Pwnagotchi's face even if I don't have a display? -* I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case? -* Why does everybody use e-ink screens for their Pwnagotchis? -* How do I connect to my Pwnagotchi? + +* [What hardware do I need to create my very own Pwnagotchi?](#what-hardware-do-i-need-to-create-my-very-own-pwnagotchi) +* [Is there any way to see my Pwnagotchi's face even if I don't have a display?](#is-there-any-way-to-see-my-pwnagotchis-face-even-if-i-dont-have-a-display) +* [How do I attach the screen to the Raspberry Pi?](#how-do-i-attach-the-screen-to-the-raspberry-pi) +* [I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case?](#i-love-my-new-pwnagotchi-but-it-kinda-looks-like-a-bomb-where-can-i-find-a-decent-case) +* [Why does everybody use e-ink screens for their Pwnagotchis?](#why-does-everybody-use-e-ink-screens-for-their-pwnagotchis) +* [How do I connect to my Pwnagotchi?](#how-do-i-connect-to-my-pwnagotchi) [**Customizing Your Pwnagotchi**](#customizing-your-pwnagotchi) -* How do I change my Pwnagotchi's name? -* I want to change the faces. What do I hack? -* I want my Pwnagotchi to speak a different language. Can it? -* I have a great idea for something cool I wish Pwnagotchi could do! + +* [How do I change my Pwnagotchi's name?](#how-do-i-change-my-pwnagotchis-name) +* [I want to change the faces. What do I hack?](#i-want-to-change-the-faces-what-do-i-hack) +* [I want my Pwnagotchi to speak a different language. Can it?](#i-want-my-pwnagotchi-to-speak-a-different-language-can-it) +* [I have a great idea for something cool I wish Pwnagotchi could do!](#i-have-a-great-idea-for-something-cool-i-wish-pwnagotchi-could-do) +* [Are there any unofficial community "hacks" for further customizing my Pwnagotchi?](#are-there-any-unofficial-community-"hacks"-for-further-customizing-my-pwnagotchi) [**Getting to Know Your Pwnagotchi**](#getting-to-know-your-pwnagotchi) -* What is MANU mode? What is AUTO mode? -* Why does the AI take 30 minutes to load? -* What is Pwnagotchi doing while it's waiting for the AI to load? -* How do I whitelist my home network so Pwnagotchi stops pwning me? + +* [What does everything on the screen mean?](#what-does-everything-on-the-screen-mean) +* [How do I whitelist my home network so Pwnagotchi stops pwning me?](#how-do-i-whitelist-my-home-network-so-pwnagotchi-stops-pwning-me) +* [What is MANU mode? What is AUTO mode?](#what-is-manu-mode-what-is-auto-mode) +* [Why does the AI take 30 minutes to load?](#why-does-the-ai-take-30-minutes-to-load) +* [What is Pwnagotchi doing while it's waiting for the AI to load?](#what-is-pwnagotchi-doing-while-its-waiting-for-the-ai-to-load) +* [How do I know when the AI is running?](#how-do-i-know-when-the-ai-is-running) +* [Where does Pwnagotchi store all the handshakes it's eaten?](#where-does-pwnagotchi-store-all-the-handshakes-its-eaten) [**Caring for Your Pwnagotchi**](#caring-for-your-pwnagotchi) -* What do all my Pwnagotchi's faces mean? -* Oh no, my Pwnagotchi is sad and bored! How do I entertain it?! -* How do I turn off my Pwnagotchi? + +* [What do all my Pwnagotchi's faces mean?](#what-do-all-my-pwnagotchis-faces-mean) +* [How do I feed my Pwnagotchi?](#how-do-i-feed-my-pwnagotchi) +* [Oh no, my Pwnagotchi is sad and bored! How do I entertain it?!](#oh-no,-my-pwnagotchi-is-sad-and-bored-how-do-i-entertain-it) +* [How do I update my Pwnagotchi?](#how-do-i-update-my-pwnagotchi) +* [I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain?](#im-extremely-emotionally-attached-to-my-pwnagotchi-how-can-i-back-up-its-brain) +* [How do I turn off my Pwnagotchi?](#how-do-i-turn-off-my-pwnagotchi) +* [Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating?](#uh-so-what-do-i-do-with-all-these-handshakes-my-pwnagotchi-has-been-eating) [**Known Quirks**](#known-quirks) -* My Pwnagotchi's log timestamps seem...unreliable. Huh? -* Help! My Pwnagotchi's SD card got corrupted. What gives? + +* [My Pwnagotchi's log timestamps seem...unreliable. Huh?](#my-pwnagotchis-log-timestamps-seemunreliable-huh) +* [Help! My Pwnagotchi's SD card got corrupted. What gives?](#help-my-pwnagotchis-sd-card-got-corrupted-what-gives) --- -## What can Pwnagotchi actually do? +## **What can Pwnagotchi actually do?** +### Why does Pwnagotchi eat handshakes? lorem ipsum dolor sit amet -## Building Your Pwnagotchi +--- +### What kinds of handshakes does Pwnagotchi eat? lorem ipsum dolor sit amet -## Customizing Your Pwnagotchi +--- +### Does Pwnagotchi support both 2.4 GHz and 5.0 GHz? lorem ipsum dolor sit amet -## Getting to Know Your Pwnagotchi +--- +### Just how politely *does* Pwnagotchi deauth? lorem ipsum dolor sit amet -## Caring for Your Pwnagotchi +--- +### Hey, I want to learn more about how Pwnagotchi actually works. lorem ipsum dolor sit amet -## Known Quirks +--- +### How is Pwnagotchi using bettercap? +lorem ipsum dolor sit amet + +--- +### What happens if I run a Pwnagotchi without the AI enabled? +lorem ipsum dolor sit amet + +--- +### How easy is it to hack Pwnagotchi to add additional functionality? +lorem ipsum dolor sit amet + +--- + +## **Building Your Pwnagotchi** +### What hardware do I need to create my very own Pwnagotchi? +lorem ipsum dolor sit amet + +--- +### Is there any way to see my Pwnagotchi's face even if I don't have a display? +lorem ipsum dolor sit amet + +--- +### How do I attach the screen to the Raspberry Pi? +lorem ipsum dolor sit amet + +--- +### I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case? +lorem ipsum dolor sit amet + +--- +### Why does everybody use e-ink screens for their Pwnagotchis? +lorem ipsum dolor sit amet + +--- +### How do I connect to my Pwnagotchi? +lorem ipsum dolor sit amet + +--------------------------------------------------------------------------------------------------------------- +## **Customizing Your Pwnagotchi** +### How do I change my Pwnagotchi's name? +lorem ipsum dolor sit amet + +--- +### I want to change the faces. What do I hack? +lorem ipsum dolor sit amet + +--- +### I want my Pwnagotchi to speak a different language. Can it? +lorem ipsum dolor sit amet + +--- +### I have a great idea for something cool I wish Pwnagotchi could do! +lorem ipsum dolor sit amet + +--- +### Are there any unofficial community "hacks" for further customizing my Pwnagotchi? +lorem ipsum dolor sit amet + +--------------------------------------------------------------------------------------------------------------- +## **Getting to Know Your Pwnagotchi** +### What does everything on the screen mean? +lorem ipsum dolor sit amet + +--- +### How do I whitelist my home network so Pwnagotchi stops pwning me? +lorem ipsum dolor sit amet + +--- +### What is MANU mode? What is AUTO mode? +lorem ipsum dolor sit amet + +--- +### Why does the AI take 30 minutes to load? +lorem ipsum dolor sit amet + +--- +### What is Pwnagotchi doing while it's waiting for the AI to load? +lorem ipsum dolor sit amet + +--- +### How do I know when the AI is running? +lorem ipsum dolor sit amet + +--- +### Where does Pwnagotchi store all the handshakes it's eaten? + +--------------------------------------------------------------------------------------------------------------- +## **Caring for Your Pwnagotchi** +### What do all my Pwnagotchi's faces mean? +lorem ipsum dolor sit amet + +--- +### How do I feed my Pwnagotchi? +lorem ipsum dolor sit amet + +--- +### Oh no, my Pwnagotchi is sad and bored! How do I entertain it?! +lorem ipsum dolor sit amet + +--- +### How do I update my Pwnagotchi? +lorem ipsum dolor sit amet + +--- +### I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain? +lorem ipsum dolor sit amet + +--- +### How do I turn off my Pwnagotchi? +lorem ipsum dolor sit amet + +--- +### Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating? + +--------------------------------------------------------------------------------------------------------------- +## **Known Quirks** +### My Pwnagotchi's log timestamps seem...unreliable. Huh? +lorem ipsum dolor sit amet + +--- +### Help! My Pwnagotchi's SD card got corrupted. What gives? lorem ipsum dolor sit amet From 7a4254a7a411ffb6c53e5dac0257dbb876317c56 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 07:36:47 -0400 Subject: [PATCH 048/150] fixed broken anchor lnks in TOC & missing lipsum txt --- docs/faq.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 47305b4..348e039 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -27,7 +27,7 @@ * [I want to change the faces. What do I hack?](#i-want-to-change-the-faces-what-do-i-hack) * [I want my Pwnagotchi to speak a different language. Can it?](#i-want-my-pwnagotchi-to-speak-a-different-language-can-it) * [I have a great idea for something cool I wish Pwnagotchi could do!](#i-have-a-great-idea-for-something-cool-i-wish-pwnagotchi-could-do) -* [Are there any unofficial community "hacks" for further customizing my Pwnagotchi?](#are-there-any-unofficial-community-"hacks"-for-further-customizing-my-pwnagotchi) +* [Are there any unofficial community "hacks" for further customizing my Pwnagotchi?](#are-there-any-unofficial-community-hacks-for-further-customizing-my-pwnagotchi) [**Getting to Know Your Pwnagotchi**](#getting-to-know-your-pwnagotchi) @@ -38,12 +38,13 @@ * [What is Pwnagotchi doing while it's waiting for the AI to load?](#what-is-pwnagotchi-doing-while-its-waiting-for-the-ai-to-load) * [How do I know when the AI is running?](#how-do-i-know-when-the-ai-is-running) * [Where does Pwnagotchi store all the handshakes it's eaten?](#where-does-pwnagotchi-store-all-the-handshakes-its-eaten) +* [What happens when my Pwnagotchi meets another Pwnagotchi?](#what-happens-when-my-pwnagotchi-meets-another-pwnagotchi) [**Caring for Your Pwnagotchi**](#caring-for-your-pwnagotchi) * [What do all my Pwnagotchi's faces mean?](#what-do-all-my-pwnagotchis-faces-mean) * [How do I feed my Pwnagotchi?](#how-do-i-feed-my-pwnagotchi) -* [Oh no, my Pwnagotchi is sad and bored! How do I entertain it?!](#oh-no,-my-pwnagotchi-is-sad-and-bored-how-do-i-entertain-it) +* [Oh no, my Pwnagotchi is sad and bored! How do I entertain it?!](#oh-no-my-pwnagotchi-is-sad-and-bored-how-do-i-entertain-it) * [How do I update my Pwnagotchi?](#how-do-i-update-my-pwnagotchi) * [I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain?](#im-extremely-emotionally-attached-to-my-pwnagotchi-how-can-i-back-up-its-brain) * [How do I turn off my Pwnagotchi?](#how-do-i-turn-off-my-pwnagotchi) @@ -162,6 +163,11 @@ lorem ipsum dolor sit amet --- ### Where does Pwnagotchi store all the handshakes it's eaten? +lorem ipsum dolor sit amet + +--- +### What happens when my Pwnagotchi meets another Pwnagotchi? +lorem ipsum dolor sit amet --------------------------------------------------------------------------------------------------------------- ## **Caring for Your Pwnagotchi** @@ -190,6 +196,7 @@ lorem ipsum dolor sit amet --- ### Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating? +lorem ipsum dolor sit amet --------------------------------------------------------------------------------------------------------------- ## **Known Quirks** From 7bde5a021057d9386d47b6e9fe2f4c11248aecf8 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 08:47:20 -0400 Subject: [PATCH 049/150] rephrased for clarity --- docs/about.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/about.md b/docs/about.md index eacbd73..2883206 100644 --- a/docs/about.md +++ b/docs/about.md @@ -17,6 +17,27 @@ Multiple units within close physical proximity can "talk" to each other, adverti Of course, it is possible to run your Pwnagotchi with the AI disabled (configurable in `config.yml`). Why might you want to do this? Perhaps you simply want to use your own fixed parameters (instead of letting the AI decide for you), or maybe you want to save battery and CPU cycles, or maybe it's just you have strong concerns about aiding and abetting baby Skynet. Whatever your particular reasons may be: an AI-disabled Pwnagotchi is still a simple and very effective automated deauther, WPA handshake sniffer, and portable [bettercap](https://www.bettercap.org/) + [webui](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#bettercaps-web-ui) dedicated hardware. +## WiFi Handshakes 101 + +In order to understand why it's valuable to have an AI that wants to eat handshakes, it's helpful to understand a little bit about how handshakes are used in the WPA/WPA2 wireless protocol. + +Before a client device that's connecting to a wireless access point—say, for instance, your phone connecting to your home WiFi network—is able to securely transmit to and receive data from that access point, a process called the **4-Way Handshake** needs to happen in order for the WPA encryption keys to be generated. This process consists of the exchange of four packets (hence the "4" in "4-Way") between the client device and the AP; these are used to derive session keys from the access point's WiFi password. Once the packets are successfully exchanged and the keys have been generated, the client device is authenticated and can start sending and receiving data packets to and from the wireless AP that are secured by encryption. + +<p align="center"> +<img src="https://i.imgur.com/nI8IE6a.png"/> +<br/> +<small>image taken from <a target="_blank" href="https://www.wifi-professionals.com/2019/01/4-way-handshake">wifi-professionals.com</a></small> +</p> + +So...what's the catch? Well, these four packets can easily be "sniffed" by an attacker monitoring nearby (say, with a Pwnagotchi :innocent:). And once recorded, that attacker can use [dictionary and/or bruteforce attacks](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2) to crack the handshakes and recover the original WiFi key. In fact, **successful recovery of the WiFi key doesn't necessarily even need all four packets!** A half-handshake (containing only two of the four packets) can be cracked, too—and in some *(most)* cases, just [a single packet is enough](https://hashcat.net/forum/thread-7717-post-41447.html), *even without clients.* + +In order to ~~eat~~ collect as many of these crackable handshake packets as possible, Pwnagotchi uses two strategies: + +- **Deauthenticating the client stations it detects.** A deauthenticated device must reauthenticate to its access point by resending the 4-Way Handshake, thereby giving Pwnagotchi another chance to sniff the handshake packets and collect more crackable material. +- **Send association frames directly to the access points themselves** +to try to force them to [leak the PMKID](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/). + +All the handshakes captured this way are saved into `.pcap` files on Pwnagotchi's filesystem. Each PCAP file that Pwnagotchi generates is organized according to access point; one PCAP will contain all the handshakes that Pwnagotchi has ever captured for that particular AP. These handshakes can later be [cracked with proper hardware and software](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2). ## License From 76f75c07515e1294b62f590dd4ef4e7e8fb88cce Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 09:28:19 -0400 Subject: [PATCH 050/150] added bluetooth face hack by Systemic (in Slack) --- docs/hacks.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/docs/hacks.md b/docs/hacks.md index 184cff0..5e47d51 100644 --- a/docs/hacks.md +++ b/docs/hacks.md @@ -36,3 +36,67 @@ Some of this guide will work with other framebuffer-based displays. - Reboot. And you should be good! + +--- +### Pwnagotchi face via Bluetooth +Last tested on | Pwnagotchi version | Working? | Reference +---------------|--------------------|----------|-----------| +2019 October 6 | Unknown | :white_check_mark: | on Android +2019 October 6 | Unknown | :white_check_mark: | on iPad iOS 9.3.5 + +A way to view your Pwnagotchi's ~~face~~ UI wirelessly via Bluetooth on a separate device. Refresh rate is the same as the e-ink display (every few seconds). This is NOT Bluetooth tethering; this is only Bluetooth as a server on pi side; you connect the Bluetooth and get a DHCP IP address and that's it. This hack cannot leverage the data connection. + +Contributed by Systemic in the Slack. + +##### 1. First Step +- Comment out the Bluetooth disable line from `/boot/config.txt` : `#dtoverlay=pi3-disable-bt` +- Change `/root/pwnagotchi/config.yml` to have `0.0.0.0` instead of `10.0.0.2` to listen as well on Bluetooth. +- Then launch the following commands: + +##### 2. Install required packages. + +```sudo apt-get install bluez bluez-tools bridge-utils dnsmasq``` + +##### 3. Configure Bluetooth and start it. +```sudo modprobe bnep +sudo brctl addbr pan0 +sudo brctl setfd pan0 0 +sudo brctl stp pan0 off +sudo ifconfig pan0 172.26.0.1 netmask 255.255.255.0 +sudo ip link set pan0 up +``` + +```cat <<- EOF > /tmp/dnsmasq_bt.conf``` + +```bind-interfaces +port=0 +interface=pan0 +listen-address=172.26.0.1 +dhcp-range=172.26.0.2,172.26.0.100,255.255.255.0,5m +dhcp-leasefile=/tmp/dnsmasq_bt.leases +dhcp-authoritative +log-dhcp +``` + +```EOF``` + +```sudo dnsmasq -C /tmp/dnsmasq_bt.conf +sudo bt-agent -c NoInputNoOutput& +sudo bt-adapter -a hci0 --set Discoverable 1 +sudo bt-adapter -a hci0 --set DiscoverableTimeout 0 +sudo bt-adapter -a hci0 --set Pairable 1 +sudo bt-adapter -a hci0 --set PairableTimeout 0 +sudo bt-network -a hci0 -s nap pan0 & +``` + +##### 4. Finally: on your phone, you have to disable all existing interfaces: + +- Shutdown WiFi. +- Shutdown mobile data. +- Connect to the newly available Bluetooth device (which has the name of your Pwnagotchi). + - Once connected, you can test: `http://172.26.0.1:8080` +- You can also install bettercap's UI (`sudo buttercap` then `ui.update`) + - You'll need to change the http caplets to change `127.0.0.1` to `0.0.0.0`. +- You can connect to the shell with a terminal emulator ... + +Happy tweaking. From 662193673544bb2270fdb38049870d745bb41cb9 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 09:38:50 -0400 Subject: [PATCH 051/150] typo --- docs/hacks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hacks.md b/docs/hacks.md index 5e47d51..6f58c3f 100644 --- a/docs/hacks.md +++ b/docs/hacks.md @@ -95,7 +95,7 @@ sudo bt-network -a hci0 -s nap pan0 & - Shutdown mobile data. - Connect to the newly available Bluetooth device (which has the name of your Pwnagotchi). - Once connected, you can test: `http://172.26.0.1:8080` -- You can also install bettercap's UI (`sudo buttercap` then `ui.update`) +- You can also install bettercap's UI (`sudo bettercap` then `ui.update`) - You'll need to change the http caplets to change `127.0.0.1` to `0.0.0.0`. - You can connect to the shell with a terminal emulator ... From 042e5adcbeb97f6560a478782ef0cade745811f3 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 09:40:08 -0400 Subject: [PATCH 052/150] minor edit --- docs/about.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about.md b/docs/about.md index 2883206..4777c21 100644 --- a/docs/about.md +++ b/docs/about.md @@ -33,7 +33,7 @@ So...what's the catch? Well, these four packets can easily be "sniffed" by an at In order to ~~eat~~ collect as many of these crackable handshake packets as possible, Pwnagotchi uses two strategies: -- **Deauthenticating the client stations it detects.** A deauthenticated device must reauthenticate to its access point by resending the 4-Way Handshake, thereby giving Pwnagotchi another chance to sniff the handshake packets and collect more crackable material. +- **Deauthenticating the client stations it detects.** A deauthenticated device must reauthenticate to its access point by re-performing the 4-Way Handshake with the AP, thereby giving Pwnagotchi another chance to sniff the handshake packets and collect more crackable material. - **Send association frames directly to the access points themselves** to try to force them to [leak the PMKID](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/). From a35d55400792620582e71c6b81b69caa2eccec63 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 10:26:11 -0400 Subject: [PATCH 053/150] minor copyediting --- docs/configure.md | 56 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index a7c251f..f85fd35 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -1,22 +1,26 @@ -# Connecting to your Pwnagotchi +# Configuration -Once you wrote the image file on the SD card, there're a few steps you'll have to follow in order to configure your unit properly. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. +Once you've [written the image file onto the SD card](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md#flashing-an-image), there're a few steps you'll have to follow in order to configure your new Pwnagotchi properly. -You'll need to configure it with a static IP address: +## Connect to your Pwnagotchi -- IP: `10.0.0.1` -- Netmask: `255.255.255.0` -- Gateway: `10.0.0.1` -- DNS (if required): `8.8.8.8` (or whatever) +1. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. +2. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. +3. You'll need to configure it with a static IP address: -If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` (if you haven't customized the hostname yet). + - IP: `10.0.0.1` + - Netmask: `255.255.255.0` + - Gateway: `10.0.0.1` + - DNS (if required): `8.8.8.8` (or whatever) -You can now connect to your unit using SSH: +4. If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` (if you haven't customized the hostname yet—if you have named your unit already, this address will be *your unit's name* + `.local`). + +5. **Congratulations!** You can now connect to your unit using SSH: ```bash ssh pi@10.0.0.2 ``` - +##### About your SSH connection The default password is `raspberry`; you should change it as soon as you log in for the first time by issuing the `passwd` command and selecting a new and more complex passphrase. If you want to login directly without entering a password (recommended!), copy your SSH public key to the unit's authorized keys: @@ -25,13 +29,17 @@ If you want to login directly without entering a password (recommended!), copy y ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 ``` -## Configuration +## Give your Pwnagotchi a name -You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/). Create the `/root/custom.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. +You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/)! -## Language Selection +Create the `/root/custom.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. -Pwnagotchi displays its UI in English by default, but it can speak several other languages! You can change `main.lang` to one of the supported languages: +## Choose your Pwnagotchi's language + +Pwnagotchi displays its UI in English by default, but it can speak several other languages! If you're fine with English, you don't need to do anything special. + +But if you want, you can change `main.lang` to one of the supported languages: - **English** *(default)* - German @@ -45,14 +53,26 @@ Pwnagotchi displays its UI in English by default, but it can speak several other ## Display Selection -Set the type of display you want to use via `ui.display.type` (if your display does not work after changing this setting, you might need to completely remove power from the Raspberry and make a clean boot). +**Set the type of display you want to use via `ui.display.type`.** +If your display does not work after changing this setting, you might need to completely remove power from the Raspberry Pi and make a clean boot. -You can configure the refresh interval of the display via `ui.fps`. We recommend using a slow refresh rate to avoid shortening the lifetime of your e-ink display. The default value is `0`, which will *only* refresh when changes are made to the screen. +**You can configure the refresh interval of the display via `ui.fps`.** We recommend using a slow refresh rate to avoid shortening the lifetime of your e-ink display. The default value is `0`, which will *only* refresh when changes are made to the screen. ## Host Connection Share -If you connect to the unit via `usb0` (thus using the data port), you might want to use the `scripts/linux_connection_share.sh`, `scripts/macos_connection_share.sh` or `scripts/win_connection_share.ps1` script to bring the interface up on your end and share internet connectivity from another interface, so you can update the unit and generally download things from the internet on it. +Want to be able to update your Pwnagotchi and access things from the internet on it? *Sure you do!* + +1. Connect to the Pwnagotchi unit via `usb0` (A.K.A., using the data port). +2. Run the appropriate connection sharing script to bring the interface up on your end and share internet connectivity from another interface: + +OS | Script Location +------|--------------------------- +Linux | `scripts/linux_connection_share.sh` +Mac OS X | `scripts/macos_connection_share.sh` +Windows | `scripts/win_connection_share.ps1` ## Troubleshooting -If your network connection keeps flapping on your device connecting to your Pwnagotchi, check if `usb0` (or equivalent) device is being controlled by NetworkManager. You can check this via `nmcli dev status`. +##### If your network connection keeps flapping on your device connecting to your Pwnagotchi. +* Check if `usb0` (or equivalent) device is being controlled by NetworkManager. +* You can check this via `nmcli dev status`. From f9435a5ad2a45f4bb889c0ebbf8d3d284ab0c3cb Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 10:31:34 -0400 Subject: [PATCH 054/150] minor copyediting --- docs/configure.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index f85fd35..b232e3e 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -7,13 +7,13 @@ Once you've [written the image file onto the SD card](https://github.com/evilsoc 1. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. 2. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. 3. You'll need to configure it with a static IP address: + - IP: `10.0.0.1` + - Netmask: `255.255.255.0` + - Gateway: `10.0.0.1` + - DNS (if required): `8.8.8.8` (or whatever) - - IP: `10.0.0.1` - - Netmask: `255.255.255.0` - - Gateway: `10.0.0.1` - - DNS (if required): `8.8.8.8` (or whatever) - -4. If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` (if you haven't customized the hostname yet—if you have named your unit already, this address will be *your unit's name* + `.local`). +4. If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` + * If you have already customized the hostname of your Pwnagotchi, `pwnagotchi.local` won't work. Instead, try *your unit's hostname* + `.local`. 5. **Congratulations!** You can now connect to your unit using SSH: From fae6115e445c65f0d61db46e8526ff7dbb1e8813 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sun, 6 Oct 2019 16:36:25 +0200 Subject: [PATCH 055/150] fix: using sha256 for the public key fingerprint --- pwnagotchi/mesh/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/mesh/__init__.py b/pwnagotchi/mesh/__init__.py index 14403d2..9e68dc8 100644 --- a/pwnagotchi/mesh/__init__.py +++ b/pwnagotchi/mesh/__init__.py @@ -11,4 +11,4 @@ def get_identity(config): pubkey = None with open(config['main']['pubkey']) as fp: pubkey = RSA.importKey(fp.read()) - return pubkey, hashlib.sha1(pubkey.exportKey('DER')).hexdigest() + return pubkey, hashlib.sha256(pubkey.exportKey('DER')).hexdigest() From 4dd7365cf58ba228d63757e77532411a8da29cd8 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 12:51:29 -0400 Subject: [PATCH 056/150] added UI diagram (will copyedit text later) --- docs/usage.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 8883cb7..fff0fb1 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -4,13 +4,16 @@ The UI is available either via display if installed, or via http://pwnagotchi.local:8080/ if you connect to the unit via `usb0` and set a static address on the network interface (change `pwnagotchi` with the hostname of your unit). - + * **CH**: Current channel the unit is operating on or `*` when hopping on all channels. * **APS**: Number of access points on the current channel and total visible access points. * **UP**: Time since the unit has been activated. * **PWND**: Number of handshakes captured in this session and number of unique networks we own at least one handshake of, from the beginning. -* **AUTO**: This indicates that the algorithm is running with AI disabled (or still loading), it disappears once the AI dependencies have been bootrapped and the neural network loaded. +* **MODE**: + * **AUTO:** This indicates that the Pwnagotchi algorithm is running in AUTOMATIC mode, with AI disabled (or still loading); it disappears once the AI dependencies have been bootstrapped and the neural network has finished loading. + * **MANU:** This appears when the unit is running in MANUAL mode. +* **FRIEND:** If another unit is nearby, its presence will be indicated here. If more than one unit is nearby, only one—whichever has the stronger signal strength—will be displayed. ## Training the AI From b4d7ea7dbf2b8c6aed3b03854fcdd0d01f579da1 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 19:59:03 +0200 Subject: [PATCH 057/150] Add peer --- scripts/preview.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/scripts/preview.py b/scripts/preview.py index 4f69a3d..06be3c6 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -8,6 +8,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), '../')) +import pwnagotchi.ui.faces as faces from pwnagotchi.ui.display import Display, VideoHandler from PIL import Image @@ -33,10 +34,26 @@ class CustomDisplay(Display): class DummyPeer: + + def __init__(self): + self.rssi = -50 + @staticmethod def name(): return "beta" + @staticmethod + def pwnd_run(): + return 50 + + @staticmethod + def pwnd_total(): + return 100 + + @staticmethod + def face(): + return faces.FRIEND + def append_images(images, horizontal=True, xmargin=0, ymargin=0): w, h = zip(*(i.size for i in images)) @@ -71,8 +88,9 @@ def main(): parser.add_argument('--lang', help="Language to use", default="en") parser.add_argument('--output', help="Path to output image (PNG)", default="preview.png") - parser.add_argument('--xmargin', type=int, default=5) - parser.add_argument('--ymargin', type=int, default=5) + parser.add_argument('--show-peer', dest="showpeer", help="This options will show a dummy peer", action="store_true") + parser.add_argument('--xmargin', help="Add X-Margin", type=int, default=5) + parser.add_argument('--ymargin', help="Add Y-Margin", type=int, default=5) args = parser.parse_args() config_template = ''' @@ -103,7 +121,8 @@ def main(): for display in list_of_displays: emotions = list() - # Starting + if args.showpeer: + display.set_closest_peer(DummyPeer()) display.on_starting() display.update() emotions.append(display.get_image()) From b7c3f41e656634913f7d01f8d7a6ad78b20ec614 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sat, 5 Oct 2019 15:39:14 +0200 Subject: [PATCH 058/150] Add wigle plugin --- pwnagotchi/plugins/default/wigle.py | 261 ++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 pwnagotchi/plugins/default/wigle.py diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py new file mode 100644 index 0000000..b87e71b --- /dev/null +++ b/pwnagotchi/plugins/default/wigle.py @@ -0,0 +1,261 @@ +__author__ = '33197631+dadav@users.noreply.github.com' +__version__ = '1.0.0' +__name__ = 'wigle' +__license__ = 'GPL3' +__description__ = 'This plugin automatically uploades collected wifis to wigle.net' + +import os +import logging +import json +from io import StringIO +import csv +from datetime import datetime +import requests +from pwnagotchi.mesh.wifi import freq_to_channel +from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA + +READY = False +ALREADY_UPLOADED = None +SKIP = None +OPTIONS = dict() + +AKMSUITE_TYPES = { + 0x00: "Reserved", + 0x01: "802.1X", + 0x02: "PSK", +} + +def _handle_packet(packet, result): + """ + Analyze each packet and extract the data from Dot11 layers + """ + + if hasattr(packet, 'cap') and 'privacy' in packet.cap: + # packet is encrypted + if 'encryption' not in result: + result['encryption'] = set() + + if packet.haslayer(Dot11Beacon): + if packet.haslayer(Dot11Beacon)\ + or packet.haslayer(Dot11ProbeResp)\ + or packet.haslayer(Dot11AssoReq)\ + or packet.haslayer(Dot11ReassoReq): + if 'bssid' not in result and hasattr(packet[Dot11], 'addr3'): + result['bssid'] = packet[Dot11].addr3 + if 'essid' not in result and hasattr(packet[Dot11Elt], 'info'): + result['essid'] = packet[Dot11Elt].info + if 'channel' not in result and hasattr(packet[Dot11Elt:3], 'info'): + result['channel'] = int(ord(packet[Dot11Elt:3].info)) + + if packet.haslayer(RadioTap): + if 'rssi' not in result and hasattr(packet[RadioTap], 'dBm_AntSignal'): + result['rssi'] = packet[RadioTap].dBm_AntSignal + if 'channel' not in result and hasattr(packet[RadioTap], 'ChannelFrequency'): + result['channel'] = freq_to_channel(packet[RadioTap].ChannelFrequency) + + # see: https://fossies.org/linux/scapy/scapy/layers/dot11.py + if packet.haslayer(Dot11EltRSN): + if hasattr(packet[Dot11EltRSN], 'akm_suites'): + auth = AKMSUITE_TYPES.get(packet[Dot11EltRSN].akm_suites[0].suite) + result['encryption'].add(f"WPA2/{auth}") + else: + result['encryption'].add("WPA2") + + if packet.haslayer(Dot11EltVendorSpecific)\ + and (packet.haslayer(Dot11EltMicrosoftWPA) + or packet.info.startswith(b'\x00P\xf2\x01\x01\x00')): + + if hasattr(packet, 'akm_suites'): + auth = AKMSUITE_TYPES.get(packet.akm_suites[0].suite) + result['encryption'].add(f"WPA2/{auth}") + else: + result['encryption'].add("WPA2") + # end see + + return result + + +def _analyze_pcap(pcap): + """ + Iterate over the packets and extract data + """ + result = dict() + + try: + packets = rdpcap(pcap) + for packet in packets: + result = _handle_packet(packet, result) + except Scapy_Exception as sc_e: + raise sc_e + + return result + + +def on_loaded(): + """ + Gets called when the plugin gets loaded + """ + global READY + global ALREADY_UPLOADED + global SKIP + + SKIP = list() + + if 'api_key' not in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None): + logging.error("WIGLE: api_key isn't set. Can't upload to wigle.net") + return + + try: + with open('/root/.wigle_uploads', 'r') as f: + ALREADY_UPLOADED = f.read().splitlines() + except OSError: + logging.warning('WIGLE: No upload-file found.') + ALREADY_UPLOADED = [] + + READY = True + + +def _extract_gps_data(path): + """ + Extract data from gps-file + + return json-obj + """ + + try: + with open(path, 'r') as json_file: + return json.load(json_file) + except OSError as os_err: + logging.error("WIGLE: %s", os_err) + except json.JSONDecodeError as json_err: + logging.error("WIGLE: %s", json_err) + + return None + +def _format_auth(data): + out = "" + for auth in data: + out = f"{out}[{auth}]" + return out + +def _transform_wigle_entry(gps_data, pcap_data): + """ + Transform to wigle entry in file + """ + dummy = StringIO() + # write kismet header + dummy.write("WigleWifi-1.4,appRelease=20190201,model=Kismet,release=2019.02.01.{},device=kismet,display=kismet,board=kismet,brand=kismet\n") + dummy.write("MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type") + + writer = csv.writer(dummy, delimiter=",", quoting=csv.QUOTE_NONE) + writer.writerow([ + pcap_data['bssid'], + pcap_data['essid'].decode('utf-8'), + _format_auth(pcap_data['encryption']), + datetime.strptime(gps_data['Updated'].rsplit('.')[0], + "%Y-%m-%dT%H:%M:%S").strftime('%Y-%m-%d %H:%M:%S'), + pcap_data['channel'], + pcap_data['rssi'], + gps_data['Latitude'], + gps_data['Longitude'], + gps_data['Altitude'], + 0, # accuracy? + 'WIFI']) + return dummy.getvalue() + +def _send_to_wigle(lines, api_key, timeout=30): + """ + Uploads the file to wigle-net + """ + + dummy = StringIO() + + for line in lines: + dummy.write(f"{line}") + + dummy.seek(0) + + headers = {'Authorization': f"Basic {api_key}", + 'Accept': 'application/json'} + data = {'donate': 'false'} + payload = {'file': dummy, 'type': 'text/csv'} + + try: + res = requests.post('https://api.wigle.net/api/v2/file/upload', + data=data, + headers=headers, + files=payload, + timeout=timeout) + json_res = res.json() + if not json_res['success']: + raise requests.exceptions.RequestException(json_res['message']) + except requests.exceptions.RequestException as re_e: + raise re_e + + +def on_internet_available(display, config, log): + """ + Called in manual mode when there's internet connectivity + """ + global ALREADY_UPLOADED + global SKIP + + if READY: + handshake_dir = config['bettercap']['handshakes'] + all_files = os.listdir(handshake_dir) + gps_files = [os.path.join(handshake_dir, filename) + for filename in all_files + if filename.endswith('.gps.json')] + gps_new = set(gps_files) - set(ALREADY_UPLOADED) - set(SKIP) + + if gps_new: + logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net") + + lines = list() + for gps_file in gps_new: + pcap_filename = gps_file.replace('.gps.json', '.pcap') + + if not os.path.exists(pcap_filename): + logging.error("WIGLE: Can't find pcap for %s", gps_file) + SKIP.append(gps_file) + continue + + gps_data = _extract_gps_data(gps_file) + try: + pcap_data = _analyze_pcap(pcap_filename) + except Scapy_Exception as sc_e: + logging.error("WIGLE: %s", sc_e) + SKIP.append(gps_file) + continue + + if 'encryption' in pcap_data: + if not pcap_data['encryption']: + pcap_data['encryption'].add('WEP') + else: + pcap_data['encryption'] = set() + pcap_data['encryption'].add('OPN') + + if len(pcap_data) < 5: + # not enough data + SKIP.append(gps_file) + continue + + new_entry = _transform_wigle_entry(gps_data, pcap_data) + lines.append(new_entry) + + if lines: + display.set('status', "Uploading gps-data to wigle.net ...") + display.update(force=True) + try: + _send_to_wigle(lines, OPTIONS['api_key']) + ALREADY_UPLOADED += gps_new + with open('/root/.wigle_uploads', 'a') as up_file: + for gps in gps_new: + up_file.write(gps + "\n") + logging.info("WIGLE: Successfuly uploaded %d files", len(gps_new)) + except requests.exceptions.RequestException as re_e: + SKIP += lines + logging.error("WIGLE: Got an exception while uploading %s", re_e) + except OSError as os_e: + SKIP += lines + logging.error("WIGLE: Got the following error: %s", os_e) From a9ef098d3291860f48ce27e075b6957c1121f039 Mon Sep 17 00:00:00 2001 From: SecurityWaffle <SecurityWaffle@users.noreply.github.com> Date: Sun, 6 Oct 2019 13:32:17 -0500 Subject: [PATCH 059/150] Fixed bug with defaults.yml path --- bin/pwnagotchi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pwnagotchi b/bin/pwnagotchi index 4733105..9b0b561 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -16,7 +16,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-C', '--config', action='store', dest='config', - default=os.path.join(os.path.abspath(os.path.dirname(pwnagotchi.__file__)), '/defaults.yml'), + default=os.path.join(os.path.abspath(os.path.dirname(pwnagotchi.__file__)), 'defaults.yml'), help='Main configuration file.') parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.yml', help='If this file exists, configuration will be merged and this will override default values.') From 90f0d6dc4ea7ac021813216edf6904eafe2b9504 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 20:32:43 +0200 Subject: [PATCH 060/150] update defaults --- pwnagotchi/defaults.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index cceac71..813b97f 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -39,6 +39,9 @@ main: wpa-sec: enabled: false api_key: ~ + wigle: + enabled: false + api_key: ~ # monitor interface to use iface: mon0 From 27255bd8ec696cc463d79abd893f9e478b8d5b86 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 20:37:28 +0200 Subject: [PATCH 061/150] Fix bug with gps.json files --- pwnagotchi/plugins/default/wpa-sec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py index 68e4171..cfe35da 100644 --- a/pwnagotchi/plugins/default/wpa-sec.py +++ b/pwnagotchi/plugins/default/wpa-sec.py @@ -61,7 +61,7 @@ def on_internet_available(display, config, log): if READY: handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) - handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames] + handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] handshake_new = set(handshake_paths) - set(ALREADY_UPLOADED) if handshake_new: From cb943ae4e168ae93ce3357bfe9a311e6622c54d1 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 20:39:16 +0200 Subject: [PATCH 062/150] Fix bug with gps.json files --- pwnagotchi/plugins/default/onlinehashcrack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/onlinehashcrack.py b/pwnagotchi/plugins/default/onlinehashcrack.py index 249ea19..2319d96 100644 --- a/pwnagotchi/plugins/default/onlinehashcrack.py +++ b/pwnagotchi/plugins/default/onlinehashcrack.py @@ -62,7 +62,7 @@ def on_internet_available(display, config, log): if READY: handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) - handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames] + handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] handshake_new = set(handshake_paths) - set(ALREADY_UPLOADED) if handshake_new: From 1cf45138b8c8793cd7e1dbc8b8e0962a5ab5214a Mon Sep 17 00:00:00 2001 From: SecurityWaffle <SecurityWaffle@users.noreply.github.com> Date: Sun, 6 Oct 2019 13:40:16 -0500 Subject: [PATCH 063/150] Fixes path to defaults.yml --- bin/pwnagotchi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pwnagotchi b/bin/pwnagotchi index 9b0b561..56300af 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -16,7 +16,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-C', '--config', action='store', dest='config', - default=os.path.join(os.path.abspath(os.path.dirname(pwnagotchi.__file__)), 'defaults.yml'), + default=os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.yml'), help='Main configuration file.') parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.yml', help='If this file exists, configuration will be merged and this will override default values.') From a3052c3b990558db9d3c60c5ed55fa1de72efc02 Mon Sep 17 00:00:00 2001 From: SecurityWaffle <SecurityWaffle@users.noreply.github.com> Date: Sun, 6 Oct 2019 13:41:55 -0500 Subject: [PATCH 064/150] fixes issue where defaults.yml is not included in the install --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 41d7475..98ee45c 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,8 @@ setup(name='pwnagotchi', license='GPL', install_requires=required, scripts=['bin/pwnagotchi'], - package_data={'pwnagotchi': ('pwnagotchi/defaults.yml',)}, + package_data={'pwnagotchi': ['defaults.yml', 'pwnagotchi/defaults.yml']}, + include_package_data=True, packages=find_packages(), classifiers=[ 'Programming Language :: Python :: 3', From 5b1bf6dd4afe62354e8499048b09e4d8a88288a4 Mon Sep 17 00:00:00 2001 From: waxwing <katherine.snyder@socraticarts.com> Date: Sun, 6 Oct 2019 15:23:37 -0400 Subject: [PATCH 065/150] added UI anatomy diagram --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4fb4153..02b9506 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ [Pwnagotchi](https://twitter.com/pwnagotchi) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding WiFi environment in order to maximize the crackable WPA key material it captures (either passively, or by performing deauthentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/), full and half WPA handshakes. - + Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54) over time to **get better at pwning WiFi things** in the environments you expose it to. From 9cf00805e007c31f37be3cac2a3e5f8ee3178901 Mon Sep 17 00:00:00 2001 From: Zenzen San <zenzenzen@riseup.net> Date: Sat, 5 Oct 2019 20:05:46 -0400 Subject: [PATCH 066/150] - Plugin geowifi saves wifi geolocation on hndshk Saves a json file with the access points with more signal whenever a handshake is captured. This data is usable to retrieve the geographic location using Google Geolocation API or Mozilla Location Service --- pwnagotchi/defaults.yml | 2 ++ pwnagotchi/plugins/default/geowifi.py | 30 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 pwnagotchi/plugins/default/geowifi.py diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index cceac71..c1fc2b6 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -25,6 +25,8 @@ main: 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' + geowifi: + enabled: false gps: enabled: false twitter: diff --git a/pwnagotchi/plugins/default/geowifi.py b/pwnagotchi/plugins/default/geowifi.py new file mode 100644 index 0000000..b7f7982 --- /dev/null +++ b/pwnagotchi/plugins/default/geowifi.py @@ -0,0 +1,30 @@ +__author__ = 'zenzen san' +__version__ = '1.0.0' +__name__ = 'geowifi' +__license__ = 'GPL3' +__description__ = 'Saves a json file with the access points with more signal whenever a handshake is captured. This data is usable to retrieve the geographic location using Google Geolocation API or Mozilla Location Service' + +import logging +import json + +def on_loaded(): + logging.info("geowifi plugin loaded. :)") + +def on_handshake(agent, filename, access_point, client_station): + info = agent.session() + aps = agent.get_access_points() + geowifi = _geowifi_location(aps) + geowifi_filename = filename.replace('.pcap', '.geowifi.json') + + logging.info("saving GEOWIFI location to %s" % (geowifi_filename)) + with open(geowifi_filename, 'w+t') as fp: + json.dump(geowifi, fp) + +def _geowifi_location(aps): + geowifi = {} + geowifi['wifiAccessPoints'] = [] + # size seems a good number to save a wifi networks location + for ap in sorted(aps,key=lambda i:i['rssi'],reverse=True)[:6]: + geowifi['wifiAccessPoints'].append({'macAddress': ap['mac'], 'signalStrength': ap['rssi']}) + return geowifi + From 8d8e4b037651f708c24a314aacb49e4fef0d3fdd Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 22:30:43 +0200 Subject: [PATCH 067/150] Rename some vars and fix some bugs --- pwnagotchi/plugins/default/wigle.py | 50 ++++++++++++++++++----------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py index b87e71b..cba5745 100644 --- a/pwnagotchi/plugins/default/wigle.py +++ b/pwnagotchi/plugins/default/wigle.py @@ -126,11 +126,10 @@ def _extract_gps_data(path): with open(path, 'r') as json_file: return json.load(json_file) except OSError as os_err: - logging.error("WIGLE: %s", os_err) + raise os_err except json.JSONDecodeError as json_err: - logging.error("WIGLE: %s", json_err) + raise json_err - return None def _format_auth(data): out = "" @@ -203,16 +202,18 @@ def on_internet_available(display, config, log): if READY: handshake_dir = config['bettercap']['handshakes'] all_files = os.listdir(handshake_dir) - gps_files = [os.path.join(handshake_dir, filename) + all_gps_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.gps.json')] - gps_new = set(gps_files) - set(ALREADY_UPLOADED) - set(SKIP) + new_gps_files = set(all_gps_files) - set(ALREADY_UPLOADED) - set(SKIP) - if gps_new: + if new_gps_files: logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net") - lines = list() - for gps_file in gps_new: + csv_entries = list() + no_err_entries = list() + + for gps_file in new_gps_files: pcap_filename = gps_file.replace('.gps.json', '.pcap') if not os.path.exists(pcap_filename): @@ -220,7 +221,17 @@ def on_internet_available(display, config, log): SKIP.append(gps_file) continue - gps_data = _extract_gps_data(gps_file) + try: + gps_data = _extract_gps_data(gps_file) + except OSError as os_err: + logging.error("WIGLE: %s", os_err) + SKIP.append(gps_file) + continue + except json.JSONDecodeError as json_err: + logging.error("WIGLE: %s", json_err) + SKIP.append(gps_file) + continue + try: pcap_data = _analyze_pcap(pcap_filename) except Scapy_Exception as sc_e: @@ -228,34 +239,37 @@ def on_internet_available(display, config, log): SKIP.append(gps_file) continue + # encrypption-key is only there if privacy-cap was set if 'encryption' in pcap_data: if not pcap_data['encryption']: pcap_data['encryption'].add('WEP') else: + # no encryption, nothing to eat :( pcap_data['encryption'] = set() pcap_data['encryption'].add('OPN') if len(pcap_data) < 5: - # not enough data + # not enough data; try next time SKIP.append(gps_file) continue new_entry = _transform_wigle_entry(gps_data, pcap_data) - lines.append(new_entry) + csv_entries.append(new_entry) + no_err_entries.append(gps_file) - if lines: + if csv_entries: display.set('status', "Uploading gps-data to wigle.net ...") display.update(force=True) try: - _send_to_wigle(lines, OPTIONS['api_key']) - ALREADY_UPLOADED += gps_new + _send_to_wigle(csv_entries, OPTIONS['api_key']) + ALREADY_UPLOADED += no_err_entries with open('/root/.wigle_uploads', 'a') as up_file: - for gps in gps_new: + for gps in no_err_entries: up_file.write(gps + "\n") - logging.info("WIGLE: Successfuly uploaded %d files", len(gps_new)) + logging.info("WIGLE: Successfuly uploaded %d files", len(no_err_entries)) except requests.exceptions.RequestException as re_e: - SKIP += lines + SKIP += no_err_entries logging.error("WIGLE: Got an exception while uploading %s", re_e) except OSError as os_e: - SKIP += lines + SKIP += no_err_entries logging.error("WIGLE: Got the following error: %s", os_e) From f6a80aae7823ba3ad70df15eb5fcf58b2d16ab1e Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 6 Oct 2019 23:00:31 +0200 Subject: [PATCH 068/150] Fix 404 url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02b9506..670ed47 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ full and half WPA handshakes.  -Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54) over time to **get better at pwning WiFi things** in the environments you expose it to. +Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.yml#L73) over time to **get better at pwning WiFi things** in the environments you expose it to. More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) doc.) From 1c251fc09381d0ac28940bd5fd3dd066fa1996c4 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Sun, 6 Oct 2019 23:25:02 +0200 Subject: [PATCH 069/150] new: fixed rsa identity generation and implemented api enrollment plugin --- bin/pwnagotchi | 8 +-- pwnagotchi/agent.py | 4 +- pwnagotchi/defaults.yml | 2 + pwnagotchi/identity.py | 47 +++++++++++++++++ pwnagotchi/mesh/__init__.py | 10 ---- pwnagotchi/mesh/utils.py | 7 ++- pwnagotchi/plugins/default/api.py | 51 +++++++++++++++++++ pwnagotchi/plugins/default/auto-backup.py | 2 +- pwnagotchi/plugins/default/auto-update.py | 2 +- pwnagotchi/plugins/default/example.py | 2 +- pwnagotchi/plugins/default/onlinehashcrack.py | 2 +- pwnagotchi/plugins/default/twitter.py | 2 +- pwnagotchi/plugins/default/wigle.py | 9 +++- pwnagotchi/plugins/default/wpa-sec.py | 2 +- pwnagotchi/utils.py | 3 ++ setup.py | 1 + 16 files changed, 127 insertions(+), 27 deletions(-) create mode 100644 pwnagotchi/identity.py create mode 100644 pwnagotchi/plugins/default/api.py diff --git a/bin/pwnagotchi b/bin/pwnagotchi index 56300af..f72d84c 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -10,6 +10,7 @@ if __name__ == '__main__': import pwnagotchi.plugins as plugins from pwnagotchi.log import SessionParser + from pwnagotchi.identity import KeyPair from pwnagotchi.agent import Agent from pwnagotchi.ui.display import Display @@ -34,10 +35,11 @@ if __name__ == '__main__': plugins.load(config) + keypair = KeyPair() display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()}) - agent = Agent(view=display, config=config) + agent = Agent(view=display, config=config, keypair=keypair) - logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, pwnagotchi.version)) + logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._keypair.fingerprint, pwnagotchi.version)) for _, plugin in plugins.loaded.items(): logging.debug("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__)) @@ -64,7 +66,7 @@ if __name__ == '__main__': time.sleep(1) if Agent.is_connected(): - plugins.on('internet_available', display, config, log) + plugins.on('internet_available', display, keypair, config, log) else: logging.info("entering auto mode ...") diff --git a/pwnagotchi/agent.py b/pwnagotchi/agent.py index 98f8aab..0e89763 100644 --- a/pwnagotchi/agent.py +++ b/pwnagotchi/agent.py @@ -17,13 +17,13 @@ RECOVERY_DATA_FILE = '/root/.pwnagotchi-recovery' class Agent(Client, AsyncAdvertiser, AsyncTrainer): - def __init__(self, view, config): + def __init__(self, view, config, keypair): Client.__init__(self, config['bettercap']['hostname'], config['bettercap']['scheme'], config['bettercap']['port'], config['bettercap']['username'], config['bettercap']['password']) - AsyncAdvertiser.__init__(self, config, view) + AsyncAdvertiser.__init__(self, config, view, keypair) AsyncTrainer.__init__(self, config) self._started_at = time.time() diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 813b97f..dff6b3b 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -6,6 +6,8 @@ main: custom_plugins: # which plugins to load and enable plugins: + api: + enabled: false auto-update: enabled: false interval: 1 # every day diff --git a/pwnagotchi/identity.py b/pwnagotchi/identity.py new file mode 100644 index 0000000..ae12490 --- /dev/null +++ b/pwnagotchi/identity.py @@ -0,0 +1,47 @@ +from Crypto.Signature import PKCS1_PSS +from Crypto.PublicKey import RSA +import Crypto.Hash.SHA256 as SHA256 +import base64 +import hashlib +import os +import logging + +DefaultPath = "/etc/pwnagotchi/" + + +class KeyPair(object): + def __init__(self, path=DefaultPath): + self.path = path + self.priv_path = os.path.join(path, "id_rsa") + self.priv_key = None + self.pub_path = "%s.pub" % self.priv_path + self.pub_key = None + + if not os.path.exists(self.path): + os.makedirs(self.path) + + if not os.path.exists(self.priv_path) or not os.path.exists(self.pub_path): + logging.info("generating %s ..." % self.priv_path) + os.system("/usr/bin/ssh-keygen -t rsa -m PEM -b 4096 -N '' -f '%s'" % self.priv_path) + + with open(self.priv_path) as fp: + self.priv_key = RSA.importKey(fp.read()) + + with open(self.pub_path) as fp: + self.pub_key = RSA.importKey(fp.read()) + self.pub_key_pem = self.pub_key.exportKey('PEM').decode("ascii") + # python is special + if 'RSA PUBLIC KEY' not in self.pub_key_pem: + self.pub_key_pem = self.pub_key_pem.replace('PUBLIC KEY', 'RSA PUBLIC KEY') + + pem = self.pub_key_pem.encode("ascii") + + self.pub_key_pem_b64 = base64.b64encode(pem).decode("ascii") + self.fingerprint = hashlib.sha256(pem).hexdigest() + + def sign(self, message): + hasher = SHA256.new(message.encode("ascii")) + signer = PKCS1_PSS.new(self.priv_key, saltLen=16) + signature = signer.sign(hasher) + signature_b64 = base64.b64encode(signature).decode("ascii") + return signature, signature_b64 \ No newline at end of file diff --git a/pwnagotchi/mesh/__init__.py b/pwnagotchi/mesh/__init__.py index 9e68dc8..e1c033f 100644 --- a/pwnagotchi/mesh/__init__.py +++ b/pwnagotchi/mesh/__init__.py @@ -1,14 +1,4 @@ import os -from Crypto.PublicKey import RSA -import hashlib - def new_session_id(): return ':'.join(['%02x' % b for b in os.urandom(6)]) - - -def get_identity(config): - pubkey = None - with open(config['main']['pubkey']) as fp: - pubkey = RSA.importKey(fp.read()) - return pubkey, hashlib.sha256(pubkey.exportKey('DER')).hexdigest() diff --git a/pwnagotchi/mesh/utils.py b/pwnagotchi/mesh/utils.py index 3b63d09..a775ad4 100644 --- a/pwnagotchi/mesh/utils.py +++ b/pwnagotchi/mesh/utils.py @@ -3,14 +3,13 @@ import logging import pwnagotchi import pwnagotchi.plugins as plugins -from pwnagotchi.mesh import get_identity class AsyncAdvertiser(object): - def __init__(self, config, view): + def __init__(self, config, view, keypair): self._config = config self._view = view - self._public_key, self._identity = get_identity(config) + self._keypair = keypair self._advertiser = None def start_advertising(self): @@ -24,7 +23,7 @@ class AsyncAdvertiser(object): self._config['main']['iface'], pwnagotchi.name(), pwnagotchi.version, - self._identity, + self._keypair.fingerprint, period=0.3, data=self._config['personality']) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py new file mode 100644 index 0000000..afd882c --- /dev/null +++ b/pwnagotchi/plugins/default/api.py @@ -0,0 +1,51 @@ +__author__ = 'evilsocket@gmail.com' +__version__ = '1.0.0' +__name__ = 'api' +__license__ = 'GPL3' +__description__ = 'This plugin signals the unit cryptographic identity to api.pwnagotchi.ai' + +import logging +import json +import requests +import pwnagotchi +from pwnagotchi.utils import StatusFile + +OPTIONS = dict() +READY = False +STATUS = StatusFile('/root/.api-enrollment.json') + + +def on_loaded(): + logging.info("api plugin loaded.") + + +def on_internet_available(ui, keypair, config, log): + global STATUS + + if STATUS.newer_then_minutes(10): + return + + try: + logging.info("api: signign enrollment request ...") + identity = "%s@%s" % (pwnagotchi.name(), keypair.fingerprint) + _, signature_b64 = keypair.sign(identity) + + api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' + enroll = { + 'identity': identity, + 'public_key': keypair.pub_key_pem_b64, + 'signature': signature_b64 + } + + logging.info("api: enrolling unit to %s ..." % api_address) + + r = requests.post(api_address, json=enroll) + if r.status_code == 200: + token = r.json() + logging.info("api: enrolled") + STATUS.update(data=json.dumps(token)) + else: + logging.error("error %d: %s" % (r.status_code, r.json())) + + except Exception as e: + logging.exception("error while enrolling the unit") diff --git a/pwnagotchi/plugins/default/auto-backup.py b/pwnagotchi/plugins/default/auto-backup.py index 731a40f..48d8597 100644 --- a/pwnagotchi/plugins/default/auto-backup.py +++ b/pwnagotchi/plugins/default/auto-backup.py @@ -33,7 +33,7 @@ def on_loaded(): logging.info("AUTO-BACKUP: Successfuly loaded.") -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): global STATUS if READY: diff --git a/pwnagotchi/plugins/default/auto-update.py b/pwnagotchi/plugins/default/auto-update.py index 4a1f135..dab44b3 100644 --- a/pwnagotchi/plugins/default/auto-update.py +++ b/pwnagotchi/plugins/default/auto-update.py @@ -23,7 +23,7 @@ def on_loaded(): READY = True -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): global STATUS if READY: diff --git a/pwnagotchi/plugins/default/example.py b/pwnagotchi/plugins/default/example.py index c18177c..2a82a18 100644 --- a/pwnagotchi/plugins/default/example.py +++ b/pwnagotchi/plugins/default/example.py @@ -20,7 +20,7 @@ def on_loaded(): # called in manual mode when there's internet connectivity -def on_internet_available(ui, config, log): +def on_internet_available(ui, keypair, config, log): pass diff --git a/pwnagotchi/plugins/default/onlinehashcrack.py b/pwnagotchi/plugins/default/onlinehashcrack.py index 2319d96..9771b88 100644 --- a/pwnagotchi/plugins/default/onlinehashcrack.py +++ b/pwnagotchi/plugins/default/onlinehashcrack.py @@ -55,7 +55,7 @@ def _upload_to_ohc(path, timeout=30): raise e -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): """ Called in manual mode when there's internet connectivity """ diff --git a/pwnagotchi/plugins/default/twitter.py b/pwnagotchi/plugins/default/twitter.py index 560903a..8f21f25 100644 --- a/pwnagotchi/plugins/default/twitter.py +++ b/pwnagotchi/plugins/default/twitter.py @@ -14,7 +14,7 @@ def on_loaded(): # called in manual mode when there's internet connectivity -def on_internet_available(ui, config, log): +def on_internet_available(ui, keypair, config, log): if log.is_new() and log.handshakes > 0: try: import tweepy diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py index cba5745..da57f94 100644 --- a/pwnagotchi/plugins/default/wigle.py +++ b/pwnagotchi/plugins/default/wigle.py @@ -12,7 +12,6 @@ import csv from datetime import datetime import requests from pwnagotchi.mesh.wifi import freq_to_channel -from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA READY = False ALREADY_UPLOADED = None @@ -26,6 +25,8 @@ AKMSUITE_TYPES = { } def _handle_packet(packet, result): + from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ + Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA """ Analyze each packet and extract the data from Dot11 layers """ @@ -76,6 +77,8 @@ def _handle_packet(packet, result): def _analyze_pcap(pcap): + from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ + Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA """ Iterate over the packets and extract data """ @@ -192,7 +195,9 @@ def _send_to_wigle(lines, api_key, timeout=30): raise re_e -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): + from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ + Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA """ Called in manual mode when there's internet connectivity """ diff --git a/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py index cfe35da..7d61b7b 100644 --- a/pwnagotchi/plugins/default/wpa-sec.py +++ b/pwnagotchi/plugins/default/wpa-sec.py @@ -54,7 +54,7 @@ def _upload_to_wpasec(path, timeout=30): raise e -def on_internet_available(display, config, log): +def on_internet_available(display, keypair, config, log): """ Called in manual mode when there's internet connectivity """ diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py index d1b2ba0..2834664 100644 --- a/pwnagotchi/utils.py +++ b/pwnagotchi/utils.py @@ -89,6 +89,9 @@ class StatusFile(object): if os.path.exists(path): self._updated = datetime.fromtimestamp(os.path.getmtime(path)) + def newer_then_minutes(self, minutes): + return self._updated is not None and ((datetime.now() - self._updated).seconds / 60) < minutes + def newer_then_days(self, days): return self._updated is not None and (datetime.now() - self._updated).days < days diff --git a/setup.py b/setup.py index 98ee45c..f8e22b3 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- from setuptools import setup, find_packages import pwnagotchi From 967ba3a7a510e650e9b098a72d45629ca8e8407e Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 00:10:00 +0200 Subject: [PATCH 070/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index afd882c..6747d95 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -22,7 +22,7 @@ def on_loaded(): def on_internet_available(ui, keypair, config, log): global STATUS - if STATUS.newer_then_minutes(10): + if STATUS.newer_then_minutes(25): return try: From 8ef1f0f377771e6300a38d1aca66cbd29c385bce Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 00:27:22 +0200 Subject: [PATCH 071/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 1 - scripts/pypi_upload.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 6747d95..480f07c 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -38,7 +38,6 @@ def on_internet_available(ui, keypair, config, log): } logging.info("api: enrolling unit to %s ..." % api_address) - r = requests.post(api_address, json=enroll) if r.status_code == 200: token = r.json() diff --git a/scripts/pypi_upload.sh b/scripts/pypi_upload.sh index 4a71125..265a56a 100755 --- a/scripts/pypi_upload.sh +++ b/scripts/pypi_upload.sh @@ -1,6 +1,6 @@ #!/bin/bash -rm -rf build dist ergo_nn.egg-info && +rm -rf build dist pwnagotchi.egg-info && python3 setup.py sdist bdist_wheel && clear && twine upload dist/* From b26d238f4091cfd671586380fcffa64f30b89ace Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 00:30:05 +0200 Subject: [PATCH 072/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/__init__.py b/pwnagotchi/__init__.py index 9905b0f..ff43494 100644 --- a/pwnagotchi/__init__.py +++ b/pwnagotchi/__init__.py @@ -1,6 +1,6 @@ import subprocess -version = '1.0.0plz4' +version = '1.0.0a' _name = None From 61da16ed915877f8cdb60cc4b1d00de175305fbe Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 01:31:37 +0200 Subject: [PATCH 073/150] fix: when the AI is ready the mode label reports AI --- pwnagotchi/ai/__init__.py | 2 +- pwnagotchi/ui/view.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pwnagotchi/ai/__init__.py b/pwnagotchi/ai/__init__.py index 359a38c..190025f 100644 --- a/pwnagotchi/ai/__init__.py +++ b/pwnagotchi/ai/__init__.py @@ -39,4 +39,4 @@ def load(config, agent, epoch, from_disk=True): for key, value in config['params'].items(): logging.info(" %s: %s" % (key, value)) - return a2c + return a2c \ No newline at end of file diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index 78e75b6..5847662 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -153,7 +153,7 @@ class View(object): self.set('face', faces.AWAKE) def on_ai_ready(self): - self.set('mode', '') + self.set('mode', ' AI') self.set('face', faces.HAPPY) self.set('status', self._voice.on_ai_ready()) self.update() From 6e9bb866c79506b310b3d05374d0d2ec2f87e6d1 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 02:00:08 +0200 Subject: [PATCH 074/150] fix: fixed epochs log parser --- pwnagotchi/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/log.py b/pwnagotchi/log.py index 416ae4f..dab58e4 100644 --- a/pwnagotchi/log.py +++ b/pwnagotchi/log.py @@ -13,7 +13,7 @@ LAST_SESSION_FILE = '/root/.pwnagotchi-last-session' class SessionParser(object): EPOCH_TOKEN = '[epoch ' - EPOCH_PARSER = re.compile(r'^\s*\[epoch (\d+)\] (.+)') + EPOCH_PARSER = re.compile(r'^.+\[epoch (\d+)\] (.+)') EPOCH_DATA_PARSER = re.compile(r'([a-z_]+)=([^\s]+)') TRAINING_TOKEN = ' training epoch ' START_TOKEN = 'connecting to http' From 00582be3746c4adc23aed00797040a2902ffc9aa Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 10:25:46 +0200 Subject: [PATCH 075/150] new: reporting session data to the api --- pwnagotchi/plugins/default/api.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 480f07c..5828085 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -7,6 +7,7 @@ __description__ = 'This plugin signals the unit cryptographic identity to api.pw import logging import json import requests +import subprocess import pwnagotchi from pwnagotchi.utils import StatusFile @@ -27,6 +28,7 @@ def on_internet_available(ui, keypair, config, log): try: logging.info("api: signign enrollment request ...") + identity = "%s@%s" % (pwnagotchi.name(), keypair.fingerprint) _, signature_b64 = keypair.sign(identity) @@ -34,7 +36,16 @@ def on_internet_available(ui, keypair, config, log): enroll = { 'identity': identity, 'public_key': keypair.pub_key_pem_b64, - 'signature': signature_b64 + 'signature': signature_b64, + 'data': { + 'duration': log.duration, + 'epochs': log.epochs, + 'train_epochs': log.train_epochs, + 'avg_reward': log.avg_reward, + 'min_reward': log.min_reward, + 'max_reward': log.max_reward, + 'uname': subprocess.getoutput("uname -a") + } } logging.info("api: enrolling unit to %s ..." % api_address) From da52bcd705d2dfdfbd72bd3743246d1646fd5018 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 11:13:28 +0200 Subject: [PATCH 076/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 5828085..60792af 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -30,6 +30,7 @@ def on_internet_available(ui, keypair, config, log): logging.info("api: signign enrollment request ...") identity = "%s@%s" % (pwnagotchi.name(), keypair.fingerprint) + # sign the identity string to prove we own both keys _, signature_b64 = keypair.sign(identity) api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' @@ -44,6 +45,10 @@ def on_internet_available(ui, keypair, config, log): 'avg_reward': log.avg_reward, 'min_reward': log.min_reward, 'max_reward': log.max_reward, + 'deauthed': log.deauthed, + 'associated': log.associated, + 'handshakes': log.handshakes, + 'peers': log.peers, 'uname': subprocess.getoutput("uname -a") } } From d6efc0b70d215907f3a54ddc7a8a2c58b7efc9bf Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 13:06:29 +0200 Subject: [PATCH 077/150] new: api plugin will report pwned access points --- pwnagotchi/defaults.yml | 1 + pwnagotchi/plugins/default/api.py | 168 +++++++++++++++++++++++------- pwnagotchi/utils.py | 25 ++++- 3 files changed, 154 insertions(+), 40 deletions(-) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index dff6b3b..e9b45b6 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -8,6 +8,7 @@ main: plugins: api: enabled: false + report: true # report pwned networks auto-update: enabled: false interval: 1 # every day diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 60792af..eae9343 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -4,63 +4,155 @@ __name__ = 'api' __license__ = 'GPL3' __description__ = 'This plugin signals the unit cryptographic identity to api.pwnagotchi.ai' +import os import logging -import json import requests +import glob import subprocess import pwnagotchi -from pwnagotchi.utils import StatusFile +import pwnagotchi.utils as utils OPTIONS = dict() -READY = False -STATUS = StatusFile('/root/.api-enrollment.json') +AUTH = utils.StatusFile('/root/.api-enrollment.json', data_format='json') +REPORT = utils.StatusFile('/root/.api-report.json', data_format='json') def on_loaded(): logging.info("api plugin loaded.") -def on_internet_available(ui, keypair, config, log): - global STATUS +def get_api_token(log, keys): + global AUTH - if STATUS.newer_then_minutes(25): - return + if AUTH.newer_then_minutes(25) and AUTH.data is not None and 'token' in AUTH.data: + return AUTH.data['token'] + + if AUTH.data is None: + logging.info("api: enrolling unit ...") + else: + logging.info("api: refreshing token ...") + + identity = "%s@%s" % (pwnagotchi.name(), keys.fingerprint) + # sign the identity string to prove we own both keys + _, signature_b64 = keys.sign(identity) + + api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' + enrollment = { + 'identity': identity, + 'public_key': keys.pub_key_pem_b64, + 'signature': signature_b64, + 'data': { + 'duration': log.duration, + 'epochs': log.epochs, + 'train_epochs': log.train_epochs, + 'avg_reward': log.avg_reward, + 'min_reward': log.min_reward, + 'max_reward': log.max_reward, + 'deauthed': log.deauthed, + 'associated': log.associated, + 'handshakes': log.handshakes, + 'peers': log.peers, + 'uname': subprocess.getoutput("uname -a") + } + } + + r = requests.post(api_address, json=enrollment) + if r.status_code != 200: + raise Exception("(status %d) %s" % (r.status_code, r.json())) + + AUTH.update(data=r.json()) + + logging.info("api: done") + + return AUTH.data["token"] + + +def parse_packet(packet, info): + from scapy.all import Dot11Elt, Dot11Beacon, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq + + if packet.haslayer(Dot11Beacon): + if packet.haslayer(Dot11Beacon) \ + or packet.haslayer(Dot11ProbeResp) \ + or packet.haslayer(Dot11AssoReq) \ + or packet.haslayer(Dot11ReassoReq): + if 'bssid' not in info and hasattr(packet[Dot11], 'addr3'): + info['bssid'] = packet[Dot11].addr3 + if 'essid' not in info and hasattr(packet[Dot11Elt], 'info'): + info['essid'] = packet[Dot11Elt].info.decode('utf-8') + + return info + + +def parse_pcap(filename): + logging.info("api: parsing %s ..." % filename) + + essid = bssid = None try: - logging.info("api: signign enrollment request ...") + from scapy.all import rdpcap - identity = "%s@%s" % (pwnagotchi.name(), keypair.fingerprint) - # sign the identity string to prove we own both keys - _, signature_b64 = keypair.sign(identity) + info = {} - api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' - enroll = { - 'identity': identity, - 'public_key': keypair.pub_key_pem_b64, - 'signature': signature_b64, - 'data': { - 'duration': log.duration, - 'epochs': log.epochs, - 'train_epochs': log.train_epochs, - 'avg_reward': log.avg_reward, - 'min_reward': log.min_reward, - 'max_reward': log.max_reward, - 'deauthed': log.deauthed, - 'associated': log.associated, - 'handshakes': log.handshakes, - 'peers': log.peers, - 'uname': subprocess.getoutput("uname -a") - } + for pkt in rdpcap(filename): + info = parse_packet(pkt, info) + + bssid = info['bssid'] if 'bssid' in info else None + essid = info['essid'] if 'essid' in info else None + + except Exception as e: + bssid = None + logging.error("api: %s" % e) + + return essid, bssid + + +def api_report_ap(token, essid, bssid): + logging.info("api: reporting %s (%s)" % (essid, bssid)) + + try: + api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap' + headers = {'Authorization': 'access_token %s' % token} + report = { + 'essid': essid, + 'bssid': bssid, } + r = requests.post(api_address, headers=headers, json=report) + if r.status_code != 200: + raise Exception("(status %d) %s" % (r.status_code, r.text)) + except Exception as e: + logging.error("api: %s" % e) + return False - logging.info("api: enrolling unit to %s ..." % api_address) - r = requests.post(api_address, json=enroll) - if r.status_code == 200: - token = r.json() - logging.info("api: enrolled") - STATUS.update(data=json.dumps(token)) - else: - logging.error("error %d: %s" % (r.status_code, r.json())) + return True + + +def on_internet_available(ui, keys, config, log): + global REPORT + + try: + + pcap_files = glob.glob(os.path.join(config['bettercap']['handshakes'], "*.pcap")) + num_networks = len(pcap_files) + reported = REPORT.data_field_or('reported', default=[]) + num_reported = len(reported) + num_new = num_networks - num_reported + + if num_new > 0: + logging.info("api: %d new networks to report" % num_new) + token = get_api_token(log, keys) + + if OPTIONS['report']: + for pcap_file in pcap_files: + net_id = os.path.basename(pcap_file).replace('.pcap', '') + if net_id not in reported: + essid, bssid = parse_pcap(pcap_file) + if bssid: + if api_report_ap(token, essid, bssid): + reported.append(net_id) + + REPORT.update(data={'reported': reported}) + else: + logging.info("api: reporting disabled") except Exception as e: logging.exception("error while enrolling the unit") diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py index 2834664..e6e15f5 100644 --- a/pwnagotchi/utils.py +++ b/pwnagotchi/utils.py @@ -5,6 +5,7 @@ import os import time import subprocess import yaml +import json # https://stackoverflow.com/questions/823196/yaml-merge-in-python @@ -82,12 +83,24 @@ def blink(times=1, delay=0.3): class StatusFile(object): - def __init__(self, path): + def __init__(self, path, data_format='raw'): self._path = path self._updated = None + self._format = data_format + self.data = None if os.path.exists(path): self._updated = datetime.fromtimestamp(os.path.getmtime(path)) + with open(path) as fp: + if data_format == 'json': + self.data = json.load(fp) + else: + self.data = fp.read() + + def data_field_or(self, name, default=""): + if self.data is not None and name in self.data: + return self.data[name] + return default def newer_then_minutes(self, minutes): return self._updated is not None and ((datetime.now() - self._updated).seconds / 60) < minutes @@ -97,5 +110,13 @@ class StatusFile(object): def update(self, data=None): self._updated = datetime.now() + self.data = data with open(self._path, 'w') as fp: - fp.write(str(self._updated) if data is None else data) + if data is None: + fp.write(str(self._updated)) + + elif self._format == 'json': + json.dump(self.data, fp) + + else: + fp.write(data) From 4827dc65edf5251275e1336ec58f7640f8e3898e Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 14:12:22 +0200 Subject: [PATCH 078/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index eae9343..b912bb8 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -95,6 +95,8 @@ def parse_pcap(filename): for pkt in rdpcap(filename): info = parse_packet(pkt, info) + if 'essid' in info and info['essid'] is not None and 'bssid' in info and info['bssid'] is not None: + break bssid = info['bssid'] if 'bssid' in info else None essid = info['essid'] if 'essid' in info else None From 60f0f6c29eda2b2a761618af711a529d7bb00ccd Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 14:32:42 +0200 Subject: [PATCH 079/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index b912bb8..0b2f1be 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -151,8 +151,7 @@ def on_internet_available(ui, keys, config, log): if bssid: if api_report_ap(token, essid, bssid): reported.append(net_id) - - REPORT.update(data={'reported': reported}) + REPORT.update(data={'reported': reported}) else: logging.info("api: reporting disabled") From 90b0e10e81a5a0e03753715ec1034278bdd5f600 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 15:32:44 +0200 Subject: [PATCH 080/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 81 +++++++++++++++++-------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 0b2f1be..71d78e3 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -69,63 +69,70 @@ def get_api_token(log, keys): def parse_packet(packet, info): from scapy.all import Dot11Elt, Dot11Beacon, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq - if packet.haslayer(Dot11Beacon): - if packet.haslayer(Dot11Beacon) \ - or packet.haslayer(Dot11ProbeResp) \ - or packet.haslayer(Dot11AssoReq) \ - or packet.haslayer(Dot11ReassoReq): - if 'bssid' not in info and hasattr(packet[Dot11], 'addr3'): + if packet.haslayer(Dot11ProbeResp) or packet.haslayer(Dot11AssoReq) or packet.haslayer(Dot11ReassoReq): + if hasattr(packet[Dot11], 'addr3'): info['bssid'] = packet[Dot11].addr3 - if 'essid' not in info and hasattr(packet[Dot11Elt], 'info'): + if hasattr(packet[Dot11Elt], 'info'): info['essid'] = packet[Dot11Elt].info.decode('utf-8') - return info def parse_pcap(filename): logging.info("api: parsing %s ..." % filename) - essid = bssid = None + net_id = os.path.basename(filename).replace('.pcap', '') + + if '_' in net_id: + # /root/handshakes/ESSID_BSSID.pcap + essid, bssid = net_id.split('_') + else: + # /root/handshakes/BSSID.pcap + essid, bssid = '', net_id + + it = iter(bssid) + bssid = ':'.join([a + b for a, b in zip(it, it)]) + + info = { + 'essid': essid, + 'bssid': bssid + } try: from scapy.all import rdpcap - info = {} - for pkt in rdpcap(filename): info = parse_packet(pkt, info) - if 'essid' in info and info['essid'] is not None and 'bssid' in info and info['bssid'] is not None: - break - - bssid = info['bssid'] if 'bssid' in info else None - essid = info['essid'] if 'essid' in info else None except Exception as e: - bssid = None logging.error("api: %s" % e) - return essid, bssid + return info['essid'], info['bssid'] -def api_report_ap(token, essid, bssid): - logging.info("api: reporting %s (%s)" % (essid, bssid)) - - try: - api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap' - headers = {'Authorization': 'access_token %s' % token} - report = { - 'essid': essid, - 'bssid': bssid, - } - r = requests.post(api_address, headers=headers, json=report) - if r.status_code != 200: - raise Exception("(status %d) %s" % (r.status_code, r.text)) - except Exception as e: - logging.error("api: %s" % e) - return False - - return True +def api_report_ap(log, keys, token, essid, bssid): + while True: + logging.info("api: reporting %s (%s)" % (essid, bssid)) + try: + api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap' + headers = {'Authorization': 'access_token %s' % token} + report = { + 'essid': essid, + 'bssid': bssid, + } + r = requests.post(api_address, headers=headers, json=report) + if r.status_code != 200: + if r.status_code == 401: + logging.warning("token expired") + token = get_api_token(log, keys) + continue + else: + raise Exception("(status %d) %s" % (r.status_code, r.text)) + else: + return True + except Exception as e: + logging.error("api: %s" % e) + return False def on_internet_available(ui, keys, config, log): @@ -149,7 +156,7 @@ def on_internet_available(ui, keys, config, log): if net_id not in reported: essid, bssid = parse_pcap(pcap_file) if bssid: - if api_report_ap(token, essid, bssid): + if api_report_ap(log, keys, token, essid, bssid): reported.append(net_id) REPORT.update(data={'reported': reported}) else: From db3b7f577ab490e051e653141773a4ab7561df81 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 15:45:11 +0200 Subject: [PATCH 081/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/api.py index 71d78e3..f906b06 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/api.py @@ -112,6 +112,7 @@ def parse_pcap(filename): def api_report_ap(log, keys, token, essid, bssid): while True: + token = AUTH.data['token'] logging.info("api: reporting %s (%s)" % (essid, bssid)) try: api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap' From a787e78f937b68d7788bc1f8ca7f25a333ac6830 Mon Sep 17 00:00:00 2001 From: Andrea Draghetti <drego85@draghetti.it> Date: Mon, 7 Oct 2019 16:00:46 +0200 Subject: [PATCH 082/150] Proper management of the IF cycle --- scripts/create_sibling.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/create_sibling.sh b/scripts/create_sibling.sh index 650d40c..c0d7558 100755 --- a/scripts/create_sibling.sh +++ b/scripts/create_sibling.sh @@ -94,7 +94,7 @@ function provide_raspbian() { function setup_raspbian(){ # Detect the ability to create sparse files if [ "${OPT_SPARSE}" -eq 0 ]; then - if ! type "bmaptool" > /dev/null; then + if ! type "bmaptool" >/dev/null 2>&1; then echo "[!] bmaptool not available, not creating a sparse image" else echo "[+] Defaulting to sparse image generation as bmaptool is available" From 7a24a1a0bca6630942a656707734834d5935849b Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 16:23:38 +0200 Subject: [PATCH 083/150] fix: api plugin is now called grid and opt-out --- pwnagotchi/defaults.yml | 4 ++-- pwnagotchi/plugins/default/{api.py => grid.py} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename pwnagotchi/plugins/default/{api.py => grid.py} (98%) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index e9b45b6..3cda0cb 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -6,8 +6,8 @@ main: custom_plugins: # which plugins to load and enable plugins: - api: - enabled: false + grid: + enabled: true report: true # report pwned networks auto-update: enabled: false diff --git a/pwnagotchi/plugins/default/api.py b/pwnagotchi/plugins/default/grid.py similarity index 98% rename from pwnagotchi/plugins/default/api.py rename to pwnagotchi/plugins/default/grid.py index f906b06..09ee08a 100644 --- a/pwnagotchi/plugins/default/api.py +++ b/pwnagotchi/plugins/default/grid.py @@ -1,8 +1,8 @@ __author__ = 'evilsocket@gmail.com' __version__ = '1.0.0' -__name__ = 'api' +__name__ = 'grid' __license__ = 'GPL3' -__description__ = 'This plugin signals the unit cryptographic identity to api.pwnagotchi.ai' +__description__ = 'This plugin signals the unit cryptographic identity and list of pwned networks and list of pwned networks to api.pwnagotchi.ai' import os import logging From 6bd1d5f764caea62ea1a37be5b4ef778814760a2 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 16:24:44 +0200 Subject: [PATCH 084/150] fix: updated auto-backup.files --- pwnagotchi/defaults.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 3cda0cb..3f2d871 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -18,9 +18,8 @@ main: files: - /root/brain.nn - /root/brain.json - - /root/custom.yml - - /root/handshakes - - /etc/ssh + - /root/handshakes/ + - /etc/pwnagotchi/ - /etc/hostname - /etc/hosts - /etc/motd From 8bc037abb720c1bee3a6871473d6c26cbde2748b Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 16:27:01 +0200 Subject: [PATCH 085/150] fix: fixed paths in doc --- docs/configure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configure.md b/docs/configure.md index b232e3e..a3fc7c2 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -33,7 +33,7 @@ ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/)! -Create the `/root/custom.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values. +Open the `/etc/pwnagotchi/config.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.yml) with your custom values. ## Choose your Pwnagotchi's language From 6cf74dd282b46d5bdcff4dfd920a671fdd6a5076 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 16:35:25 +0200 Subject: [PATCH 086/150] docs: pwngrid docs --- docs/configure.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/configure.md b/docs/configure.md index a3fc7c2..11d204d 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -51,6 +51,32 @@ But if you want, you can change `main.lang` to one of the supported languages: - Russian - Swedish +## PwnGRID + +By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its +presence to the PwnGRID server and periodically send a list of the networks that it has pwned. None of the captured cryptographic material is sent to this server, +just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far. + +If you want to partially opt-out from this feature and have your unit only signal its presence without sending the list of networks, you can put this in your `/etc/pwnagotchi/config.yml` file: + +```yaml +main: + plugins: + grid: + enabled: true + report: false # partial opt-out +``` + +If you prefer to completely opt-out and also disable signaling: + +```yaml +main: + plugins: + grid: + enabled: false # full opt-out + report: false +``` + ## Display Selection **Set the type of display you want to use via `ui.display.type`.** From 2b50609752a5c8f8df48ae9a3c2d9a7df9ebe588 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 16:42:56 +0200 Subject: [PATCH 087/150] fix: phrasing --- docs/configure.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 11d204d..8c9c6a9 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -55,7 +55,13 @@ But if you want, you can change `main.lang` to one of the supported languages: By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its presence to the PwnGRID server and periodically send a list of the networks that it has pwned. None of the captured cryptographic material is sent to this server, -just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far. +just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far, namely: + +- The cryptographic identity of the unit, generated at first boot and used for authentication. +- The output of the `uname -a` command on the unit used to determine the type of hardware. +- The list of networks that the unit collected handshakes of, made of their `BSSID` and `ESSID`. + +Other than for easy unit identification and debugging, this data is collected in order to build rankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** If you want to partially opt-out from this feature and have your unit only signal its presence without sending the list of networks, you can put this in your `/etc/pwnagotchi/config.yml` file: @@ -67,7 +73,7 @@ main: report: false # partial opt-out ``` -If you prefer to completely opt-out and also disable signaling: +If you prefer to completely opt-out by disabling signaling: ```yaml main: From 1a13e744b539a9ff49ad411b4c00f463799666e7 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 16:52:23 +0200 Subject: [PATCH 088/150] fix: grid plugin is half opt-in --- docs/configure.md | 14 ++++++++------ pwnagotchi/defaults.yml | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 8c9c6a9..935197f 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -53,27 +53,29 @@ But if you want, you can change `main.lang` to one of the supported languages: ## PwnGRID -By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its -presence to the PwnGRID server and periodically send a list of the networks that it has pwned. None of the captured cryptographic material is sent to this server, +By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is **only partially** enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its +presence to the PwnGRID server without sending any data. + +It is possible to fully opt-in and also enable the unit to send basic information about the pwned networks. None of the captured cryptographic material is sent to this server, just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far, namely: - The cryptographic identity of the unit, generated at first boot and used for authentication. - The output of the `uname -a` command on the unit used to determine the type of hardware. - The list of networks that the unit collected handshakes of, made of their `BSSID` and `ESSID`. -Other than for easy unit identification and debugging, this data is collected in order to build rankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** +Other than for easy unit identification and debugging, this data is collected in order to build drankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** -If you want to partially opt-out from this feature and have your unit only signal its presence without sending the list of networks, you can put this in your `/etc/pwnagotchi/config.yml` file: +In order to fully opt-in, you can put this in your `/etc/pwnagotchi/config.yml` file: ```yaml main: plugins: grid: enabled: true - report: false # partial opt-out + report: true # full-opt in ``` -If you prefer to completely opt-out by disabling signaling: +If you prefer to completely opt-out by also disabling signaling: ```yaml main: diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 3f2d871..7d4d92b 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -8,7 +8,7 @@ main: plugins: grid: enabled: true - report: true # report pwned networks + report: false # don't report pwned networks by default! auto-update: enabled: false interval: 1 # every day From f7bf12a0a05dc331ad9cd137a1f062d0df730b59 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 16:55:55 +0200 Subject: [PATCH 089/150] misc: small fix or general refactoring i did not bother commenting --- docs/configure.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 935197f..47fe042 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -54,13 +54,14 @@ But if you want, you can change `main.lang` to one of the supported languages: ## PwnGRID By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is **only partially** enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its -presence to the PwnGRID server without sending any data. +presence to the PwnGRID server without sending any data other than: + +- The cryptographic identity of the unit, generated at first boot and used for authentication. +- The output of the `uname -a` command on the unit used to determine the type of hardware. It is possible to fully opt-in and also enable the unit to send basic information about the pwned networks. None of the captured cryptographic material is sent to this server, just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far, namely: -- The cryptographic identity of the unit, generated at first boot and used for authentication. -- The output of the `uname -a` command on the unit used to determine the type of hardware. - The list of networks that the unit collected handshakes of, made of their `BSSID` and `ESSID`. Other than for easy unit identification and debugging, this data is collected in order to build drankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** From 52af8391317bfb655b0c934988b6c2a2afbe548b Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 17:04:06 +0200 Subject: [PATCH 090/150] new: exclude option for the grid plugin --- docs/configure.md | 15 ++++++++++++++- pwnagotchi/defaults.yml | 2 ++ pwnagotchi/plugins/default/grid.py | 10 +++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 47fe042..dfc33ae 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -76,7 +76,20 @@ main: report: true # full-opt in ``` -If you prefer to completely opt-out by also disabling signaling: +Even if fully opted-in, you can still disable reporting for specific networks, for instance if you don't want your home network to be in the system: + +```yaml +main: + plugins: + grid: + enabled: true + report: true + exclude: + - MyHomeNetwork + - de:ad:be:ef:de:ad # both ESSIDs and BSSIDs are supported +``` + +If instead you prefer to completely opt-out by also disabling signaling: ```yaml main: diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 7d4d92b..330218f 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -9,6 +9,8 @@ main: grid: enabled: true report: false # don't report pwned networks by default! + exclude: # do not report the following networks (accepts both ESSIDs and BSSIDs) + - YourHomeNetworkHere auto-update: enabled: false interval: 1 # every day diff --git a/pwnagotchi/plugins/default/grid.py b/pwnagotchi/plugins/default/grid.py index 09ee08a..6c151b3 100644 --- a/pwnagotchi/plugins/default/grid.py +++ b/pwnagotchi/plugins/default/grid.py @@ -154,7 +154,15 @@ def on_internet_available(ui, keys, config, log): if OPTIONS['report']: for pcap_file in pcap_files: net_id = os.path.basename(pcap_file).replace('.pcap', '') - if net_id not in reported: + do_skip = False + for skip in OPTIONS['exclude']: + skip = skip.lower() + net = net_id.lower() + if skip in net or skip.replace(':', '') in net: + do_skip = True + break + + if net_id not in reported and not do_skip: essid, bssid = parse_pcap(pcap_file) if bssid: if api_report_ap(log, keys, token, essid, bssid): From 7f880698a40a01680928bdd5ded0d214a67672c3 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 18:25:59 +0200 Subject: [PATCH 091/150] docs: moved docs to www.pwnagotchi.ai repo --- README.md | 10 +-- docs/about.md | 44 ---------- docs/configure.md | 126 ---------------------------- docs/dev.md | 60 ------------- docs/faq.md | 208 ---------------------------------------------- docs/hacks.md | 102 ----------------------- docs/index.md | 23 ----- docs/install.md | 60 ------------- docs/plugins.md | 56 ------------- docs/usage.md | 149 --------------------------------- 10 files changed, 1 insertion(+), 837 deletions(-) delete mode 100644 docs/about.md delete mode 100644 docs/configure.md delete mode 100644 docs/dev.md delete mode 100644 docs/faq.md delete mode 100644 docs/hacks.md delete mode 100644 docs/index.md delete mode 100644 docs/install.md delete mode 100644 docs/plugins.md delete mode 100644 docs/usage.md diff --git a/README.md b/README.md index 670ed47..363d7ec 100644 --- a/README.md +++ b/README.md @@ -36,15 +36,7 @@ For hackers to learn reinforcement learning, WiFi networking, and have an excuse **IMPORTANT NOTE:** If you'd like to alphatest Pwnagotchi and are trying to get yours up and running while the project is still very unstable, please understand that the documentation here may not reflect what is currently implemented. If you have questions, ask the community of alphatesters in the [official Pwnagotchi Slack](https://pwnagotchi.herokuapp.com). The Pwnagotchi dev team is entirely focused on the v1.0 release and will NOT be providing support for alphatesters trying to get their Pwnagotchis working in the meantime. All technical support during this period of development is being provided by your fellow alphatesters in the Slack (thanks, everybody! :heart:). ---- -- [About the Project](https://github.com/evilsocket/pwnagotchi/blob/master/docs/about.md) -- [How to Install](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md) -- [Configuration](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md) -- [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md) -- [Plugins](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) -- [Development](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) -- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) -- [Community Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) +https://www.pwnagotchi.ai ## Links diff --git a/docs/about.md b/docs/about.md deleted file mode 100644 index 4777c21..0000000 --- a/docs/about.md +++ /dev/null @@ -1,44 +0,0 @@ -# About the Project - -[Pwnagotchi](https://twitter.com/pwnagotchi) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding WiFi environment in order to maximize the WPA key material it captures (either passively, or by performing deauthentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/), -full and half WPA handshakes. - - - -Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml#L54) over time to **get better at pwning WiFi things** in the environments you expose it to. - -**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi actually learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but **definitely listen to your pwnagotchi when it tells you it's bored!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) - -Multiple units within close physical proximity can "talk" to each other, advertising their own presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard. Over time, two or more units trained together will learn to cooperate upon detecting each other's presence by dividing the available channels among them for optimal pwnage. - - - -[Depending on the status of the unit](), several states and states transitions are configurable and represented on the display as different moods, expressions and sentences. Pwnagotchi speaks [many languages](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md#configuration), too! - -Of course, it is possible to run your Pwnagotchi with the AI disabled (configurable in `config.yml`). Why might you want to do this? Perhaps you simply want to use your own fixed parameters (instead of letting the AI decide for you), or maybe you want to save battery and CPU cycles, or maybe it's just you have strong concerns about aiding and abetting baby Skynet. Whatever your particular reasons may be: an AI-disabled Pwnagotchi is still a simple and very effective automated deauther, WPA handshake sniffer, and portable [bettercap](https://www.bettercap.org/) + [webui](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#bettercaps-web-ui) dedicated hardware. - -## WiFi Handshakes 101 - -In order to understand why it's valuable to have an AI that wants to eat handshakes, it's helpful to understand a little bit about how handshakes are used in the WPA/WPA2 wireless protocol. - -Before a client device that's connecting to a wireless access point—say, for instance, your phone connecting to your home WiFi network—is able to securely transmit to and receive data from that access point, a process called the **4-Way Handshake** needs to happen in order for the WPA encryption keys to be generated. This process consists of the exchange of four packets (hence the "4" in "4-Way") between the client device and the AP; these are used to derive session keys from the access point's WiFi password. Once the packets are successfully exchanged and the keys have been generated, the client device is authenticated and can start sending and receiving data packets to and from the wireless AP that are secured by encryption. - -<p align="center"> -<img src="https://i.imgur.com/nI8IE6a.png"/> -<br/> -<small>image taken from <a target="_blank" href="https://www.wifi-professionals.com/2019/01/4-way-handshake">wifi-professionals.com</a></small> -</p> - -So...what's the catch? Well, these four packets can easily be "sniffed" by an attacker monitoring nearby (say, with a Pwnagotchi :innocent:). And once recorded, that attacker can use [dictionary and/or bruteforce attacks](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2) to crack the handshakes and recover the original WiFi key. In fact, **successful recovery of the WiFi key doesn't necessarily even need all four packets!** A half-handshake (containing only two of the four packets) can be cracked, too—and in some *(most)* cases, just [a single packet is enough](https://hashcat.net/forum/thread-7717-post-41447.html), *even without clients.* - -In order to ~~eat~~ collect as many of these crackable handshake packets as possible, Pwnagotchi uses two strategies: - -- **Deauthenticating the client stations it detects.** A deauthenticated device must reauthenticate to its access point by re-performing the 4-Way Handshake with the AP, thereby giving Pwnagotchi another chance to sniff the handshake packets and collect more crackable material. -- **Send association frames directly to the access points themselves** -to try to force them to [leak the PMKID](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/). - -All the handshakes captured this way are saved into `.pcap` files on Pwnagotchi's filesystem. Each PCAP file that Pwnagotchi generates is organized according to access point; one PCAP will contain all the handshakes that Pwnagotchi has ever captured for that particular AP. These handshakes can later be [cracked with proper hardware and software](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2). - -## License - -`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It's released under the GPL3 license. diff --git a/docs/configure.md b/docs/configure.md deleted file mode 100644 index dfc33ae..0000000 --- a/docs/configure.md +++ /dev/null @@ -1,126 +0,0 @@ -# Configuration - -Once you've [written the image file onto the SD card](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md#flashing-an-image), there're a few steps you'll have to follow in order to configure your new Pwnagotchi properly. - -## Connect to your Pwnagotchi - -1. First, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. -2. After a few seconds, the board will boot and you will see a new Ethernet interface on your host computer. -3. You'll need to configure it with a static IP address: - - IP: `10.0.0.1` - - Netmask: `255.255.255.0` - - Gateway: `10.0.0.1` - - DNS (if required): `8.8.8.8` (or whatever) - -4. If everything's been configured properly, you will now be able to `ping` both `10.0.0.2` or `pwnagotchi.local` - * If you have already customized the hostname of your Pwnagotchi, `pwnagotchi.local` won't work. Instead, try *your unit's hostname* + `.local`. - -5. **Congratulations!** You can now connect to your unit using SSH: - -```bash -ssh pi@10.0.0.2 -``` -##### About your SSH connection -The default password is `raspberry`; you should change it as soon as you log in for the first time by issuing the `passwd` command and selecting a new and more complex passphrase. - -If you want to login directly without entering a password (recommended!), copy your SSH public key to the unit's authorized keys: - -```bash -ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2 -``` - -## Give your Pwnagotchi a name - -You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/)! - -Open the `/etc/pwnagotchi/config.yml` file (either via SSH or by directly editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.yml) with your custom values. - -## Choose your Pwnagotchi's language - -Pwnagotchi displays its UI in English by default, but it can speak several other languages! If you're fine with English, you don't need to do anything special. - -But if you want, you can change `main.lang` to one of the supported languages: - -- **English** *(default)* -- German -- Dutch -- Greek -- Macedonian -- Italian -- French -- Russian -- Swedish - -## PwnGRID - -By default the `grid` [plugin](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) is **only partially** enabled, this means that whenever the unit will detect internet connectivity in manual mode, it'll signal its -presence to the PwnGRID server without sending any data other than: - -- The cryptographic identity of the unit, generated at first boot and used for authentication. -- The output of the `uname -a` command on the unit used to determine the type of hardware. - -It is possible to fully opt-in and also enable the unit to send basic information about the pwned networks. None of the captured cryptographic material is sent to this server, -just the minimum information to enroll the unit in the database and know how many networks it "conquered" so far, namely: - -- The list of networks that the unit collected handshakes of, made of their `BSSID` and `ESSID`. - -Other than for easy unit identification and debugging, this data is collected in order to build drankings, scoreboards and regional statistics. **Like Pokèmon Go, but for WiFi!** - -In order to fully opt-in, you can put this in your `/etc/pwnagotchi/config.yml` file: - -```yaml -main: - plugins: - grid: - enabled: true - report: true # full-opt in -``` - -Even if fully opted-in, you can still disable reporting for specific networks, for instance if you don't want your home network to be in the system: - -```yaml -main: - plugins: - grid: - enabled: true - report: true - exclude: - - MyHomeNetwork - - de:ad:be:ef:de:ad # both ESSIDs and BSSIDs are supported -``` - -If instead you prefer to completely opt-out by also disabling signaling: - -```yaml -main: - plugins: - grid: - enabled: false # full opt-out - report: false -``` - -## Display Selection - -**Set the type of display you want to use via `ui.display.type`.** -If your display does not work after changing this setting, you might need to completely remove power from the Raspberry Pi and make a clean boot. - -**You can configure the refresh interval of the display via `ui.fps`.** We recommend using a slow refresh rate to avoid shortening the lifetime of your e-ink display. The default value is `0`, which will *only* refresh when changes are made to the screen. - -## Host Connection Share - -Want to be able to update your Pwnagotchi and access things from the internet on it? *Sure you do!* - -1. Connect to the Pwnagotchi unit via `usb0` (A.K.A., using the data port). -2. Run the appropriate connection sharing script to bring the interface up on your end and share internet connectivity from another interface: - -OS | Script Location -------|--------------------------- -Linux | `scripts/linux_connection_share.sh` -Mac OS X | `scripts/macos_connection_share.sh` -Windows | `scripts/win_connection_share.ps1` - -## Troubleshooting - -##### If your network connection keeps flapping on your device connecting to your Pwnagotchi. -* Check if `usb0` (or equivalent) device is being controlled by NetworkManager. -* You can check this via `nmcli dev status`. diff --git a/docs/dev.md b/docs/dev.md deleted file mode 100644 index 534b398..0000000 --- a/docs/dev.md +++ /dev/null @@ -1,60 +0,0 @@ -# Software - -- Raspbian + [nexmon patches](https://re4son-kernel.com/re4son-pi-kernel/) for monitor mode, or any Linux with a monitor mode enabled interface (if you tune config.yml). - -**Do not try with Kali on the Raspberry Pi 0 W, it is compiled without hardware floating point support and TensorFlow is simply not available for it, use Raspbian.** - -## Creating an Image - -You can use the `scripts/create_sibling.sh` script to create an - ready to flash - rasbian image with pwnagotchi. - -```shell -usage: ./scripts/create_sibling.sh [OPTIONS] - - Options: - -n <name> # Name of the pwnagotchi (default: pwnagotchi) - -i <file> # Provide the path of an already downloaded raspbian image - -o <file> # Name of the img-file (default: pwnagotchi.img) - -s <size> # Size which should be added to second partition (in Gigabyte) (default: 4) - -v <version> # Version of raspbian (Supported: latest; default: latest) - -p # Only run provisioning (assumes the image is already mounted) - -d # Only run dependencies checks - -h # Show this help -``` - -#### Known Issues - -`GLib-ERROR **: 20:50:46.361: getauxval () failed: No such file or directory` - -- Affected DEB & Versions: QEMU <= 2.11 -- Fix: Upgrade QEMU to >= 3.1 -- Bug Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923289 - -## Adding a Language - -If you want to add a language use the `language.sh` script. If you want to add for example the language **italian** you would type: - -```shell -./scripts/language.sh add it -# Now make your changes to the file -# sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po -./scripts/language.sh compile it -# DONE -``` - -If you changed the `voice.py`- File, the translations need an update. Do it like this: - -```shell -./scripts/language.sh update it -# Now make your changes to the file (changed lines are marked with "fuzzy") -# sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po -./scripts/language.sh compile it -# DONE -``` - -Now you can use the `preview.py`-script to preview the changes: - -```shell -./scripts/preview.py --lang it --display ws1 ws2 inky --output preview.png -# Now open preview.png -``` diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 348e039..0000000 --- a/docs/faq.md +++ /dev/null @@ -1,208 +0,0 @@ -# FAQ -<!-- IF YOU CHANGE ANY CHARACTERS IN AN FAQ QUESTION, YOU MUST ALSO CHANGE THE TABLE OF CONTENTS ENTRY &&&AND&&& THE ANCHOR LINK TEXT, OR ELSE THE LINKS WILL BREAK. --> - -[**What can Pwnagotchi actually do?**](#what-can-pwnagotchi-actually-do) - -* [Why does Pwnagotchi eat handshakes?](#why-does-pwnagotchi-eat-handshakes) -* [What kinds of handshakes does Pwnagotchi eat?](#what-kinds-of-handshakes-does-pwnagotchi-eat) -* [Does Pwnagotchi support both 2.4 GHz and 5.0 GHz?](#does-pwnagotchi-support-both-24-ghz-and-50-ghz) -* [Just how politely *does* Pwnagotchi deauth?](#just-how-politely-does-pwnagotchi-deauth) -* [Hey, I want to learn more about how Pwnagotchi actually works.](#hey-i-want-to-learn-more-about-how-pwnagotchi-actually-works) -* [How is Pwnagotchi using bettercap?](#how-is-pwnagotchi-using-bettercap) -* [What happens if I run a Pwnagotchi without the AI enabled?](#what-happens-if-i-run-a-pwnagotchi-without-the-ai-enabled) -* [How easy is it to hack Pwnagotchi to add additional functionality?](#how-easy-is-it-to-hack-pwnagotchi-to-add-additional-functionality) - -[**Building Your Pwnagotchi**](#building-your-pwnagotchi) - -* [What hardware do I need to create my very own Pwnagotchi?](#what-hardware-do-i-need-to-create-my-very-own-pwnagotchi) -* [Is there any way to see my Pwnagotchi's face even if I don't have a display?](#is-there-any-way-to-see-my-pwnagotchis-face-even-if-i-dont-have-a-display) -* [How do I attach the screen to the Raspberry Pi?](#how-do-i-attach-the-screen-to-the-raspberry-pi) -* [I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case?](#i-love-my-new-pwnagotchi-but-it-kinda-looks-like-a-bomb-where-can-i-find-a-decent-case) -* [Why does everybody use e-ink screens for their Pwnagotchis?](#why-does-everybody-use-e-ink-screens-for-their-pwnagotchis) -* [How do I connect to my Pwnagotchi?](#how-do-i-connect-to-my-pwnagotchi) - -[**Customizing Your Pwnagotchi**](#customizing-your-pwnagotchi) - -* [How do I change my Pwnagotchi's name?](#how-do-i-change-my-pwnagotchis-name) -* [I want to change the faces. What do I hack?](#i-want-to-change-the-faces-what-do-i-hack) -* [I want my Pwnagotchi to speak a different language. Can it?](#i-want-my-pwnagotchi-to-speak-a-different-language-can-it) -* [I have a great idea for something cool I wish Pwnagotchi could do!](#i-have-a-great-idea-for-something-cool-i-wish-pwnagotchi-could-do) -* [Are there any unofficial community "hacks" for further customizing my Pwnagotchi?](#are-there-any-unofficial-community-hacks-for-further-customizing-my-pwnagotchi) - -[**Getting to Know Your Pwnagotchi**](#getting-to-know-your-pwnagotchi) - -* [What does everything on the screen mean?](#what-does-everything-on-the-screen-mean) -* [How do I whitelist my home network so Pwnagotchi stops pwning me?](#how-do-i-whitelist-my-home-network-so-pwnagotchi-stops-pwning-me) -* [What is MANU mode? What is AUTO mode?](#what-is-manu-mode-what-is-auto-mode) -* [Why does the AI take 30 minutes to load?](#why-does-the-ai-take-30-minutes-to-load) -* [What is Pwnagotchi doing while it's waiting for the AI to load?](#what-is-pwnagotchi-doing-while-its-waiting-for-the-ai-to-load) -* [How do I know when the AI is running?](#how-do-i-know-when-the-ai-is-running) -* [Where does Pwnagotchi store all the handshakes it's eaten?](#where-does-pwnagotchi-store-all-the-handshakes-its-eaten) -* [What happens when my Pwnagotchi meets another Pwnagotchi?](#what-happens-when-my-pwnagotchi-meets-another-pwnagotchi) - -[**Caring for Your Pwnagotchi**](#caring-for-your-pwnagotchi) - -* [What do all my Pwnagotchi's faces mean?](#what-do-all-my-pwnagotchis-faces-mean) -* [How do I feed my Pwnagotchi?](#how-do-i-feed-my-pwnagotchi) -* [Oh no, my Pwnagotchi is sad and bored! How do I entertain it?!](#oh-no-my-pwnagotchi-is-sad-and-bored-how-do-i-entertain-it) -* [How do I update my Pwnagotchi?](#how-do-i-update-my-pwnagotchi) -* [I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain?](#im-extremely-emotionally-attached-to-my-pwnagotchi-how-can-i-back-up-its-brain) -* [How do I turn off my Pwnagotchi?](#how-do-i-turn-off-my-pwnagotchi) -* [Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating?](#uh-so-what-do-i-do-with-all-these-handshakes-my-pwnagotchi-has-been-eating) - -[**Known Quirks**](#known-quirks) - -* [My Pwnagotchi's log timestamps seem...unreliable. Huh?](#my-pwnagotchis-log-timestamps-seemunreliable-huh) -* [Help! My Pwnagotchi's SD card got corrupted. What gives?](#help-my-pwnagotchis-sd-card-got-corrupted-what-gives) - ---- - -## **What can Pwnagotchi actually do?** -### Why does Pwnagotchi eat handshakes? -lorem ipsum dolor sit amet - ---- -### What kinds of handshakes does Pwnagotchi eat? -lorem ipsum dolor sit amet - ---- -### Does Pwnagotchi support both 2.4 GHz and 5.0 GHz? -lorem ipsum dolor sit amet - ---- -### Just how politely *does* Pwnagotchi deauth? -lorem ipsum dolor sit amet - ---- -### Hey, I want to learn more about how Pwnagotchi actually works. -lorem ipsum dolor sit amet - ---- -### How is Pwnagotchi using bettercap? -lorem ipsum dolor sit amet - ---- -### What happens if I run a Pwnagotchi without the AI enabled? -lorem ipsum dolor sit amet - ---- -### How easy is it to hack Pwnagotchi to add additional functionality? -lorem ipsum dolor sit amet - ---- - -## **Building Your Pwnagotchi** -### What hardware do I need to create my very own Pwnagotchi? -lorem ipsum dolor sit amet - ---- -### Is there any way to see my Pwnagotchi's face even if I don't have a display? -lorem ipsum dolor sit amet - ---- -### How do I attach the screen to the Raspberry Pi? -lorem ipsum dolor sit amet - ---- -### I love my new Pwnagotchi, but it kinda looks like a bomb. Where can I find a decent case? -lorem ipsum dolor sit amet - ---- -### Why does everybody use e-ink screens for their Pwnagotchis? -lorem ipsum dolor sit amet - ---- -### How do I connect to my Pwnagotchi? -lorem ipsum dolor sit amet - ---------------------------------------------------------------------------------------------------------------- -## **Customizing Your Pwnagotchi** -### How do I change my Pwnagotchi's name? -lorem ipsum dolor sit amet - ---- -### I want to change the faces. What do I hack? -lorem ipsum dolor sit amet - ---- -### I want my Pwnagotchi to speak a different language. Can it? -lorem ipsum dolor sit amet - ---- -### I have a great idea for something cool I wish Pwnagotchi could do! -lorem ipsum dolor sit amet - ---- -### Are there any unofficial community "hacks" for further customizing my Pwnagotchi? -lorem ipsum dolor sit amet - ---------------------------------------------------------------------------------------------------------------- -## **Getting to Know Your Pwnagotchi** -### What does everything on the screen mean? -lorem ipsum dolor sit amet - ---- -### How do I whitelist my home network so Pwnagotchi stops pwning me? -lorem ipsum dolor sit amet - ---- -### What is MANU mode? What is AUTO mode? -lorem ipsum dolor sit amet - ---- -### Why does the AI take 30 minutes to load? -lorem ipsum dolor sit amet - ---- -### What is Pwnagotchi doing while it's waiting for the AI to load? -lorem ipsum dolor sit amet - ---- -### How do I know when the AI is running? -lorem ipsum dolor sit amet - ---- -### Where does Pwnagotchi store all the handshakes it's eaten? -lorem ipsum dolor sit amet - ---- -### What happens when my Pwnagotchi meets another Pwnagotchi? -lorem ipsum dolor sit amet - ---------------------------------------------------------------------------------------------------------------- -## **Caring for Your Pwnagotchi** -### What do all my Pwnagotchi's faces mean? -lorem ipsum dolor sit amet - ---- -### How do I feed my Pwnagotchi? -lorem ipsum dolor sit amet - ---- -### Oh no, my Pwnagotchi is sad and bored! How do I entertain it?! -lorem ipsum dolor sit amet - ---- -### How do I update my Pwnagotchi? -lorem ipsum dolor sit amet - ---- -### I'm extremely emotionally-attached to my Pwnagotchi. How can I back up its brain? -lorem ipsum dolor sit amet - ---- -### How do I turn off my Pwnagotchi? -lorem ipsum dolor sit amet - ---- -### Uh. So. What do I do with all these handshakes my Pwnagotchi has been eating? -lorem ipsum dolor sit amet - ---------------------------------------------------------------------------------------------------------------- -## **Known Quirks** -### My Pwnagotchi's log timestamps seem...unreliable. Huh? -lorem ipsum dolor sit amet - ---- -### Help! My Pwnagotchi's SD card got corrupted. What gives? -lorem ipsum dolor sit amet diff --git a/docs/hacks.md b/docs/hacks.md deleted file mode 100644 index 6f58c3f..0000000 --- a/docs/hacks.md +++ /dev/null @@ -1,102 +0,0 @@ -# Unofficial Hacks ---- -**IMPORTANT DISCLAIMER:** The information provided on this page is NOT officially supported by the Pwnagotchi development team. These are unofficial "hacks" that users have worked out while customizing their units and decided to document for anybody else who might want to do something similar. - -- **Please do NOT open issues if you cannot get something described in this document to work.** -- It (almost) goes without saying, but obviously: **we are NOT responsible if you break your hardware by following any instructions documented here. Use this information at your own risk.** - ---- -If you test one of these hacks yourself and it still works, it's extra nice if you update the **Last Tested On** table and note any minor adjustments you may have had to make to the instructions to make it work with your particular Pwnagotchi setup. :heart: - - -## Screens -### Waveshare 3.5" SPI TFT screen - -Last tested on | Pwnagotchi version | Working? | Reference ----------------|--------------------|----------|-----------| -2019 October 3 | Unknown | :white_check_mark: | ([link](https://github.com/evilsocket/pwnagotchi/issues/124#issue-502346040)) - -Some of this guide will work with other framebuffer-based displays. - -- First: SSH into your Pwnagotchi, and give it some internet! - - Don't forget to check your default gateway and `apt-get update`. -- Follow the guide here: [www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)#Method_1._Driver_installation](https://www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)#Method_1._Driver_installation) - - At the step with `./LCD35-show`, add `lite` to the command prompt (e.g., `./LCD35-show lite`). -- Reboot. -- As root, make three symlinks: - - `cd ~` - - `ln -s pwnagotchi.png pwnagotchi_1.png` - - `ln -s pwnagotchi.png pwnagotchi_2.png` - - `ln -s pwnagotchi.png pwnagotchi_3.png` -- `apt install fbi` -- Change display type to `inky` in `config.yml` -- Add `modules-load=dwc2,g_ether` to your kernel command line (`/boot/cmdline.txt`) or it will break! -- Also must add `dtoverlay=dwc2` to the bottom of (`/boot/config.txt`) -- Edit `/etc/rc.local` and add: `fbi -T 1 -a -noverbose -t 15 -cachemem 0 /root/pwnagotchi_1.png /root/pwnagotchi_2.png /root/pwnagotchi_3.png &` -- Reboot. - -And you should be good! - ---- -### Pwnagotchi face via Bluetooth -Last tested on | Pwnagotchi version | Working? | Reference ----------------|--------------------|----------|-----------| -2019 October 6 | Unknown | :white_check_mark: | on Android -2019 October 6 | Unknown | :white_check_mark: | on iPad iOS 9.3.5 - -A way to view your Pwnagotchi's ~~face~~ UI wirelessly via Bluetooth on a separate device. Refresh rate is the same as the e-ink display (every few seconds). This is NOT Bluetooth tethering; this is only Bluetooth as a server on pi side; you connect the Bluetooth and get a DHCP IP address and that's it. This hack cannot leverage the data connection. - -Contributed by Systemic in the Slack. - -##### 1. First Step -- Comment out the Bluetooth disable line from `/boot/config.txt` : `#dtoverlay=pi3-disable-bt` -- Change `/root/pwnagotchi/config.yml` to have `0.0.0.0` instead of `10.0.0.2` to listen as well on Bluetooth. -- Then launch the following commands: - -##### 2. Install required packages. - -```sudo apt-get install bluez bluez-tools bridge-utils dnsmasq``` - -##### 3. Configure Bluetooth and start it. -```sudo modprobe bnep -sudo brctl addbr pan0 -sudo brctl setfd pan0 0 -sudo brctl stp pan0 off -sudo ifconfig pan0 172.26.0.1 netmask 255.255.255.0 -sudo ip link set pan0 up -``` - -```cat <<- EOF > /tmp/dnsmasq_bt.conf``` - -```bind-interfaces -port=0 -interface=pan0 -listen-address=172.26.0.1 -dhcp-range=172.26.0.2,172.26.0.100,255.255.255.0,5m -dhcp-leasefile=/tmp/dnsmasq_bt.leases -dhcp-authoritative -log-dhcp -``` - -```EOF``` - -```sudo dnsmasq -C /tmp/dnsmasq_bt.conf -sudo bt-agent -c NoInputNoOutput& -sudo bt-adapter -a hci0 --set Discoverable 1 -sudo bt-adapter -a hci0 --set DiscoverableTimeout 0 -sudo bt-adapter -a hci0 --set Pairable 1 -sudo bt-adapter -a hci0 --set PairableTimeout 0 -sudo bt-network -a hci0 -s nap pan0 & -``` - -##### 4. Finally: on your phone, you have to disable all existing interfaces: - -- Shutdown WiFi. -- Shutdown mobile data. -- Connect to the newly available Bluetooth device (which has the name of your Pwnagotchi). - - Once connected, you can test: `http://172.26.0.1:8080` -- You can also install bettercap's UI (`sudo bettercap` then `ui.update`) - - You'll need to change the http caplets to change `127.0.0.1` to `0.0.0.0`. -- You can connect to the shell with a terminal emulator ... - -Happy tweaking. diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index b998af0..0000000 --- a/docs/index.md +++ /dev/null @@ -1,23 +0,0 @@ -# Documentation - -- [About the Project](https://github.com/evilsocket/pwnagotchi/blob/master/docs/about.md) -- [How to Install](https://github.com/evilsocket/pwnagotchi/blob/master/docs/install.md) -- [Configuration](https://github.com/evilsocket/pwnagotchi/blob/master/docs/configure.md) -- [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md) -- [Plugins](https://github.com/evilsocket/pwnagotchi/blob/master/docs/plugins.md) -- [Development](https://github.com/evilsocket/pwnagotchi/blob/master/docs/dev.md) -- [FAQ](https://github.com/evilsocket/pwnagotchi/blob/master/docs/faq.md) -- [Community Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) - -## Links - - | Official Links ----------|------- -Slack | [pwnagotchi.slack.com](https://pwnagotchi.herokuapp.com) -Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) -Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) -Website | [pwnagotchi.ai](https://pwnagotchi.ai/) - -## License - -`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It's released under the GPL3 license. diff --git a/docs/install.md b/docs/install.md deleted file mode 100644 index 9f143e8..0000000 --- a/docs/install.md +++ /dev/null @@ -1,60 +0,0 @@ -# Installation - -The project has been developed to run on a Raspberry Pi 0 W configured as an [USB Ethernet gadget](https://learn.adafruit.com/turning-your-raspberry-pi-zero-into-a-usb-gadget/ethernet-gadget) device in order to connect to it via USB. However, given the proper configuration tweaks, any GNU/Linux computer with a WiFi interface that supports monitor mode could be used. - -**An important note about the AI:** a network trained with a specific WiFi interface will ONLY work with another interface if it supports the *exact same* WiFi channels of the first one. For instance, you CANNOT use a neural network trained on a Raspberry Pi Zero W (that only supports 2.4Ghz channels) with a 5Ghz antenna; you will need to train one from scratch for those channels. - -## Required Hardware - -- [Raspberry Pi Zero W](https://www.raspberrypi.org/products/raspberry-pi-zero-w/).† -- A micro SD card, 8GB recommended, **preferably of good quality and speed**. -- A decent power bank (with 1500 mAh you get ~2 hours with AI on). -- One of the supported displays (optional). - -† Many users have gotten Pwnagotchi running on other types of Raspberry Pi, but the RPi0W is the "vanilla" hardware config for Pwnagotchi. - -### Display - -The display is an optional component as the UI is also rendered via a web interface available via the USB cable. If you connect to `usb0` (by using the data port on the unit) and point your browser to the web ui (see `config.yml`), your unit can work in "headless mode". - -If, instead, you want to fully enjoy walking around and literally looking at your unit's face, the supported display models are: - -- [Waveshare eInk Display (both V1 and V2)](https://www.waveshare.com/2.13inch-e-paper-hat.htm) - - [Product comparison](https://www.waveshare.com/4.3inch-e-paper.htm) (scroll down to `Selection Guide`) - - [GitHub](https://github.com/waveshare/e-Paper/tree/master/RaspberryPi%26JetsonNano/python) -- [Pimoroni Inky pHAT](https://shop.pimoroni.com/products/inky-phat) - - [Product page](https://shop.pimoroni.com/products/inky-phat) - - [GitHub](https://github.com/pimoroni/inky) -- [PaPiRus eInk Screen](https://uk.pi-supply.com/products/papirus-zero-epaper-screen-phat-pi-zero) - -Needless to say, we are always happy to receive pull requests adding support for new models. - -**One thing to note:** Not all displays are created equally! TFT displays, for example, work similar to an HDMI display, and they are NOT supported. Currently, all the officially-supported displays are I2C displays. If you are still interested in using unsupported displays, you may be able to find a community-submitted hack in the [Screens](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md#screens) section of the [Hacks](https://github.com/evilsocket/pwnagotchi/blob/master/docs/hacks.md) page. We are not responsible for anything you break by trying to use any display that is not officially supported by the development team! - -#### Color vs. Black & White displays - -Some of the supported displays support both **Black & White** and **Colored** versions. One common question whether there are meaningful differences between the two. There are: -- Color displays have a much slower refresh rate. In some cases, it can take up to 15 seconds; if slow refresh rates are something that you want to avoid, we recommend you use B&W displays. -- The 3-color 2.13" Waveshare displays have a slightly smaller pixel layout (104x212) compared to their B&W counterparts (122x250). - -#### Recommendations -- Avoid the Waveshare eInk **3-color** display. The refresh time is 15 seconds. -- Avoid the Pimoroni Inky pHAT **v1.** They're discontinued due to a faulty hardware part source used in manufacturing that resulted in high failure rates. -- Many users seem to prefer the Inky pHATs. There are two primary reasons: - - The Inkys feature better documentation and SDK support. - - Many Waveshare resellers do not disclose the version of the Waveshare boards they are selling (v1 vs v2), and the type they are selling can be fairly unclear (i.e., Waveshare 2.13 vs 2.13 B vs. 2.13C, and so on.) - -## Flashing an Image - -The easiest way to create a new Pwnagotchi is downloading the latest stable image from [our release page](https://github.com/evilsocket/pwnagotchi/releases) and write it to your SD card. You will need to use an image writing tool to install the image you have downloaded on your SD card. - -[balenaEtcher](https://www.balena.io/etcher/) is a graphical SD card writing tool that works on Mac OS, Linux and Windows, and is the easiest option for most users. balenaEtcher also supports writing images directly from the zip file, without any unzipping required. To write your image with balenaEtcher: - -- Download the latest [Pwnagotchi .img file](https://github.com/evilsocket/pwnagotchi/releases). -- Download [balenaEtcher](https://www.balena.io/etcher/) and install it. -- Connect an SD card reader with the SD card inside. -- Open balenaEtcher and select from your hard drive the Raspberry Pi .img or .zip file you wish to write to the SD card. -- Select the SD card you wish to write your image to. -- Review your selections and click 'Flash!' to begin writing data to the SD card. - -Your SD card is now ready for the first boot! diff --git a/docs/plugins.md b/docs/plugins.md deleted file mode 100644 index 0038df9..0000000 --- a/docs/plugins.md +++ /dev/null @@ -1,56 +0,0 @@ -# Plugins - -Pwnagotchi has a simple plugins system that you can use to customize your unit and its behaviour. You can place your plugins anywhere -as python files and then edit the `config.yml` file (`main.plugins` value) to point to their containing folder. Check the [plugins folder](https://github.com/evilsocket/pwnagotchi/tree/master/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/) for a list of default plugins and all the callbacks that you can define for your own customizations. - -Here's as an example the GPS plugin: - -```python -__author__ = 'evilsocket@gmail.com' -__version__ = '1.0.0' -__name__ = 'gps' -__license__ = 'GPL3' -__description__ = 'Save GPS coordinates whenever an handshake is captured.' -__enabled__ = True # set to false if you just don't use GPS - -import core -import json -import os - -device = '/dev/ttyUSB0' -speed = 19200 -running = False - - -def on_loaded(): - logging.info("GPS plugin loaded for %s" % device) - - -def on_ready(agent): - global running - - if os.path.exists(device): - logging.info("enabling GPS bettercap's module for %s" % device) - try: - agent.run('gps off') - except: - pass - - agent.run('set gps.device %s' % device) - agent.run('set gps.speed %d' % speed) - agent.run('gps on') - running = True - else: - logging.info("no GPS detected") - - -def on_handshake(agent, filename, access_point, client_station): - if running: - info = agent.session() - gps = info['gps'] - gps_filename = filename.replace('.pcap', '.gps.json') - - logging.info("saving GPS to %s (%s)" % (gps_filename, gps)) - with open(gps_filename, 'w+t') as fp: - json.dump(gps, fp) -``` diff --git a/docs/usage.md b/docs/usage.md deleted file mode 100644 index fff0fb1..0000000 --- a/docs/usage.md +++ /dev/null @@ -1,149 +0,0 @@ -# Usage - -## User Interface - -The UI is available either via display if installed, or via http://pwnagotchi.local:8080/ if you connect to the unit via `usb0` and set a static address on the network interface (change `pwnagotchi` with the hostname of your unit). - - - -* **CH**: Current channel the unit is operating on or `*` when hopping on all channels. -* **APS**: Number of access points on the current channel and total visible access points. -* **UP**: Time since the unit has been activated. -* **PWND**: Number of handshakes captured in this session and number of unique networks we own at least one handshake of, from the beginning. -* **MODE**: - * **AUTO:** This indicates that the Pwnagotchi algorithm is running in AUTOMATIC mode, with AI disabled (or still loading); it disappears once the AI dependencies have been bootstrapped and the neural network has finished loading. - * **MANU:** This appears when the unit is running in MANUAL mode. -* **FRIEND:** If another unit is nearby, its presence will be indicated here. If more than one unit is nearby, only one—whichever has the stronger signal strength—will be displayed. - -## Training the AI - -At its core Pwnagotchi is a very simple creature: we could summarize its main algorithm as: - -```python -# main loop -while True: - # ask bettercap for all visible access points and their clients - aps = get_all_visible_access_points() - # loop each AP - for ap in aps: - # send an association frame in order to grab the PMKID - send_assoc(ap) - # loop each client station of the AP - for client in ap.clients: - # deauthenticate the client to get its half or full handshake - deauthenticate(client) - - wait_for_loot() -``` - -Despite its simplicity, this logic is controlled by several parameters that regulate the wait times, the timeouts, on which channels to hop and so on. - -From `config.yml`: - -```yaml -personality: - # advertise our presence - advertise: true - # perform a deauthentication attack to client stations in order to get full or half handshakes - deauth: true - # send association frames to APs in order to get the PMKID - associate: true - # list of channels to recon on, or empty for all channels - channels: [] - # minimum WiFi signal strength in dBm - min_rssi: -200 - # number of seconds for wifi.ap.ttl - ap_ttl: 120 - # number of seconds for wifi.sta.ttl - sta_ttl: 300 - # time in seconds to wait during channel recon - recon_time: 30 - # number of inactive epochs after which recon_time gets multiplied by recon_inactive_multiplier - max_inactive_scale: 2 - # if more than max_inactive_scale epochs are inactive, recon_time *= recon_inactive_multiplier - recon_inactive_multiplier: 2 - # time in seconds to wait during channel hopping if activity has been performed - hop_recon_time: 10 - # time in seconds to wait during channel hopping if no activity has been performed - min_recon_time: 5 - # maximum amount of deauths/associations per BSSID per session - max_interactions: 3 - # maximum amount of misses before considering the data stale and triggering a new recon - max_misses_for_recon: 5 - # number of active epochs that triggers the excited state - excited_num_epochs: 10 - # number of inactive epochs that triggers the bored state - bored_num_epochs: 15 - # number of inactive epochs that triggers the sad state - sad_num_epochs: 25 -``` - -There is no optimal set of parameters for every situation: when the unit is moving (during a walk for instance) smaller timeouts and RSSI thresholds might be preferred in order to quickly remove routers that are not in range anymore, while when stationary in high density areas (like an office) other parameters might be better. The role of the AI is to observe what's going on at the WiFi level, and adjust those parameters in order to maximize the cumulative reward of that loop / epoch. - -## Reward Function - -After each iteration of the main loop (an `epoch`), the reward, a score that represents how well the parameters performed, is computed as (an excerpt from `pwnagotchi/ai/reward.py`): - -```python -# state contains the information of the last epoch -# epoch_n is the number of the last epoch -tot_epochs = epoch_n + 1e-20 # 1e-20 is added to avoid a division by 0 -tot_interactions = max(state['num_deauths'] + state['num_associations'], state['num_handshakes']) + 1e-20 -tot_channels = wifi.NumChannels - -# ideally, for each interaction we would have an handshake -h = state['num_handshakes'] / tot_interactions -# small positive rewards the more active epochs we have -a = .2 * (state['active_for_epochs'] / tot_epochs) -# make sure we keep hopping on the widest channel spectrum -c = .1 * (state['num_hops'] / tot_channels) -# small negative reward if we don't see aps for a while -b = -.3 * (state['blind_for_epochs'] / tot_epochs) -# small negative reward if we interact with things that are not in range anymore -m = -.3 * (state['missed_interactions'] / tot_interactions) -# small negative reward for inactive epochs -i = -.2 * (state['inactive_for_epochs'] / tot_epochs) - -reward = h + a + c + b + i + m -``` - -By maximizing this reward value, the AI learns over time to find the set of parameters that better perform with the current environmental conditions. - -## BetterCAP's Web UI - -Moreover, given that the unit is running bettercap with API and Web UI, you'll be able to use the unit as a WiFi penetration testing portable station by accessing `http://pwnagotchi.local/`. - - - -## Update your Pwnagotchi - -You can use the `scripts/update_pwnagotchi.sh` script to update to the most recent version of pwnagotchi. - -```shell -usage: ./update_pwnagitchi.sh [OPTIONS] - - Options: - -v # Version to update to, can be a branch or commit. (default: master) - -u # Url to clone from. (default: https://github.com/evilsocket/pwnagotchi) - -m # Mode to restart to. (Supported: auto manual; default: auto) - -b # Backup the current pwnagotchi config. - -r # Restore the current pwnagotchi config. -b will be enabled. - -h # Shows this help. Shows this help. - -``` - -## Backup your Pwnagotchi - -You can use the `scripts/backup.sh` script to backup the important files of your unit. - -```shell -usage: ./scripts/backup.sh HOSTNAME backup.zip -``` - -## Random Info - -* **On a rpi0w, it'll take approximately 30 minutes to load the AI**. -* `/var/log/pwnagotchi.log` is your friend. -* if connected to a laptop via usb data port, with internet connectivity shared, magic things will happen. -* checkout the `ui.video` section of the `config.yml` - if you don't want to use a display, you can connect to it with the browser and a cable. -* If you get `[FAILED] Failed to start Remount Root and Kernel File Systems.` while booting pwnagotchi, make sure the `PARTUUID`s for `rootfs` and `boot` partitions are the same in `/etc/fstab`. Use `sudo blkid` to find those values when you are using `create_sibling.sh`. From 0577972867a06c51a1aee032fcf42f8d8c581c72 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 18:42:57 +0200 Subject: [PATCH 092/150] new: if more than one peer are present, display their number (closes #201) --- pwnagotchi/agent.py | 3 ++- pwnagotchi/ui/view.py | 10 ++++++++-- scripts/preview.py | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pwnagotchi/agent.py b/pwnagotchi/agent.py index 0e89763..71e87f5 100644 --- a/pwnagotchi/agent.py +++ b/pwnagotchi/agent.py @@ -296,7 +296,8 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer): def _update_peers(self): peer = self._advertiser.closest_peer() - self._view.set_closest_peer(peer) + tot = self._advertiser.num_peers() + self._view.set_closest_peer(peer, tot) def _save_recovery_data(self): logging.warning("writing recovery data to %s ..." % RECOVERY_DATA_FILE) diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index 5847662..3289eb1 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -168,7 +168,7 @@ class View(object): self.set('aps', "%d" % log.associated) self.set('shakes', '%d (%s)' % (log.handshakes, \ utils.total_unique_handshakes(self._config['bettercap']['handshakes']))) - self.set_closest_peer(log.last_peer) + self.set_closest_peer(log.last_peer, log.peers) def is_normal(self): return self._state.get('face') not in ( @@ -188,7 +188,7 @@ class View(object): self.set('status', self._voice.on_normal()) self.update() - def set_closest_peer(self, peer): + def set_closest_peer(self, peer, num_total): if peer is None: self.set('friend_face', None) self.set('friend_name', None) @@ -207,6 +207,12 @@ class View(object): name += '│' * (4 - num_bars) name += ' %s %d (%d)' % (peer.name(), peer.pwnd_run(), peer.pwnd_total()) + if num_total > 1: + if num_total > 9000: + name += ' of over 9000' + else: + name += ' of %d' % num_total + self.set('friend_face', peer.face()) self.set('friend_name', name) self.update() diff --git a/scripts/preview.py b/scripts/preview.py index 06be3c6..e0ef7c4 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -122,7 +122,7 @@ def main(): for display in list_of_displays: emotions = list() if args.showpeer: - display.set_closest_peer(DummyPeer()) + display.set_closest_peer(DummyPeer(), 10) display.on_starting() display.update() emotions.append(display.get_image()) From 4f694ddb83244f094a6a0c5a0aa40c3b329ea50d Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 19:42:29 +0200 Subject: [PATCH 093/150] new: added clean shutdown button to the web ui (closes #161) --- pwnagotchi/__init__.py | 14 ++++++++++++++ pwnagotchi/ui/display.py | 27 ++++++++++++++++++++++++--- pwnagotchi/ui/view.py | 16 +++++++++++++++- pwnagotchi/voice.py | 5 +++++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/pwnagotchi/__init__.py b/pwnagotchi/__init__.py index ff43494..c343bb3 100644 --- a/pwnagotchi/__init__.py +++ b/pwnagotchi/__init__.py @@ -1,4 +1,8 @@ import subprocess +import os +import logging +import time +import pwnagotchi.ui.view as view version = '1.0.0a' @@ -46,3 +50,13 @@ def temperature(celsius=True): temp = int(fp.read().strip()) c = int(temp / 1000) return c if celsius else ((c * (9 / 5)) + 32) + + +def shutdown(): + logging.warning("shutting down ...") + if view.ROOT: + view.ROOT.on_shutdown() + # give it some time to refresh the ui + time.sleep(5) + os.system("sync") + os.system("halt") diff --git a/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py index 76e9a24..ffefa18 100644 --- a/pwnagotchi/ui/display.py +++ b/pwnagotchi/ui/display.py @@ -15,11 +15,29 @@ class VideoHandler(BaseHTTPRequestHandler): _lock = Lock() _index = """<html> <head> - <title>%s</title> + <title>%s</title> + <style> + .block { + -webkit-appearance: button; + -moz-appearance: button; + appearance: button; + + display: block; + cursor: pointer; + text-align: center; + } + </style> </head> <body> - <img src="/ui" id="ui"/> - + <div style="position: absolute; top:0; left:0; width:100%%;"> + <img src="/ui" id="ui" style="width:100%%"/> + <br/> + <hr/> + <form action="/shutdown" onsubmit="return confirm('This will halt the unit, continue?');"> + <input type="submit" class="block" value="Shutdown"/> + </form> + </div> + <script type="text/javascript"> window.onload = function() { var image = document.getElementById("ui"); @@ -50,6 +68,9 @@ class VideoHandler(BaseHTTPRequestHandler): except: pass + elif self.path.startswith('/shutdown'): + pwnagotchi.shutdown() + elif self.path.startswith('/ui'): with self._lock: self.send_response(200) diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index 3289eb1..3d247f9 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -15,7 +15,7 @@ from pwnagotchi.ui.state import State WHITE = 0xff BLACK = 0x00 - +ROOT = None def setup_display_specifics(config): width = 0 @@ -57,9 +57,12 @@ def setup_display_specifics(config): class View(object): def __init__(self, config, state={}): + global ROOT + self._render_cbs = [] self._config = config self._canvas = None + self._frozen = False self._lock = Lock() self._voice = Voice(lang=config['main']['lang']) @@ -119,6 +122,8 @@ class View(object): logging.warning("ui.fps is 0, the display will only update for major changes") self._ignore_changes = ('uptime', 'name') + ROOT = self + def add_element(self, key, elem): self._state.add_element(key, elem) @@ -261,6 +266,12 @@ class View(object): self.on_normal() + def on_shutdown(self): + self.set('face', faces.SLEEP) + self.set('status', self._voice.on_shutdown()) + self.update(force=True) + self._frozen = True + def on_bored(self): self.set('face', faces.BORED) self.set('status', self._voice.on_bored()) @@ -323,6 +334,9 @@ class View(object): def update(self, force=False): with self._lock: + if self._frozen: + return + changes = self._state.changes(ignore=self._ignore_changes) if force or len(changes): self._canvas = Image.new('1', (self._width, self._height), WHITE) diff --git a/pwnagotchi/voice.py b/pwnagotchi/voice.py index 9268b11..aa12334 100644 --- a/pwnagotchi/voice.py +++ b/pwnagotchi/voice.py @@ -90,6 +90,11 @@ class Voice: self._('Zzzzz'), self._('ZzzZzzz ({secs}s)').format(secs=secs)]) + def on_shutdown(self): + return random.choice([ + self._('Good night.'), + self._('Zzz')]) + def on_awakening(self): return random.choice(['...', '!']) From 72e6668c172b6b8a83d4a9d9ffb052e28ea1f470 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Mon, 7 Oct 2019 20:50:46 +0200 Subject: [PATCH 094/150] fix: fixed config merge with user config exists but it's empty --- pwnagotchi/utils.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py index e6e15f5..8c30e94 100644 --- a/pwnagotchi/utils.py +++ b/pwnagotchi/utils.py @@ -20,13 +20,15 @@ def merge_config(user, default): def load_config(args): - with open(args.config, 'rt') as fp: + with open(args.config) as fp: config = yaml.safe_load(fp) if os.path.exists(args.user_config): - with open(args.user_config, 'rt') as fp: + with open(args.user_config) as fp: user_config = yaml.safe_load(fp) - config = merge_config(user_config, config) + # if the file is empty, safe_load will return None and merge_config will boom. + if user_config: + config = merge_config(user_config, config) return config From 1f99a249c63b09152b89bdd5eb354b1d2c795391 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Mon, 7 Oct 2019 19:59:28 +0200 Subject: [PATCH 095/150] Put pcap parsing in utils class --- pwnagotchi/plugins/default/grid.py | 24 ++----- pwnagotchi/plugins/default/wigle.py | 35 +++++------ pwnagotchi/utils.py | 97 +++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 39 deletions(-) diff --git a/pwnagotchi/plugins/default/grid.py b/pwnagotchi/plugins/default/grid.py index 6c151b3..dfc54ad 100644 --- a/pwnagotchi/plugins/default/grid.py +++ b/pwnagotchi/plugins/default/grid.py @@ -11,6 +11,7 @@ import glob import subprocess import pwnagotchi import pwnagotchi.utils as utils +from pwnagotchi.utils import WifiInfo, extract_from_pcap OPTIONS = dict() AUTH = utils.StatusFile('/root/.api-enrollment.json', data_format='json') @@ -67,17 +68,6 @@ def get_api_token(log, keys): return AUTH.data["token"] -def parse_packet(packet, info): - from scapy.all import Dot11Elt, Dot11Beacon, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq - if packet.haslayer(Dot11Beacon): - if packet.haslayer(Dot11ProbeResp) or packet.haslayer(Dot11AssoReq) or packet.haslayer(Dot11ReassoReq): - if hasattr(packet[Dot11], 'addr3'): - info['bssid'] = packet[Dot11].addr3 - if hasattr(packet[Dot11Elt], 'info'): - info['essid'] = packet[Dot11Elt].info.decode('utf-8') - return info - - def parse_pcap(filename): logging.info("api: parsing %s ..." % filename) @@ -94,20 +84,16 @@ def parse_pcap(filename): bssid = ':'.join([a + b for a, b in zip(it, it)]) info = { - 'essid': essid, - 'bssid': bssid + WifiInfo.ESSID: essid, + WifiInfo.BSSID: bssid, } try: - from scapy.all import rdpcap - - for pkt in rdpcap(filename): - info = parse_packet(pkt, info) - + info = extract_from_pcap(filename, [WifiInfo.BSSID, WifiInfo.ESSID]) except Exception as e: logging.error("api: %s" % e) - return info['essid'], info['bssid'] + return info[WifiInfo.ESSID], info[WifiInfo.BSSID] def api_report_ap(log, keys, token, essid, bssid): diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py index da57f94..4f89ad4 100644 --- a/pwnagotchi/plugins/default/wigle.py +++ b/pwnagotchi/plugins/default/wigle.py @@ -12,6 +12,7 @@ import csv from datetime import datetime import requests from pwnagotchi.mesh.wifi import freq_to_channel +from pwnagotchi.utils import WifiInfo, FieldNotFoundError, extract_from_pcap READY = False ALREADY_UPLOADED = None @@ -151,13 +152,13 @@ def _transform_wigle_entry(gps_data, pcap_data): writer = csv.writer(dummy, delimiter=",", quoting=csv.QUOTE_NONE) writer.writerow([ - pcap_data['bssid'], - pcap_data['essid'].decode('utf-8'), - _format_auth(pcap_data['encryption']), + pcap_data[WifiInfo.BSSID], + pcap_data[WifiInfo.ESSID], + _format_auth(pcap_data[WifiInfo.ENCRYPTION]), datetime.strptime(gps_data['Updated'].rsplit('.')[0], "%Y-%m-%dT%H:%M:%S").strftime('%Y-%m-%d %H:%M:%S'), - pcap_data['channel'], - pcap_data['rssi'], + pcap_data[WifiInfo.CHANNEL], + pcap_data[WifiInfo.RSSI], gps_data['Latitude'], gps_data['Longitude'], gps_data['Altitude'], @@ -238,23 +239,17 @@ def on_internet_available(display, keypair, config, log): continue try: - pcap_data = _analyze_pcap(pcap_filename) - except Scapy_Exception as sc_e: - logging.error("WIGLE: %s", sc_e) + pcap_data = extract_from_pcap(pcap_filename, [WifiInfo.BSSID, + WifiInfo.ESSID, + WifiInfo.ENCRYPTION, + WifiInfo.CHANNEL, + WifiInfo.RSSI]) + except FieldNotFoundError: + logging.error("WIGLE: Could not extract all informations. Skip %s", gps_file) SKIP.append(gps_file) continue - - # encrypption-key is only there if privacy-cap was set - if 'encryption' in pcap_data: - if not pcap_data['encryption']: - pcap_data['encryption'].add('WEP') - else: - # no encryption, nothing to eat :( - pcap_data['encryption'] = set() - pcap_data['encryption'].add('OPN') - - if len(pcap_data) < 5: - # not enough data; try next time + except Scapy_Exception as sc_e: + logging.error("WIGLE: %s", sc_e) SKIP.append(gps_file) continue diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py index 8c30e94..bde9fd9 100644 --- a/pwnagotchi/utils.py +++ b/pwnagotchi/utils.py @@ -1,4 +1,5 @@ from datetime import datetime +from enum import Enum import logging import glob import os @@ -83,6 +84,102 @@ def blink(times=1, delay=0.3): time.sleep(delay) led(True) +class WifiInfo(Enum): + """ + Fields you can extract from a pcap file + """ + BSSID = 0 + ESSID = 1 + ENCRYPTION = 2 + CHANNEL = 3 + RSSI = 4 + +class FieldNotFoundError(Exception): + pass + + +def extract_from_pcap(path, fields): + """ + Search in pcap-file for specified information + + path: Path to pcap file + fields: Array of fields that should be extracted + + If a field is not found, FieldNotFoundError is raised + """ + results = dict() + for field in fields: + if not isinstance(field, WifiInfo): + raise TypeError("Invalid field") + + subtypes = set() + + if field == WifiInfo.BSSID: + from scapy.all import Dot11Beacon, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq, Dot11, sniff + subtypes.add('beacon') + bpf_filter = " or ".join([f"wlan type mgt subtype {subtype}" for subtype in subtypes]) + packets = sniff(offline=path, filter=bpf_filter) + try: + for packet in packets: + if packet.haslayer(Dot11Beacon): + if hasattr(packet[Dot11], 'addr3'): + results[field] = packet[Dot11].addr3 + break + else: # magic + raise FieldNotFoundError("Could not find field [BSSID]") + except Exception: + raise FieldNotFoundError("Could not find field [BSSID]") + elif field == WifiInfo.ESSID: + from scapy.all import Dot11Beacon, Dot11ReassoReq, Dot11AssoReq, Dot11, sniff, Dot11Elt + subtypes.add('beacon') + subtypes.add('assoc-req') + subtypes.add('reassoc-req') + bpf_filter = " or ".join([f"wlan type mgt subtype {subtype}" for subtype in subtypes]) + packets = sniff(offline=path, filter=bpf_filter) + try: + for packet in packets: + if packet.haslayer(Dot11Elt) and hasattr(packet[Dot11Elt], 'info'): + results[field] = packet[Dot11Elt].info.decode('utf-8') + break + else: # magic + raise FieldNotFoundError("Could not find field [ESSID]") + except Exception: + raise FieldNotFoundError("Could not find field [ESSID]") + elif field == WifiInfo.ENCRYPTION: + from scapy.all import Dot11Beacon, sniff + subtypes.add('beacon') + bpf_filter = " or ".join([f"wlan type mgt subtype {subtype}" for subtype in subtypes]) + packets = sniff(offline=path, filter=bpf_filter) + try: + for packet in packets: + if packet.haslayer(Dot11Beacon) and hasattr(packet[Dot11Beacon], 'network_stats'): + stats = packet[Dot11Beacon].network_stats() + if 'crypto' in stats: + results[field] = stats['crypto'] # set with encryption types + break + else: # magic + raise FieldNotFoundError("Could not find field [ENCRYPTION]") + except Exception: + raise FieldNotFoundError("Could not find field [ENCRYPTION]") + elif field == WifiInfo.CHANNEL: + from scapy.all import sniff, RadioTap + from pwnagotchi.mesh.wifi import freq_to_channel + packets = sniff(offline=path, count=1) + try: + results[field] = freq_to_channel(packets[0][RadioTap].ChannelFrequency) + except Exception: + raise FieldNotFoundError("Could not find field [CHANNEL]") + elif field == WifiInfo.RSSI: + from scapy.all import sniff, RadioTap + from pwnagotchi.mesh.wifi import freq_to_channel + packets = sniff(offline=path, count=1) + try: + results[field] = packets[0][RadioTap].dBm_AntSignal + except Exception: + raise FieldNotFoundError("Could not find field [RSSI]") + + return results + class StatusFile(object): def __init__(self, path, data_format='raw'): From 80e307d28dd36261f7f5e8098255044792985d7c Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Mon, 7 Oct 2019 23:35:18 +0200 Subject: [PATCH 096/150] Add OPTIONS to gps-plugin --- pwnagotchi/defaults.yml | 2 ++ pwnagotchi/plugins/default/gps.py | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 330218f..62a70eb 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -31,6 +31,8 @@ main: - 'scp /tmp/backup.tar.gz pwnagotchi@10.0.0.1:/home/pwnagotchi/backups/backup-$(date +%s).tar.gz' gps: enabled: false + speed: 19200 + device: /dev/ttyUSB0 twitter: enabled: false consumer_key: aaa diff --git a/pwnagotchi/plugins/default/gps.py b/pwnagotchi/plugins/default/gps.py index f7aa56e..c49b926 100644 --- a/pwnagotchi/plugins/default/gps.py +++ b/pwnagotchi/plugins/default/gps.py @@ -8,9 +8,8 @@ import logging import json import os -device = '/dev/ttyUSB0' -speed = 19200 running = False +OPTIONS = dict() def on_loaded(): @@ -27,8 +26,8 @@ def on_ready(agent): except: pass - agent.run('set gps.device %s' % device) - agent.run('set gps.speed %d' % speed) + agent.run('set gps.device %s' % OPTIONS['device']) + agent.run('set gps.speed %d' % OPTIONS['speed']) agent.run('gps on') running = True else: From 260728fffcf1d1c45b8544be0d686d08f2de967a Mon Sep 17 00:00:00 2001 From: Zenzen San <zenzenzen@riseup.net> Date: Mon, 7 Oct 2019 22:50:02 -0400 Subject: [PATCH 097/150] When internet is available it converts wifips files in geolocation --- pwnagotchi/plugins/default/geowifi.py | 69 ++++++++++++++++++++------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/pwnagotchi/plugins/default/geowifi.py b/pwnagotchi/plugins/default/geowifi.py index b7f7982..283fadc 100644 --- a/pwnagotchi/plugins/default/geowifi.py +++ b/pwnagotchi/plugins/default/geowifi.py @@ -1,30 +1,67 @@ __author__ = 'zenzen san' __version__ = '1.0.0' -__name__ = 'geowifi' +__name__ = 'wifips' __license__ = 'GPL3' -__description__ = 'Saves a json file with the access points with more signal whenever a handshake is captured. This data is usable to retrieve the geographic location using Google Geolocation API or Mozilla Location Service' +__description__ = """Saves a json file with the access points with more signal + whenever a handshake is captured. + When internet is available the files are converted in geo locations + using Mozilla LocationService """ import logging import json +import os +from urllib.request import urlopen +from datetime import datetime +from time import sleep + +URL_API_MOZILLA_LOCATION_SERVICE = 'https://location.services.mozilla.com/v1/geolocate?key=' +KEY_API_MOZILLA_LOCATION_SERVICE = 'test' def on_loaded(): - logging.info("geowifi plugin loaded. :)") + logging.info("wifips plugin loaded. :)") + +def on_internet_available(ui, keypair, config): + try: + for ff in os.listdir('/root/handshakes'): + geo_file = os.path.join('/root/handshakes', ff.replace('.wifips.json','.geo.json')) + if not os.path.isfile(geo_file): + if ff.endswith(".wifips.json"): + ff = os.path.join('/root/handshakes',ff) + with open(ff, 'r') as fp: + data = fp.read() + geo = _get_geolocation_moz_wifips(data) + with open(geo_file, 'w+t') as fp: + fp.write(geo.decode('ascii')) + logging.info("wifips plugin: saving coordinates for: {}".format(ff.replace('.wifips.json',''))) + sleep(.500) + except Exception as e: + logging.exception('WIFIPS PLUGIN ERROR') + +def on_ready(agent): + pass def on_handshake(agent, filename, access_point, client_station): + wifips = _get_wifips(agent) + wifips_filename = filename.replace('.pcap', '.wifips.json') + logging.info("wifips plugin: saving location to %s" % (wifips_filename)) + with open(wifips_filename, 'w+t') as fp: + json.dump(wifips, fp) + +def _get_wifips(agent): info = agent.session() aps = agent.get_access_points() - geowifi = _geowifi_location(aps) - geowifi_filename = filename.replace('.pcap', '.geowifi.json') + wifips = {} + wifips['wifiAccessPoints'] = [] + # 6 seems a good number to save a wifi networks location + for ap in sorted(aps,key=lambda i:i['rssi'],reverse=True)[:6]: + wifips['wifiAccessPoints'].append({'macAddress': ap['mac'], 'signalStrength': ap['rssi']}) + return wifips - logging.info("saving GEOWIFI location to %s" % (geowifi_filename)) - with open(geowifi_filename, 'w+t') as fp: - json.dump(geowifi, fp) - -def _geowifi_location(aps): - geowifi = {} - geowifi['wifiAccessPoints'] = [] - # size seems a good number to save a wifi networks location - for ap in sorted(aps,key=lambda i:i['rssi'],reverse=True)[:6]: - geowifi['wifiAccessPoints'].append({'macAddress': ap['mac'], 'signalStrength': ap['rssi']}) - return geowifi +def _get_geolocation_moz_wifips(post_data): + geourl = URL_API_MOZILLA_LOCATION_SERVICE+KEY_API_MOZILLA_LOCATION_SERVICE + try: + response = urlopen(geourl, post_data.encode('ascii')).read() + return response + except Exception as e: + logging.exception('WIFIPS PLUGIN - Something went wrong with Mozilla Location Service') From 817c9c2854826ff27e6e16ad7e79c6f886a116e0 Mon Sep 17 00:00:00 2001 From: Zenzen San <zenzenzen@riseup.net> Date: Mon, 7 Oct 2019 22:54:09 -0400 Subject: [PATCH 098/150] plugin renamed wifips --- pwnagotchi/defaults.yml | 2 +- pwnagotchi/plugins/default/{geowifi.py => wifips.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename pwnagotchi/plugins/default/{geowifi.py => wifips.py} (100%) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index c1fc2b6..911e059 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -25,7 +25,7 @@ main: 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' - geowifi: + wifips: enabled: false gps: enabled: false diff --git a/pwnagotchi/plugins/default/geowifi.py b/pwnagotchi/plugins/default/wifips.py similarity index 100% rename from pwnagotchi/plugins/default/geowifi.py rename to pwnagotchi/plugins/default/wifips.py From 1c11af8dce1e1e849d232d836c7455f4d58ca0ad Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Tue, 8 Oct 2019 13:33:11 +0100 Subject: [PATCH 099/150] builder changes --- .travis.yml | 9 +-- builder/pwnagotchi.json | 1 + builder/pwnagotchi.yml | 158 +++++++++++++++++++++------------------- 3 files changed, 86 insertions(+), 82 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0826bfc..35069f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,15 +22,8 @@ branches: cache: apt: true before_script: - - wget https://download.qemu.org/qemu-4.1.0.tar.xz - - tar xvJf qemu-4.1.0.tar.xz - - cd qemu-4.1.0 - - "./configure --target-list=arm-softmmu" - - make -j$(nproc) - - sudo make install - - cd $TRAVIS_BUILD_DIR - sudo apt-get -y update || true - - sudo apt-get -y install qemu-user-static binfmt-support bmap-tools kpartx + - sudo apt-get -y install qemu-system-arm qemu-user-static binfmt-support bmap-tools kpartx - sudo update-binfmts --display script: - sudo make clean diff --git a/builder/pwnagotchi.json b/builder/pwnagotchi.json index beac17b..b979f50 100644 --- a/builder/pwnagotchi.json +++ b/builder/pwnagotchi.json @@ -15,6 +15,7 @@ "type": "shell", "inline": [ "sed -i 's/^\\([^#]\\)/#\\1/g' /etc/ld.so.preload", + "dpkg-architecture", "apt-get -y update", "apt-get install -y ansible" ] diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index fab9c2e..f73deb6 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -11,11 +11,11 @@ - "dtoverlay=dwc2" - "dtparam=spi=on" - "dtoverlay=spi1-3cs" - - "dtoverlay=pi3-disable-bt" - - "dtparam=audio=off" services: enable: - dphys-swapfile.service + - pwnagotchi.service + - bettercap.service disable: - apt-daily.timer - apt-daily.service @@ -26,23 +26,6 @@ - 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 @@ -57,6 +40,7 @@ - git - build-essential - python3-pip + - python3-mpi4py - unzip - gawk - libopenmpi-dev @@ -65,6 +49,7 @@ - libqtgui4 - libqt4-test - libopenjp2-7 + - libtiff5 - tcpdump - lsof - libilmbase23 @@ -76,6 +61,7 @@ - libpcap-dev - libusb-1.0-0-dev - libnetfilter-queue-dev + - libopenmpi3 - dphys-swapfile - kalipi-kernel - kalipi-bootloader @@ -88,26 +74,7 @@ - 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" @@ -171,16 +138,30 @@ command: "python3 -c 'import sys;print(sys.path.pop())'" register: pip_target - - 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: clone pwnagotchi repository + git: + repo: https://github.com/evilsocket/pwnagotchi.git + dest: /usr/local/src/pwnagotchi - - 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: build pwnagotchi wheel + command: "python3 setup.py sdist bdist_wheel" + args: + chdir: /usr/local/src/pwnagotchi + + - name: install opencv-python + pip: + name: "https://www.piwheels.hostedpi.com/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl" + extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}" - 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" + pip: + name: "https://www.piwheels.hostedpi.com/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl" + extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}" + + - name: install pwnagotchi wheel and dependencies + pip: + name: "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}" + extra_args: "--no-cache-dir" - name: fetch bettercap release information uri: @@ -208,22 +189,6 @@ chdir: /tmp/caplets target: install - - name: clone pwnagotchi repository - git: - repo: https://github.com/evilsocket/pwnagotchi.git - dest: /tmp/pwnagotchi - - - name: copy pwnagotchi files to final destination - copy: - src: /tmp/pwnagotchi/sdcard/rootfs/root/pwnagotchi/ - dest: /root/pwnagotchi/ - mode: preserve - - - name: remove pwnagotchi files from temporary repository - file: - path: /tmp/pwnagotchi - state: absent - - name: create cpuusage script copy: dest: /usr/bin/cpuusage @@ -270,9 +235,9 @@ /usr/bin/bootblink 10 & # start a detached screen session with bettercap if ifconfig | grep usb0 | grep RUNNING; then - /usr/bin/pwnagotchi --manual + /usr/local/bin/pwnagotchi --manual else - /usr/bin/pwnagotchi + /usr/local/bin/pwnagotchi fi - name: create monstart script @@ -291,7 +256,7 @@ #!/usr/bin/env bash ifconfig mon0 down && iw dev mon0 del - - name: configure rc.local + - name: add HDMI powersave to rc.local blockinfile: path: /etc/rc.local insertbefore: "exit 0" @@ -299,7 +264,6 @@ if ! /opt/vc/bin/tvservice -s | grep HDMI; then /opt/vc/bin/tvservice -o fi - /root/pwnagotchi/scripts/startup.sh & - name: create /etc/pwnagotchi/config.yml blockinfile: @@ -374,13 +338,6 @@ regexp: '(.*)$' line: '\1 modules-load=dwc2,g_ether' - - name: configure ssh - lineinfile: - dest: /etc/ssh/sshd_config - backup: no - regexp: '#?PermitRootLogin (.*)$' - line: 'PermitRootLogin yes' - - name: configure motd copy: dest: /etc/motd @@ -394,17 +351,63 @@ apt: autoremove: yes + - name: add bettercap service to systemd + copy: + dest: /etc/systemd/system/bettercap.service + content: | + [Unit] + Description=bettercap api.rest service. + Documentation=https://bettercap.org + Wants=network.target + After=network.target + + [Service] + Type=simple + PermissionsStartOnly=true + ExecStart=/usr/bin/bettercap -no-colors -caplet pwnagotchi + Restart=always + RestartSec=30 + + [Install] + WantedBy=multi-user.target + notify: + - reload systemd services + + - name: add pwnagotchi service to systemd + copy: + dest: /etc/systemd/system/pwnagotchi.service + content: | + [Unit] + Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning. + Documentation=https://pwnagotchi.ai + Wants=network.target + After=bettercap.service + + [Service] + Type=simple + PermissionsStartOnly=true + ExecStart=/usr/bin/pwnagotchi-launcher + Restart=always + RestartSec=30 + + [Install] + WantedBy=multi-user.target + notify: + - reload systemd services + - name: enable services systemd: - name: "{{services.enable}}" + name: "{{ item }}" state: started enabled: yes + with_items: "{{ services.enable }}" - name: disable unecessary services systemd: - name: "{{services.disable}}" + name: "{{ item }}" state: stopped enabled: no + with_items: "{{ services.disable }}" - name: remove ssh keys file: @@ -412,3 +415,10 @@ path: "{{item}}" with_fileglob: - "/etc/ssh/ssh_host*_key*" + + handlers: + - name: reload systemd services + systemd: + daemon_reload: yes + + From 55d99836e7e74a36c6d2ea4306bcb8ab3046f432 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 14:54:03 +0200 Subject: [PATCH 100/150] fix: on_internet_available plugins callback is now called for both MANU and AUTO mode (fixes #210) --- bin/pwnagotchi | 24 ++++--- pwnagotchi/agent.py | 5 ++ pwnagotchi/log.py | 68 ++++++++++--------- pwnagotchi/mesh/utils.py | 3 + pwnagotchi/plugins/default/auto-backup.py | 4 +- pwnagotchi/plugins/default/auto-update.py | 4 +- pwnagotchi/plugins/default/example.py | 2 +- pwnagotchi/plugins/default/grid.py | 54 ++++++++------- pwnagotchi/plugins/default/onlinehashcrack.py | 5 +- pwnagotchi/plugins/default/twitter.py | 22 +++--- pwnagotchi/plugins/default/wigle.py | 5 +- pwnagotchi/plugins/default/wpa-sec.py | 5 +- pwnagotchi/ui/view.py | 16 ++--- pwnagotchi/voice.py | 24 +++---- 14 files changed, 139 insertions(+), 102 deletions(-) diff --git a/bin/pwnagotchi b/bin/pwnagotchi index f72d84c..9cd77d8 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -9,7 +9,6 @@ if __name__ == '__main__': import pwnagotchi.utils as utils import pwnagotchi.plugins as plugins - from pwnagotchi.log import SessionParser from pwnagotchi.identity import KeyPair from pwnagotchi.agent import Agent from pwnagotchi.ui.display import Display @@ -51,22 +50,23 @@ if __name__ == '__main__': elif args.do_manual: logging.info("entering manual mode ...") - log = SessionParser(config) + agent.last_session.parse() + 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)) + agent.last_session.duration_human, + agent.last_session.epochs, + agent.last_session.train_epochs, + agent.last_session.avg_reward, + agent.last_session.min_reward, + agent.last_session.max_reward)) while True: - display.on_manual_mode(log) + display.on_manual_mode(agent.last_session) time.sleep(1) if Agent.is_connected(): - plugins.on('internet_available', display, keypair, config, log) + plugins.on('internet_available', agent) else: logging.info("entering auto mode ...") @@ -104,5 +104,9 @@ if __name__ == '__main__': # WiFi electromagnetic fields affect time like gravitational fields # affect ours ... neat ^_^ agent.next_epoch() + + if Agent.is_connected(): + plugins.on('internet_available', agent) + except Exception as e: logging.exception("main loop exception") diff --git a/pwnagotchi/agent.py b/pwnagotchi/agent.py index 71e87f5..b29e164 100644 --- a/pwnagotchi/agent.py +++ b/pwnagotchi/agent.py @@ -9,6 +9,7 @@ import _thread import pwnagotchi.utils as utils import pwnagotchi.plugins as plugins +from pwnagotchi.log import LastSession from pwnagotchi.bettercap import Client from pwnagotchi.mesh.utils import AsyncAdvertiser from pwnagotchi.ai.train import AsyncTrainer @@ -35,6 +36,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer): self._last_pwnd = None self._history = {} self._handshakes = {} + self.last_session = LastSession(self._config) @staticmethod def is_connected(): @@ -48,6 +50,9 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer): def config(self): return self._config + def view(self): + return self._view + def supported_channels(self): return self._supported_channels diff --git a/pwnagotchi/log.py b/pwnagotchi/log.py index dab58e4..0f05789 100644 --- a/pwnagotchi/log.py +++ b/pwnagotchi/log.py @@ -11,7 +11,7 @@ from file_read_backwards import FileReadBackwards LAST_SESSION_FILE = '/root/.pwnagotchi-last-session' -class SessionParser(object): +class LastSession(object): EPOCH_TOKEN = '[epoch ' EPOCH_PARSER = re.compile(r'^.+\[epoch (\d+)\] (.+)') EPOCH_DATA_PARSER = re.compile(r'([a-z_]+)=([^\s]+)') @@ -70,27 +70,27 @@ class SessionParser(object): if started_at is None: started_at = stopped_at - if SessionParser.DEAUTH_TOKEN in line and line not in cache: + if LastSession.DEAUTH_TOKEN in line and line not in cache: self.deauthed += 1 cache[line] = 1 - elif SessionParser.ASSOC_TOKEN in line and line not in cache: + elif LastSession.ASSOC_TOKEN in line and line not in cache: self.associated += 1 cache[line] = 1 - elif SessionParser.HANDSHAKE_TOKEN in line and line not in cache: + elif LastSession.HANDSHAKE_TOKEN in line and line not in cache: self.handshakes += 1 cache[line] = 1 - elif SessionParser.TRAINING_TOKEN in line: + elif LastSession.TRAINING_TOKEN in line: self.train_epochs += 1 - elif SessionParser.EPOCH_TOKEN in line: + elif LastSession.EPOCH_TOKEN in line: self.epochs += 1 - m = SessionParser.EPOCH_PARSER.findall(line) + m = LastSession.EPOCH_PARSER.findall(line) if m: epoch_num, epoch_data = m[0] - m = SessionParser.EPOCH_DATA_PARSER.findall(epoch_data) + m = LastSession.EPOCH_DATA_PARSER.findall(epoch_data) for key, value in m: if key == 'reward': reward = float(value) @@ -101,7 +101,7 @@ class SessionParser(object): elif reward > self.max_reward: self.max_reward = reward - elif SessionParser.PEER_TOKEN in line: + elif LastSession.PEER_TOKEN in line: m = self._peer_parser.findall(line) if m: name, pubkey, rssi, sid, pwnd_tot, uptime = m[0] @@ -134,6 +134,30 @@ class SessionParser(object): self.duration_human = ', '.join(self.duration_human) self.avg_reward /= (self.epochs if self.epochs else 1) + def parse(self): + lines = [] + + if os.path.exists(self.path): + with FileReadBackwards(self.path, encoding="utf-8") as fp: + for line in fp: + line = line.strip() + if line != "" and line[0] != '[': + continue + lines.append(line) + if LastSession.START_TOKEN in line: + break + lines.reverse() + + if len(lines) == 0: + lines.append("Initial Session"); + + self.last_session = lines + self.last_session_id = hashlib.md5(lines[0].encode()).hexdigest() + self.last_saved_session_id = self._get_last_saved_session_id() + + self._parse_stats() + self.parsed = True + def __init__(self, config): self.config = config self.voice = Voice(lang=config['main']['lang']) @@ -150,28 +174,10 @@ class SessionParser(object): self.last_peer = None self._peer_parser = re.compile( 'detected unit (.+)@(.+) \(v.+\) on channel \d+ \(([\d\-]+) dBm\) \[sid:(.+) pwnd_tot:(\d+) uptime:(\d+)\]') - - lines = [] - - if os.path.exists(self.path): - with FileReadBackwards(self.path, encoding="utf-8") as fp: - for line in fp: - line = line.strip() - if line != "" and line[0] != '[': - continue - lines.append(line) - if SessionParser.START_TOKEN in line: - break - lines.reverse() - - if len(lines) == 0: - lines.append("Initial Session"); - - self.last_session = lines - self.last_session_id = hashlib.md5(lines[0].encode()).hexdigest() - self.last_saved_session_id = self._get_last_saved_session_id() - - self._parse_stats() + self.last_session = [] + self.last_session_id = None + self.last_saved_session_id = None + self.parsed = False def is_new(self): return self.last_session_id != self.last_saved_session_id diff --git a/pwnagotchi/mesh/utils.py b/pwnagotchi/mesh/utils.py index a775ad4..3a67ed9 100644 --- a/pwnagotchi/mesh/utils.py +++ b/pwnagotchi/mesh/utils.py @@ -12,6 +12,9 @@ class AsyncAdvertiser(object): self._keypair = keypair self._advertiser = None + def keypair(self): + return self._keypair + def start_advertising(self): _thread.start_new_thread(self._adv_worker, ()) diff --git a/pwnagotchi/plugins/default/auto-backup.py b/pwnagotchi/plugins/default/auto-backup.py index 48d8597..7c9901c 100644 --- a/pwnagotchi/plugins/default/auto-backup.py +++ b/pwnagotchi/plugins/default/auto-backup.py @@ -33,7 +33,7 @@ def on_loaded(): logging.info("AUTO-BACKUP: Successfuly loaded.") -def on_internet_available(display, keypair, config, log): +def on_internet_available(agent): global STATUS if READY: @@ -42,6 +42,8 @@ def on_internet_available(display, keypair, config, log): files_to_backup = " ".join(OPTIONS['files']) try: + display = agent.view() + logging.info("AUTO-BACKUP: Backing up ...") display.set('status', 'Backing up ...') display.update() diff --git a/pwnagotchi/plugins/default/auto-update.py b/pwnagotchi/plugins/default/auto-update.py index dab44b3..8a01a45 100644 --- a/pwnagotchi/plugins/default/auto-update.py +++ b/pwnagotchi/plugins/default/auto-update.py @@ -23,13 +23,15 @@ def on_loaded(): READY = True -def on_internet_available(display, keypair, config, log): +def on_internet_available(agent): global STATUS if READY: if STATUS.newer_then_days(OPTIONS['interval']): return + display = agent.view() + try: display.set('status', 'Updating ...') display.update() diff --git a/pwnagotchi/plugins/default/example.py b/pwnagotchi/plugins/default/example.py index 2a82a18..3f3e570 100644 --- a/pwnagotchi/plugins/default/example.py +++ b/pwnagotchi/plugins/default/example.py @@ -20,7 +20,7 @@ def on_loaded(): # called in manual mode when there's internet connectivity -def on_internet_available(ui, keypair, config, log): +def on_internet_available(agent): pass diff --git a/pwnagotchi/plugins/default/grid.py b/pwnagotchi/plugins/default/grid.py index dfc54ad..5427dbb 100644 --- a/pwnagotchi/plugins/default/grid.py +++ b/pwnagotchi/plugins/default/grid.py @@ -22,16 +22,16 @@ def on_loaded(): logging.info("api plugin loaded.") -def get_api_token(log, keys): +def get_api_token(last_session, keys): global AUTH if AUTH.newer_then_minutes(25) and AUTH.data is not None and 'token' in AUTH.data: return AUTH.data['token'] if AUTH.data is None: - logging.info("api: enrolling unit ...") + logging.info("grid: enrolling unit ...") else: - logging.info("api: refreshing token ...") + logging.info("grid: refreshing token ...") identity = "%s@%s" % (pwnagotchi.name(), keys.fingerprint) # sign the identity string to prove we own both keys @@ -43,16 +43,16 @@ def get_api_token(log, keys): 'public_key': keys.pub_key_pem_b64, 'signature': signature_b64, 'data': { - 'duration': log.duration, - 'epochs': log.epochs, - 'train_epochs': log.train_epochs, - 'avg_reward': log.avg_reward, - 'min_reward': log.min_reward, - 'max_reward': log.max_reward, - 'deauthed': log.deauthed, - 'associated': log.associated, - 'handshakes': log.handshakes, - 'peers': log.peers, + 'duration': last_session.duration, + 'epochs': last_session.epochs, + 'train_epochs': last_session.train_epochs, + 'avg_reward': last_session.avg_reward, + 'min_reward': last_session.min_reward, + 'max_reward': last_session.max_reward, + 'deauthed': last_session.deauthed, + 'associated': last_session.associated, + 'handshakes': last_session.handshakes, + 'peers': last_session.peers, 'uname': subprocess.getoutput("uname -a") } } @@ -63,13 +63,13 @@ def get_api_token(log, keys): AUTH.update(data=r.json()) - logging.info("api: done") + logging.info("grid: done") return AUTH.data["token"] def parse_pcap(filename): - logging.info("api: parsing %s ..." % filename) + logging.info("grid: parsing %s ..." % filename) net_id = os.path.basename(filename).replace('.pcap', '') @@ -91,15 +91,15 @@ def parse_pcap(filename): try: info = extract_from_pcap(filename, [WifiInfo.BSSID, WifiInfo.ESSID]) except Exception as e: - logging.error("api: %s" % e) + logging.error("grid: %s" % e) return info[WifiInfo.ESSID], info[WifiInfo.BSSID] -def api_report_ap(log, keys, token, essid, bssid): +def api_report_ap(last_session, keys, token, essid, bssid): while True: token = AUTH.data['token'] - logging.info("api: reporting %s (%s)" % (essid, bssid)) + logging.info("grid: reporting %s (%s)" % (essid, bssid)) try: api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap' headers = {'Authorization': 'access_token %s' % token} @@ -111,21 +111,23 @@ def api_report_ap(log, keys, token, essid, bssid): if r.status_code != 200: if r.status_code == 401: logging.warning("token expired") - token = get_api_token(log, keys) + token = get_api_token(last_session, keys) continue else: raise Exception("(status %d) %s" % (r.status_code, r.text)) else: return True except Exception as e: - logging.error("api: %s" % e) + logging.error("grid: %s" % e) return False -def on_internet_available(ui, keys, config, log): +def on_internet_available(agent): global REPORT try: + config = agent.config() + keys = agent.keypair() pcap_files = glob.glob(os.path.join(config['bettercap']['handshakes'], "*.pcap")) num_networks = len(pcap_files) @@ -134,10 +136,10 @@ def on_internet_available(ui, keys, config, log): num_new = num_networks - num_reported if num_new > 0: - logging.info("api: %d new networks to report" % num_new) - token = get_api_token(log, keys) - if OPTIONS['report']: + logging.info("grid: %d new networks to report" % num_new) + token = get_api_token(agent.last_session, agent.keypair()) + for pcap_file in pcap_files: net_id = os.path.basename(pcap_file).replace('.pcap', '') do_skip = False @@ -151,11 +153,11 @@ def on_internet_available(ui, keys, config, log): if net_id not in reported and not do_skip: essid, bssid = parse_pcap(pcap_file) if bssid: - if api_report_ap(log, keys, token, essid, bssid): + if api_report_ap(agent.last_session, keys, token, essid, bssid): reported.append(net_id) REPORT.update(data={'reported': reported}) else: - logging.info("api: reporting disabled") + logging.debug("grid: reporting disabled") except Exception as e: logging.exception("error while enrolling the unit") diff --git a/pwnagotchi/plugins/default/onlinehashcrack.py b/pwnagotchi/plugins/default/onlinehashcrack.py index 9771b88..25a18d6 100644 --- a/pwnagotchi/plugins/default/onlinehashcrack.py +++ b/pwnagotchi/plugins/default/onlinehashcrack.py @@ -55,11 +55,14 @@ def _upload_to_ohc(path, timeout=30): raise e -def on_internet_available(display, keypair, config, log): +def on_internet_available(agent): """ Called in manual mode when there's internet connectivity """ if READY: + display = agent.view() + config = agent.config() + handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] diff --git a/pwnagotchi/plugins/default/twitter.py b/pwnagotchi/plugins/default/twitter.py index 8f21f25..fd247ca 100644 --- a/pwnagotchi/plugins/default/twitter.py +++ b/pwnagotchi/plugins/default/twitter.py @@ -14,8 +14,12 @@ def on_loaded(): # called in manual mode when there's internet connectivity -def on_internet_available(ui, keypair, config, log): - if log.is_new() and log.handshakes > 0: +def on_internet_available(agent): + config = agent.config() + display = agent.view() + last_session = agent.last_session + + if last_session.is_new() and last_session.handshakes > 0: try: import tweepy except ImportError: @@ -26,20 +30,20 @@ def on_internet_available(ui, keypair, 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) + display.on_manual_mode(last_session) + display.update(force=True) + display.image().save(picture, 'png') + display.set('status', 'Tweeting...') + display.update(force=True) try: 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) + tweet = Voice(lang=config['main']['lang']).on_last_session_tweet(last_session) api.update_with_media(filename=picture, status=tweet) - log.save_session_id() + last_session.save_session_id() logging.info("tweeted: %s" % tweet) except Exception as e: diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py index 4f89ad4..c72c1de 100644 --- a/pwnagotchi/plugins/default/wigle.py +++ b/pwnagotchi/plugins/default/wigle.py @@ -196,7 +196,7 @@ def _send_to_wigle(lines, api_key, timeout=30): raise re_e -def on_internet_available(display, keypair, config, log): +def on_internet_available(agent): from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA """ @@ -206,6 +206,9 @@ def on_internet_available(display, keypair, config, log): global SKIP if READY: + config = agent.config() + display = agent.view() + handshake_dir = config['bettercap']['handshakes'] all_files = os.listdir(handshake_dir) all_gps_files = [os.path.join(handshake_dir, filename) diff --git a/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py index 7d61b7b..0f6b854 100644 --- a/pwnagotchi/plugins/default/wpa-sec.py +++ b/pwnagotchi/plugins/default/wpa-sec.py @@ -54,11 +54,14 @@ def _upload_to_wpasec(path, timeout=30): raise e -def on_internet_available(display, keypair, config, log): +def on_internet_available(agent): """ Called in manual mode when there's internet connectivity """ if READY: + config = agent.config() + display = agent.view() + handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index 3d247f9..354d910 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -163,17 +163,17 @@ class View(object): self.set('status', self._voice.on_ai_ready()) self.update() - def on_manual_mode(self, log): + def on_manual_mode(self, last_session): self.set('mode', 'MANU') - self.set('face', faces.SAD if log.handshakes == 0 else faces.HAPPY) - self.set('status', self._voice.on_log(log)) - self.set('epoch', "%04d" % log.epochs) - self.set('uptime', log.duration) + self.set('face', faces.SAD if last_session.handshakes == 0 else faces.HAPPY) + self.set('status', self._voice.on_last_session_data(last_session)) + self.set('epoch', "%04d" % last_session.epochs) + self.set('uptime', last_session.duration) self.set('channel', '-') - self.set('aps', "%d" % log.associated) - self.set('shakes', '%d (%s)' % (log.handshakes, \ + self.set('aps', "%d" % last_session.associated) + self.set('shakes', '%d (%s)' % (last_session.handshakes, \ utils.total_unique_handshakes(self._config['bettercap']['handshakes']))) - self.set_closest_peer(log.last_peer, log.peers) + self.set_closest_peer(last_session.last_peer, last_session.peers) def is_normal(self): return self._state.get('face') not in ( diff --git a/pwnagotchi/voice.py b/pwnagotchi/voice.py index aa12334..a5305f6 100644 --- a/pwnagotchi/voice.py +++ b/pwnagotchi/voice.py @@ -125,23 +125,23 @@ class Voice: def on_rebooting(self): return self._("Ops, something went wrong ... Rebooting ...") - def on_log(self, log): - status = self._('Kicked {num} stations\n').format(num=log.deauthed) - status += self._('Made {num} new friends\n').format(num=log.associated) - status += self._('Got {num} handshakes\n').format(num=log.handshakes) - if log.peers == 1: + def on_last_session_data(self, last_session): + status = self._('Kicked {num} stations\n').format(num=last_session.deauthed) + status += self._('Made {num} new friends\n').format(num=last_session.associated) + status += self._('Got {num} handshakes\n').format(num=last_session.handshakes) + if last_session.peers == 1: status += self._('Met 1 peer') - elif log.peers > 0: - status += self._('Met {num} peers').format(num=log.peers) + elif last_session.peers > 0: + status += self._('Met {num} peers').format(num=last_session.peers) return status - def on_log_tweet(self, log): + def on_last_session_tweet(self, last_session): return self._( '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').format( - duration=log.duration_human, - deauthed=log.deauthed, - associated=log.associated, - handshakes=log.handshakes) + duration=last_session.duration_human, + deauthed=last_session.deauthed, + associated=last_session.associated, + handshakes=last_session.handshakes) def hhmmss(self, count, fmt): if count > 1: From 47654660eedd22a11c628f445d8eb3199ff9a882 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 15:09:32 +0200 Subject: [PATCH 101/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/grid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/grid.py b/pwnagotchi/plugins/default/grid.py index 5427dbb..368ebcd 100644 --- a/pwnagotchi/plugins/default/grid.py +++ b/pwnagotchi/plugins/default/grid.py @@ -19,7 +19,7 @@ REPORT = utils.StatusFile('/root/.api-report.json', data_format='json') def on_loaded(): - logging.info("api plugin loaded.") + logging.info("grid plugin loaded.") def get_api_token(last_session, keys): From 42dcc5fc22677508788f19f38dfd5dc6ffb7969b Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Tue, 8 Oct 2019 14:12:05 +0100 Subject: [PATCH 102/150] add override file check to loader --- builder/pwnagotchi.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 3bb6203..d81a8ea 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -235,7 +235,13 @@ /usr/bin/bootblink 10 & # start a detached screen session with bettercap if ifconfig | grep usb0 | grep RUNNING; then - /usr/local/bin/pwnagotchi --manual + # if override file exists, go into auto mode + if [ -f /root/.pwnagotchi-auto ]; then + rm /root/.pwnagotchi-auto + /usr/local/bin/pwnagotchi + else + /usr/local/bin/pwnagotchi --manual + fi else /usr/local/bin/pwnagotchi fi From bba025e64c4df4852b6745867f655d9619179749 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 15:26:32 +0200 Subject: [PATCH 103/150] fix: fixed released script --- scripts/release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.sh b/scripts/release.sh index 2f1343a..fafb08f 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -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/__init__.py +VERSION_FILE=$(dirname "${BASH_SOURCE[0]}")/../pwnagotchi/__init__.py echo "version file is $VERSION_FILE" CURRENT_VERSION=$(cat $VERSION_FILE | grep version | cut -d"'" -f2) TO_UPDATE=( From 3a081afefea9d8bb908cbb0937cbd0168fc27434 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 15:28:11 +0200 Subject: [PATCH 104/150] releasing v1.0.0RC0 --- pwnagotchi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/__init__.py b/pwnagotchi/__init__.py index c343bb3..fd8a5a0 100644 --- a/pwnagotchi/__init__.py +++ b/pwnagotchi/__init__.py @@ -4,7 +4,7 @@ import logging import time import pwnagotchi.ui.view as view -version = '1.0.0a' +version = '1.0.0RC0' _name = None From 833f4486ade8b385fbf296a72bb0d2caf98ef6a0 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Tue, 8 Oct 2019 14:59:12 +0100 Subject: [PATCH 105/150] hard code bettercap release until better solution is implemented --- builder/pwnagotchi.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 3bb6203..93b1155 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -26,6 +26,8 @@ - triggerhappy.service - ifup@wlan0.service packages: + bettercap: + url: "https://github.com/bettercap/bettercap/releases/download/v2.25/bettercap_linux_armv6l_2.25.zip" apt: remove: - rasberrypi-net-mods @@ -76,9 +78,6 @@ - fonts-dejavu-extra - python3-pil - bettercap: - query: "assets[?contains(name, 'armv6l')].browser_download_url" - tasks: - name: selected hostname @@ -163,15 +162,9 @@ name: "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}" extra_args: "--no-cache-dir" - - 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 }}" + src: "{{ packages.bettercap.url }}" dest: /usr/bin remote_src: yes exclude: From 88b1eacd74f9dedcc89a550315f60dd7561772c0 Mon Sep 17 00:00:00 2001 From: root <root@RossDebian> Date: Tue, 8 Oct 2019 15:58:14 +0100 Subject: [PATCH 106/150] Plugin to refresh display every X changes --- pwnagotchi/defaults.yml | 3 +++ pwnagotchi/plugins/default/screen_refresh.py | 27 ++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 pwnagotchi/plugins/default/screen_refresh.py diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 62a70eb..031d94e 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -48,6 +48,9 @@ main: wigle: enabled: false api_key: ~ + screen_refresh: + enabled: false + refresh_interval: 50 # monitor interface to use iface: mon0 diff --git a/pwnagotchi/plugins/default/screen_refresh.py b/pwnagotchi/plugins/default/screen_refresh.py new file mode 100644 index 0000000..7268151 --- /dev/null +++ b/pwnagotchi/plugins/default/screen_refresh.py @@ -0,0 +1,27 @@ +__author__ = 'pwnagotcchi [at] rossmarks [dot] uk' +__version__ = '1.0.0' +__name__ = 'screen_refresh' +__license__ = 'GPL3' +__description__ = 'Refresh he e-ink display after X amount of updates' + +import logging + +from pwnagotchi.ui.components import LabeledValue +from pwnagotchi.ui.view import BLACK +import pwnagotchi.ui.fonts as fonts + +OPTIONS = dict() +update_count = 0; + +def on_loaded(): + logging.info("Screen refresh plugin loaded") + +def on_ui_update(ui): + global update_count + update_count += 1 + if update_count == OPTIONS['refresh_interval']: + ui._init_display() + ui.set('status', "Screen cleaned") + logging.info("Screen refreshing") + update_count = 0 + From 0c81d25aec21bfe52d6a4fd996a2c11c4e7305bb Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 17:04:46 +0200 Subject: [PATCH 107/150] misc: small fix or general refactoring i did not bother commenting --- pwnagotchi/plugins/default/screen_refresh.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pwnagotchi/plugins/default/screen_refresh.py b/pwnagotchi/plugins/default/screen_refresh.py index 7268151..72c2a1c 100644 --- a/pwnagotchi/plugins/default/screen_refresh.py +++ b/pwnagotchi/plugins/default/screen_refresh.py @@ -6,22 +6,19 @@ __description__ = 'Refresh he e-ink display after X amount of updates' import logging -from pwnagotchi.ui.components import LabeledValue -from pwnagotchi.ui.view import BLACK -import pwnagotchi.ui.fonts as fonts - OPTIONS = dict() update_count = 0; + def on_loaded(): logging.info("Screen refresh plugin loaded") + def on_ui_update(ui): - global update_count + global update_count update_count += 1 if update_count == OPTIONS['refresh_interval']: ui._init_display() ui.set('status', "Screen cleaned") logging.info("Screen refreshing") update_count = 0 - From c75c0679b15c543b90aeae9ce4084a7585df0a4f Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 17:04:59 +0200 Subject: [PATCH 108/150] releasing v1.0.0RC1 --- pwnagotchi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/__init__.py b/pwnagotchi/__init__.py index fd8a5a0..4265d1c 100644 --- a/pwnagotchi/__init__.py +++ b/pwnagotchi/__init__.py @@ -4,7 +4,7 @@ import logging import time import pwnagotchi.ui.view as view -version = '1.0.0RC0' +version = '1.0.0RC1' _name = None From fe7da076d74ae2ac1ad6b4d9c05b73a7cee7544b Mon Sep 17 00:00:00 2001 From: Zenzen San <zenzenzen@riseup.net> Date: Sat, 5 Oct 2019 20:05:46 -0400 Subject: [PATCH 109/150] - Plugin geowifi saves wifi geolocation on hndshk Saves a json file with the access points with more signal whenever a handshake is captured. This data is usable to retrieve the geographic location using Google Geolocation API or Mozilla Location Service --- pwnagotchi/defaults.yml | 2 ++ pwnagotchi/plugins/default/geowifi.py | 30 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 pwnagotchi/plugins/default/geowifi.py diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 031d94e..bb6ff08 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -29,6 +29,8 @@ main: 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' + geowifi: + enabled: false gps: enabled: false speed: 19200 diff --git a/pwnagotchi/plugins/default/geowifi.py b/pwnagotchi/plugins/default/geowifi.py new file mode 100644 index 0000000..b7f7982 --- /dev/null +++ b/pwnagotchi/plugins/default/geowifi.py @@ -0,0 +1,30 @@ +__author__ = 'zenzen san' +__version__ = '1.0.0' +__name__ = 'geowifi' +__license__ = 'GPL3' +__description__ = 'Saves a json file with the access points with more signal whenever a handshake is captured. This data is usable to retrieve the geographic location using Google Geolocation API or Mozilla Location Service' + +import logging +import json + +def on_loaded(): + logging.info("geowifi plugin loaded. :)") + +def on_handshake(agent, filename, access_point, client_station): + info = agent.session() + aps = agent.get_access_points() + geowifi = _geowifi_location(aps) + geowifi_filename = filename.replace('.pcap', '.geowifi.json') + + logging.info("saving GEOWIFI location to %s" % (geowifi_filename)) + with open(geowifi_filename, 'w+t') as fp: + json.dump(geowifi, fp) + +def _geowifi_location(aps): + geowifi = {} + geowifi['wifiAccessPoints'] = [] + # size seems a good number to save a wifi networks location + for ap in sorted(aps,key=lambda i:i['rssi'],reverse=True)[:6]: + geowifi['wifiAccessPoints'].append({'macAddress': ap['mac'], 'signalStrength': ap['rssi']}) + return geowifi + From dfeaad36edda096ca6d342dc9be82c7618c19c27 Mon Sep 17 00:00:00 2001 From: Zenzen San <zenzenzen@riseup.net> Date: Mon, 7 Oct 2019 22:50:02 -0400 Subject: [PATCH 110/150] When internet is available it converts wifips files in geolocation --- pwnagotchi/plugins/default/geowifi.py | 69 ++++++++++++++++++++------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/pwnagotchi/plugins/default/geowifi.py b/pwnagotchi/plugins/default/geowifi.py index b7f7982..283fadc 100644 --- a/pwnagotchi/plugins/default/geowifi.py +++ b/pwnagotchi/plugins/default/geowifi.py @@ -1,30 +1,67 @@ __author__ = 'zenzen san' __version__ = '1.0.0' -__name__ = 'geowifi' +__name__ = 'wifips' __license__ = 'GPL3' -__description__ = 'Saves a json file with the access points with more signal whenever a handshake is captured. This data is usable to retrieve the geographic location using Google Geolocation API or Mozilla Location Service' +__description__ = """Saves a json file with the access points with more signal + whenever a handshake is captured. + When internet is available the files are converted in geo locations + using Mozilla LocationService """ import logging import json +import os +from urllib.request import urlopen +from datetime import datetime +from time import sleep + +URL_API_MOZILLA_LOCATION_SERVICE = 'https://location.services.mozilla.com/v1/geolocate?key=' +KEY_API_MOZILLA_LOCATION_SERVICE = 'test' def on_loaded(): - logging.info("geowifi plugin loaded. :)") + logging.info("wifips plugin loaded. :)") + +def on_internet_available(ui, keypair, config): + try: + for ff in os.listdir('/root/handshakes'): + geo_file = os.path.join('/root/handshakes', ff.replace('.wifips.json','.geo.json')) + if not os.path.isfile(geo_file): + if ff.endswith(".wifips.json"): + ff = os.path.join('/root/handshakes',ff) + with open(ff, 'r') as fp: + data = fp.read() + geo = _get_geolocation_moz_wifips(data) + with open(geo_file, 'w+t') as fp: + fp.write(geo.decode('ascii')) + logging.info("wifips plugin: saving coordinates for: {}".format(ff.replace('.wifips.json',''))) + sleep(.500) + except Exception as e: + logging.exception('WIFIPS PLUGIN ERROR') + +def on_ready(agent): + pass def on_handshake(agent, filename, access_point, client_station): + wifips = _get_wifips(agent) + wifips_filename = filename.replace('.pcap', '.wifips.json') + logging.info("wifips plugin: saving location to %s" % (wifips_filename)) + with open(wifips_filename, 'w+t') as fp: + json.dump(wifips, fp) + +def _get_wifips(agent): info = agent.session() aps = agent.get_access_points() - geowifi = _geowifi_location(aps) - geowifi_filename = filename.replace('.pcap', '.geowifi.json') + wifips = {} + wifips['wifiAccessPoints'] = [] + # 6 seems a good number to save a wifi networks location + for ap in sorted(aps,key=lambda i:i['rssi'],reverse=True)[:6]: + wifips['wifiAccessPoints'].append({'macAddress': ap['mac'], 'signalStrength': ap['rssi']}) + return wifips - logging.info("saving GEOWIFI location to %s" % (geowifi_filename)) - with open(geowifi_filename, 'w+t') as fp: - json.dump(geowifi, fp) - -def _geowifi_location(aps): - geowifi = {} - geowifi['wifiAccessPoints'] = [] - # size seems a good number to save a wifi networks location - for ap in sorted(aps,key=lambda i:i['rssi'],reverse=True)[:6]: - geowifi['wifiAccessPoints'].append({'macAddress': ap['mac'], 'signalStrength': ap['rssi']}) - return geowifi +def _get_geolocation_moz_wifips(post_data): + geourl = URL_API_MOZILLA_LOCATION_SERVICE+KEY_API_MOZILLA_LOCATION_SERVICE + try: + response = urlopen(geourl, post_data.encode('ascii')).read() + return response + except Exception as e: + logging.exception('WIFIPS PLUGIN - Something went wrong with Mozilla Location Service') From 6a87c681fa7b35d767794b44d4c134bfff5fa19f Mon Sep 17 00:00:00 2001 From: Zenzen San <zenzenzen@riseup.net> Date: Mon, 7 Oct 2019 22:54:09 -0400 Subject: [PATCH 111/150] plugin renamed wifips --- pwnagotchi/defaults.yml | 2 +- pwnagotchi/plugins/default/{geowifi.py => wifips.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename pwnagotchi/plugins/default/{geowifi.py => wifips.py} (100%) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index bb6ff08..2bf27d6 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -29,7 +29,7 @@ main: 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' - geowifi: + wifips: enabled: false gps: enabled: false diff --git a/pwnagotchi/plugins/default/geowifi.py b/pwnagotchi/plugins/default/wifips.py similarity index 100% rename from pwnagotchi/plugins/default/geowifi.py rename to pwnagotchi/plugins/default/wifips.py From bd8f2936c58efdba421f0867f01c599bb947064e Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 18:52:36 +0200 Subject: [PATCH 112/150] fix: grid plugin was not enrolling partially opted-in units --- pwnagotchi/plugins/default/grid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/grid.py b/pwnagotchi/plugins/default/grid.py index 368ebcd..066b794 100644 --- a/pwnagotchi/plugins/default/grid.py +++ b/pwnagotchi/plugins/default/grid.py @@ -135,10 +135,10 @@ def on_internet_available(agent): num_reported = len(reported) num_new = num_networks - num_reported + token = get_api_token(agent.last_session, agent.keypair()) if num_new > 0: if OPTIONS['report']: logging.info("grid: %d new networks to report" % num_new) - token = get_api_token(agent.last_session, agent.keypair()) for pcap_file in pcap_files: net_id = os.path.basename(pcap_file).replace('.pcap', '') From d222935aea9f35d67fee669302905fc726368db6 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 19:21:54 +0200 Subject: [PATCH 113/150] new: defaults are now copied into /etc/pwnagotchi/defaults.yml if it doesn't exist or different from release defaults --- bin/pwnagotchi | 3 +-- pwnagotchi/utils.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/bin/pwnagotchi b/bin/pwnagotchi index 9cd77d8..3b69026 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -15,8 +15,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument('-C', '--config', action='store', dest='config', - default=os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.yml'), + parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.yml', help='Main configuration file.') parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.yml', help='If this file exists, configuration will be merged and this will override default values.') diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py index bde9fd9..4afcc69 100644 --- a/pwnagotchi/utils.py +++ b/pwnagotchi/utils.py @@ -7,6 +7,9 @@ import time import subprocess import yaml import json +import shutil + +import pwnagotchi # https://stackoverflow.com/questions/823196/yaml-merge-in-python @@ -21,6 +24,28 @@ def merge_config(user, default): def load_config(args): + default_config_path = os.path.dirname(args.config) + if not os.path.exists(default_config_path): + os.makedirs(default_config_path) + + ref_defaults_file = os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.yml') + ref_defaults_data = None + + if not os.path.exists(args.config): + # logging not configured here yet + print("copying %s to %s ..." % (ref_defaults_file, args.config)) + shutil.copy(ref_defaults_file, args.config) + else: + with open(ref_defaults_file) as fp: + ref_defaults_data = fp.read() + + with open(args.config) as fp: + defaults_data = fp.read() + + if ref_defaults_data != defaults_data: + print("!!! file in %s is different than release defaults, overwriting !!!" % args.config) + shutil.copy(ref_defaults_file, args.config) + with open(args.config) as fp: config = yaml.safe_load(fp) From 223bce8abccd6f67ee60fde7ec5662a446ded30f Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 19:29:22 +0200 Subject: [PATCH 114/150] fix: fixed paths in scripts/backup.sh --- scripts/backup.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/backup.sh b/scripts/backup.sh index 8ebf274..213ed94 100755 --- a/scripts/backup.sh +++ b/scripts/backup.sh @@ -10,9 +10,8 @@ TEMP_BACKUP_FOLDER=/tmp/pwnagotchi_backup FILES_TO_BACKUP=( /root/brain.nn /root/brain.json - /root/custom.yaml /root/handshakes - /etc/ssh + /etc/pwnagotchi/ /etc/hostname /etc/hosts /etc/motd From d86e3cbc272bf69754022a731feaa1184c17cb28 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 19:30:18 +0200 Subject: [PATCH 115/150] misc: removed legacy update script --- scripts/update_pwnagotchi.sh | 121 ----------------------------------- 1 file changed, 121 deletions(-) delete mode 100755 scripts/update_pwnagotchi.sh diff --git a/scripts/update_pwnagotchi.sh b/scripts/update_pwnagotchi.sh deleted file mode 100755 index 9299f1a..0000000 --- a/scripts/update_pwnagotchi.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/bash -# Default variables -GIT_FOLDER="/tmp/pwnagotchi" -GIT_URL="https://github.com/evilsocket/pwnagotchi/" -VERSION="master" -SUPPORTED_RESTART_MODES=( 'auto' 'manual' ) -MODE="auto" -BACKUPCONFIG=0 -RESTORECONFIG=0 - -# Functions -function usage() { - cat <<EOF - - usage: $0 [OPTIONS] - Note: This should be run from the pwnagotchi itself! - - Options: - -v # Version to update to, can be a branch or commit. (default: master) - -u # Url to clone from. (default: https://github.com/evilsocket/pwnagotchi) - -m # Mode to restart to. (Supported: ${SUPPORTED_RESTART_MODES[*]}; default: auto) - -b # Backup the current pwnagotchi config and hostname references, then overwrite with defaults. - -r # Restore the current pwnagotchi config and hostname references after upgrade. (-b will be enabled.) - -h # Shows this help. - -EOF - exit 0 -} - -function test_root() { - if ! [ $(id -u) = 0 ]; then - echo "[!] This script must be run as root." - exit 1 - fi -} - -function test_github() { - wget -q --spider $GIT_URL - if [ $? -ne 0 ]; then - echo "[!] Cannot reach github. This script requires internet access, ensure connection sharing is working." - exit 2 - fi -} - -while getopts "v:u:m:brh" o; do - case "${o}" in - v) - VERSION="${OPTARG}" - ;; - u) - GIT_URL="${OPTARG}" - ;; - m) - if [[ "${SUPPORTED_RESTART_MODES[*]}" =~ ${OPTARG} ]]; then - MODE="${OPTARG}" - else - usage - fi - ;; - b) - BACKUPCONFIG=1 - ;; - r) - BACKUPCONFIG=1 - RESTORECONFIG=1 - ;; - h) - usage - ;; - *) - usage - ;; - esac -done -shift $((OPTIND-1)) - -echo "[+] Checking prerequisites." -test_root -test_github - -# clean up old files, clone master, set checkout to commit if needed. -echo "[+] Cloning to $GIT_FOLDER..." -rm $GIT_FOLDER -rf -git clone $GIT_URL $GIT_FOLDER -q -cd $GIT_FOLDER -if [ $VERSION != "master" ]; then - git checkout $VERSION -q -fi - -if [ $BACKUPCONFIG -eq 1 ]; then - echo "[+] Creating backup of config.yml and hostname references" - 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 - mv /etc/network/interfaces /root/interfaces.bak -f -fi - -echo "[+] Installing $(git log -1 --format="%h")" -rm /root/pwnagotchi -rf # ensures old files are removed -rsync -aPq $GIT_FOLDER/sdcard/boot/* /boot/ -rsync -aPq $GIT_FOLDER/sdcard/rootfs/* / -cd /tmp -rm $GIT_FOLDER -rf - -if [ $RESTORECONFIG -eq 1 ]; then - echo "[+] Restoring backup of config.yml and hostname references" - mv /root/config.yml.bak /root/pwnagotchi/config.yml -f - mv /root/hosts.bak /etc/hosts -f - mv /root/hostname.bak /etc/hostname -f - mv /root/interfaces.bak /etc/network/interfaces -f - mv /etc/motd.bak /etc/motd -f -fi - -echo "[+] Restarting pwnagotchi in $MODE mode. $( screen -X -S pwnagotchi quit)" -if [ $MODE == "auto" ]; then - sudo -H -u root /usr/bin/screen -dmS pwnagotchi -c /root/pwnagotchi/data/screenrc.auto -elif [ $MODE == "manual" ]; then - sudo -H -u root /usr/bin/screen -dmS pwnagotchi -c /root/pwnagotchi/data/screenrc.manual -fi -echo "[+] Finished" From dfb484624a9d825252aa0e087f8e9b6a1df43689 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Tue, 8 Oct 2019 20:10:21 +0200 Subject: [PATCH 116/150] Some improvements --- pwnagotchi/plugins/default/net-pos.py | 144 ++++++++++++++++++++++++++ pwnagotchi/plugins/default/wifips.py | 67 ------------ 2 files changed, 144 insertions(+), 67 deletions(-) create mode 100644 pwnagotchi/plugins/default/net-pos.py delete mode 100644 pwnagotchi/plugins/default/wifips.py diff --git a/pwnagotchi/plugins/default/net-pos.py b/pwnagotchi/plugins/default/net-pos.py new file mode 100644 index 0000000..277077d --- /dev/null +++ b/pwnagotchi/plugins/default/net-pos.py @@ -0,0 +1,144 @@ +__author__ = 'zenzen san' +__version__ = '1.0.0' +__name__ = 'net-pos' +__license__ = 'GPL3' +__description__ = """Saves a json file with the access points with more signal + whenever a handshake is captured. + When internet is available the files are converted in geo locations + using Mozilla LocationService """ + +import logging +import json +import os +import requests + +MOZILLA_API_URL = 'https://location.services.mozilla.com/v1/geolocate?key={api}' +ALREADY_SAVED = None +SKIP = None +READY = False +OPTIONS = {} + + +def on_loaded(): + global ALREADY_SAVED + global SKIP + global READY + + SKIP = list() + + if 'api_key' not in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None): + logging.error("NET-POS: api_key isn't set. Can't use mozilla's api.") + return + + try: + with open('/root/.net_pos_saved', 'r') as f: + ALREADY_SAVED = f.read().splitlines() + except OSError: + logging.warning('NET-POS: No net-pos-file found.') + ALREADY_SAVED = [] + + READY = True + logging.info("net-pos plugin loaded.") + +def _append_saved(path): + to_save = list() + if isinstance(path, str): + to_save.append(path) + elif isinstance(path, list): + to_save += path + else: + raise TypeError("Expected list or str, got %s" % type(path)) + + with open('/root/.net_pos_saved', 'a') as saved_file: + for x in to_save: + saved_file.write(x + "\n") + +def on_internet_available(agent): + global SKIP + + if READY: + config = agent.config() + display = agent.view() + handshake_dir = config['bettercap']['handshakes'] + + all_files = os.listdir(handshake_dir) + all_np_files = [os.path.join(handshake_dir, filename) + for filename in all_files + if filename.endswith('.net-pos.json')] + new_np_files = set(all_np_files) - set(ALREADY_SAVED) - set(SKIP) + + if new_np_files: + logging.info("NET-POS: Found {num} new net-pos files. Fetching positions ...", len(new_np_files)) + display.set('status', f"Found {len(new_np_files)} new net-pos files. Fetching positions ...") + display.update(force=True) + for idx, np_file in enumerate(new_np_files): + + geo_file = np_file.replace('.net-pos.json', '.geo.json') + if os.path.exists(geo_file): + # got already the position + ALREADY_SAVED.append(np_file) + _append_saved(np_file) + continue + + try: + geo_data = _get_geo_data(np_file) # returns json obj + except requests.exceptions.RequestException as req_e: + logging.error("NET-POS: %s", req_e) + SKIP += np_file + except json.JSONDecodeError as js_e: + logging.error("NET-POS: %s", js_e) + SKIP += np_file + except OSError as os_e: + logging.error("NET-POS: %s", os_e) + SKIP += np_file + + with open(geo_file, 'w+t') as sf: + json.dump(geo_data, sf) + + ALREADY_SAVED.append(np_file) + _append_saved(np_file) + + display.set('status', f"Fetching positions ({idx+1}/{len(new_np_files)}") + display.update(force=True) + + +def on_handshake(agent, filename, access_point, client_station): + netpos = _get_netpos(agent) + netpos_filename = filename.replace('.pcap', '.net-pos.json') + logging.info("NET-POS: Saving net-location to %s", netpos_filename) + + try: + with open(netpos_filename, 'w+t') as fp: + json.dump(netpos, fp) + except OSError as os_e: + logging.error("NET-POS: %s", os_e) + + +def _get_netpos(agent): + aps = agent.get_access_points() + netpos = {} + netpos['wifiAccessPoints'] = list() + # 6 seems a good number to save a wifi networks location + for access_point in sorted(aps, key=lambda i: i['rssi'], reverse=True)[:6]: + netpos['wifiAccessPoints'].append({'macAddress': access_point['mac'], + 'signalStrength': access_point['rssi']}) + return netpos + +def _get_geo_data(path, timeout=30): + geourl = MOZILLA_API_URL.format(api=OPTIONS['api_key']) + + try: + with open(path, "r") as json_file: + data = json.load(json_file) + except json.JSONDecodeError as js_e: + raise js_e + except OSError as os_e: + raise os_e + + try: + result = requests.post(geourl, + json=data, + timeout=timeout) + return result.json() + except requests.exceptions.RequestException as req_e: + raise req_e diff --git a/pwnagotchi/plugins/default/wifips.py b/pwnagotchi/plugins/default/wifips.py deleted file mode 100644 index 283fadc..0000000 --- a/pwnagotchi/plugins/default/wifips.py +++ /dev/null @@ -1,67 +0,0 @@ -__author__ = 'zenzen san' -__version__ = '1.0.0' -__name__ = 'wifips' -__license__ = 'GPL3' -__description__ = """Saves a json file with the access points with more signal - whenever a handshake is captured. - When internet is available the files are converted in geo locations - using Mozilla LocationService """ - -import logging -import json -import os -from urllib.request import urlopen -from datetime import datetime -from time import sleep - -URL_API_MOZILLA_LOCATION_SERVICE = 'https://location.services.mozilla.com/v1/geolocate?key=' -KEY_API_MOZILLA_LOCATION_SERVICE = 'test' - -def on_loaded(): - logging.info("wifips plugin loaded. :)") - -def on_internet_available(ui, keypair, config): - try: - for ff in os.listdir('/root/handshakes'): - geo_file = os.path.join('/root/handshakes', ff.replace('.wifips.json','.geo.json')) - if not os.path.isfile(geo_file): - if ff.endswith(".wifips.json"): - ff = os.path.join('/root/handshakes',ff) - with open(ff, 'r') as fp: - data = fp.read() - geo = _get_geolocation_moz_wifips(data) - with open(geo_file, 'w+t') as fp: - fp.write(geo.decode('ascii')) - logging.info("wifips plugin: saving coordinates for: {}".format(ff.replace('.wifips.json',''))) - sleep(.500) - except Exception as e: - logging.exception('WIFIPS PLUGIN ERROR') - -def on_ready(agent): - pass - -def on_handshake(agent, filename, access_point, client_station): - wifips = _get_wifips(agent) - wifips_filename = filename.replace('.pcap', '.wifips.json') - logging.info("wifips plugin: saving location to %s" % (wifips_filename)) - with open(wifips_filename, 'w+t') as fp: - json.dump(wifips, fp) - -def _get_wifips(agent): - info = agent.session() - aps = agent.get_access_points() - wifips = {} - wifips['wifiAccessPoints'] = [] - # 6 seems a good number to save a wifi networks location - for ap in sorted(aps,key=lambda i:i['rssi'],reverse=True)[:6]: - wifips['wifiAccessPoints'].append({'macAddress': ap['mac'], 'signalStrength': ap['rssi']}) - return wifips - -def _get_geolocation_moz_wifips(post_data): - geourl = URL_API_MOZILLA_LOCATION_SERVICE+KEY_API_MOZILLA_LOCATION_SERVICE - try: - response = urlopen(geourl, post_data.encode('ascii')).read() - return response - except Exception as e: - logging.exception('WIFIPS PLUGIN - Something went wrong with Mozilla Location Service') - From 65600f95e50305552e4994f59bde1b46cf0b70ca Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Tue, 8 Oct 2019 20:28:00 +0200 Subject: [PATCH 117/150] Add gps validy check --- pwnagotchi/plugins/default/wigle.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py index c72c1de..b3f3329 100644 --- a/pwnagotchi/plugins/default/wigle.py +++ b/pwnagotchi/plugins/default/wigle.py @@ -241,6 +241,12 @@ def on_internet_available(agent): SKIP.append(gps_file) continue + if gps_data['Latitude'] == 0 and gps_data['Longitude'] == 0: + logging.warning("WIGLE: Not enough gps-informations for %s. Trying again next time.", gps_file) + SKIP.append(gps_file) + continue + + try: pcap_data = extract_from_pcap(pcap_filename, [WifiInfo.BSSID, WifiInfo.ESSID, From 4916b3bf7c0fb5274516c800148b62efa3a874da Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Tue, 8 Oct 2019 19:36:17 +0100 Subject: [PATCH 118/150] fix builder to set image version and hostname on build --- .travis.yml | 2 +- Makefile | 2 +- builder/pwnagotchi.json | 6 ++---- builder/pwnagotchi.yml | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 35069f9..c5fe50b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ before_script: script: - sudo make clean - sudo -E env "PATH=$PATH" make install - - sudo make image -e PWN_HOSTNAME=pwnagotchi VERSION=$TRAVIS_TAG + - sudo make image -e PWN_HOSTNAME=pwnagotchi PWN_VERSION=$TRAVIS_TAG notifications: slack: secure: aovN87lswg+TTLobxJpevC0p2F4omTAlsOzeKqLysRW55o5rRhRC1SgwRkWUl19yr49nsyffwmv/b7OcyQiWIVnz1bxxE9XOKP8zgRMA/bKKcyAcPktPqHXsALIQDseXyl0kz7fwdkRWg0UC2HpKqi5koAhmBYTX/fbzieyeHCbcQ7lbFfVFIepE1401y9m1IqUHcHuGfFhMvTaSDIpXrDXnWdA8+gDAl0HKJv41MIsgmffbh/QhD2jLBWzItjxFC3llmNfy88pnzCk0+HBMY/4272LXb0czX7et5HJeM74oxPqkb3aKXFxZgNaDl7cYdV+kzj9dfKUk47hAqwbxlirit5WvHI1Br1VyA90+PFvcC/p41J8gCv0IlcB5vjWN8NKWA1J+Y1F+KvrujMvGtgd0foHZvaSutuRODhI1cBh5rYAiLCroRSlvKMw3IJRyCRstYgUlMIJ3cI2Ova/kU44KtDVmjT9VE/pPkhkHBPvcYThL6skZTdl19E/RlormLu3XObG1aHLZ+Znxe/aL7tWHi0KMOlpy+TMDdps4go7URnJ8yitHtIvU/zMtBrztIwN0Oy2JLKXrS5qIijmRAkBLxe0NxuG01DYFzEO3KtnRirP4uSe3QcrjyP4sqPrVhrjl3TR6gwg8V1juvDXB4e2h8yCpaUW5AdSBOlx9riY= diff --git a/Makefile b/Makefile index 0d898dd..1344670 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ install: cp /tmp/packer-builder-arm-image/packer-builder-arm-image /usr/bin image: - cd builder && sudo /usr/bin/packer build pwnagotchi.json + cd builder && sudo /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json mv builder/output-pwnagotchi/image pwnagotchi-raspbian-lite-$(PWN_VERSION).img zip pwnagotchi-raspbian-lite-$(PWN_VERSION).zip pwnagotchi-raspbian-lite-$(PWN_VERSION).img diff --git a/builder/pwnagotchi.json b/builder/pwnagotchi.json index b979f50..d7ec0ca 100644 --- a/builder/pwnagotchi.json +++ b/builder/pwnagotchi.json @@ -1,7 +1,4 @@ { - "variables": { - "home": "{{env `HOME`}}" - }, "builders": [{ "name": "pwnagotchi", "type": "arm-image", @@ -22,7 +19,8 @@ }, { "type":"ansible-local", - "playbook_file": "pwnagotchi.yml" + "playbook_file": "pwnagotchi.yml", + "command": "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION={{user `pwn_version`}} PWN_HOSTNAME={{user `pwn_hostname`}} ansible-playbook" }, { "type": "shell", diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 3bb6203..855ac1c 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -5,7 +5,7 @@ vars: pwnagotchi: hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}" - version: "{{ lookup('env', 'PWN_VERSION') | default('master', true) }} " + version: "{{ lookup('env', 'PWN_VERSION') | default('master', true) }}" system: boot_options: - "dtoverlay=dwc2" From ca3ece7f2b1b904b49e2abe8050653efa171f501 Mon Sep 17 00:00:00 2001 From: Zenzen San <56201767+zenzen666@users.noreply.github.com> Date: Tue, 8 Oct 2019 19:01:44 +0000 Subject: [PATCH 119/150] changed plugin name from wifips to net-pos --- pwnagotchi/defaults.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 2bf27d6..67ab8e5 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -29,7 +29,7 @@ main: 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' - wifips: + net-pos: enabled: false gps: enabled: false From 46df27f860b96c9e97c6e3ba1dcd6144ea16593f Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Tue, 8 Oct 2019 21:32:31 +0200 Subject: [PATCH 120/150] Add OPTIONS --- pwnagotchi/plugins/default/gps.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pwnagotchi/plugins/default/gps.py b/pwnagotchi/plugins/default/gps.py index c49b926..234df3a 100644 --- a/pwnagotchi/plugins/default/gps.py +++ b/pwnagotchi/plugins/default/gps.py @@ -13,14 +13,14 @@ OPTIONS = dict() def on_loaded(): - logging.info("gps plugin loaded for %s" % device) + logging.info("gps plugin loaded for %s" % OPTIONS['device']) def on_ready(agent): global running - if os.path.exists(device): - logging.info("enabling gps bettercap's module for %s" % device) + if os.path.exists(OPTIONS['device']): + logging.info("enabling gps bettercap's module for %s" % OPTIONS['device']) try: agent.run('gps off') except: From 41f50c3436e5b652a94cdc1d0da19da62c9d033e Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Tue, 8 Oct 2019 21:41:34 +0200 Subject: [PATCH 121/150] Also ship locale with package --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f8e22b3..dab1f93 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup(name='pwnagotchi', license='GPL', install_requires=required, scripts=['bin/pwnagotchi'], - package_data={'pwnagotchi': ['defaults.yml', 'pwnagotchi/defaults.yml']}, + package_data={'pwnagotchi': ['defaults.yml', 'pwnagotchi/defaults.yml', 'locale/*/LC_MESSAGES/*.mo']}, include_package_data=True, packages=find_packages(), classifiers=[ From 31b4095fa5e507b89e3a98fb3dd084fb4cde8688 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Tue, 8 Oct 2019 22:28:57 +0200 Subject: [PATCH 122/150] Fix path --- scripts/language.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/language.sh b/scripts/language.sh index 247911f..08bd97b 100755 --- a/scripts/language.sh +++ b/scripts/language.sh @@ -6,8 +6,8 @@ DEPENDENCIES=( 'xgettext' 'msgfmt' 'msgmerge' ) COMMANDS=( 'add' 'update' 'delete' 'compile' ) REPO_DIR="$(dirname "$(dirname "$(realpath "$0")")")" -LOCALE_DIR="${REPO_DIR}/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale" -VOICE_FILE="${REPO_DIR}/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py" +LOCALE_DIR="${REPO_DIR}/pwnagotchi/locale" +VOICE_FILE="${REPO_DIR}/pwnagotchi/voice.py" function usage() { cat <<EOF From 5a3ddd9133444f4bf31b66782784a2301bab0808 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Tue, 8 Oct 2019 23:28:37 +0200 Subject: [PATCH 123/150] misc: small fix or general refactoring i did not bother commenting --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 363d7ec..7dc839b 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ full and half WPA handshakes. Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning based "AI" *(yawn)*, Pwnagotchi tunes [its own parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.yml#L73) over time to **get better at pwning WiFi things** in the environments you expose it to. -More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) doc.) +More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://www.pwnagotchi.ai/usage/#training-the-ai) doc.) -**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi actually learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://github.com/evilsocket/pwnagotchi/blob/master/docs/usage.md#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but **definitely listen to your Pwnagotchi when it tells you it's bored!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) +**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi actually learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://www.pwnagotchi.ai/usage/#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but **definitely listen to your Pwnagotchi when it tells you it's bored!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) Multiple units within close physical proximity can "talk" to each other, advertising their own presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard. Over time, two or more units trained together will learn to cooperate upon detecting each other's presence by dividing the available channels among them for optimal pwnage. From b5d92c86c8da431c012ef59a075d4d84aae8ba72 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Tue, 8 Oct 2019 22:31:27 +0100 Subject: [PATCH 124/150] enable bettercap ui on manual mode --- builder/pwnagotchi.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index bdb5761..7ab12d2 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -182,6 +182,9 @@ chdir: /tmp/caplets target: install + - name: install bettercap http ui + command: "/usr/bin/bettercap -eval 'caplets.update; ui.update; q'" + - name: create cpuusage script copy: dest: /usr/bin/cpuusage @@ -239,6 +242,26 @@ /usr/local/bin/pwnagotchi fi + - name: create bettercap-launcher script + copy: + dest: /usr/bin/bettercap-launcher + mode: 0755 + content: | + #!/usr/bin/env bash + # blink 10 times to signal ready state + /usr/bin/bootblink 10 & + if ifconfig | grep usb0 | grep RUNNING; then + # if override file exists, go into auto mode + if [ -f /root/.pwnagotchi-auto ]; then + rm /root/.pwnagotchi-auto + /usr/bin/bettercap -no-colors -caplet pwnagotchi-auto + else + /usr/bin/bettercap -no-colors -caplet pwnagotchi-manual + fi + else + /usr/bin/bettercap -no-colors -caplet pwnagotchi-auto + fi + - name: create monstart script copy: dest: /usr/bin/monstart From 0294eeff5f2b0487aa5be31f9f11071d3e9be708 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Tue, 8 Oct 2019 22:31:27 +0100 Subject: [PATCH 125/150] enable bettercap ui on manual mode --- builder/pwnagotchi.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index f6d1128..003db66 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -182,6 +182,9 @@ chdir: /tmp/caplets target: install + - name: install bettercap http ui + command: "/usr/bin/bettercap -eval 'caplets.update; ui.update; q'" + - name: create cpuusage script copy: dest: /usr/bin/cpuusage @@ -239,6 +242,26 @@ /usr/local/bin/pwnagotchi fi + - name: create bettercap-launcher script + copy: + dest: /usr/bin/bettercap-launcher + mode: 0755 + content: | + #!/usr/bin/env bash + # blink 10 times to signal ready state + /usr/bin/bootblink 10 & + if ifconfig | grep usb0 | grep RUNNING; then + # if override file exists, go into auto mode + if [ -f /root/.pwnagotchi-auto ]; then + rm /root/.pwnagotchi-auto + /usr/bin/bettercap -no-colors -caplet pwnagotchi-auto + else + /usr/bin/bettercap -no-colors -caplet pwnagotchi-manual + fi + else + /usr/bin/bettercap -no-colors -caplet pwnagotchi-auto + fi + - name: create monstart script copy: dest: /usr/bin/monstart From 7be10e76b98c3dc0d9d3a39574dc90ab49644d1b Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Tue, 8 Oct 2019 22:32:51 +0100 Subject: [PATCH 126/150] enable bettercap ui on manual mode --- builder/pwnagotchi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 003db66..9adee94 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -402,7 +402,7 @@ [Service] Type=simple PermissionsStartOnly=true - ExecStart=/usr/bin/bettercap -no-colors -caplet pwnagotchi + ExecStart=/usr/local/bin/bettercap-launcher Restart=always RestartSec=30 From e1e2fe73b7dfab87d0297db595a5c26d597017a8 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Wed, 9 Oct 2019 00:33:19 +0100 Subject: [PATCH 127/150] fix path and ui install --- builder/pwnagotchi.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 9adee94..cc28d3e 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -28,6 +28,7 @@ packages: bettercap: url: "https://github.com/bettercap/bettercap/releases/download/v2.25/bettercap_linux_armv6l_2.25.zip" + ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip" apt: remove: - rasberrypi-net-mods @@ -182,8 +183,12 @@ chdir: /tmp/caplets target: install - - name: install bettercap http ui - command: "/usr/bin/bettercap -eval 'caplets.update; ui.update; q'" + - name: download and install bettercap ui + unarchive: + src: "{{ packages.bettercap.ui }}" + dest: /usr/local/share/bettercap/ + remote_src: yes + mode: 0755 - name: create cpuusage script copy: @@ -402,7 +407,7 @@ [Service] Type=simple PermissionsStartOnly=true - ExecStart=/usr/local/bin/bettercap-launcher + ExecStart=/usr/bin/bettercap-launcher Restart=always RestartSec=30 From 837a234da8ff162c1ec8e98ce9ebe98c139fe4b2 Mon Sep 17 00:00:00 2001 From: Zenzen San <zenzenzen@riseup.net> Date: Tue, 8 Oct 2019 19:35:24 -0400 Subject: [PATCH 128/150] added default key --- pwnagotchi/defaults.yml | 1 + pwnagotchi/plugins/default/wifips.py | 67 ---------------------------- 2 files changed, 1 insertion(+), 67 deletions(-) delete mode 100644 pwnagotchi/plugins/default/wifips.py diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 67ab8e5..dbb7b6b 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -31,6 +31,7 @@ main: - 'scp /tmp/backup.tar.gz pwnagotchi@10.0.0.1:/home/pwnagotchi/backups/backup-$(date +%s).tar.gz' net-pos: enabled: false + api_key: 'test' gps: enabled: false speed: 19200 diff --git a/pwnagotchi/plugins/default/wifips.py b/pwnagotchi/plugins/default/wifips.py deleted file mode 100644 index 283fadc..0000000 --- a/pwnagotchi/plugins/default/wifips.py +++ /dev/null @@ -1,67 +0,0 @@ -__author__ = 'zenzen san' -__version__ = '1.0.0' -__name__ = 'wifips' -__license__ = 'GPL3' -__description__ = """Saves a json file with the access points with more signal - whenever a handshake is captured. - When internet is available the files are converted in geo locations - using Mozilla LocationService """ - -import logging -import json -import os -from urllib.request import urlopen -from datetime import datetime -from time import sleep - -URL_API_MOZILLA_LOCATION_SERVICE = 'https://location.services.mozilla.com/v1/geolocate?key=' -KEY_API_MOZILLA_LOCATION_SERVICE = 'test' - -def on_loaded(): - logging.info("wifips plugin loaded. :)") - -def on_internet_available(ui, keypair, config): - try: - for ff in os.listdir('/root/handshakes'): - geo_file = os.path.join('/root/handshakes', ff.replace('.wifips.json','.geo.json')) - if not os.path.isfile(geo_file): - if ff.endswith(".wifips.json"): - ff = os.path.join('/root/handshakes',ff) - with open(ff, 'r') as fp: - data = fp.read() - geo = _get_geolocation_moz_wifips(data) - with open(geo_file, 'w+t') as fp: - fp.write(geo.decode('ascii')) - logging.info("wifips plugin: saving coordinates for: {}".format(ff.replace('.wifips.json',''))) - sleep(.500) - except Exception as e: - logging.exception('WIFIPS PLUGIN ERROR') - -def on_ready(agent): - pass - -def on_handshake(agent, filename, access_point, client_station): - wifips = _get_wifips(agent) - wifips_filename = filename.replace('.pcap', '.wifips.json') - logging.info("wifips plugin: saving location to %s" % (wifips_filename)) - with open(wifips_filename, 'w+t') as fp: - json.dump(wifips, fp) - -def _get_wifips(agent): - info = agent.session() - aps = agent.get_access_points() - wifips = {} - wifips['wifiAccessPoints'] = [] - # 6 seems a good number to save a wifi networks location - for ap in sorted(aps,key=lambda i:i['rssi'],reverse=True)[:6]: - wifips['wifiAccessPoints'].append({'macAddress': ap['mac'], 'signalStrength': ap['rssi']}) - return wifips - -def _get_geolocation_moz_wifips(post_data): - geourl = URL_API_MOZILLA_LOCATION_SERVICE+KEY_API_MOZILLA_LOCATION_SERVICE - try: - response = urlopen(geourl, post_data.encode('ascii')).read() - return response - except Exception as e: - logging.exception('WIFIPS PLUGIN - Something went wrong with Mozilla Location Service') - From 56f680e7754d508daabf024e63eb639c756ef5f4 Mon Sep 17 00:00:00 2001 From: Zenzen San <zenzenzen@riseup.net> Date: Tue, 8 Oct 2019 20:46:15 -0400 Subject: [PATCH 129/150] fixed typo in display message --- pwnagotchi/plugins/default/net-pos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/net-pos.py b/pwnagotchi/plugins/default/net-pos.py index 277077d..27f04d9 100644 --- a/pwnagotchi/plugins/default/net-pos.py +++ b/pwnagotchi/plugins/default/net-pos.py @@ -98,7 +98,7 @@ def on_internet_available(agent): ALREADY_SAVED.append(np_file) _append_saved(np_file) - display.set('status', f"Fetching positions ({idx+1}/{len(new_np_files)}") + display.set('status', f"Fetching positions ({idx+1}/{len(new_np_files)})") display.update(force=True) From f7b869035759bd7549d927146d9994989d079321 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Wed, 9 Oct 2019 09:47:00 +0100 Subject: [PATCH 130/150] move monstart and monstop to systemd --- builder/pwnagotchi.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index cc28d3e..7f29ded 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -407,7 +407,9 @@ [Service] Type=simple PermissionsStartOnly=true + ExecStartPre=/usr/bin/monstart ExecStart=/usr/bin/bettercap-launcher + ExecStopPost=/usr/bin/monstop Restart=always RestartSec=30 From 8af56040349092520b2905a4c0517b4e43e6f962 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Wed, 9 Oct 2019 11:59:11 +0200 Subject: [PATCH 131/150] fix: fixed a LastSession bug which prevented the grid plugin from working when no previous session was available (fixes #228) --- pwnagotchi/log.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/pwnagotchi/log.py b/pwnagotchi/log.py index 0f05789..fa3ddc1 100644 --- a/pwnagotchi/log.py +++ b/pwnagotchi/log.py @@ -22,6 +22,29 @@ class LastSession(object): HANDSHAKE_TOKEN = '!!! captured new handshake ' PEER_TOKEN = 'detected unit ' + def __init__(self, config): + self.config = config + self.voice = Voice(lang=config['main']['lang']) + self.path = config['main']['log'] + self.last_session = [] + self.last_session_id = '' + self.last_saved_session_id = '' + self.duration = '' + self.duration_human = '' + self.deauthed = 0 + self.associated = 0 + self.handshakes = 0 + self.peers = 0 + self.last_peer = None + self.epochs = 0 + self.train_epochs = 0 + self.min_reward = 1000 + self.max_reward = -1000 + self.avg_reward = 0 + self._peer_parser = re.compile( + 'detected unit (.+)@(.+) \(v.+\) on channel \d+ \(([\d\-]+) dBm\) \[sid:(.+) pwnd_tot:(\d+) uptime:(\d+)\]') + self.parsed = False + def _get_last_saved_session_id(self): saved = '' try: @@ -158,26 +181,5 @@ class LastSession(object): self._parse_stats() self.parsed = True - 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 = '' - self.duration = '' - self.duration_human = '' - self.deauthed = 0 - self.associated = 0 - self.handshakes = 0 - self.peers = 0 - self.last_peer = None - self._peer_parser = re.compile( - 'detected unit (.+)@(.+) \(v.+\) on channel \d+ \(([\d\-]+) dBm\) \[sid:(.+) pwnd_tot:(\d+) uptime:(\d+)\]') - self.last_session = [] - self.last_session_id = None - self.last_saved_session_id = None - self.parsed = False - def is_new(self): return self.last_session_id != self.last_saved_session_id From fac4b5c4602494f1d120d4faa05f8a49ec85bc47 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Wed, 9 Oct 2019 12:01:44 +0200 Subject: [PATCH 132/150] fix: don't allow plugins to make the main process crash --- pwnagotchi/plugins/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pwnagotchi/plugins/__init__.py b/pwnagotchi/plugins/__init__.py index 38d29e7..6dd7b64 100644 --- a/pwnagotchi/plugins/__init__.py +++ b/pwnagotchi/plugins/__init__.py @@ -1,6 +1,7 @@ import os import glob import importlib, importlib.util +import logging default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default") loaded = {} @@ -13,10 +14,13 @@ def dummy_callback(): def on(event_name, *args, **kwargs): global loaded cb_name = 'on_%s' % event_name - for _, plugin in loaded.items(): + for plugin_name, plugin in loaded.items(): if cb_name in plugin.__dict__: # print("calling %s %s(%s)" %(cb_name, args, kwargs)) - plugin.__dict__[cb_name](*args, **kwargs) + try: + plugin.__dict__[cb_name](*args, **kwargs) + except Exception as e: + logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e)) def load_from_file(filename): From d4d8c39205202f0864af38f22200b1ad3a5bdfc7 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Wed, 9 Oct 2019 12:04:28 +0200 Subject: [PATCH 133/150] fix: creating handshakes folder if it doesn't exist on startup (fixes #227) --- pwnagotchi/agent.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pwnagotchi/agent.py b/pwnagotchi/agent.py index b29e164..6430ae6 100644 --- a/pwnagotchi/agent.py +++ b/pwnagotchi/agent.py @@ -38,6 +38,9 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer): self._handshakes = {} self.last_session = LastSession(self._config) + if not os.path.exists(config['bettercap']['handshakes']): + os.makedirs(config['bettercap']['handshakes']) + @staticmethod def is_connected(): try: From 1ab183831371e8a406e0208f368add6314caa207 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Wed, 9 Oct 2019 13:54:09 +0200 Subject: [PATCH 134/150] fix: fixed auto-update plugin to use pip3 based update process from pypi.org --- pwnagotchi/defaults.yml | 1 + pwnagotchi/plugins/default/auto-update.py | 28 ++++++++++++----------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index dbb7b6b..5f00d42 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -13,6 +13,7 @@ main: - YourHomeNetworkHere auto-update: enabled: false + system: false # set to true to also enable system updates via apt interval: 1 # every day auto-backup: enabled: false diff --git a/pwnagotchi/plugins/default/auto-update.py b/pwnagotchi/plugins/default/auto-update.py index 8a01a45..43ea6ff 100644 --- a/pwnagotchi/plugins/default/auto-update.py +++ b/pwnagotchi/plugins/default/auto-update.py @@ -17,12 +17,17 @@ 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.") + logging.error("auto-update: Interval is not set.") return READY = True +def run(cmd): + return subprocess.Popen(cmd, shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None, + executable="/bin/bash") + + def on_internet_available(agent): global STATUS @@ -36,23 +41,20 @@ def on_internet_available(agent): display.set('status', 'Updating ...') display.update() - logging.info("AUTO-UPDATE: updating packages index ...") + logging.info("auto-update: updating pwnagotchi ...") + run('pip3 install --upgrade --upgrade-strategy only-if-needed pwnagotchi').wait() - update = subprocess.Popen('apt update -y', shell=True, stdin=None, - stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") - update.wait() + if OPTIONS['system']: + logging.info("auto-update: updating packages index ...") + run('apt update -y').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.") + logging.info("auto-update: updating packages ...") + run('apt upgrade -y').wait() + logging.info("auto-update: complete.") STATUS.update() except Exception as e: - logging.exception("AUTO-UPDATE ERROR") + logging.exception("auto-update ERROR") display.set('status', 'Updated!') display.update() From a93348db64d7c0299afb3dadcf610a32c8ada6e8 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Wed, 9 Oct 2019 13:58:30 +0200 Subject: [PATCH 135/150] fix: installing missing smbus dependency (fixes #234) --- builder/pwnagotchi.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 7f29ded..c824e88 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -44,6 +44,7 @@ - build-essential - python3-pip - python3-mpi4py + - python3-smbus - unzip - gawk - libopenmpi-dev From 319b2bc31f750cacb2ac1c1033598318cfe7e341 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Wed, 9 Oct 2019 14:46:41 +0200 Subject: [PATCH 136/150] fix: fixed text overlap for waveshare displays (ref #229) --- pwnagotchi/ui/view.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index 354d910..b743d06 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -39,7 +39,7 @@ def setup_display_specifics(config): width = 200 height = 96 face_pos = (0, int(height / 4)) - name_pos = (int(width / 2) - 15, int(height * .15)) + name_pos = (5, int(height * .15)) status_pos = (int(width / 2) - 15, int(height * .30)) elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1', @@ -49,8 +49,8 @@ def setup_display_specifics(config): width = 250 height = 122 face_pos = (0, 40) - name_pos = (125, 20) - status_pos = (125, 35) + name_pos = (5, 20) + status_pos = (125, 20) return width, height, face_pos, name_pos, status_pos From 3c2a3485c4992b59bf52918ed909fc6c00f0fa51 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Wed, 9 Oct 2019 14:48:37 +0200 Subject: [PATCH 137/150] fix: attempt of a fix for inky and papirus displays (ref #229) --- pwnagotchi/ui/view.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index b743d06..423992f 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -30,8 +30,8 @@ def setup_display_specifics(config): width = 212 height = 104 face_pos = (0, int(height / 4)) - name_pos = (int(width / 2) - 15, int(height * .15)) - status_pos = (int(width / 2) - 15, int(height * .30)) + name_pos = (5, int(height * .15)) + status_pos = (int(width / 2) - 15, int(height * .15)) elif config['ui']['display']['type'] in ('papirus', 'papi'): fonts.setup(10, 8, 10, 23) @@ -40,7 +40,7 @@ def setup_display_specifics(config): height = 96 face_pos = (0, int(height / 4)) name_pos = (5, int(height * .15)) - status_pos = (int(width / 2) - 15, int(height * .30)) + status_pos = (int(width / 2) - 15, int(height * .15)) elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1', 'ws_2', 'ws2', 'waveshare_2', 'waveshare2'): From 1b7a5e1f7c9b14480b857ba20324536625b3587d Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Wed, 9 Oct 2019 15:19:57 +0200 Subject: [PATCH 138/150] releasing v1.0.0RC2 --- pwnagotchi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/__init__.py b/pwnagotchi/__init__.py index 4265d1c..a1b6a32 100644 --- a/pwnagotchi/__init__.py +++ b/pwnagotchi/__init__.py @@ -4,7 +4,7 @@ import logging import time import pwnagotchi.ui.view as view -version = '1.0.0RC1' +version = '1.0.0RC2' _name = None From a2fa33f2fb601118f92a90a3846d5ff6bf8e4ed1 Mon Sep 17 00:00:00 2001 From: evilsocket <evilsocket@users.noreply.github.com> Date: Wed, 9 Oct 2019 16:03:12 +0200 Subject: [PATCH 139/150] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..084515c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHubSponsors-enabled usernames e.g., [user1, user2] +patreon: evilsocket +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From d2c160308c5218905569aa9a97a77d5eefe20e08 Mon Sep 17 00:00:00 2001 From: bitwave <github@oomlu.de> Date: Wed, 9 Oct 2019 17:46:53 +0200 Subject: [PATCH 140/150] updated german translation --- pwnagotchi/locale/de/LC_MESSAGES/voice.mo | Bin 4126 -> 4204 bytes pwnagotchi/locale/de/LC_MESSAGES/voice.po | 10 ++++++++-- pwnagotchi/locale/voice.pot | 8 +++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pwnagotchi/locale/de/LC_MESSAGES/voice.mo b/pwnagotchi/locale/de/LC_MESSAGES/voice.mo index fd5058bf9014298b2e0eb5961ac5631aaafd9374..8944e859db04407d31442b9cb1e369c182ff987f 100644 GIT binary patch delta 1234 zcmY+^Pe@cz6vy#nJ*)YnQ}$<?YU)mtrcI-kNrL@x5o#`?fr$PthAFZv4Vfs6n5|K5 z8rp=ku+?mW5DE%v69c&kYGWHgB8jL~Q3OGKe>2aZ%bfRlcjkHTJ?A`p;J&!RY=d{t z7@foh;#G}VA6{I{g)#1#1@I=W#v-<03D@8|4B==0_aC^NdAQcB3FD}B4`3H&(cyhu zX;!f3+-Tu9Sce}mir;)gOUza=PvTPSLT%84&oF~c80N<Y>_8o?7xj0iaV?Ib*1d&6 ze25YDw@2LYu@_ttSVleg33YN?Ry}bXQ%v`v4tfE}z^<X5yNwP@r~^!69OqE${Xsn+ zrJU<9g+cbWUT&zGWss!pD3X+&LLDsU&xic^C@Rx2tj9apP1`0=nayy~MjtSZKT!v0 zq)%$qUM?{_hy|50$IVvE<7S+|J@^t;+TW-Qli}(@J*bu)LoGDyJC53T5+nEowQ(8W z;w);P$CQs-dtXQWwZJ@IsANh<y^5m#upL)mKWf7hNRoCLwa^%<$CK#rsqYMSF#m;W zRhrZ2xo%V<hj9yDico)Te3vgAmax)0REg(M2mFpIZ9U~uYhtL@IDg*k&)a-=Bki{R zsCD|0kDcT~rz}Y*0qt+w2uD!m|AsQqXsc4WYB9Yd?Zif+m1rb1^ipXoA`*mNJ`H8A zq1J37QiPIG1q$keN~&yB8kJcu<9b3v*{ULHi(0XhXd$)}3!{^pU4%-lwlx!aZx)9B zmFT_N;eVmZs2XXVgBDdrteEwN0wZVK@cDCt`PQk|Ud9U^A92^O4rcQ=5>v6j*P2k@ YmHb(ED4Wv*wV|>X9QgM@aXND3FC<iF_5c6? delta 1157 zcmY+^Pe@cz6vy#1^~T9`(k9Juvb<)~AIGUC3j@PVR0vW;5VRUMMiRI&aAOSBB3cEB zEeZz0ts9vNjDj{2*v?$FsA*YQFsnom`~Kd$L0&lT^X}!&{d3OKRabVUFR5V0XnjNn z@iJz135U1ypxp}0>TwKPaRQIwBixG>KfZ*`^s9InYpC_oakH~{1|5#$9(;_fSF%}N zk_;?h8-DP9#TNR1FoBJA(FQH}hJF^cQ592|V3H1$LH(|PX}pM9w}?A&5}WWoCfMJe z@Y2P=Gt|VlsFSat!y4u=wIe#{AnFFgsCgsk@Gj~AW!#UiQ0sj{&HsrR+(eRN8PXbO zf9vFhOIQ}!%X)qPwC@j~Zg?Jd;dLB{nT?`u^n!;rn8Q52M{WEY+p(@8%ACU^^b2?p zZ(!*hFH^iIl?$jFRZ$CUplaUQ7%i0ZUO;VFM5S^9HU9xF<8#zT<J6T`dx|>9EGk3q zP^DQ(l7Id17X!O7&h@lm8*(|zqZaB%)wGBX?|5ghkN#_93HyPXw}!e%lG7c+9@NHH z(BVzrf7nF+mD(}`I^jI36dzHgSoZzTzW>ep9Vw&LP`_VCef>!uR3=QXGRir^zm`s< z8e3XHJ=H(|-*A`(-`7XfOXvn_I-uG%!V$Wmno6OjgB&1w2xYIEpn~B+b*X)XvJ-xM zs5W+#N0@Td4Nekjxrp$o6rUngOQlgI$rAsX{#A4lC;WgiM?&m4p|Yr@r|$)~D{H~! TpmMc-HC7pFSPZ7yo3H%^hE`aI diff --git a/pwnagotchi/locale/de/LC_MESSAGES/voice.po b/pwnagotchi/locale/de/LC_MESSAGES/voice.po index fa9afdf..8b96f33 100644 --- a/pwnagotchi/locale/de/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/de/LC_MESSAGES/voice.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-10-05 14:10+0200\n" +"POT-Creation-Date: 2019-10-09 17:42+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" @@ -121,6 +121,12 @@ msgstr "" msgid "ZzzZzzz ({secs}s)" msgstr "" +msgid "Good night." +msgstr "Gute Nacht." + +msgid "Zzz" +msgstr "" + #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Warte für {secs}s ..." @@ -139,7 +145,7 @@ msgstr "Verbinde mit {what}" #, python-brace-format msgid "Yo {what}!" -msgstr "" +msgstr "Jo {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" diff --git a/pwnagotchi/locale/voice.pot b/pwnagotchi/locale/voice.pot index b6489ba..cf02362 100644 --- a/pwnagotchi/locale/voice.pot +++ b/pwnagotchi/locale/voice.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-10-05 14:10+0200\n" +"POT-Creation-Date: 2019-10-09 17:42+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" @@ -122,6 +122,12 @@ msgstr "" msgid "ZzzZzzz ({secs}s)" msgstr "" +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "" From 63d95a53c01997f0b3bbcce0bbba2035f8b5babd Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Wed, 9 Oct 2019 16:57:00 +0100 Subject: [PATCH 141/150] add papirus display system requirements --- builder/pwnagotchi.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml index 7f29ded..c57e38c 100644 --- a/builder/pwnagotchi.yml +++ b/builder/pwnagotchi.yml @@ -11,11 +11,14 @@ - "dtoverlay=dwc2" - "dtparam=spi=on" - "dtoverlay=spi1-3cs" + - "dtoverlay=i2c_arm=on" + - "dtoverlay=i2c1=on" services: enable: - dphys-swapfile.service - pwnagotchi.service - bettercap.service + - epd-fuse.service disable: - apt-daily.timer - apt-daily.service @@ -78,6 +81,10 @@ - fonts-dejavu-core - fonts-dejavu-extra - python3-pil + - python3-smbus + - libfuse-dev + - bc + - fonts-freefont-ttf tasks: @@ -134,6 +141,33 @@ path: /etc/dphys-swapfile content: "CONF_SWAPSIZE=1024" + - name: clone papirus repository + git: + repo: https://github.com/repaper/gratis.git + dest: /usr/local/src/gratis + + - name: build papirus service + make: + chdir: /usr/local/src/gratis + target: rpi + params: + EPD_IO: epd_io.h + PANEL_VERSION: 'V231_G2' + + - name: install papirus service + make: + chdir: /usr/local/src/gratis + target: rpi-install + params: + EPD_IO: epd_io.h + PANEL_VERSION: 'V231_G2' + + - name: configure papirus display size + lineinfile: + dest: /etc/default/epd-fuse + regexp: "#EPD_SIZE=2.0" + line: "EPD_SIZE=2.0" + - name: acquire python3 pip target command: "python3 -c 'import sys;print(sys.path.pop())'" register: pip_target From 2c1a9c471cbf7d8158a7fe7c18c0f802fae04e60 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Wed, 9 Oct 2019 17:50:53 +0100 Subject: [PATCH 142/150] generate sha256sum from the generated image, add it to the release --- .travis.yml | 4 +++- Makefile | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5fe50b..2395be5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,9 @@ deploy: secure: vBUokTv94n8s65STUgTiD6I0Iy8KXbBRvQUrAof8XG+U4ZMsH5PmDTpS+wz+SaxI6o0PRkfyOiPVdARhiKAFnfatG3q9EHllMQwqRR2YIju51A3aCxgEJ5uWDoybwQdipERUMMYwUO/8XZaRRpwFD2bdQBFWkBtQyMcAkrEL8BXckwQQ531oDN2hK5gAiTllqsOswV2idwUlBRU9jOtStzff+UgUYsp/ZebsRodyOYkEB2Ev15yARo2HTXbyZ2icwHPtMbx5zmNUSRtxs9a4hfzaK3m6ctK8qLYYUdQvXub/ruuACapdw4Ez88LY1agTecbZhFYmJzv8oANH1e4VUI4owuHnZCpU6LRutS4wOhglrkOrGo6lSUlJeA+RtQjyjBugjej9DDtDyyIlRU1ZaBF3qWR9N5EXKuquf0olOfmUR67ap1NykE9VUpzkYjkoVRTiPs/e2onM/nRNOvAQcIt75FD13u+Y/DcYQ8r7KpMIu1HNdtbVx8gMeq76bRhP1YdDg2jm+DdJ21KWjf5QHsbyoXDfJzdKlCloLIlAU3EPJhMoXsnNzre0/FXeUl6dfteR1axNS6U7e/vKsQ9rlUFZWIQaeVPjfXmFKblNNVQ5uFrrsB/EGHcJl7IUx5fvcRT5hMMNwC660YxVkBXDbRb5fxMW5/+K0BOi9cP6en8= skip_cleanup: true file_glob: true - file: pwnagotchi-*.zip + file: + - pwnagotchi-*.zip + - pwnagotchi-*.sha on: tags: true repo: evilsocket/pwnagotchi diff --git a/Makefile b/Makefile index 1344670..103fa62 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,8 @@ install: image: cd builder && sudo /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json mv builder/output-pwnagotchi/image pwnagotchi-raspbian-lite-$(PWN_VERSION).img - zip pwnagotchi-raspbian-lite-$(PWN_VERSION).zip pwnagotchi-raspbian-lite-$(PWN_VERSION).img + sha256sum pwnagotchi-raspbian-lite-$(PWN_VERSION).img > pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256 + zip pwnagotchi-raspbian-lite-$(PWN_VERSION).zip pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256 pwnagotchi-raspbian-lite-$(PWN_VERSION).img clean: rm -rf /tmp/packer-builder-arm-image From b2a7462b44dd743648bd184be37897eb7bdad206 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <caquino@users.noreply.github.com> Date: Wed, 9 Oct 2019 18:55:22 +0100 Subject: [PATCH 143/150] Fix typo add sha256 to extension --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2395be5..933c5ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ deploy: file_glob: true file: - pwnagotchi-*.zip - - pwnagotchi-*.sha + - pwnagotchi-*.sha256 on: tags: true repo: evilsocket/pwnagotchi From 315bfd29e50686bbbf42d8d621df81e02fddeaab Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Wed, 9 Oct 2019 19:11:14 +0100 Subject: [PATCH 144/150] add pt-BR translation --- pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo | Bin 0 -> 4290 bytes pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po | 209 +++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo create mode 100644 pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po diff --git a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo new file mode 100644 index 0000000000000000000000000000000000000000..88f2298633744b2b139064d1222dd04701755fa2 GIT binary patch literal 4290 zcmbuBON<;x8OO_Dz$}n}d4>e2!6CNs&NwE3?M+BB@owxT`@&w^vO$5G?%J8kc6Uws zv76b|3W-CILX-mvLcjr`2uwIcyaEyk<*-6XAn}lpkT`&pLk=L$91w`#SKTvfTW~<M zrsvmHQ(g7Fs{Zq*gZsavxK484!TtMvO3lNkU(FZSclRsxdiXu~cKB0x6#fpr1OB7w zKlmD@-c0)lybGR>&$5DYyeQ{1JRB`~#Hv{tDj&_q|rBcfwm69)@qD{Q!I&T!k|K zr{GUu3h#nP`0*fIhGI_-%6lXDF8B<Td9T4K_!7Jo{u1(0f8?tLUxqSn|AE@>JD{e0 z8XkjJq1gE(6#Jir;@`KShChe0{wwf3@SjlTyPe?4_<P{H;aMnl^x#`y3U7g*g@i&q z0|}{mwrPI_;;Q;O6nnqbv|ni2FGBJEXHe$(Ej+zXsXs$m=e9T0alRAYOM9kiZ$OF9 z(@^aACj21$F}xkV3?*;>f)eMGZ>;@$6w3GtN*q5AB@SPP;@=M&{s!_<f8k5w@^|<W zyoDg1fWL+^j~Fd>9Bg<D$~?1B;`eFDM{V*YaeNXITJ=?U7(NGO{I8(o@fE1y0gSs7 z9))7(5?@k3k3#X!!}r54Kw19<_yPC}DD(ZT=|8|r;$I6&Jnx5M_dH}N^+_msdkjk6 zhE2PIlDBOrdHY<$r=ZyXMJRFk8e}Q;eMs8EC4S1>^4_7)Q^!o^mpSj@79ZZnEtl9Y zm)Iwl_|@T-I%L{#k;bs!+VH*HALSN5$Jg=Z;Yh<*!4GpEZQ3Ur9)ovt%N`TIB^F}8 z_#*k1>o#tw$q#W$?54S8@5n`2hB!;DHAhUyo8(gJOfJcr<X&@sgnN8R+*Eg7=hnod zj_MYLkDMu8*4L%i+nWPZ?x-`~C)0Y<>b@`ac2=c3I<uR4V6wOvn0327Osd=@J1sS9 zO;rwTR=TL3WIHv{jygMeZ}LttrOugXT{Bv*Bqp<EOP#Yx!lIc;?M_QCxyXtUskNmZ z`Kr_W!mv?enNDnZxX?YT*K%jEM&=kz>xgACo9ON4VMn`yA=b9^6<_7LyP`K;lIX&W zFu-K%MMs@;(|Z1Js#i8MgP+mBwe*G3<RKQFPDjn_HOriZ*Cz3q)pg%+GZP<jyXJGN z=e0>0$uvEhaXp5XeWJ{5YLn4T{R6Whp=QpsiUxYE%39&=#D#tdPU6;V-C3A8{J3HB z5vGr4->?M0W*SRmF6_|TG5IK+&vrC9((5i-$C&N7&Q83eql9pkMGKF4-6UcFQQkI9 z{w-r9#Z6elET*)2doO=Gd--RcL#*C}Ej%Z_UlZ%~p#ho3mtn}WK2)ra$hZ1<Rg^lm zk&7{oG_bx@3k%!0(3#hl-NUY>9>Mw^`4s2EfVxW@3Z+<-!Bq-sAyh-(hZq>WX_EDp zTJU~dUNyO|h{ciZ!bZhTaa1ju7>6d++_*ZW774@&J+w9tkM#&?7HZKIg^gQkaipW% zrB=!+81C0}ROQYWt{jD=FPY(Rl8t)Bx}TuNh7t`MsLIOviRe+gOTOph5q6UrseIx^ z%asF?6W(4P7Sj|9sT#=So8(Y$=AK82QJ=Rx?<eJYVc<%PVKF^(TwNf+CL6JAx#{!u zqSb1tRjMnqvUA7X;!krDtPXe%?n!w`qL>Ib(@|5A7YAurweNl08%ZQ(+p3G1lQTk| z<@L;(yf<o%f4yv6FfbJ9bz!+IJBXcZrM4vq546<ff%n5Atg$y=pPkoHSH?$EOI_XC z;<I&KUZr_ezQPYOU~5Ya*!JP8P^rtR(l#g*R`@J#P^cT13Q=#m(kA&Uxqr+?WqUqu zU!o}3WixuEd*+eu**Se_?)-)M<)x|fcIfl6y;$^J+<vI)7wuI)qo-Du+vn|ulXu#) z?57!hWv+X^y|~<-?do&OGmG=bt}ZXlO)Z$BY_H}fD-u)sd`6#PV>-i@=ssRKa9`AP zn9zPOwVlYP_w!=buT0;zSFK5B^g?&(>_xHbzJ-Mc6W8&1e{al;ekeCvE@581g0)q@ zvW1;moL`*VbL>Rt?x{0AE2*^h>S&0?C1=@jD$`~6$~l`Cw)|LCuC-6?jT6JyY~G&B zA|F$zGx}7|l~bZKL#nkj6QRjsuQ7ZqGz+$GP@2paIx@qO(oA`*ci`mY8LJJ$HvG6D z($hwJYUz<_Yvoz7c;#-Mq7ff{@QwJGmRe4BztPlbORo?bn`@h5UN*4N?swXWpD55A zGX`nY8^OYYp(b^G&nBKjM{vqyY^y!}VFxig$9j$iU9go%kjsrW{hVVcvUx`>BRF#v z+q)90CdVmfbmq`x@ku0St|%E%?mp)ky5+J#-OULytvPi)TH_cp9!NlJ>`d&N7i%SA zd(y9O{*hxQc5FfRD66HFD?JW{1r3?ou&xYt7`@?QLe|)lv!HDn;U!&$uG-#8<2)u~ zVK@;9ED7IJC|m0jA-!3jEW$!WN|W|>pC4y}aW|+GMNl-s*H(&&0h3xO{(qI~My5xh zpcO%?T&^FlIPGesk}M2!B&;wp;=c%1kg!P=5)HF^ZTCe>6<!ME6PsLDsi9~%n+8TA zyPSv*{MGOr$T5<>r&|xZt)`x31B`v2;$NB+svB$9xOu&Pt=K$vF*#kN0&4}^eO{_H zh*^vUB4P{?X_R6+<M|(iYGVlpnr^f+{Ita4REwF`Jc;eQ*Xn0XYUGS`SCTjamg`>b zvIs{dLsY5E9^}j<BG@M3gqBWJ%-vA$vo3@-#dIS|_K6C4G@?^6^?{6a!{b=Vh%Os# ze9TawR5>2<-)n#a*o%<q5ay<a$2w+8LJ>Alecp?bg*Y_F|9IlvA3WhwGf}iI=Pj1~ zrwDb?6;=-hl|F1JhM{cBH|ZLVn4zJJY$eA)bGG~s+Ljx%EjNv}`C8k&Xj`BEJbuiH Qx?Q(mNJQh}a9ke$20@~S`Tzg` literal 0 HcmV?d00001 diff --git a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po new file mode 100644 index 0000000..858b617 --- /dev/null +++ b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po @@ -0,0 +1,209 @@ +# pwnagotchi Brazilian Portuguese translation file. +# Copyright (C) 2019 Cassiano Aquino +# This file is distributed under the same license as the pwnagotchi package. +# Cassiano Aquino <cassianoaquino@me.com>, 2019. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-10-05 14:10+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Cassiano Aquino <cassianoaquino@me.com>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: Brazilian Portuguese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Oi! Eu sou o Pwnagotchi! Iniciando ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Novo dia, Nova caça, Novos pwns!" + +msgid "Hack the Planet!" +msgstr "Hackei o Planeta!" + +msgid "AI ready." +msgstr "AI pronta." + +msgid "The neural network is ready." +msgstr "A rede neural está pronta." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Ei, o canal {channel} está livre! Seu AP ira agradecer." + +msgid "I'm bored ..." +msgstr "Estou entediado" + +msgid "Let's go for a walk!" +msgstr "Vamos dar uma caminhada!" + +msgid "This is the best day of my life!" +msgstr "Este e o melhor dia da minha vida!" + +msgid "Shitty day :/" +msgstr "Dia de merda :/" + +msgid "I'm extremely bored ..." +msgstr "Estou extremamente entediado" + +msgid "I'm very sad ..." +msgstr "Estou muito triste ..." + +msgid "I'm sad" +msgstr "Estou triste" + +msgid "I'm living the life!" +msgstr "Estou aproveitando a vida!" + +msgid "I pwn therefore I am." +msgstr "pwn, logo existo." + +msgid "So many networks!!!" +msgstr "Quantas redes!!!" + +msgid "I'm having so much fun!" +msgstr "Estou me divertindo muito!" + +msgid "My crime is that of curiosity ..." +msgstr "Meu crime é o crime da curiodidade ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you. {name}" +msgstr "Olá {name}! Prazer em conhecê-lo. {name}" + +#, python-brace-format +msgid "Unit {name} is nearby! {name}" +msgstr "Unidade {name} está próxima! {name}" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... até logo {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} desapareceu ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Oops ... {name} desapareceu." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} perdido!" + +msgid "Missed!" +msgstr "Perdido!" + +msgid "Nobody wants to play with me ..." +msgstr "Ninguém quer brincar comigo ..." + +msgid "I feel so alone ..." +msgstr "Estou tão sozinho ..." + +msgid "Where's everybody?!" +msgstr "Aonde está todo mundo?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Cochilando por {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Aguardando por {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Olhando ao redor ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Ei {what} vamos ser amigos!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Associando com {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Oi {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Acabei de decidir que {mac} não precisa de WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "De-autenticando {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kickbanning {mac}" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Legal, nos capturamos {num} handshake{plural} novo{plural}!" + +msgid "Ops, something went wrong ... Rebooting ..." +msgstr "Ops, algo falhou ... Reiniciando ..." + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Kickei {num} estações\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Fiz {num} novos amigos\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Peguei {num} handshakes\n" + +msgid "Met 1 peer" +msgstr "Conheci 1 peer" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Conheci {num} peers" + +#, 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 "" +"Eu estou pwning fazem {duration} e kickei {deauthed} clientes! Eu também conheci " +"{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "horas" + +msgid "minutes" +msgstr "minutos" + +msgid "seconds" +msgstr "segundos" + +msgid "hour" +msgstr "hora" + +msgid "minute" +msgstr "minuto" + +msgid "second" +msgstr "segundo" From 19b0e00bf525db407cdf5f8ae164e6129a2cf541 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Wed, 9 Oct 2019 19:13:27 +0100 Subject: [PATCH 145/150] add pt-BR translation --- pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo | Bin 4290 -> 4291 bytes pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo index 88f2298633744b2b139064d1222dd04701755fa2..78749bd2ba338c523a34b033a26fbe7181b2fb4a 100644 GIT binary patch delta 412 zcmXZYze@sP7{Kwz)I2pkQ&2Gn?@)uorOmCWrKNvB(50!R7I6@6a8S|BMX<4^hJr(A z6Krt^@_%q>5Srcc`{=>T^LgKwd!HY-w7#s5(21KCk=MA0gP(YT8?56$R<V92lEo&T zV;fBzU=gQSz`5@lS+XVQyB#jjOo&|J4{Dx3Qe4Ti$t2}q4K>jX=CFk<=@2s5M+0Yg zi7%+%f1`!JSi&gXmC-?M^p23iChCBXXye#)kA>g4sBp1DP56h}Sd>>fp^Y)TM(z9- tsU-~z;{%59$v?O8lyes&*z+Bs4lqIeGtUMaYKi9!gSHX4_uk^U@E%@XCw~9{ delta 410 zcmXZYKTASU7{~D+>di~my;hKt4PD8inwn~AYH8^W2)Eru7jba7(2$f{OQ2)4Nk~^{ z4031)>OD9#1kP>Y_t6W7=fgRi^ZYsE;5`^-Qqh{Er<`<%uUNtfp5j05;mL}$i6Pqk zCi>XHZG6Bj93_4uOTPqr_lIxjtxCuEg_cL2A{BAn)7&GpMCVw*D`crf*uWcfv4{Kk zh<5)I1N^}qbm?vv56~LDB;+wf8?cUL?E2B7@G}P$4nEKlPSF~3d1Vs@xQu6LonIie pR7D4Cn88M}Z{iaBHfHfU@eXZ(d$gbFTLEh6CB99U-FT2II5X*qCg}hG diff --git a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po index 858b617..bf27f67 100644 --- a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po @@ -27,7 +27,7 @@ msgid "New day, new hunt, new pwns!" msgstr "Novo dia, Nova caça, Novos pwns!" msgid "Hack the Planet!" -msgstr "Hackei o Planeta!" +msgstr "Hackeie o Planeta!" msgid "AI ready." msgstr "AI pronta." From ad87ea479135bddda7123ee7b1463db49b4adcaa Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Wed, 9 Oct 2019 19:14:42 +0100 Subject: [PATCH 146/150] add pt-BR translation --- pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo | Bin 4291 -> 4299 bytes pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo index 78749bd2ba338c523a34b033a26fbe7181b2fb4a..e462ab32eb3d09e07c5a12e7b1905521d4bec720 100644 GIT binary patch delta 367 zcmXZYKTASU7{~D+dGjCFs~15-4epI1aJZm2x;sVC(i;eLchMyq91MpB0|(n`bO;O@ zA{-oD8iaZm4ncF@qX!<I&v_m==RD`E!gcuCJQ(C9ja(_fCoJKo^#=+4k_xDdw1gto z@Ed(hdD0P<tqnBiTwoD9Na&7a?t8!_K6@JIoW-1m*|5P7w-}(Gl}@pagsw?BY-1UF zc!G~;26)Fa{6cfypY8i>s!&%kjV&~TU*#_Mlikobq|rqO`?kKtIQ0+{IJQpEykLgr cXI_xI^s%^)c_}A|qG&R8k9Uj3o11n10q#d6lmGw# delta 357 zcmXZYF>3-r5QgC~YV_p1Qv@R#At#C;Ht7SY(xkaRAjIBA8?g{9EL60!5wuJn;jj}@ z*a!;oUswpKEqw<UmU(sucK6#ocsp<X_AqcoWNeE(;{x-zF`kei7fKed_=|2*<Q4xg zjUh)QhZ$oX^_&lU!X`4LN6Fv_<G6HWAXO&cG<3reJv`$JTBIvu4K=7wN#Pe3u#F|0 zp$7QD3hq(Q`!{`yP5QtRCh#3K_-D$$Cu`7%(r94>J7(R*N7j9e;m|lmeZd^{XI98v R5(Ja5W6hU#G1%H}<ObZGBKH6Q diff --git a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po index bf27f67..8088ba7 100644 --- a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po @@ -40,7 +40,7 @@ msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Ei, o canal {channel} está livre! Seu AP ira agradecer." msgid "I'm bored ..." -msgstr "Estou entediado" +msgstr "Estou entediado ..." msgid "Let's go for a walk!" msgstr "Vamos dar uma caminhada!" @@ -52,7 +52,7 @@ msgid "Shitty day :/" msgstr "Dia de merda :/" msgid "I'm extremely bored ..." -msgstr "Estou extremamente entediado" +msgstr "Estou extremamente entediado ..." msgid "I'm very sad ..." msgstr "Estou muito triste ..." From da116ea2ad708157c0d855e264583e06acd159eb Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Wed, 9 Oct 2019 19:15:40 +0100 Subject: [PATCH 147/150] add pt-BR translation --- pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo | Bin 4299 -> 4288 bytes pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo index e462ab32eb3d09e07c5a12e7b1905521d4bec720..3905aad40a09d9513eddaa553959d56caa194684 100644 GIT binary patch delta 236 zcmXZVtqKA`6o%pB-zYAuYY|bw9Z{@afZZZ?!6t$RUD&~3(&#p;_po7MtHELrgEwLG z77V@%1H*av4xIU3`mU2({@4+b*cEBv6E%L&M^zK);}Cf=Wq7#2Hg2(p5jHW#0X{JM z?`!vpqpYWkRUBg-C!WlNE}63^;Tns$v)P~`AFzxk>lrKL3pVhIbV-s%g^}Z!Rp>AG M;U+La`gVW0Kbt@tC;$Ke delta 262 zcmXZVuWJHv7{~Ev{+cM<d0}Iy;d|m37!L7YFxVDWmXG_2enB~K2wJpQ#Hh_`Gzb?A z8tj$@qj6m<%X<$F9-bGU2cG9Sxo3BGOibTIWNV2O@q#YiP@!XsRI!1Se9{Wo!4i(J zf-8K%J-*^M{@?qJ=MGC{at(9XLI*zz@-F-m%^;0mn8I<KCzv76@DUfWOU#nj_>3Fm smmH#bvYvNC6%0(jt+cO#;lPC2XkWLL=Xuf$gRbc<@AgylV_hHq0C12cr~m)} diff --git a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po index 8088ba7..1e7f967 100644 --- a/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po @@ -73,7 +73,7 @@ msgid "I'm having so much fun!" msgstr "Estou me divertindo muito!" msgid "My crime is that of curiosity ..." -msgstr "Meu crime é o crime da curiodidade ..." +msgstr "Meu crime é ser curioso ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" From d0f34f952864cb5e4ebd4898b992e4fcf0ad0cf4 Mon Sep 17 00:00:00 2001 From: Cassiano Aquino <cassianoaquino@me.com> Date: Wed, 9 Oct 2019 20:41:36 +0100 Subject: [PATCH 148/150] add pt-BT to the lang comment on config.yml --- pwnagotchi/defaults.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 5f00d42..92aa9c6 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -1,6 +1,6 @@ # main algorithm configuration main: - # currently implemented: en (default), de, el, fr, it, mk, nl, ru, se + # currently implemented: en (default), de, el, fr, it, mk, nl, ru, se, pt-BR lang: en # custom plugins path, if null only default plugins with be loaded custom_plugins: From 078ab632496aa1746b227d106ad8bfe95138cd0e Mon Sep 17 00:00:00 2001 From: Simone Margaritelli <evilsocket@gmail.com> Date: Wed, 9 Oct 2019 23:14:02 +0200 Subject: [PATCH 149/150] new: grid plugin now reports brain.json info --- pwnagotchi/plugins/default/grid.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pwnagotchi/plugins/default/grid.py b/pwnagotchi/plugins/default/grid.py index 066b794..40cd1c6 100644 --- a/pwnagotchi/plugins/default/grid.py +++ b/pwnagotchi/plugins/default/grid.py @@ -8,6 +8,7 @@ import os import logging import requests import glob +import json import subprocess import pwnagotchi import pwnagotchi.utils as utils @@ -37,6 +38,13 @@ def get_api_token(last_session, keys): # sign the identity string to prove we own both keys _, signature_b64 = keys.sign(identity) + brain = {} + try: + with open('/root/brain.json') as fp: + brain = json.load(fp) + except: + pass + api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' enrollment = { 'identity': identity, @@ -53,7 +61,8 @@ def get_api_token(last_session, keys): 'associated': last_session.associated, 'handshakes': last_session.handshakes, 'peers': last_session.peers, - 'uname': subprocess.getoutput("uname -a") + 'uname': subprocess.getoutput("uname -a"), + 'brain': brain } } From d72c1d9c93bbedb280fe80462c45c64ee53d6ae8 Mon Sep 17 00:00:00 2001 From: diego <dpcalvo@gmail.com> Date: Wed, 9 Oct 2019 16:44:55 -0500 Subject: [PATCH 150/150] Add support for spanish language --- pwnagotchi/defaults.yml | 2 +- pwnagotchi/locale/es/LC_MESSAGES/voice.mo | Bin 0 -> 4379 bytes pwnagotchi/locale/es/LC_MESSAGES/voice.po | 214 ++++++++++++++++++++++ 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 pwnagotchi/locale/es/LC_MESSAGES/voice.mo create mode 100644 pwnagotchi/locale/es/LC_MESSAGES/voice.po diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 5f00d42..071aa37 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -1,6 +1,6 @@ # main algorithm configuration main: - # currently implemented: en (default), de, el, fr, it, mk, nl, ru, se + # currently implemented: en (default), de, el, fr, it, mk, nl, ru, se, es lang: en # custom plugins path, if null only default plugins with be loaded custom_plugins: diff --git a/pwnagotchi/locale/es/LC_MESSAGES/voice.mo b/pwnagotchi/locale/es/LC_MESSAGES/voice.mo new file mode 100644 index 0000000000000000000000000000000000000000..00ee652c7572c24767b28aa10f99be20823937c8 GIT binary patch literal 4379 zcmb`JTWnlM8Gxs>1>!=1atkelSwfSh@owxi(8Mim-NYm=c5KB?qV@%5&l&GdeD=)t zT<mPDeW8y$Auj4uAp#XukcTQlhzK5#5k)*y2qXjuK|x3qfe;d+5~v8if6iHN5}uGa z^6_`(_Rl~6W&Zv3UAKKf@f_oQKkx5vQECZ(<5vFhyn35byWk(;yW!v90l4dSrQQP{ zfOo(rpp2h`Z-)uo2ZvDPeia^t--8<d6TTDP^Tul3A^29>bMU=zrRF+(7wu=@o8Yrh z<bN4ngD=8;Fk$d6_&F$gJr8Bwcj0dM6DV^31SjCFZ&vDU@J>jV+5;PKAC!5gpy>T6 zsNoPEgwH|I^G8th|2dR-e})?F!dSBZ{qO^D7K*%2LYcn_?}nd&qQ~=4O#LE!2mB_y z6TSp7k@^|L<my*-`?ruIt3N=|`!99-wYvQ;D0a9V;UZ@@Y}}&Me#nrT$5}GI0>vL| zQ1tr}l=<I+_rM=OnfE*RVfc6WK6v+AlzIprfqP*e%6UHzW!=}Ita}NHJ%3sEzYb-e z-55{oH^o2k&m-{ra0O1vI7)mF&Ox!Og<`i)*X=LXyb8sCKZX)_uRvU&UW50-e?!sl zUMAfK=iwfh!TaH7q1gL7P~z!jDEs{eir%k7k-LXM@#7IF`ksMu&Q9Ha24Y(EEEGR} z1&Z9SK~zxRhT_K`Lh<9vb^E7K{P+tfetf0ot5DAIS15A+0Z~QWK?64iTf6}t=9T9l zujn;;4)7s%lzg7%72n8tL>JLRo))j{E&h~8^p_lJ@=A<|{YTG7_z-_y{~VzqI*Xp8 z2loX%r7dwKF)nr@Y=T|H7yEhTIm9b5CC^8B#b4qZdF0Gucgb1t)9BHo4~R*29*HSJ zEBJT{iVq*)6<dFZ_d#CqpPHT5nKkjCrDpToN6r*3?dZbmt<A0}hU&QY$)w)2y5kGI zm6pAsPVJ`dnl#S4X5DV}lQJ{Ou%S*^Qx;vD7A~rUZ1qeuR42WUb?Q3ZqQ&<}#?JbD zLY*?vx@M(bPE2ZxhB{@F1O-ylv%`iy?IJ4*^{g%Qz?ZFhglU6XOP$zaf3DkBuVv1n znaCJS>WFPpo9M0jW2jxu6l)v$Nnd7qc3E$_B+<DUpnyr&^Oid0CiVROo?hNe4QGzJ zuAx^7lLfo9S}irN*DNw~uTA1ptE;~LW-4dM?3&N4p4X<wOvJQl#?>5J_F|FQo=pZf z^>@vNm>rqDGV1EJGHry_i3|N=oW!l!sxvon7~HVg0M$p*H!KFQsYVl#g&uk<#y5rY z=}_Y@z3!rQl-Y_aPsKwWC77$o8=RQsCJ_a&@|LM%!7@kOU7IyXF@@DzWB(1uv4EVt z>|UEKd?&tB5$*NfE}rHr{orT4H(wv%Z}mi(7dp0)i%||Yu)jnO8{0V7sn_S-$6Z5x z0`1%QQw|p<R9$kQ5SDonj!Hl+glOpaU<0E!O|srl3*N8GQj_@-Timym+bAFA2h^g8 zInYjI*GHXDix^^B_pQysXEj5bxmt92ZsUep9Ox)>Ju6`q6!&X7Dl_MER}6yFPn&*! z#~am-RX;|J4IvtmsZ5LN3+oZPr+wSU1N6o<68YGRmMgkAC#+uT=aU2ruIh^LO?;>~ zGtVc%sL$B8_dDUb(sc#Ou$i7Yq*idSNe66OZ2D|HZ!{WemFP;XWbvq5&QsqCt6jds z@g%&&Q3&Fgy+Iw3XS=<yYll2<55$s^w(4x^B-iCzmZ#Qa?LlKSdfvF8U<lId%yM2b zh)lK;+hT-A8|r-5`+gqw7|ZLBc`fzi=$dM%r!HNRm%mHb`Bs`w$-gil6PVT|^~1kh z^;}vOwnnbd#HVqMT-~@`uzcOss{>#;^A~JXH0R^yIRb-Bo6%FPsn+zw8Qb?+(Ok?s zE^a<vcJk(`pV1S`OU*NO!^zC%2{LI$AD)^%)|{SdP94*Sr)Q=fJUGQOv0(C|xtf_Y zPfX#n867*@@qKzp>xbj&<M6S^Oj7nzpSMc$bgOsq5!R)h(sXQd)tcUnUYI?7^6c!% zIsNd$!efbR`K&XZGo$mqNnPHZSe##+8y{f0H8pYErv*{fTpje$vEbf1L=?L8A-Pkt z+!h}zi?!y_@jOv=&1TKHH1aV)IiruZT`{5NXNeP1Q|gP9mo(<y92J+7Hg$&b;@d^J z;d(wNK0<{l?S>y!mht^E>XBNLQ*5eQQ%elR=JC>|M(&KLYq`spZjw8`wW_LPjpin= zt|LRu@CmBbWyhuw`Daj<>w&QFk<AKXZb@Q1)JC0SV{|q>*YTt+Ba<SJb)?d`=8!ep zm$5=SqwcJ3Uz4&!eKVbmn+K`em#I2zEh#;?q3+D}`u0*VnG~5tt8=-oy)I13Tx3Z1 z@@8uNq$b=%DnkV=dl931HOIe!HtjOY94C?KRaaGrOiX^_=o@FS4=S@RrutC#D5zCk z@{M}+1_PlNdu_NF(^XQ7j9sx&)#{a0)zA|*J{81@LAm8@IF;ypU5y%`cE3#W?HB&L zMp=r>s_C`e_6up{j_oTqmMMvuku&r_uFA;wwy*quYt?LI+94uBy>gj;x+EN`a^<<@ zBFoip>WVcNP7_W_!Iz&Rqwtpn7_Ax~$312@5m$qyh@A_uB^D~F=_a>Wf6<X<7?AMd zSA~rk9M`c^F1k5|M&_8{XB+{HF{ZWSen@uORlbEjo2zA;#l(qhq_?l7F7j1<O9=># zuj|?t*S|SdvF~Nkr%a86CfbKEtV?U~`e|{Cj<>H6-YMS*F_(z>Q5%=lMbB`>VNOQ{ z&=<;%$;8c)T0zbdHDHp1)67lcwy#ph5*eY~IW%>ZCKBN%5H3mELsV>}>K!j7m8GV| z#1SQL!N_;0dR~$h)kBJgi;C!#P?c2f*$dcMw66r#(LSnqsJwkuF0D}V5L%{WgZv-~ zb+3|2hDRYo`nK}k*gU9uHAT~EV&O5RY|8Hw|EM|}enE_6<@~lSt(qJC<ozEjUS_Cx fnW-zDuPUCGir10dM%<}+)po)1^<mJyijDsOn8u%Y literal 0 HcmV?d00001 diff --git a/pwnagotchi/locale/es/LC_MESSAGES/voice.po b/pwnagotchi/locale/es/LC_MESSAGES/voice.po new file mode 100644 index 0000000..d9006da --- /dev/null +++ b/pwnagotchi/locale/es/LC_MESSAGES/voice.po @@ -0,0 +1,214 @@ +# pwnagotchi voice data +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR diegopastor <dpastor29@alumnos.uaq.mx>, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-10-09 17:42+0200\n" +"PO-Revision-Date: 2019-10-09 21:07+0000\n" +"Last-Translator: diegopastor <dpastor29@alumnos.uaq.mx>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: spanish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hola, soy Pwnagotchi! Empezando ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nuevo día, nueva cazería, nuevos pwns!" + +msgid "Hack the Planet!" +msgstr "Hackea el planeta!" + +msgid "AI ready." +msgstr "IA lista." + +msgid "The neural network is ready." +msgstr "La red neuronal está lista." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Oye, el canal {channel} está libre! Tú AP lo agradecerá." + +msgid "I'm bored ..." +msgstr "Estoy aburrido ..." + +msgid "Let's go for a walk!" +msgstr "Vamos por un paseo!" + +msgid "This is the best day of my life!" +msgstr "Este es el mejor día de mi vida!" + +msgid "Shitty day :/" +msgstr "Día de mierda :/" + +msgid "I'm extremely bored ..." +msgstr "Estoy extremadamente aburrido ..." + +msgid "I'm very sad ..." +msgstr "Estoy muy triste ..." + +msgid "I'm sad" +msgstr "Estoy triste." + +msgid "I'm living the life!" +msgstr "Estoy viviendo la vida!" + +msgid "I pwn therefore I am." +msgstr "Pwneo, por lo tanto, existo" + +msgid "So many networks!!!" +msgstr "Cuantas redes!!!" + +msgid "I'm having so much fun!" +msgstr "Me estoy divirtiendo mucho!" + +msgid "My crime is that of curiosity ..." +msgstr "Mi único crimen es la curiosidad ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you. {name}" +msgstr "Hola {name}! encantado de conocerte." + +#, python-brace-format +msgid "Unit {name} is nearby! {name}" +msgstr "La unidad {name} está cerca!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... adiós {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} se fue ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Uy ... {name} se fue" + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} perdido!" + +msgid "Missed!" +msgstr "Perdido!" + +msgid "Nobody wants to play with me ..." +msgstr "Nadie quiere jugar conmigo ..." + +msgid "I feel so alone ..." +msgstr "Me siento tan solo ..." + +msgid "Where's everybody?!" +msgstr "Dónde está todo el mundo?" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Tomándo una siesta por {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Buenas noches." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Esperando {secs}s .." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Mirando al rededor ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Oye {what} seamos amigos!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Asociando a {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Ey {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Acabo de decidir que {mac} no necesita WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Desautenticando a {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Expulsando y banneando a {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Genial, obtuvimos {num} nuevo{plural} handshake{plural}!" + +msgid "Ops, something went wrong ... Rebooting ..." +msgstr "Oops, algo salió mal ... Reiniciándo ..." + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Expulsamos {num} estaciones\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Hicimos {num} nuevos amigos\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Obtuvimos {num} handshakes\n" + +msgid "Met 1 peer" +msgstr "Conocí 1 igual" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Conocí {num} iguales" + +#, 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 "" +"He estado pwneando por {duration} y expulsé {deauthed} clientes! También conocí" +"{associated} nuevos amigos y me comí {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "horas" + +msgid "minutes" +msgstr "minutos" + +msgid "seconds" +msgstr "segundos" + +msgid "hour" +msgstr "hora" + +msgid "minute" +msgstr "minuto" + +msgid "second" +msgstr "segundo"