mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Compare commits
508 Commits
Author | SHA1 | Date | |
---|---|---|---|
c3df982870 | |||
6f7b07f21f | |||
4c583cf184 | |||
9a8616df51 | |||
28c2ef294c | |||
255ca31837 | |||
661687e180 | |||
fb8ee9dbee | |||
d98be99c6b | |||
08e9656bb5 | |||
dbcc488900 | |||
94b01b2fc7 | |||
9b4b239deb | |||
4fc029d638 | |||
9d0ada2a33 | |||
da156fde4b | |||
edaadfdbd4 | |||
0341ac0202 | |||
60832e788d | |||
87a51d1de7 | |||
db85c68e63 | |||
c98745cd1c | |||
d91fe8e17e | |||
8da625e7e2 | |||
6bab0b36d1 | |||
19de2a2d8f | |||
5d4c2e3b3a | |||
e7eba208bd | |||
b47371cfe6 | |||
56b70fcc83 | |||
d698c35fef | |||
1f5785f823 | |||
d78220b9ba | |||
faca5b2904 | |||
234b38e4e4 | |||
27b9ec49da | |||
87730543d1 | |||
67e28fa7ab | |||
7285863c00 | |||
23eae9c65a | |||
89a85bed78 | |||
393981e0ba | |||
31e542f45a | |||
c433a6c2d5 | |||
7dd809afe0 | |||
d7f7dac0d7 | |||
2dc45bc4b4 | |||
29d1ca6728 | |||
1d635d955b | |||
f20d4017bd | |||
5d668ae34e | |||
9d0df49fb6 | |||
e1f22cd6a0 | |||
f4a7588a48 | |||
c66728f03e | |||
20ddc9425c | |||
e1bcea681a | |||
43d58b77c9 | |||
40918029e7 | |||
54986ef831 | |||
1400e8aac8 | |||
5a398c70bb | |||
81d377f491 | |||
1135ec3df1 | |||
d3231a11ce | |||
a00d94b520 | |||
beb8308969 | |||
2893632ee5 | |||
0b3b38bb44 | |||
d4874a18e1 | |||
5a113a2163 | |||
e82621c80b | |||
1337494e74 | |||
675e275f34 | |||
7a0301b57f | |||
4774a983f9 | |||
83b9077e09 | |||
ef0680a140 | |||
a54579cf3d | |||
c08f33c2c5 | |||
fe7e7ec31a | |||
10d387afcf | |||
4748b671bc | |||
17f04f7506 | |||
76c4888e88 | |||
d44b8d02b6 | |||
02454597b2 | |||
cb207d4d70 | |||
7b9150af6b | |||
8787c1bdd3 | |||
ec93838b7a | |||
f6d5a481bb | |||
eee3eb962b | |||
847e9f5908 | |||
af1544535c | |||
0fbf209881 | |||
cf14f3f663 | |||
948fe89ce6 | |||
89af46c6fc | |||
8d1a5babe8 | |||
042d5ba765 | |||
58857058a4 | |||
5e6443ae58 | |||
0e06d3bd76 | |||
64f7c6e1e5 | |||
8442ce93be | |||
730fa7dc8e | |||
0959140098 | |||
7c4764bff8 | |||
e179165850 | |||
0140a1fc97 | |||
cb0c6c6e44 | |||
0ceeb94111 | |||
6bf50a36dd | |||
fb0fda4d0d | |||
51f86d0286 | |||
614e844a51 | |||
fd16b2c94d | |||
a4d5930477 | |||
f9b2a76665 | |||
80279ca02b | |||
c7a06a4e43 | |||
9b969375b7 | |||
61eff05227 | |||
21151dd9ef | |||
cd3eb0963b | |||
a41a8a277f | |||
4b7a3bc138 | |||
6fa6ca8a67 | |||
6b5631373a | |||
de5cd8705f | |||
a317528aeb | |||
1ec6931740 | |||
45138c7fae | |||
dcd91268a1 | |||
27c8a113de | |||
9ea6728a26 | |||
af4e4d9a35 | |||
b2175cbcc4 | |||
28c61ae02c | |||
358d400d23 | |||
09aace1cf7 | |||
fc0c72f7df | |||
b13243466b | |||
bb34980179 | |||
ff0c1aa2f6 | |||
7f2b3c7106 | |||
3e5d802e8d | |||
3e5e2bfb30 | |||
aa4c352bbd | |||
578731e978 | |||
6d9131b4f6 | |||
4c826c90cb | |||
1b11383011 | |||
6e93616ed7 | |||
824cd7efef | |||
246e280201 | |||
4e6b513fc7 | |||
5dbdf1e8de | |||
b28a0eb606 | |||
97f7e71ad7 | |||
89b9eb0e46 | |||
d1f0b7bb08 | |||
7e26085016 | |||
772d844526 | |||
0377b396c9 | |||
3fc319b650 | |||
f34471e044 | |||
de7e9b1c6d | |||
868314e244 | |||
7a8cc36a38 | |||
a3dce7fcbd | |||
0472faa2bf | |||
ae2eb77c7b | |||
b30daddb80 | |||
f81a8a7b75 | |||
cb99e99e0e | |||
3c07d6860e | |||
2cd564137d | |||
bcd49f3b40 | |||
05f75b1605 | |||
f98350f8a8 | |||
29b1ec1ce9 | |||
7764e1dfdf | |||
35deab25f2 | |||
39d6a774e9 | |||
4d024afded | |||
bad8d458cc | |||
c2d231a3fb | |||
724e674134 | |||
a70772b312 | |||
4f07fce44b | |||
5c8e57fd83 | |||
9c0891b9c6 | |||
d543144d27 | |||
4f332c14f0 | |||
b95ec0dd0f | |||
09ecfeed66 | |||
de62008d2d | |||
df3666777b | |||
459cc8e54c | |||
d1c5cd5e5d | |||
d16fa0097c | |||
ffa80a5d28 | |||
11fe8a607c | |||
7dc0eb4591 | |||
9b64b67745 | |||
55ba9e0ee8 | |||
b016766e87 | |||
387e11a5c5 | |||
913c22193d | |||
8be4d40960 | |||
26fbedd52d | |||
79c590fb38 | |||
ef3696510b | |||
7b7bbc4aad | |||
b18546f30d | |||
4d1aa79bfd | |||
6145692db0 | |||
a97f45859a | |||
8c0b66edfb | |||
4dd7520d16 | |||
0700bb2c1a | |||
4639870dc5 | |||
3d6e920ec6 | |||
ec47242af3 | |||
71e405fa63 | |||
9911cdb5bb | |||
10c4e5a984 | |||
ee1d1dba9b | |||
e4b95700a5 | |||
0b987656b5 | |||
f36d4aea77 | |||
3ccfbbc940 | |||
f29c0e08df | |||
0c44b02765 | |||
39c38d130b | |||
81f71710b6 | |||
14ae34860f | |||
b3df38e921 | |||
ae108b402c | |||
9e08a6294c | |||
68d123c4ec | |||
ad4b6891a6 | |||
260e501628 | |||
ad9f800b13 | |||
83e9ee6318 | |||
5b20c0fb16 | |||
6a217f3684 | |||
5bbbcea2af | |||
07e7ec661c | |||
706ae3b152 | |||
5713613451 | |||
b6b5e92f6b | |||
85856b2698 | |||
bdc6afd0be | |||
a5679a8f6c | |||
6f16c65132 | |||
9217338a74 | |||
cafc8de564 | |||
1cf8faddda | |||
3c4cb155d3 | |||
6edd91f9b9 | |||
246996a50a | |||
5956eb22b5 | |||
b8ecc7a75f | |||
644d342daa | |||
a78042b0fd | |||
8edf72c8ef | |||
53e4b7f2ba | |||
f6f30632f8 | |||
a8563d8107 | |||
dab9b8c4bb | |||
4ade52925d | |||
cf1e8aa4fa | |||
2d02b8c2a0 | |||
12b386f74e | |||
fa2b8b97c4 | |||
a2fd70ee27 | |||
13bedbc834 | |||
c67915af00 | |||
131bfc4c1c | |||
d21c2cb30c | |||
90d1e33eb3 | |||
b9b5ae2fc8 | |||
8fba99fa08 | |||
c918b620a8 | |||
6c3e5229ea | |||
3486020e54 | |||
29ecad81cd | |||
489deb09d7 | |||
42d1f92e02 | |||
99f75bf5eb | |||
4918b9a921 | |||
f55a08609d | |||
4a2b8e71a3 | |||
4fb7752493 | |||
e9b4667ffc | |||
ccf491a5dc | |||
c47fb379cb | |||
1d216a959c | |||
7108edc2ad | |||
3bfa73087c | |||
fa154ee279 | |||
34700dd30b | |||
dda8f24904 | |||
89103257ea | |||
76045b3564 | |||
e04badc254 | |||
32cea62999 | |||
2b0824c8ab | |||
4a076bd229 | |||
d5ef7dc303 | |||
f21998214b | |||
d0f4796265 | |||
e582925a8f | |||
111f871ea2 | |||
4393854718 | |||
ccce288182 | |||
deefada29c | |||
28c1e112c8 | |||
a45fd4627b | |||
164755590f | |||
ae2c0e3db4 | |||
13515dfa98 | |||
af632abc63 | |||
4029b2ffa0 | |||
32d244ec6f | |||
79e141320c | |||
330110843c | |||
dc5b297a82 | |||
e8f23d654c | |||
aedbf6bbfb | |||
0b3fc98f1c | |||
7edaa9040e | |||
6b6f5923ee | |||
d6bd65333e | |||
f81f1cedfc | |||
f6fa4ef39e | |||
e2ac10babb | |||
bff310ea3e | |||
a53130321f | |||
730cdad7cd | |||
85746046e1 | |||
fbe8211a10 | |||
ea4f495ab8 | |||
afb840212d | |||
82b64baaed | |||
244d3c03f9 | |||
c440a73824 | |||
48b3d6dcb5 | |||
b9531c60a2 | |||
27aac52e1f | |||
77998cd214 | |||
03c33e1be3 | |||
4f63af63a8 | |||
6ceb27f837 | |||
5d5cde5409 | |||
2630068c7b | |||
de940d0e77 | |||
0f36a10567 | |||
65a93812ef | |||
022ebccf30 | |||
e5812e86c1 | |||
1ee940c798 | |||
e3f0da6193 | |||
f9efbb56cd | |||
89a589af72 | |||
a100933678 | |||
3584f3f551 | |||
3fdcebc90c | |||
e33bec1d31 | |||
de0a09e39d | |||
dbb83b4825 | |||
956c083985 | |||
62c324730c | |||
8dc6f78610 | |||
0542c002d0 | |||
702d463194 | |||
087fd9cf09 | |||
28335419f6 | |||
db444588dd | |||
de08804b7f | |||
9d6a7e9441 | |||
14480346fe | |||
7b6ad4e99a | |||
313edb1524 | |||
820f2cef59 | |||
c76d9f4909 | |||
69ff6cd03c | |||
09f6281666 | |||
2f1c216387 | |||
87d8beb9be | |||
e63be1ffd8 | |||
d5755103a0 | |||
2014c22262 | |||
5295692da9 | |||
b7eb86d55c | |||
81c3581138 | |||
7d8b66e1cb | |||
689d39c450 | |||
8eccd71c6b | |||
caf73700fd | |||
e9c12dde94 | |||
3f40c0b7b3 | |||
318e3480ce | |||
1614d1f94d | |||
4e1f08f6a8 | |||
cbc601e38b | |||
20bae7adaa | |||
ed62ac0b84 | |||
f0ecee0555 | |||
8e99ae419d | |||
88957059ef | |||
c42e8d0f5d | |||
5b5718c186 | |||
70f0dcb891 | |||
8f02c36d63 | |||
9941f093e9 | |||
de07226be1 | |||
6603e01005 | |||
e2fa3ca138 | |||
357ffc67e5 | |||
ea8fb5e533 | |||
31afd6d0ba | |||
b38274e652 | |||
518e8c219c | |||
4e07fbf1aa | |||
b3e81a95c8 | |||
050966215b | |||
fef442edbb | |||
1064936503 | |||
a762a7f763 | |||
8fd0e358a9 | |||
9a941c1d46 | |||
ead5a3baac | |||
ac2973889d | |||
8991dd6811 | |||
415e5cd551 | |||
47705ba1a2 | |||
7c7bbc770b | |||
ab83de4905 | |||
8f7741cd9e | |||
835886e6f6 | |||
6b2039a8e6 | |||
d53d0c7841 | |||
6c40998b51 | |||
a71a90ba3d | |||
b03f6f747b | |||
a8ba88c9cc | |||
f597bd6d29 | |||
a3f103ac06 | |||
e0a068e51d | |||
dc1b3c7635 | |||
3657859a73 | |||
33ff5a0bf8 | |||
666f65c640 | |||
84d45a0d45 | |||
06a4491008 | |||
5485f83ca6 | |||
8a242a707b | |||
8a3eacb5d2 | |||
ab541458fa | |||
ac345c2ee7 | |||
468cfd9f4f | |||
d6bc5c0e66 | |||
4905eb6b26 | |||
697a7778b1 | |||
9287283ee7 | |||
8013109ef7 | |||
f140fe1a2d | |||
8fe503c67d | |||
d81013412e | |||
3c701822bd | |||
2f5ddb492a | |||
9e3324221d | |||
b069b82984 | |||
cb7d965271 | |||
7abf9ff8da | |||
18fb956251 | |||
afb1d11cd8 | |||
627be80e6c | |||
c4c4d6c417 | |||
8dcae13ce9 | |||
1352e99774 | |||
2182d7c29d | |||
703c05a93b | |||
697cc5d88b | |||
4b04f9b7a5 | |||
c7b94a0707 | |||
5116bac2a7 | |||
51625e61f9 | |||
bf9a0a96c1 | |||
bd03f07aa8 | |||
3f13df8f20 | |||
ce0275f2ae | |||
b971f18f75 | |||
ea9d11d018 | |||
8a572f1b70 | |||
0c4f2a5093 | |||
7edd752664 | |||
cc550aa236 | |||
ff033d41d3 | |||
6d0a0d8d5f | |||
b33af167d4 | |||
9053762e71 | |||
26fef7dd99 | |||
5dd17291f7 |
36
.github/ISSUE_TEMPLATE.md
vendored
36
.github/ISSUE_TEMPLATE.md
vendored
@ -1,36 +0,0 @@
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behaviour
|
||||
<!--- If you're describing a bug, tell us what should happen -->
|
||||
<!--- If you're suggesting a change/improvement, tell us how it should work -->
|
||||
|
||||
## Current Behaviour
|
||||
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
|
||||
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||
<!--- or ideas how to implement the addition or change -->
|
||||
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Context
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
||||
|
||||
## Your Environment
|
||||
|
||||
- [ ] You're using the official images
|
||||
|
||||
- [ ] You're using a raspberry pi 0
|
||||
|
||||
- [ ] You're using a supported LCD
|
||||
|
||||
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
63
.github/ISSUE_TEMPLATE.yml
vendored
Normal file
63
.github/ISSUE_TEMPLATE.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
name: Bug Report
|
||||
description: File a bug report.
|
||||
title: "[Bug]: "
|
||||
labels: ["bug", "triage"]
|
||||
projects: ["pwnagotchi"]
|
||||
assignees:
|
||||
- jayofelony
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: input
|
||||
id: contact
|
||||
attributes:
|
||||
label: Contact Details
|
||||
description: How can we get in touch with you if we need more info?
|
||||
placeholder: ex. email@example.com
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
value: "A bug happened!"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of our software are you running?
|
||||
options:
|
||||
- 2.9.5.2
|
||||
- 2.9.5.3
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: 3rd-party-hardware
|
||||
attributes:
|
||||
label: 3rd Party Hardware
|
||||
description: Are you using any 3rd party hardware? By selecting "Yes", you agree that you have tested the issue without the 3rd party hardware. And acknowledge that the issue may be related to the 3rd party hardware, for which we cannot provide full support.
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
default: 1
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/jayofelony/pwnagotchi/blob/noai/CODE_OF_CONDUCT.md).
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,32 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Environment (please complete the following information):**
|
||||
- Pwnagotchi version
|
||||
- OS version
|
||||
- Type of hardware
|
||||
- Any additional hardware used
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
63
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
63
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
name: Bug Report
|
||||
description: File a bug report.
|
||||
title: "[Bug]: "
|
||||
labels: ["bug", "triage"]
|
||||
projects: ["pwnagotchi"]
|
||||
assignees:
|
||||
- jayofelony
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: input
|
||||
id: contact
|
||||
attributes:
|
||||
label: Contact Details
|
||||
description: How can we get in touch with you if we need more info?
|
||||
placeholder: ex. email@example.com
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
value: "A bug happened!"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of our software are you running?
|
||||
options:
|
||||
- 2.9.5.2
|
||||
- 2.9.5.3
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: 3rd-party-hardware
|
||||
attributes:
|
||||
label: 3rd Party Hardware
|
||||
description: Are you using any 3rd party hardware? By selecting "Yes", you agree that you have tested the issue without the 3rd party hardware. And acknowledge that the issue may be related to the 3rd party hardware, for which we cannot provide full support.
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
default: 1
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/jayofelony/pwnagotchi/blob/noai/CODE_OF_CONDUCT.md).
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
59
.github/PULL_REQUEST_TEMPLATE.yml
vendored
Normal file
59
.github/PULL_REQUEST_TEMPLATE.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
name: Pull Request Template
|
||||
description: Template for submitting pull requests.
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Description
|
||||
Provide a detailed description of your changes here.
|
||||
|
||||
- type: textarea
|
||||
id: motivation_and_context
|
||||
attributes:
|
||||
label: Motivation and Context
|
||||
description: Why is this change necessary? What problem does it solve?
|
||||
placeholder: Add details here. Link to open issues if applicable.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: issue_proposal
|
||||
attributes:
|
||||
label: Did you raise an issue for this change? ([required](https://github.com/evilsocket/pwnagotchi/blob/master/CONTRIBUTING.md))
|
||||
options:
|
||||
- label: "Yes, I raised an issue."
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: testing
|
||||
attributes:
|
||||
label: How has this been tested?
|
||||
description: Provide a detailed description of how your changes were tested.
|
||||
placeholder: Add details about your testing environment, the tests you ran, and the impact of your changes.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: types_of_changes
|
||||
attributes:
|
||||
label: What types of changes does this Pull Request introduce?
|
||||
options:
|
||||
- label: "Bug fix (non-breaking change that fixes an issue)"
|
||||
- label: "New feature (non-breaking change that adds functionality)"
|
||||
- label: "Breaking change (fix or feature that affects existing functionality)"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Verify the following
|
||||
options:
|
||||
- label: "My code follows the code style of this project."
|
||||
- label: "My change requires a change to the documentation."
|
||||
- label: "I have updated the documentation accordingly."
|
||||
- label: "I have read the [CONTRIBUTION](https://github.com/evilsocket/pwnagotchi/blob/master/CONTRIBUTING.md) guide."
|
||||
- label: "I have signed off my commits with `git commit -s`."
|
||||
validations:
|
||||
required: true
|
74
.github/workflows/publish.yml
vendored
74
.github/workflows/publish.yml
vendored
@ -1,74 +0,0 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version number'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Extract version from file
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=$(cut -d "'" -f2 < pwnagotchi/_version.py)
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Get latest tag
|
||||
uses: actions-ecosystem/action-get-latest-tag@v1
|
||||
id: get-latest-tag
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set LAST_VERSION as an environment variable
|
||||
run: echo "LAST_VERSION=${{ steps.get-latest-tag.outputs.tag }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate release notes
|
||||
id: generate_release_notes
|
||||
run: |
|
||||
COMMITS=$(git log --merges --pretty=format:"* %s" $LAST_VERSION--$VERSION | sed 's/$/\\n/g')
|
||||
CONTRIBUTORS=$(git shortlog -sn $LAST_VERSION--$VERSION | awk '{print "* @" $2}' | sed 's/$/\\n/g')
|
||||
RELEASE_BODY="**Full Changelog**: https://github.com/jayofelony/pwnagotchi/compare/$LAST_VERSION...$VERSION"
|
||||
echo "RELEASE_BODY=$RELEASE_BODY" >> $GITHUB_ENV
|
||||
|
||||
- name: Install qemu dependencies
|
||||
run: sudo apt update && sudo apt install qemu-user-static qemu-utils xz-utils -y
|
||||
|
||||
- name: Build img file
|
||||
run: ls -la .; pwd; make all
|
||||
|
||||
- name: Transfer 32bit.img to docker and give permissions
|
||||
run: sudo chown runner:docker "pwnagotchi-32bit.img"
|
||||
|
||||
- name: Transfer 64bit.img to docker and give permissions
|
||||
run: sudo chown runner:docker "pwnagotchi-64bit.img"
|
||||
|
||||
- name: PiShrink
|
||||
run: |
|
||||
wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
|
||||
chmod +x pishrink.sh
|
||||
sudo mv pishrink.sh /usr/local/bin
|
||||
find /home/runner/work/ -type f -name "*.img" -exec sudo pishrink.sh -aZ {} \;
|
||||
|
||||
- name: Change name of 32.img.xz to add version
|
||||
run: mv "pwnagotchi-32bit.img.xz" "pwnagotchi-$VERSION-32bit.img.xz"
|
||||
|
||||
- name: Change name of 64.img.xz to add version
|
||||
run: mv "pwnagotchi-64bit.img.xz" "pwnagotchi-$VERSION-64bit.img.xz"
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
prerelease: true
|
||||
tag_name: v${{ env.VERSION }}
|
||||
name: Pwnagotchi v${{ env.VERSION }}
|
||||
files: |
|
||||
pwnagotchi-${{ env.VERSION }}-32bit.img.xz
|
||||
pwnagotchi-${{ env.VERSION }}-64bit.img.xz
|
||||
body: ${{ env.RELEASE_BODY }}
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.pyc
|
||||
.vscode
|
19
.idea/deployment.xml
generated
19
.idea/deployment.xml
generated
@ -1,23 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PublishConfigData" serverName="pwnagotchi" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
|
||||
<component name="PublishConfigData" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
|
||||
<option name="confirmBeforeUploading" value="false" />
|
||||
<serverData>
|
||||
<paths name="pwnagotchi">
|
||||
<serverdata>
|
||||
<mappings>
|
||||
<mapping deploy="/usr/local/lib/python3.11/dist-packages/pwnagotchi" local="$PROJECT_DIR$/pwnagotchi" web="/" />
|
||||
<mapping deploy="/usr/local/bin" local="$PROJECT_DIR$/bin" />
|
||||
<mapping local="" />
|
||||
</mappings>
|
||||
<excludedPaths>
|
||||
<excludedPath local="true" path="$PROJECT_DIR$/venv" />
|
||||
<excludedPath local="true" path="$PROJECT_DIR$/pwnagotchi.egg-info" />
|
||||
<excludedPath local="true" path="$PROJECT_DIR$/dist" />
|
||||
<excludedPath local="true" path="$PROJECT_DIR$/builder/packer-builder-arm" />
|
||||
</excludedPaths>
|
||||
</serverdata>
|
||||
</paths>
|
||||
</serverData>
|
||||
</component>
|
||||
</project>
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -3,7 +3,7 @@
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.11 (pwnagotchi)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (pwnagotchi)" project-jdk-type="Python SDK" />
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
|
3
.idea/pwnagotchi.iml
generated
3
.idea/pwnagotchi.iml
generated
@ -2,9 +2,10 @@
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.12 (pwnagotchi)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
|
10
.idea/sshConfigs.xml
generated
Normal file
10
.idea/sshConfigs.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SshConfigs">
|
||||
<configs>
|
||||
<sshConfig authType="PASSWORD" connectionConfig="{"serverAliveInterval":300}" id="8b69df7d-cec5-421f-8edf-53ed6233f6b6" port="22" customName="pwnagotchi" nameFormat="CUSTOM" useOpenSSHConfig="true">
|
||||
<option name="customName" value="pwnagotchi" />
|
||||
</sshConfig>
|
||||
</configs>
|
||||
</component>
|
||||
</project>
|
14
.idea/webServers.xml
generated
Normal file
14
.idea/webServers.xml
generated
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebServers">
|
||||
<option name="servers">
|
||||
<webServer id="cf2a1148-a103-4472-a782-7debdc6dabf9" name="pwnagotchi">
|
||||
<fileTransfer accessType="SFTP" port="22" sshConfigId="8b69df7d-cec5-421f-8edf-53ed6233f6b6" sshConfig="pwnagotchi">
|
||||
<advancedOptions>
|
||||
<advancedOptions dataProtectionLevel="Private" passiveMode="true" shareSSLContext="true" />
|
||||
</advancedOptions>
|
||||
</fileTransfer>
|
||||
</webServer>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -1,9 +1,6 @@
|
||||
exclude *.pyc .DS_Store .gitignore MANIFEST.in
|
||||
include setup.py
|
||||
include distribute_setup.py
|
||||
exclude *.pyc .gitignore MANIFEST.in
|
||||
include README.md
|
||||
include LICENSE
|
||||
recursive-include bin *
|
||||
recursive-include pwnagotchi *.py
|
||||
recursive-include pwnagotchi *.yml
|
||||
recursive-include pwnagotchi *.*
|
||||
|
52
Makefile
52
Makefile
@ -1,33 +1,3 @@
|
||||
PACKER_VERSION := 1.10.1
|
||||
PWN_HOSTNAME := pwnagotchi
|
||||
PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py)
|
||||
|
||||
MACHINE_TYPE := $(shell uname -m)
|
||||
ifneq (,$(filter x86_64,$(MACHINE_TYPE)))
|
||||
GOARCH := amd64
|
||||
else ifneq (,$(filter i686,$(MACHINE_TYPE)))
|
||||
GOARCH := 386
|
||||
else ifneq (,$(filter arm64% aarch64%,$(MACHINE_TYPE)))
|
||||
GOARCH := arm64
|
||||
else ifneq (,$(filter arm%,$(MACHINE_TYPE)))
|
||||
GOARCH := arm
|
||||
else
|
||||
GOARCH := amd64
|
||||
$(warning Unable to detect CPU arch from machine type $(MACHINE_TYPE), assuming $(GOARCH))
|
||||
endif
|
||||
|
||||
# The Ansible part of the build can inadvertently change the active hostname of
|
||||
# the build machine while updating the permanent hostname of the build image.
|
||||
# If the unshare command is available, use it to create a separate namespace
|
||||
# so hostname changes won't affect the build machine.
|
||||
UNSHARE := $(shell command -v unshare)
|
||||
ifneq (,$(UNSHARE))
|
||||
UNSHARE := $(UNSHARE) --uts
|
||||
endif
|
||||
|
||||
# sudo apt-get install qemu-user-static qemu-utils
|
||||
all: clean packer image
|
||||
|
||||
update_langs:
|
||||
@for lang in pwnagotchi/locale/*/; do\
|
||||
echo "updating language: $$lang ..."; \
|
||||
@ -39,25 +9,3 @@ compile_langs:
|
||||
echo "compiling language: $$lang ..."; \
|
||||
./scripts/language.sh compile $$(basename $$lang); \
|
||||
done
|
||||
|
||||
packer: clean
|
||||
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
|
||||
unzip /tmp/packer.zip -d /tmp
|
||||
sudo mv /tmp/packer /usr/bin/packer
|
||||
|
||||
image: clean packer
|
||||
export LC_ALL=en_GB.UTF-8
|
||||
cd builder && sudo /usr/bin/packer init combined.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" combined.json.pkr.hcl
|
||||
|
||||
bullseye: clean packer
|
||||
export LC_ALL=en_GB.UTF-8
|
||||
cd builder && sudo /usr/bin/packer init data/32bit/raspberrypi32.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/32bit/raspberrypi32.json.pkr.hcl
|
||||
sudo pishrink -vaZ pwnagotchi-32bit.img
|
||||
|
||||
bookworm: clean packer
|
||||
export LC_ALL=en_GB.UTF-8
|
||||
cd builder && sudo /usr/bin/packer init data/64bit/raspberrypi64.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/64bit/raspberrypi64.json.pkr.hcl
|
||||
sudo pishrink -vaZ pwnagotchi-64bit.img
|
||||
|
||||
clean:
|
||||
- rm -rf /tmp/packer*
|
||||
|
35
README.md
35
README.md
@ -3,37 +3,40 @@ This is the main source for all forks:
|
||||
- RPiZeroW (32bit)
|
||||
- RPiZero2W, RPi3, RPi4, RPi5 (64bit)
|
||||
|
||||
**For installation docs check out the [wiki](https://github.com/jayofelony/pwnagotchi/wiki)!**
|
||||
|
||||
If you want to sponsor this project you can use GH Sponsor or cryptocurrency:
|
||||
|
||||
[GH Sponsor](https://github.com/sponsors/jayofelony)
|
||||
|
||||
Or send some ethereum: 0x33ceC4Abe80fDE460a924d596d4dE31Bc0767bb6
|
||||
|
||||
**Proudly partnering with [PiSugar](https://www.pisugar.com)!!**
|
||||
|
||||
---
|
||||
|
||||
[Pwnagotchi](https://pwnagotchi.ai/) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),
|
||||
[Pwnagotchi](https://pwnagotchi.org/) is a Raspberry Pi leveraging [bettercap](https://www.bettercap.org/) that survives from its surrounding Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),
|
||||
full and half WPA handshakes.
|
||||
|
||||

|
||||
|
||||
Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning-based "AI" *(yawn)*, Pwnagotchi tunes [its parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.toml) over time to **get better at pwning Wi-Fi things to** in the environments you expose it to.
|
||||
The "old" Pwnagotchi used to have AI to help it learn from its environment, but since then AI seemed to destabilize the Wi-Fi firmware. So I have chosen to remove the AI completely to give the Pwnagotchi more up-time and longer battery life when taking it on a walk.
|
||||
|
||||
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 learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://www.pwnagotchi.ai/usage/#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but ** listen to your Pwnagotchi when it tells you it's boring!** Bring it into novel Wi-Fi environments with you and have it observe new networks and capture new handshakes—and you'll see. :)
|
||||
|
||||
Multiple units within close physical proximity can "talk" to each other, advertising their presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard. Over time, two or more units trained together will learn to cooperate upon detecting each other's presence by dividing the available channels among them for optimal pwnage.
|
||||
Multiple units within close physical proximity can "talk" to each other, advertising their presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard.
|
||||
|
||||
## Documentation
|
||||
|
||||
https://www.pwnagotchi.ai
|
||||
https://github.com/jayofelony/pwnagotchi/wiki
|
||||
https://pwnagotchi.org
|
||||
|
||||
## Links
|
||||
|
||||
| | Official Links |
|
||||
|-----------|-------------------------------------------------------------|
|
||||
| Website | [pwnagotchi.ai](https://pwnagotchi.ai/) |
|
||||
| Forum | [community.pwnagotchi.ai](https://community.pwnagotchi.ai/) |
|
||||
| Slack | [pwnagotchi.slack.com](https://invite.pwnagotchi.ai/) |
|
||||
| Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) |
|
||||
| Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) |
|
||||
| | Official Links |
|
||||
|-----------|----------------------------------------------------------|
|
||||
| Website | [pwnagotchi.org](https://pwnagotchi.org/) |
|
||||
| Chat | [discord](https://discord.gg/PGgnzFbz4M) |
|
||||
| Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) |
|
||||
|
||||
## License
|
||||
|
||||
`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.
|
||||
`pwnagotchi` created by [@evilsocket](https://x.com/evilsocket) and updated by [us](https://github.com/jayofelony/pwnagotchi/graphs/contributors). It is released under the GPL3 license.
|
||||
|
@ -1,173 +0,0 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
arm = {
|
||||
version = "1.0.0"
|
||||
source = "github.com/cdecoux/builder-arm"
|
||||
}
|
||||
ansible = {
|
||||
source = "github.com/hashicorp/ansible"
|
||||
version = ">= 1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "pwn_hostname" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "pwn_version" {
|
||||
type = string
|
||||
}
|
||||
|
||||
source "arm" "rpi64-pwnagotchi" {
|
||||
file_checksum_url = "https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz.sha256"
|
||||
file_urls = ["https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz"]
|
||||
file_checksum_type = "sha256"
|
||||
file_target_extension = "xz"
|
||||
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||
image_path = "../pwnagotchi-64bit.img"
|
||||
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||
image_build_method = "resize"
|
||||
image_size = "9G"
|
||||
image_type = "dos"
|
||||
image_partitions {
|
||||
name = "boot"
|
||||
type = "c"
|
||||
start_sector = "8192"
|
||||
filesystem = "fat"
|
||||
size = "256M"
|
||||
mountpoint = "/boot/firmware"
|
||||
}
|
||||
image_partitions {
|
||||
name = "root"
|
||||
type = "83"
|
||||
start_sector = "532480"
|
||||
filesystem = "ext4"
|
||||
size = "0"
|
||||
mountpoint = "/"
|
||||
}
|
||||
}
|
||||
|
||||
source "arm" "rpi32-pwnagotchi" {
|
||||
file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz.sha256"
|
||||
file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz"]
|
||||
file_checksum_type = "sha256"
|
||||
file_target_extension = "xz"
|
||||
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||
image_path = "../pwnagotchi-32bit.img"
|
||||
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||
image_build_method = "resize"
|
||||
image_size = "9G"
|
||||
image_type = "dos"
|
||||
image_partitions {
|
||||
name = "boot"
|
||||
type = "c"
|
||||
start_sector = "8192"
|
||||
filesystem = "fat"
|
||||
size = "256M"
|
||||
mountpoint = "/boot"
|
||||
}
|
||||
image_partitions {
|
||||
name = "root"
|
||||
type = "83"
|
||||
start_sector = "532480"
|
||||
filesystem = "ext4"
|
||||
size = "0"
|
||||
mountpoint = "/"
|
||||
}
|
||||
}
|
||||
|
||||
# a build block invokes sources and runs provisioning steps on them. The
|
||||
# documentation for build blocks can be found here:
|
||||
# https://www.packer.io/docs/from-1.5/blocks/build
|
||||
build {
|
||||
name = "Raspberry Pi 64 Pwnagotchi"
|
||||
sources = ["source.arm.rpi64-pwnagotchi"]
|
||||
|
||||
provisioner "file" {
|
||||
destination = "/usr/bin/"
|
||||
sources = [
|
||||
"data/64bit/usr/bin/bettercap-launcher",
|
||||
"data/64bit/usr/bin/hdmioff",
|
||||
"data/64bit/usr/bin/hdmion",
|
||||
"data/64bit/usr/bin/monstart",
|
||||
"data/64bit/usr/bin/monstop",
|
||||
"data/64bit/usr/bin/pwnagotchi-launcher",
|
||||
"data/64bit/usr/bin/pwnlib",
|
||||
]
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["chmod +x /usr/bin/*"]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
destination = "/etc/systemd/system/"
|
||||
sources = [
|
||||
"data/64bit/etc/systemd/system/bettercap.service",
|
||||
"data/64bit/etc/systemd/system/pwnagotchi.service",
|
||||
"data/64bit/etc/systemd/system/pwngrid-peer.service",
|
||||
]
|
||||
}
|
||||
provisioner "file" {
|
||||
destination = "/etc/update-motd.d/01-motd"
|
||||
source = "data/64bit/etc/update-motd.d/01-motd"
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["chmod +x /etc/update-motd.d/*"]
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
|
||||
}
|
||||
provisioner "ansible-local" {
|
||||
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
||||
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
||||
playbook_file = "data/64bit/raspberrypi64.yml"
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
name = "Raspberry Pi 32 Pwnagotchi"
|
||||
sources = ["source.arm.rpi32-pwnagotchi"]
|
||||
provisioner "file" {
|
||||
destination = "/usr/bin/"
|
||||
sources = [
|
||||
"data/32bit/usr/bin/bettercap-launcher",
|
||||
"data/32bit/usr/bin/hdmioff",
|
||||
"data/32bit/usr/bin/hdmion",
|
||||
"data/32bit/usr/bin/monstart",
|
||||
"data/32bit/usr/bin/monstop",
|
||||
"data/32bit/usr/bin/pwnagotchi-launcher",
|
||||
"data/32bit/usr/bin/pwnlib",
|
||||
]
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["chmod +x /usr/bin/*"]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
destination = "/etc/systemd/system/"
|
||||
sources = [
|
||||
"data/32bit/etc/systemd/system/bettercap.service",
|
||||
"data/32bit/etc/systemd/system/pwnagotchi.service",
|
||||
"data/32bit/etc/systemd/system/pwngrid-peer.service",
|
||||
]
|
||||
}
|
||||
provisioner "file" {
|
||||
destination = "/etc/update-motd.d/01-motd"
|
||||
source = "data/32bit/etc/update-motd.d/01-motd"
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["chmod +x /etc/update-motd.d/*"]
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
|
||||
}
|
||||
provisioner "ansible-local" {
|
||||
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
||||
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
||||
playbook_dir = "data/32bit/extras/"
|
||||
playbook_file = "data/32bit/raspberrypi32.yml"
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
_show_complete()
|
||||
{
|
||||
local cur opts node_names all_options opt_line
|
||||
all_options="
|
||||
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --check-update --donate {plugins,google}
|
||||
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
|
||||
pwnagotchi plugins list -i --installed -h --help
|
||||
pwnagotchi plugins install -h --help
|
||||
pwnagotchi plugins uninstall -h --help
|
||||
pwnagotchi plugins enable -h --help
|
||||
pwnagotchi plugins disable -h --help
|
||||
pwnagotchi plugins update -h --help
|
||||
pwnagotchi plugins upgrade -h --help
|
||||
pwnagotchi google -h --help {login,refresh}
|
||||
pwnagotchi google login -h --help
|
||||
pwnagotchi google refresh -h --help
|
||||
"
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
# shellcheck disable=SC2124
|
||||
cmd="${COMP_WORDS[@]:0:${#COMP_WORDS[@]}-1}"
|
||||
opt_line="$(grep -m1 "$cmd" <<<"$all_options")"
|
||||
if [[ ${cur} == -* ]] ; then
|
||||
opts="$(echo "$opt_line" | tr ' ' '\n' | awk '/^ *-/{gsub("[^a-zA-Z0-9-]","",$1);print $1}')"
|
||||
# shellcheck disable=SC2207
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')"
|
||||
# shellcheck disable=SC2207
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
}
|
||||
|
||||
complete -F _show_complete pwnagotchi
|
@ -1,62 +0,0 @@
|
||||
# A sample configuration for dhcpcd.
|
||||
# See dhcpcd.conf(5) for details.
|
||||
|
||||
# Allow users of this group to interact with dhcpcd via the control socket.
|
||||
#controlgroup wheel
|
||||
|
||||
# Inform the DHCP server of our hostname for DDNS.
|
||||
hostname
|
||||
|
||||
# Use the hardware address of the interface for the Client ID.
|
||||
clientid
|
||||
# or
|
||||
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
|
||||
# Some non-RFC compliant DHCP servers do not reply with this set.
|
||||
# In this case, comment out duid and enable clientid above.
|
||||
#duid
|
||||
|
||||
# Persist interface configuration when dhcpcd exits.
|
||||
persistent
|
||||
|
||||
# Rapid commit support.
|
||||
# Safe to enable by default because it requires the equivalent option set
|
||||
# on the server to actually work.
|
||||
option rapid_commit
|
||||
|
||||
# A list of options to request from the DHCP server.
|
||||
option domain_name_servers, domain_name, domain_search, host_name
|
||||
option classless_static_routes
|
||||
# Respect the network MTU. This is applied to DHCP routes.
|
||||
option interface_mtu
|
||||
|
||||
# Most distributions have NTP support.
|
||||
#option ntp_servers
|
||||
|
||||
# A ServerID is required by RFC2131.
|
||||
require dhcp_server_identifier
|
||||
|
||||
# Generate SLAAC address using the Hardware Address of the interface
|
||||
#slaac hwaddr
|
||||
# OR generate Stable Private IPv6 Addresses based from the DUID
|
||||
slaac private
|
||||
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# !! DO NOT EDIT THESE LINES BELOW PLEASE !!
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
# static IP configuration:
|
||||
denyinterfaces wlan0
|
||||
|
||||
interface eth0
|
||||
static domain_name_servers=8.8.8.8 1.1.1.1
|
||||
metric 201
|
||||
|
||||
interface usb0
|
||||
static ip_address=10.0.0.2/24
|
||||
static routers=10.0.0.1
|
||||
static domain_name_servers=10.0.0.1 8.8.8.8 1.1.1.1
|
||||
metric 202
|
||||
|
||||
interface bnep0
|
||||
static domain_name_servers=8.8.8.8 1.1.1.1
|
||||
metric 203
|
@ -1,26 +0,0 @@
|
||||
# /etc/dphys-swapfile - user settings for dphys-swapfile package
|
||||
# author Neil Franklin, last modification 2010.05.05
|
||||
# copyright ETH Zuerich Physics Departement
|
||||
# use under either modified/non-advertising BSD or GPL license
|
||||
|
||||
# this file is sourced with . so full normal sh syntax applies
|
||||
|
||||
# the default settings are added as commented out CONF_*=* lines
|
||||
|
||||
|
||||
# where we want the swapfile to be, this is the default
|
||||
#CONF_SWAPFILE=/var/swap
|
||||
|
||||
# set size to absolute value, leaving empty (default) then uses computed value
|
||||
# you most likely don't want this, unless you have an special disk situation
|
||||
CONF_SWAPSIZE=2048
|
||||
|
||||
# set size to computed value, this times RAM size, dynamically adapts,
|
||||
# guarantees that there is enough swap without wasting disk space on excess
|
||||
#CONF_SWAPFACTOR=2
|
||||
|
||||
# restrict size (computed and absolute!) to maximally this limit
|
||||
# can be set to empty for no limit, but beware of filled partitions!
|
||||
# this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it
|
||||
# but is also sensible on 64bit to prevent filling /var or even / partition
|
||||
#CONF_MAXSWAP=2048
|
@ -1,16 +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.
|
||||
|
||||
timedatectl set-ntp true
|
||||
|
||||
exit 0
|
@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=bettercap api.rest service.
|
||||
Documentation=https://bettercap.org
|
||||
Wants=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/bettercap-launcher
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,20 +0,0 @@
|
||||
[Unit]
|
||||
Description=Bluetooth service
|
||||
Documentation=man:bluetoothd(8)
|
||||
ConditionPathIsDirectory=/sys/class/bluetooth
|
||||
|
||||
[Service]
|
||||
Type=dbus
|
||||
BusName=org.bluez
|
||||
ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp
|
||||
NotifyAccess=main
|
||||
#WatchdogSec=10
|
||||
#Restart=on-failure
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
LimitNPROC=1
|
||||
ProtectHome=true
|
||||
ProtectSystem=full
|
||||
|
||||
[Install]
|
||||
WantedBy=bluetooth.target
|
||||
Alias=dbus-org.bluez.service
|
@ -1,6 +0,0 @@
|
||||
[Unit]
|
||||
After=hciuart.service bluetooth.service
|
||||
Before=
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/bin/sleep 5
|
@ -1,19 +0,0 @@
|
||||
[Unit]
|
||||
Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.
|
||||
Documentation=https://pwnagotchi.ai
|
||||
Wants=network.target
|
||||
After=pwngrid-peer.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=~
|
||||
ExecStart=/usr/bin/pwnagotchi-launcher
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
TasksMax=infinity
|
||||
LimitNPROC=infinity
|
||||
StandardOutput=null
|
||||
StandardError=null
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,16 +0,0 @@
|
||||
[Unit]
|
||||
Description=pwngrid peer service.
|
||||
Documentation=https://pwnagotchi.ai
|
||||
Wants=network.target
|
||||
After=bettercap.service
|
||||
|
||||
[Service]
|
||||
Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1
|
||||
Environment=LD_LIBRARY_PATH=/usr/local/lib
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /etc/pwnagotchi/log/pwngrid-peer.log -iface wlan0mon
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,33 +0,0 @@
|
||||
#!/bin/sh
|
||||
_hostname=$(hostname)
|
||||
_version=$(cut -d"'" -f2 < /usr/local/lib/python3.9/dist-packages/pwnagotchi/_version.py)
|
||||
echo
|
||||
echo "(◕‿‿◕) $_hostname"
|
||||
echo
|
||||
echo " Hi! I'm a pwnagotchi $_version, please take good care of me!"
|
||||
echo " Here are some basic things you need to know to raise me properly!"
|
||||
echo
|
||||
echo " If you want to change my configuration, use /etc/pwnagotchi/config.toml"
|
||||
echo " All plugin config files are located in /etc/pwnagotchi/conf.d/"
|
||||
echo " Read the readme if you want to use gdrivesync plugin!!"
|
||||
echo
|
||||
echo " All the configuration options can be found on /etc/pwnagotchi/default.toml,"
|
||||
echo " but don't change this file because I will recreate it every time I'm restarted!"
|
||||
echo
|
||||
echo " I use oPwnGrid as my main API, you can check stats at https://opwngrid.xyz"
|
||||
echo
|
||||
echo " I'm managed by systemd. Here are some basic commands."
|
||||
echo
|
||||
echo " If you want to know what I'm doing, you can check my logs with the command"
|
||||
echo " - pwnlog"
|
||||
echo " - sudo pwnagotchi --version, to check the current version"
|
||||
echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
|
||||
echo " - sudo pwnagotchi --check-update, to see if there is a new version available"
|
||||
echo
|
||||
echo " If you want to know if I'm running, you can use"
|
||||
echo " sudo systemctl status pwnagotchi"
|
||||
echo
|
||||
echo " You can restart me using"
|
||||
echo " pwnkill"
|
||||
echo
|
||||
echo " You learn more about me at https://pwnagotchi.ai/"
|
@ -1,40 +0,0 @@
|
||||
# Install nexmon to fix wireless scanning (takes 2.5G of space)
|
||||
- name: clone nexmon repository
|
||||
git:
|
||||
repo: https://github.com/DrSchottky/nexmon.git
|
||||
dest: /usr/local/src/nexmon
|
||||
|
||||
- name: make firmware
|
||||
shell: "source ./setup_env.sh && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
|
||||
- name: "make firmware patch ({{ item.name }})"
|
||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/ && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
environment:
|
||||
QEMU_UNAME: "{{ item.kernel }}"
|
||||
ARCHFLAGS: "{{ item.arch_flags }}"
|
||||
|
||||
- name: "install new firmware ({{ item.name }})"
|
||||
copy:
|
||||
src: "/usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/{{ item.firmware }}"
|
||||
dest: "/usr/lib/firmware/brcm/{{ item.firmware }}"
|
||||
follow: true
|
||||
environment:
|
||||
QEMU_UNAME: "{{ item.kernel }}"
|
||||
ARCHFLAGS: "{{ item.arch_flags }}"
|
||||
|
||||
- name: backup original driver
|
||||
command: "mv /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
|
||||
|
||||
- name: copy modified driver
|
||||
copy:
|
||||
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_6.1.y-nexmon/brcmfmac.ko"
|
||||
dest: "/usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
|
||||
|
||||
- name : load brcmfmac drivers
|
||||
command: "/sbin/depmod -a {{ item.kernel }}"
|
@ -1,94 +0,0 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
arm = {
|
||||
version = "1.0.0"
|
||||
source = "github.com/cdecoux/builder-arm"
|
||||
}
|
||||
ansible = {
|
||||
source = "github.com/hashicorp/ansible"
|
||||
version = ">= 1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "pwn_hostname" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "pwn_version" {
|
||||
type = string
|
||||
}
|
||||
|
||||
source "arm" "rpi32-pwnagotchi" {
|
||||
file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz.sha256"
|
||||
file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz"]
|
||||
file_checksum_type = "sha256"
|
||||
file_target_extension = "xz"
|
||||
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||
image_path = "../../pwnagotchi-32bit.img"
|
||||
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||
image_build_method = "resize"
|
||||
image_size = "9G"
|
||||
image_type = "dos"
|
||||
image_partitions {
|
||||
name = "boot"
|
||||
type = "c"
|
||||
start_sector = "8192"
|
||||
filesystem = "fat"
|
||||
size = "256M"
|
||||
mountpoint = "/boot"
|
||||
}
|
||||
image_partitions {
|
||||
name = "root"
|
||||
type = "83"
|
||||
start_sector = "532480"
|
||||
filesystem = "ext4"
|
||||
size = "0"
|
||||
mountpoint = "/"
|
||||
}
|
||||
}
|
||||
build {
|
||||
name = "Raspberry Pi 32 Pwnagotchi"
|
||||
sources = ["source.arm.rpi32-pwnagotchi"]
|
||||
provisioner "file" {
|
||||
destination = "/usr/bin/"
|
||||
sources = [
|
||||
"data/32bit/usr/bin/bettercap-launcher",
|
||||
"data/32bit/usr/bin/hdmioff",
|
||||
"data/32bit/usr/bin/hdmion",
|
||||
"data/32bit/usr/bin/monstart",
|
||||
"data/32bit/usr/bin/monstop",
|
||||
"data/32bit/usr/bin/pwnagotchi-launcher",
|
||||
"data/32bit/usr/bin/pwnlib",
|
||||
]
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["chmod +x /usr/bin/*"]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
destination = "/etc/systemd/system/"
|
||||
sources = [
|
||||
"data/32bit/etc/systemd/system/bettercap.service",
|
||||
"data/32bit/etc/systemd/system/pwnagotchi.service",
|
||||
"data/32bit/etc/systemd/system/pwngrid-peer.service",
|
||||
]
|
||||
}
|
||||
provisioner "file" {
|
||||
destination = "/etc/update-motd.d/01-motd"
|
||||
source = "data/32bit/etc/update-motd.d/01-motd"
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["chmod +x /etc/update-motd.d/*"]
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
|
||||
}
|
||||
provisioner "ansible-local" {
|
||||
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
||||
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
||||
playbook_dir = "data/32bit/extras/"
|
||||
playbook_file = "data/32bit/raspberrypi32.yml"
|
||||
}
|
||||
}
|
@ -1,599 +0,0 @@
|
||||
---
|
||||
- hosts:
|
||||
- 127.0.0.1
|
||||
gather_facts: true
|
||||
become: true
|
||||
vars:
|
||||
boards:
|
||||
- {
|
||||
kernel: "6.1.21+",
|
||||
name: "PiZeroW",
|
||||
firmware: "brcmfmac43430-sdio.bin",
|
||||
patch: "bcm43430a1/7_45_41_46",
|
||||
cpu: arm1176,
|
||||
arch_flags: "-arch armv6l"
|
||||
}
|
||||
- {
|
||||
kernel: "6.1.21-v7+",
|
||||
name: "PiZero2W",
|
||||
firmware: "brcmfmac43436-sdio.bin",
|
||||
patch: "bcm43436b0/9_88_4_65",
|
||||
cpu: any, #cortex-a53
|
||||
arch_flags: "-arch armv7l"
|
||||
}
|
||||
- {
|
||||
kernel: "6.1.21-v7l+",
|
||||
name: "Pi4b_32",
|
||||
firmware: "brcmfmac43455-sdio.bin",
|
||||
patch: "bcm43455c0/7_45_206",
|
||||
cpu: any, #cortex-a72
|
||||
arch_flags: "-arch armv7l"
|
||||
}
|
||||
kernel:
|
||||
min: "6.1"
|
||||
full: "6.1.21+"
|
||||
full_2w: "6.1.21-v7+"
|
||||
full_4b: "6.1.21-v7l+"
|
||||
arch: "v6l"
|
||||
pwnagotchi:
|
||||
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
||||
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}"
|
||||
custom_plugin_dir: "/usr/local/share/pwnagotchi/custom-plugins"
|
||||
system:
|
||||
boot_options:
|
||||
- "#### pwnagotchi additions"
|
||||
- "# this pwnagotchi image is 32-bit only no v8+ headers to build nexmon for 64 bit"
|
||||
- "arm_64bit=0"
|
||||
- "# dwc2 for RNDIS. comment out, and remove dwc2 and g_ether from cmdline.txt for X306 usb battery hat"
|
||||
- "dtoverlay=dwc2"
|
||||
- "dtoverlay=spi0-0cs"
|
||||
- "dtparam=i2c1=on"
|
||||
- "dtparam=i2c_arm=on"
|
||||
- "dtparam=spi=on"
|
||||
- "gpu_mem=16"
|
||||
- "#### audio out on pins 18 and 19"
|
||||
- "#dtoverlay=audremap,pins_18_19"
|
||||
- "#### touchscreen on waveshare touch e-paper"
|
||||
- "#dtoverlay=goodix,interrupt=27,reset=22"
|
||||
- "#### for PWM backlighting on pimoroni displayhatmini"
|
||||
- "dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4"
|
||||
modules:
|
||||
- "i2c-dev"
|
||||
services:
|
||||
enable:
|
||||
- bettercap.service
|
||||
- bluetooth.service
|
||||
- dphys-swapfile.service
|
||||
- fstrim.timer
|
||||
- pwnagotchi.service
|
||||
- pwngrid-peer.service
|
||||
disable:
|
||||
- apt-daily-upgrade.service
|
||||
- apt-daily-upgrade.timer
|
||||
- apt-daily.service
|
||||
- apt-daily.timer
|
||||
- ifup@wlan0.service
|
||||
- triggerhappy.service
|
||||
- wpa_supplicant.service
|
||||
packages:
|
||||
caplets:
|
||||
source: "https://github.com/jayofelony/caplets.git"
|
||||
bettercap:
|
||||
source: "https://github.com/jayofelony/bettercap.git"
|
||||
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.2/bettercap-2.32.2-armhf.zip"
|
||||
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||
opwngrid:
|
||||
source: "https://github.com/jayofelony/pwngrid.git"
|
||||
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.7/pwngrid-1.10.7-armhf.zip"
|
||||
torch:
|
||||
wheel: "torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl"
|
||||
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl"
|
||||
torchvision:
|
||||
wheel: "torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
|
||||
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
|
||||
apt:
|
||||
downgrade:
|
||||
- libpcap-dev_1.9.1-4_armhf.deb
|
||||
- libpcap0.8-dbg_1.9.1-4_armhf.deb
|
||||
- libpcap0.8-dev_1.9.1-4_armhf.deb
|
||||
- libpcap0.8_1.9.1-4_armhf.deb
|
||||
hold:
|
||||
- firmware-atheros
|
||||
- firmware-brcm80211
|
||||
- firmware-libertas
|
||||
- firmware-misc-nonfree
|
||||
- firmware-realtek
|
||||
- libpcap-dev
|
||||
- libpcap0.8
|
||||
- libpcap0.8-dev
|
||||
- libpcap0.8-dbg
|
||||
remove:
|
||||
- avahi-daemon
|
||||
- nfs-common
|
||||
- triggerhappy
|
||||
- wpasupplicant
|
||||
install:
|
||||
- autoconf
|
||||
- bc
|
||||
- bison
|
||||
- bluez
|
||||
- build-essential
|
||||
- curl
|
||||
- dkms
|
||||
- dphys-swapfile
|
||||
- espeak-ng
|
||||
- evtest
|
||||
- fbi
|
||||
- flex
|
||||
- fonts-dejavu
|
||||
- fonts-dejavu-core
|
||||
- fonts-dejavu-extra
|
||||
- fonts-freefont-ttf
|
||||
- g++
|
||||
- gawk
|
||||
- gcc-arm-none-eabi
|
||||
- git
|
||||
- libatlas-base-dev
|
||||
- libavcodec58
|
||||
- libavformat58
|
||||
- libblas-dev
|
||||
- libbluetooth-dev
|
||||
- libbz2-dev
|
||||
- libc-ares-dev
|
||||
- libc6-dev
|
||||
- libcpuinfo-dev
|
||||
- libdbus-1-dev
|
||||
- libdbus-glib-1-dev
|
||||
- libeigen3-dev
|
||||
- libelf-dev
|
||||
- libffi-dev
|
||||
- libfl-dev
|
||||
- libfuse-dev
|
||||
- libgdbm-dev
|
||||
- libgl1-mesa-glx
|
||||
- libgmp3-dev
|
||||
- libgstreamer1.0-0
|
||||
- libhdf5-dev
|
||||
- liblapack-dev
|
||||
- libncursesw5-dev
|
||||
- libnetfilter-queue-dev
|
||||
- libopenblas-dev
|
||||
- libopenjp2-7
|
||||
- libopenmpi-dev
|
||||
- libopenmpi3
|
||||
- libpcap-dev
|
||||
- libprotobuf-dev
|
||||
- libraspberrypi-bin
|
||||
- libraspberrypi-dev
|
||||
- libraspberrypi-doc
|
||||
- libraspberrypi0
|
||||
- libsleef-dev
|
||||
- libsqlite3-dev
|
||||
- libssl-dev
|
||||
- libswscale5
|
||||
- libtiff5
|
||||
- libtool
|
||||
- libts-bin
|
||||
- libusb-1.0-0-dev
|
||||
- lsof
|
||||
- make
|
||||
- ntp
|
||||
- python3-flask
|
||||
- python3-flask-cors
|
||||
- python3-flaskext.wtf
|
||||
- python3-pil
|
||||
- python3-pip
|
||||
- python3-protobuf
|
||||
- python3-smbus
|
||||
- qpdf
|
||||
- raspberrypi-kernel-headers
|
||||
- rsync
|
||||
- screen
|
||||
- tcpdump
|
||||
- texinfo
|
||||
- time
|
||||
- tk-dev
|
||||
- unzip
|
||||
- vim
|
||||
- wget
|
||||
- wl
|
||||
- xxd
|
||||
- zlib1g-dev
|
||||
|
||||
tasks:
|
||||
- name: Create pi user
|
||||
copy:
|
||||
dest: /boot/userconf
|
||||
content: |
|
||||
pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
|
||||
|
||||
- name: change hostname
|
||||
lineinfile:
|
||||
dest: /etc/hostname
|
||||
regexp: '^raspberrypi'
|
||||
line: "{{pwnagotchi.hostname}}"
|
||||
state: present
|
||||
when: lookup('file', '/etc/hostname') == "raspberrypi"
|
||||
register: hostname
|
||||
|
||||
- name: add hostname to /etc/hosts
|
||||
lineinfile:
|
||||
dest: /etc/hosts
|
||||
regexp: '^127\.0\.1\.1[ \t]+raspberrypi'
|
||||
line: "127.0.1.1\t{{pwnagotchi.hostname}}"
|
||||
state: present
|
||||
when: hostname.changed
|
||||
|
||||
- name: Create custom plugin directory
|
||||
file:
|
||||
path: '{{ pwnagotchi.custom_plugin_dir }}'
|
||||
state: directory
|
||||
|
||||
- name: remove current rc.local
|
||||
file:
|
||||
path: /etc/rc.local
|
||||
state: absent
|
||||
|
||||
- name: update apt package cache
|
||||
apt:
|
||||
update_cache: yes
|
||||
|
||||
- name: install packages
|
||||
apt:
|
||||
name: "{{ packages.apt.install }}"
|
||||
state: present
|
||||
|
||||
- name: update pip3, setuptools, wheel
|
||||
shell: "python3 -m pip install --upgrade pip setuptools wheel"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src
|
||||
|
||||
###########################################
|
||||
#
|
||||
# libpcap v1.9 - build from source
|
||||
#
|
||||
###########################################
|
||||
|
||||
# check for presence, then it can re-run in later parts if needed
|
||||
# use the "make" built in
|
||||
|
||||
# install libpcap before bettercap and pwngrid, so they use it
|
||||
- name: clone libpcap v1.9 from github
|
||||
git:
|
||||
repo: 'https://github.com/the-tcpdump-group/libpcap.git'
|
||||
dest: /usr/local/src/libpcap
|
||||
version: libpcap-1.9
|
||||
|
||||
- name: build and install libpcap into /usr/local/lib
|
||||
shell: "./configure && make && make install"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/libpcap
|
||||
|
||||
- name: remove libpcap build folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/libpcap
|
||||
|
||||
- name: create symlink /usr/local/lib/libpcap.so.1.9.1
|
||||
file:
|
||||
src: /usr/local/lib/libpcap.so.1.9.1
|
||||
dest: /usr/local/lib/libpcap.so.0.8
|
||||
state: link
|
||||
|
||||
###############################################################
|
||||
# Install nexmon to fix wireless scanning (takes 2.5G of space)
|
||||
###############################################################
|
||||
|
||||
# Install nexmon for all boards
|
||||
- name: build and install nexmon as needed
|
||||
include_tasks: nexmon.yml
|
||||
loop: "{{ boards }}"
|
||||
|
||||
# some pizero2w have the pizeroW wifi chip
|
||||
# could this be a link instead of a copy? and force, only if not a link?
|
||||
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
|
||||
copy:
|
||||
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
||||
dest: /usr/lib/firmware/brcm/brcmfmac43436s-sdio.bin
|
||||
follow: true
|
||||
|
||||
# delete blob files that make nexmon sad
|
||||
- name: Delete the firmware blob files to avoid some nexmon crashing
|
||||
file:
|
||||
state: absent
|
||||
path: '{{ item }}'
|
||||
loop:
|
||||
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43430b0-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob
|
||||
|
||||
# To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation
|
||||
- name: Delete nexmon content & directory
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/nexmon/
|
||||
|
||||
- name: clone pwnagotchi repository
|
||||
git:
|
||||
repo: https://github.com/jayofelony/pwnagotchi.git
|
||||
dest: /usr/local/src/pwnagotchi
|
||||
register: pwnagotchigit
|
||||
|
||||
# is this even necessary? Can't we just link from /home/pi/pwnagotchi to /usr/local/{bin,lib,etc}
|
||||
# then just git update in the home dir and encourage hacking?
|
||||
# make owned by pi.pi, and custom plugins.
|
||||
- name: build pwnagotchi wheel
|
||||
command: "python3 setup.py sdist bdist_wheel"
|
||||
args:
|
||||
chdir: /usr/local/src/pwnagotchi
|
||||
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
|
||||
|
||||
- name: download torch whl
|
||||
get_url:
|
||||
url: "{{ packages.torch.url }}"
|
||||
dest: /usr/local/src/
|
||||
|
||||
- name: download torchvision whl
|
||||
get_url:
|
||||
url: "{{ packages.torchvision.url }}"
|
||||
dest: /usr/local/src/
|
||||
|
||||
- name: install 32-bit pwnagotchi wheel and dependencies with 32-bit torch wheels
|
||||
pip:
|
||||
name:
|
||||
- "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}"
|
||||
- "{{ packages.torch.url }}"
|
||||
- "{{ packages.torchvision.url }}"
|
||||
extra_args: "--no-cache-dir"
|
||||
environment:
|
||||
QEMU_CPU: arm1176
|
||||
QEMU_UNAME: "{{ kernel.full }}"
|
||||
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
|
||||
|
||||
- name: create /usr/local/share/pwnagotchi/ folder
|
||||
file:
|
||||
path: /usr/local/share/pwnagotchi/
|
||||
state: directory
|
||||
|
||||
- name: remove pwnagotchi folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/pwnagotchi
|
||||
|
||||
- name: remove torch whl
|
||||
file:
|
||||
state: absent
|
||||
path: "{{ lookup('fileglob', '/usr/local/src/torch*.whl') }}"
|
||||
|
||||
##########################################
|
||||
#
|
||||
# pwngrid, bettercap
|
||||
#
|
||||
##########################################
|
||||
|
||||
- name: Install go-1.21
|
||||
unarchive:
|
||||
src: https://go.dev/dl/go1.21.6.linux-armv6l.tar.gz
|
||||
dest: /usr/local
|
||||
remote_src: yes
|
||||
register: golang
|
||||
|
||||
- name: Update .bashrc for go-1.21
|
||||
blockinfile:
|
||||
dest: /home/pi/.bashrc
|
||||
state: present
|
||||
block: |
|
||||
export GOPATH=$HOME/go
|
||||
export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
|
||||
alias custom='cd /usr/local/share/pwnagotchi/custom-plugins/'
|
||||
alias config='sudo nano /etc/pwnagotchi/config.toml'
|
||||
alias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered "s/,[[:digit:]]\\{3\\}\\]//g" | cut -d " " -f 2-'
|
||||
alias pwnver='python3 -c "import pwnagotchi as p; print(p.__version__)"'
|
||||
alias pwnkill='sudo killall -USR1 pwnagotchi'
|
||||
when: golang.changed
|
||||
|
||||
- name: download pwngrid
|
||||
unarchive:
|
||||
remote_src: yes
|
||||
src: "{{ packages.opwngrid.url }}"
|
||||
dest: /usr/local/bin/
|
||||
mode: 0755
|
||||
|
||||
- name: download and install bettercap
|
||||
unarchive:
|
||||
src: "{{ packages.bettercap.url }}"
|
||||
dest: /usr/local/bin
|
||||
remote_src: yes
|
||||
exclude:
|
||||
- README.md
|
||||
- LICENSE.md
|
||||
mode: 0755
|
||||
|
||||
- name: clone bettercap caplets
|
||||
git:
|
||||
repo: "{{ packages.caplets.source }}"
|
||||
dest: /tmp/caplets
|
||||
register: capletsgit
|
||||
|
||||
- name: install bettercap caplets
|
||||
make:
|
||||
chdir: /tmp/caplets
|
||||
target: install
|
||||
when: capletsgit.changed
|
||||
|
||||
- name: download and install bettercap ui
|
||||
unarchive:
|
||||
src: "{{ packages.bettercap.ui }}"
|
||||
dest: /usr/local/share/bettercap/
|
||||
remote_src: yes
|
||||
mode: 0755
|
||||
|
||||
# to always have the bettercap webui available (because why not?)
|
||||
- name: copy pwnagotchi-manual over pwnagotchi-auto caplet
|
||||
ansible.builtin.copy:
|
||||
src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap
|
||||
dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap
|
||||
force: true
|
||||
ignore_errors: true
|
||||
|
||||
- name: create /etc/pwnagotchi folder
|
||||
file:
|
||||
path: /etc/pwnagotchi
|
||||
state: directory
|
||||
|
||||
- name: create log folder
|
||||
file:
|
||||
path: /home/pi/logs
|
||||
state: directory
|
||||
|
||||
- name: check if user configuration exists
|
||||
stat:
|
||||
path: /etc/pwnagotchi/config.toml
|
||||
register: user_config
|
||||
|
||||
- name: create /etc/pwnagotchi/config.toml
|
||||
copy:
|
||||
dest: /etc/pwnagotchi/config.toml
|
||||
content: |
|
||||
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
|
||||
# Example:
|
||||
# ui.display.enabled = true
|
||||
# ui.display.type = "waveshare_4"
|
||||
when: not user_config.stat.exists
|
||||
|
||||
- name: Delete motd 10-uname
|
||||
file:
|
||||
state: absent
|
||||
path: /etc/update-motd.d/10-uname
|
||||
|
||||
- name: enable ssh on boot
|
||||
file:
|
||||
path: /boot/ssh
|
||||
state: touch
|
||||
|
||||
- name: adjust /boot/config.txt
|
||||
lineinfile:
|
||||
dest: /boot/config.txt
|
||||
insertafter: EOF
|
||||
line: '{{ item }}'
|
||||
with_items: "{{system.boot_options}}"
|
||||
|
||||
- name: adjust /etc/modules
|
||||
lineinfile:
|
||||
dest: /etc/modules
|
||||
insertafter: EOF
|
||||
line: '{{ item }}'
|
||||
with_items: "{{system.modules}}"
|
||||
|
||||
- name: change root partition
|
||||
replace:
|
||||
dest: /boot/cmdline.txt
|
||||
backup: no
|
||||
regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+"
|
||||
replace: "root=/dev/mmcblk0p2"
|
||||
|
||||
- name: configure /boot/cmdline.txt
|
||||
lineinfile:
|
||||
path: /boot/cmdline.txt
|
||||
backrefs: True
|
||||
state: present
|
||||
backup: no
|
||||
regexp: '(.*)$'
|
||||
line: '\1 modules-load=dwc2,g_ether'
|
||||
|
||||
- name: add firmware packages to hold
|
||||
dpkg_selections:
|
||||
name: "{{ item }}"
|
||||
selection: hold
|
||||
with_items: "{{ packages.apt.hold }}"
|
||||
|
||||
- name: disable unnecessary services
|
||||
systemd:
|
||||
name: "{{ item }}"
|
||||
state: stopped
|
||||
enabled: no
|
||||
with_items: "{{ services.disable }}"
|
||||
|
||||
- name: enable services
|
||||
systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: true
|
||||
state: stopped
|
||||
with_items: "{{ services.enable }}"
|
||||
|
||||
#- name: remove golang build libraries
|
||||
# file:
|
||||
# state: absent
|
||||
# path: /root/go
|
||||
|
||||
#- name: remove golang
|
||||
# file:
|
||||
# state: absent
|
||||
# path: /usr/local/go
|
||||
|
||||
- name: make /root readable, becauase that's where all the files are
|
||||
file:
|
||||
path: /root
|
||||
mode: '755'
|
||||
|
||||
- name: fix permissions on /home/pi
|
||||
file:
|
||||
path: /home/pi
|
||||
owner: pi
|
||||
group: pi
|
||||
recurse: true
|
||||
|
||||
- name: remove unnecessary apt packages
|
||||
apt:
|
||||
name: "{{ packages.apt.remove }}"
|
||||
state: absent
|
||||
purge: yes
|
||||
|
||||
- name: remove dependencies that are no longer required
|
||||
apt:
|
||||
autoremove: yes
|
||||
|
||||
- name: clean apt cache
|
||||
apt:
|
||||
autoclean: true
|
||||
|
||||
- name: remove golang build libraries
|
||||
file:
|
||||
state: absent
|
||||
path: /root/go
|
||||
|
||||
- name: remove pre-collected packages zip
|
||||
file:
|
||||
path: /root/go_pkgs.tgz
|
||||
state: absent
|
||||
|
||||
- name: remove golang
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/go
|
||||
|
||||
- name: remove /root/.cache (pip cache)
|
||||
file:
|
||||
state: absent
|
||||
path: /root/.cache
|
||||
|
||||
- name: remove ssh keys
|
||||
file:
|
||||
state: absent
|
||||
path: "{{ item }}"
|
||||
with_fileglob:
|
||||
- "/etc/ssh/ssh_host*_key*"
|
||||
|
||||
- name: regenerate ssh keys
|
||||
shell: "dpkg-reconfigure openssh-server"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
|
||||
handlers:
|
||||
- name: reload systemd services
|
||||
systemd:
|
||||
daemon_reload: yes
|
@ -1,15 +0,0 @@
|
||||
client_config_backend: file
|
||||
client_config_file: /root/client_secrets.json
|
||||
client_config:
|
||||
client_id: <YOUR CLIENT ID>
|
||||
client_secret: <YOUR CLIENT SECRET>
|
||||
|
||||
save_credentials: True
|
||||
save_credentials_backend: file
|
||||
save_credentials_file: /root/credentials.json
|
||||
|
||||
get_refresh_token: True
|
||||
|
||||
oauth_scope:
|
||||
- https://www.googleapis.com/auth/drive
|
||||
- https://www.googleapis.com/auth/drive.install
|
@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
|
||||
# we need to decrypt something
|
||||
if is_crypted_mode; then
|
||||
while ! is_decrypted; do
|
||||
echo "Waiting for decryption..."
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
# check if wifi driver is bugged
|
||||
if ! check_brcm; then
|
||||
if ! reload_brcm; then
|
||||
echo "Could not reload wifi driver. Reboot"
|
||||
reboot
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
|
||||
# start wlan0mon
|
||||
start_monitor_interface
|
||||
|
||||
if is_auto_mode_no_delete; then
|
||||
/usr/local/bin/bettercap -no-colors -caplet pwnagotchi-auto -iface wlan0mon
|
||||
else
|
||||
/usr/local/bin/bettercap -no-colors -caplet pwnagotchi-manual -iface wlan0mon
|
||||
fi
|
@ -1,149 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
from urllib.parse import parse_qsl
|
||||
|
||||
|
||||
_HTML_FORM_TEMPLATE = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Decryption</title>
|
||||
<style>
|
||||
body {{ text-align: center; padding: 150px; }}
|
||||
h1 {{ font-size: 50px; }}
|
||||
body {{ font: 20px Helvetica, sans-serif; color: #333; }}
|
||||
article {{ display: block; text-align: center; width: 650px; margin: 0 auto;}}
|
||||
input {{
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ccc;
|
||||
}}
|
||||
input[type=password] {{
|
||||
width: 75%;
|
||||
font-size: 24px;
|
||||
}}
|
||||
input[type=submit] {{
|
||||
cursor: pointer;
|
||||
width: 75%;
|
||||
}}
|
||||
input[type=submit]:hover {{
|
||||
background-color: #d9d9d9;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<h1>Decryption</h1>
|
||||
<p>Some of your files are encrypted.</p>
|
||||
<p>Please provide the decryption password.</p>
|
||||
<div>
|
||||
<form action="/set-password" method="POST">
|
||||
{password_fields}
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
POST_RESPONSE = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
/* Center the loader */
|
||||
#loader {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
z-index: 1;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: -75px 0 0 -75px;
|
||||
border: 16px solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
border-top: 16px solid #3498db;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
-webkit-animation: spin 2s linear infinite;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
#myDiv {
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
|
||||
function checkPwnagotchi() {
|
||||
var target = 'http://' + document.location.hostname + ':8080/';
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', target);
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200 || xhr.status == 401) {
|
||||
window.location.replace(target);
|
||||
}else{
|
||||
setTimeout(checkPwnagotchi, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
setTimeout(checkPwnagotchi, 1000);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body style="margin:0;">
|
||||
|
||||
<div id="loader"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
HTML_FORM = None
|
||||
|
||||
|
||||
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(HTML_FORM.encode())
|
||||
|
||||
def do_POST(self):
|
||||
content_length = int(self.headers['Content-Length'])
|
||||
body = self.rfile.read(content_length)
|
||||
for mapping, password in parse_qsl(body.decode('UTF-8')):
|
||||
with open('/tmp/.pwnagotchi-secret-{}'.format(mapping), 'wt') as pwfile:
|
||||
pwfile.write(password)
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(POST_RESPONSE.encode())
|
||||
|
||||
|
||||
with open('/root/.pwnagotchi-crypted') as crypted_file:
|
||||
mappings = [line.split()[0] for line in crypted_file.readlines()]
|
||||
fields = ''.join(['<label for="{m}">Passphrase for {m}:</label>\n<input type="password" id="{m}" name="{m}" value=""><br>'.format(m=m)
|
||||
for m in mappings])
|
||||
HTML_FORM = _HTML_FORM_TEMPLATE.format(password_fields=fields)
|
||||
|
||||
httpd = HTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler)
|
||||
httpd.serve_forever()
|
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
sudo /usr/bin/tvservice -o
|
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
sudo /usr/bin/tvservice -p
|
@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
start_monitor_interface
|
@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
stop_monitor_interface
|
@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
|
||||
# we need to decrypt something
|
||||
if is_crypted_mode; then
|
||||
while ! is_decrypted; do
|
||||
echo "Waiting for decryption..."
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
if is_auto_mode; then
|
||||
/usr/local/bin/pwnagotchi
|
||||
systemctl restart bettercap
|
||||
else
|
||||
/usr/local/bin/pwnagotchi --manual
|
||||
fi
|
@ -1,184 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# check if brcm is stuck
|
||||
check_brcm() {
|
||||
if [[ "$(journalctl -n10 -k --since -5m | grep -c 'brcmf_cfg80211_nexmon_set_channel.*Set Channel failed')" -ge 5 ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# reload mod
|
||||
reload_brcm() {
|
||||
if ! modprobe -r brcmfmac; then
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
if ! modprobe brcmfmac; then
|
||||
return 1
|
||||
fi
|
||||
sleep 2
|
||||
iw dev wlan0 set power_save off
|
||||
return 0
|
||||
}
|
||||
|
||||
# starts mon0
|
||||
start_monitor_interface() {
|
||||
rfkill unblock all
|
||||
ifconfig wlan0 up
|
||||
iw dev wlan0 set power_save off
|
||||
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor
|
||||
rfkill unblock all
|
||||
ifconfig wlan0 down
|
||||
ifconfig wlan0mon up
|
||||
iw dev wlan0mon set power_save off
|
||||
}
|
||||
|
||||
# stops mon0
|
||||
stop_monitor_interface() {
|
||||
ifconfig wlan0mon down && iw dev wlan0mon del
|
||||
reload_brcm
|
||||
ifconfig wlan0 up
|
||||
}
|
||||
|
||||
# returns 0 if the specificed network interface is up
|
||||
is_interface_up() {
|
||||
if grep -qi 'up' /sys/class/net/"$1"/operstate; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# returns 0 if conditions for AUTO mode are met
|
||||
is_auto_mode() {
|
||||
# check override file first
|
||||
if [ -f /root/.pwnagotchi-manual ]; then
|
||||
# remove the override file if found
|
||||
rm -rf /root/.pwnagotchi-manual
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check override file first
|
||||
if [ -f /root/.pwnagotchi-auto ]; then
|
||||
# remove the override file if found
|
||||
rm -rf /root/.pwnagotchi-auto
|
||||
return 0
|
||||
fi
|
||||
|
||||
# if usb0 is up, we're in MANU
|
||||
if is_interface_up usb0; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# if eth0 is up (for other boards), we're in MANU
|
||||
if is_interface_up eth0; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# no override, but none of the interfaces is up -> AUTO
|
||||
return 0
|
||||
}
|
||||
|
||||
# returns 0 if conditions for AUTO mode are met
|
||||
is_auto_mode_no_delete() {
|
||||
# check override file first
|
||||
if [ -f /root/.pwnagotchi-manual ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check override file first
|
||||
if [ -f /root/.pwnagotchi-auto ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# if usb0 is up, we're in MANU
|
||||
if is_interface_up usb0; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# if eth0 is up (for other boards), we're in MANU
|
||||
if is_interface_up eth0; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# no override, but none of the interfaces is up -> AUTO
|
||||
return 0
|
||||
}
|
||||
|
||||
# check if we need to decrypt something
|
||||
is_crypted_mode() {
|
||||
if [ -f /root/.pwnagotchi-crypted ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# decryption loop
|
||||
is_decrypted() {
|
||||
while read -r mapping container mount; do
|
||||
# mapping = name the device or file will be mapped to
|
||||
# container = the luks encrypted device or file
|
||||
# mount = the mountpoint
|
||||
|
||||
# fail if not mounted
|
||||
if ! mountpoint -q "$mount" >/dev/null 2>&1; then
|
||||
if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then
|
||||
</tmp/.pwnagotchi-secret-"$mapping" read -r SECRET
|
||||
if ! test -b /dev/disk/by-id/dm-uuid-*"$(cryptsetup luksUUID "$container" | tr -d -)"*; then
|
||||
if echo -n "$SECRET" | cryptsetup luksOpen -d- "$container" "$mapping" >/dev/null 2>&1; then
|
||||
echo "Container decrypted!"
|
||||
fi
|
||||
fi
|
||||
|
||||
if mount /dev/mapper/"$mapping" "$mount" >/dev/null 2>&1; then
|
||||
echo "Mounted /dev/mapper/$mapping to $mount"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! ip -4 addr show wlan0 | grep inet >/dev/null 2>&1; then
|
||||
>/dev/null 2>&1 ip addr add 192.168.0.10/24 dev wlan0
|
||||
fi
|
||||
|
||||
if ! pgrep -f decryption-webserver >/dev/null 2>&1; then
|
||||
>/dev/null 2>&1 decryption-webserver &
|
||||
fi
|
||||
|
||||
if ! pgrep wpa_supplicant >/dev/null 2>&1; then
|
||||
>/tmp/wpa_supplicant.conf cat <<EOF
|
||||
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
||||
update_config=1
|
||||
ap_scan=2
|
||||
|
||||
network={
|
||||
ssid="DECRYPT-ME"
|
||||
mode=2
|
||||
key_mgmt=WPA-PSK
|
||||
psk="pwnagotchi"
|
||||
frequency=2437
|
||||
}
|
||||
EOF
|
||||
>/dev/null 2>&1 wpa_supplicant -u -s -O -D nl80211 -i wlan0 -c /tmp/wpa_supplicant.conf &
|
||||
fi
|
||||
|
||||
if ! pgrep dnsmasq >/dev/null 2>&1; then
|
||||
>/dev/null 2>&1 dnsmasq -k -p 53 -h -O "6,192.168.0.10" -A "/#/192.168.0.10" -i wlan0 -K -F 192.168.0.50,192.168.0.60,255.255.255.0,24h &
|
||||
fi
|
||||
|
||||
return 1
|
||||
fi
|
||||
done </root/.pwnagotchi-crypted
|
||||
|
||||
# overwrite passwords
|
||||
python3 -c 'print("A"*4096)' | tee /tmp/.pwnagotchi-secret-* >/dev/null
|
||||
# delete
|
||||
rm /tmp/.pwnagotchi-secret-*
|
||||
sync # flush
|
||||
|
||||
pkill wpa_supplicant
|
||||
pkill dnsmasq
|
||||
pid="$(pgrep -f "decryption-webserver")"
|
||||
[[ -n "$pid" ]] && kill "$pid"
|
||||
|
||||
return 0
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
[main]
|
||||
plugins=keyfile,ifupdown
|
||||
|
||||
[ifupdown]
|
||||
managed=true
|
@ -1,36 +0,0 @@
|
||||
_show_complete()
|
||||
{
|
||||
local cur opts node_names all_options opt_line
|
||||
all_options="
|
||||
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --wizard --check-update --donate {plugins,google}
|
||||
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
|
||||
pwnagotchi plugins list -i --installed -h --help
|
||||
pwnagotchi plugins install -h --help
|
||||
pwnagotchi plugins uninstall -h --help
|
||||
pwnagotchi plugins enable -h --help
|
||||
pwnagotchi plugins disable -h --help
|
||||
pwnagotchi plugins update -h --help
|
||||
pwnagotchi plugins upgrade -h --help
|
||||
pwnagotchi google -h --help {login,refresh}
|
||||
pwnagotchi google login -h --help
|
||||
pwnagotchi google refresh -h --help
|
||||
"
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
# shellcheck disable=SC2124
|
||||
cmd="${COMP_WORDS[@]:0:${#COMP_WORDS[@]}-1}"
|
||||
opt_line="$(grep -m1 "$cmd" <<<"$all_options")"
|
||||
if [[ ${cur} == -* ]] ; then
|
||||
opts="$(echo "$opt_line" | tr ' ' '\n' | awk '/^ *-/{gsub("[^a-zA-Z0-9-]","",$1);print $1}')"
|
||||
# shellcheck disable=SC2207
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')"
|
||||
# shellcheck disable=SC2207
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
}
|
||||
|
||||
complete -F _show_complete pwnagotchi
|
@ -1,26 +0,0 @@
|
||||
# /etc/dphys-swapfile - user settings for dphys-swapfile package
|
||||
# author Neil Franklin, last modification 2010.05.05
|
||||
# copyright ETH Zuerich Physics Departement
|
||||
# use under either modified/non-advertising BSD or GPL license
|
||||
|
||||
# this file is sourced with . so full normal sh syntax applies
|
||||
|
||||
# the default settings are added as commented out CONF_*=* lines
|
||||
|
||||
|
||||
# where we want the swapfile to be, this is the default
|
||||
#CONF_SWAPFILE=/var/swap
|
||||
|
||||
# set size to absolute value, leaving empty (default) then uses computed value
|
||||
# you most likely don't want this, unless you have an special disk situation
|
||||
CONF_SWAPSIZE=2048
|
||||
|
||||
# set size to computed value, this times RAM size, dynamically adapts,
|
||||
# guarantees that there is enough swap without wasting disk space on excess
|
||||
#CONF_SWAPFACTOR=2
|
||||
|
||||
# restrict size (computed and absolute!) to maximally this limit
|
||||
# can be set to empty for no limit, but beware of filled partitions!
|
||||
# this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it
|
||||
# but is also sensible on 64bit to prevent filling /var or even / partition
|
||||
#CONF_MAXSWAP=2048
|
@ -1,2 +0,0 @@
|
||||
allow-hotplug eth0
|
||||
iface eth0 inet dhcp
|
@ -1,2 +0,0 @@
|
||||
auto lo
|
||||
iface lo inet loopback
|
@ -1,8 +0,0 @@
|
||||
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
|
||||
metric 101
|
@ -1,2 +0,0 @@
|
||||
allow-hotplug wlan0
|
||||
iface wlan0 inet static
|
@ -1,16 +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.
|
||||
|
||||
timedatectl set-ntp true
|
||||
|
||||
exit 0
|
@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=bettercap api.rest service.
|
||||
Documentation=https://bettercap.org
|
||||
Wants=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/bettercap-launcher
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,20 +0,0 @@
|
||||
[Unit]
|
||||
Description=Bluetooth service
|
||||
Documentation=man:bluetoothd(8)
|
||||
ConditionPathIsDirectory=/sys/class/bluetooth
|
||||
|
||||
[Service]
|
||||
Type=dbus
|
||||
BusName=org.bluez
|
||||
ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp
|
||||
NotifyAccess=main
|
||||
#WatchdogSec=10
|
||||
#Restart=on-failure
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
LimitNPROC=1
|
||||
ProtectHome=true
|
||||
ProtectSystem=full
|
||||
|
||||
[Install]
|
||||
WantedBy=bluetooth.target
|
||||
Alias=dbus-org.bluez.service
|
@ -1,20 +0,0 @@
|
||||
[Unit]
|
||||
Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.
|
||||
Documentation=https://pwnagotchi.org
|
||||
Wants=network.target
|
||||
After=pwngrid-peer.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=~
|
||||
ExecStart=/usr/bin/pwnagotchi-launcher
|
||||
ExecStopPost=/usr/bin/bash -c "if egrep -qi 'personality.clear_on_exit[ =]*true' /etc/pwnagotchi/config.toml ; then /usr/local/bin/pwnagotchi --clear; fi"
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
TasksMax=infinity
|
||||
LimitNPROC=infinity
|
||||
StandardOutput=null
|
||||
StandardError=null
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,16 +0,0 @@
|
||||
[Unit]
|
||||
Description=pwngrid peer service.
|
||||
Documentation=https://pwnagotchi.ai
|
||||
Wants=network.target
|
||||
After=bettercap.service
|
||||
|
||||
[Service]
|
||||
Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1
|
||||
Environment=LD_LIBRARY_PATH=/usr/local/lib
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /etc/pwnagotchi/log/pwngrid-peer.log -iface wlan0mon
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,34 +0,0 @@
|
||||
#!/bin/sh
|
||||
_hostname=$(hostname)
|
||||
_version=$(cut -d"'" -f2 < /usr/local/lib/python3.11/dist-packages/pwnagotchi/_version.py)
|
||||
echo
|
||||
echo "(☉_☉ ) $_hostname"
|
||||
echo
|
||||
echo " Hi! I'm a pwnagotchi $_version, please take good care of me!"
|
||||
echo " Here are some basic things you need to know to raise me properly!"
|
||||
echo
|
||||
echo " If you want to change my configuration, use /etc/pwnagotchi/config.toml"
|
||||
echo " All plugin config files are located in /etc/pwnagotchi/conf.d/"
|
||||
echo " Read the readme if you want to use gdrivesync plugin!!"
|
||||
echo
|
||||
echo " All the configuration options can be found on /etc/pwnagotchi/default.toml,"
|
||||
echo " but don't change this file because I will recreate it every time I'm restarted!"
|
||||
echo
|
||||
echo " I use oPwnGrid as my main API, you can check stats at https://opwngrid.xyz"
|
||||
echo
|
||||
echo " I'm managed by systemd. Here are some basic commands."
|
||||
echo
|
||||
echo " If you want to know what I'm doing, you can check my logs with the command"
|
||||
echo " - pwnlog"
|
||||
echo " - sudo pwnagotchi --wizard, to help set up a config.toml"
|
||||
echo " - sudo pwnagotchi --version, to check the current version"
|
||||
echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
|
||||
echo " - sudo pwnagotchi --check-update, to see if there is a new version available"
|
||||
echo
|
||||
echo " If you want to know if I'm running, you can use"
|
||||
echo " sudo systemctl status pwnagotchi"
|
||||
echo
|
||||
echo " You can restart me using"
|
||||
echo " pwnkill"
|
||||
echo
|
||||
echo " You can learn more about me at https://pwnagotchi.org/"
|
@ -1,100 +0,0 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
arm = {
|
||||
version = "1.0.0"
|
||||
source = "github.com/cdecoux/builder-arm"
|
||||
}
|
||||
ansible = {
|
||||
source = "github.com/hashicorp/ansible"
|
||||
version = ">= 1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "pwn_hostname" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "pwn_version" {
|
||||
type = string
|
||||
}
|
||||
|
||||
source "arm" "rpi64-pwnagotchi" {
|
||||
file_checksum_url = "https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz.sha256"
|
||||
file_urls = ["https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz"]
|
||||
file_checksum_type = "sha256"
|
||||
file_target_extension = "xz"
|
||||
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||
image_path = "../../../pwnagotchi-64bit.img"
|
||||
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||
image_build_method = "resize"
|
||||
image_size = "9G"
|
||||
image_type = "dos"
|
||||
image_partitions {
|
||||
name = "boot"
|
||||
type = "c"
|
||||
start_sector = "8192"
|
||||
filesystem = "fat"
|
||||
size = "256M"
|
||||
mountpoint = "/boot/firmware"
|
||||
}
|
||||
image_partitions {
|
||||
name = "root"
|
||||
type = "83"
|
||||
start_sector = "532480"
|
||||
filesystem = "ext4"
|
||||
size = "0"
|
||||
mountpoint = "/"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
# a build block invokes sources and runs provisioning steps on them. The
|
||||
# documentation for build blocks can be found here:
|
||||
# https://www.packer.io/docs/from-1.5/blocks/build
|
||||
build {
|
||||
name = "Raspberry Pi 64 Pwnagotchi"
|
||||
sources = ["source.arm.rpi64-pwnagotchi"]
|
||||
|
||||
provisioner "file" {
|
||||
destination = "/usr/bin/"
|
||||
sources = [
|
||||
"data/64bit/usr/bin/bettercap-launcher",
|
||||
"data/64bit/usr/bin/hdmioff",
|
||||
"data/64bit/usr/bin/hdmion",
|
||||
"data/64bit/usr/bin/monstart",
|
||||
"data/64bit/usr/bin/monstop",
|
||||
"data/64bit/usr/bin/pwnagotchi-launcher",
|
||||
"data/64bit/usr/bin/pwnlib",
|
||||
]
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["chmod +x /usr/bin/*"]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
destination = "/etc/systemd/system/"
|
||||
sources = [
|
||||
"data/64bit/etc/systemd/system/bettercap.service",
|
||||
"data/64bit/etc/systemd/system/pwnagotchi.service",
|
||||
"data/64bit/etc/systemd/system/pwngrid-peer.service",
|
||||
]
|
||||
}
|
||||
provisioner "file" {
|
||||
destination = "/etc/update-motd.d/01-motd"
|
||||
source = "data/64bit/etc/update-motd.d/01-motd"
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["chmod +x /etc/update-motd.d/*"]
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
|
||||
}
|
||||
provisioner "ansible-local" {
|
||||
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
||||
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
||||
playbook_file = "data/64bit/raspberrypi64.yml"
|
||||
}
|
||||
}
|
@ -1,718 +0,0 @@
|
||||
---
|
||||
- hosts:
|
||||
- 127.0.0.1
|
||||
gather_facts: true
|
||||
become: true
|
||||
vars:
|
||||
kernel:
|
||||
min: "6.6"
|
||||
full: "6.6.20+rpt-rpi-v8"
|
||||
full_pi5: "6.6.20+rpt-rpi-2712"
|
||||
pwnagotchi:
|
||||
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
||||
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
|
||||
system:
|
||||
boot_options:
|
||||
- "dtoverlay=dwc2"
|
||||
- "dtparam=i2c1=on"
|
||||
- "dtparam=i2c_arm=on"
|
||||
- "dtparam=spi=on"
|
||||
- "gpu_mem=16"
|
||||
- "[pi0]"
|
||||
- "dtoverlay=spi0-0cs"
|
||||
- "[pi3]"
|
||||
- "dtoverlay=spi0-0cs"
|
||||
- "[pi4]"
|
||||
- "dtoverlay=spi0-0cs"
|
||||
- "[pi5]"
|
||||
- "dtoverlay=spi0-0cs"
|
||||
modules:
|
||||
- "i2c-dev"
|
||||
services:
|
||||
enable:
|
||||
- bettercap.service
|
||||
- fstrim.timer
|
||||
- pwnagotchi.service
|
||||
- pwngrid-peer.service
|
||||
disable:
|
||||
- apt-daily-upgrade.service
|
||||
- apt-daily-upgrade.timer
|
||||
- apt-daily.service
|
||||
- apt-daily.timer
|
||||
- bluetooth.service
|
||||
- ifup@wlan0.service
|
||||
packages:
|
||||
caplets:
|
||||
source: "https://github.com/jayofelony/caplets.git"
|
||||
bettercap:
|
||||
source: "https://github.com/jayofelony/bettercap.git"
|
||||
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.4/bettercap-2.32.4.zip"
|
||||
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||
pwngrid:
|
||||
source: "https://github.com/jayofelony/pwngrid.git"
|
||||
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.5/pwngrid-1.10.5-aarch64.zip"
|
||||
apt:
|
||||
downgrade:
|
||||
- libpcap-dev_1.9.1-4_arm64.deb
|
||||
- libpcap0.8-dbg_1.9.1-4_arm64.deb
|
||||
- libpcap0.8-dev_1.9.1-4_arm64.deb
|
||||
- libpcap0.8_1.9.1-4_arm64.deb
|
||||
hold:
|
||||
- firmware-atheros
|
||||
- firmware-brcm80211
|
||||
- firmware-libertas
|
||||
- firmware-misc-nonfree
|
||||
- firmware-realtek
|
||||
- libpcap-dev
|
||||
- libpcap0.8
|
||||
- libpcap0.8-dbg
|
||||
- libpcap0.8-dev
|
||||
remove:
|
||||
- avahi-daemon
|
||||
- dhpys-swapfile
|
||||
- libcurl-ocaml-dev
|
||||
- libssl-ocaml-dev
|
||||
- nfs-common
|
||||
- triggerhappy
|
||||
- wpasupplicant
|
||||
install:
|
||||
- aircrack-ng
|
||||
- autoconf
|
||||
- bc
|
||||
- bison
|
||||
- bluez
|
||||
- build-essential
|
||||
- curl
|
||||
- dkms
|
||||
- dphys-swapfile
|
||||
- fbi
|
||||
- firmware-atheros
|
||||
- firmware-brcm80211
|
||||
- firmware-libertas
|
||||
- firmware-misc-nonfree
|
||||
- firmware-realtek
|
||||
- flex
|
||||
- fonts-dejavu
|
||||
- fonts-dejavu-core
|
||||
- fonts-dejavu-extra
|
||||
- fonts-freefont-ttf
|
||||
- g++
|
||||
- gawk
|
||||
- gcc-arm-none-eabi
|
||||
- git
|
||||
- hcxtools
|
||||
- libatlas-base-dev
|
||||
- libavcodec59
|
||||
- libavformat59
|
||||
- libblas-dev
|
||||
- libbluetooth-dev
|
||||
- libbz2-dev
|
||||
- libc-ares-dev
|
||||
- libc6-dev
|
||||
- libcap-dev
|
||||
- libcurl-ocaml-dev
|
||||
- libdbus-1-dev
|
||||
- libdbus-glib-1-dev
|
||||
- libeigen3-dev
|
||||
- libelf-dev
|
||||
- libffi-dev
|
||||
- libfl-dev
|
||||
- libfuse-dev
|
||||
- libgdbm-dev
|
||||
- libgl1-mesa-glx
|
||||
- libgmp3-dev
|
||||
- libgstreamer1.0-0
|
||||
- libhdf5-dev
|
||||
- liblapack-dev
|
||||
- libncursesw5-dev
|
||||
- libnetfilter-queue-dev
|
||||
- libopenblas-dev
|
||||
- libopenjp2-7
|
||||
- libopenmpi-dev
|
||||
- libopenmpi3
|
||||
- libpcap-dev
|
||||
- libraspberrypi-bin
|
||||
- libraspberrypi-dev
|
||||
- libraspberrypi-doc
|
||||
- libraspberrypi0
|
||||
- libsqlite3-dev
|
||||
- libssl-dev
|
||||
- libssl-ocaml-dev
|
||||
- libswscale5
|
||||
- libtiff6
|
||||
- libtool
|
||||
- libusb-1.0-0-dev
|
||||
- lsof
|
||||
- make
|
||||
- ntp
|
||||
- python3-dbus
|
||||
- python3-flask
|
||||
- python3-flask-cors
|
||||
- python3-flaskext.wtf
|
||||
- python3-gast
|
||||
- python3-pil
|
||||
- python3-pip
|
||||
- python3-pycryptodome
|
||||
- python3-requests
|
||||
- python3-scapy
|
||||
- python3-setuptools
|
||||
- python3-smbus
|
||||
- python3-smbus2
|
||||
- python3-spidev
|
||||
- python3-tweepy
|
||||
- python3-werkzeug
|
||||
- python3-yaml
|
||||
- qpdf
|
||||
- raspberrypi-kernel-headers
|
||||
- rsync
|
||||
- screen
|
||||
- tcpdump
|
||||
- texinfo
|
||||
- time
|
||||
- tk-dev
|
||||
- unzip
|
||||
- vim
|
||||
- wget
|
||||
- wl
|
||||
- xxd
|
||||
- zlib1g-dev
|
||||
environment:
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
|
||||
tasks:
|
||||
# First we install packages
|
||||
- name: install packages
|
||||
apt:
|
||||
name: "{{ packages.apt.install }}"
|
||||
state: present
|
||||
update_cache: yes
|
||||
install_recommends: false
|
||||
|
||||
- name: update pip3, setuptools, wheel
|
||||
shell: "python3 -m pip install --upgrade pip setuptools wheel --break-system-packages"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src
|
||||
|
||||
# Now we set up /boot/firmware
|
||||
- name: Create pi user
|
||||
copy:
|
||||
dest: /boot/firmware/userconf
|
||||
content: |
|
||||
pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
|
||||
|
||||
- name: enable ssh on boot
|
||||
file:
|
||||
path: /boot/firmware/ssh
|
||||
state: touch
|
||||
|
||||
- name: remove current rc.local
|
||||
file:
|
||||
path: /etc/rc.local
|
||||
state: absent
|
||||
|
||||
- name: adjust /boot/firmware/config.txt
|
||||
blockinfile:
|
||||
dest: /boot/firmware/config.txt
|
||||
insertafter: EOF
|
||||
block: |
|
||||
dtoverlay=dwc2
|
||||
dtparam=i2c1=on
|
||||
dtparam=i2c_arm=on"
|
||||
dtparam=spi=on
|
||||
gpu_mem=16
|
||||
[pi0]
|
||||
dtoverlay=spi0-0cs
|
||||
[pi3]
|
||||
dtoverlay=spi0-0cs
|
||||
[pi4]
|
||||
dtoverlay=spi0-0cs
|
||||
[pi5]
|
||||
dtoverlay=spi0-0cs
|
||||
|
||||
- name: change root partition
|
||||
replace:
|
||||
dest: /boot/firmware/cmdline.txt
|
||||
backup: no
|
||||
regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+"
|
||||
replace: "root=/dev/mmcblk0p2"
|
||||
|
||||
- name: configure /boot/firmware/cmdline.txt
|
||||
lineinfile:
|
||||
path: /boot/firmware/cmdline.txt
|
||||
backrefs: True
|
||||
state: present
|
||||
backup: no
|
||||
regexp: '(.*)$'
|
||||
line: '\1 modules-load=dwc2,g_ether'
|
||||
|
||||
- name: change hostname
|
||||
lineinfile:
|
||||
dest: /etc/hostname
|
||||
regexp: '^raspberrypi'
|
||||
line: "{{pwnagotchi.hostname}}"
|
||||
state: present
|
||||
when: lookup('file', '/etc/hostname') == "raspberrypi"
|
||||
register: hostname
|
||||
|
||||
- name: add hostname to /etc/hosts
|
||||
lineinfile:
|
||||
dest: /etc/hosts
|
||||
regexp: '^127\.0\.1\.1[ \t]+raspberrypi'
|
||||
line: "127.0.1.1\t{{pwnagotchi.hostname}}"
|
||||
state: present
|
||||
when: hostname.changed
|
||||
|
||||
# Now we disable sap and a2dp, we don't use them on rpi
|
||||
- name: disable sap plugin for bluetooth.service
|
||||
lineinfile:
|
||||
dest: /lib/systemd/system/bluetooth.service
|
||||
regexp: '^ExecStart=/usr/libexec/bluetooth/bluetoothd$'
|
||||
line: 'ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp'
|
||||
state: present
|
||||
|
||||
###########################################
|
||||
#
|
||||
# libpcap v1.9 - build from source
|
||||
#
|
||||
###########################################
|
||||
|
||||
# check for presence, then it can re-run in later parts if needed
|
||||
# use the "make" built in
|
||||
|
||||
# install libpcap before bettercap and pwngrid, so they use it
|
||||
- name: clone libpcap v1.9 from github
|
||||
git:
|
||||
repo: 'https://github.com/the-tcpdump-group/libpcap.git'
|
||||
dest: /usr/local/src/libpcap
|
||||
version: libpcap-1.9
|
||||
|
||||
- name: build and install libpcap into /usr/local/lib
|
||||
shell: "./configure && make && make install"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/libpcap
|
||||
|
||||
- name: remove libpcap build folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/libpcap
|
||||
|
||||
- name: create symlink /usr/local/lib/libpcap.so.1.9.1
|
||||
file:
|
||||
src: /usr/local/lib/libpcap.so.1.9.1
|
||||
dest: /usr/local/lib/libpcap.so.0.8
|
||||
state: link
|
||||
|
||||
# install latest hcxtools
|
||||
|
||||
- name: clone hcxtools
|
||||
git:
|
||||
repo: https://github.com/ZerBea/hcxtools.git
|
||||
dest: /usr/local/src/hcxtools
|
||||
|
||||
- name: install hcxtools
|
||||
shell: "make && make install"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/hcxtools
|
||||
|
||||
- name: remove hcxtools directory
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/hcxtools
|
||||
|
||||
- name: clone nexmon repository
|
||||
git:
|
||||
repo: https://github.com/DrSchottky/nexmon.git
|
||||
dest: /usr/local/src/nexmon
|
||||
|
||||
# FIRST WE BUILD DRIVER FOR RPi5
|
||||
|
||||
- name: make firmware, RPi5
|
||||
shell: "source ./setup_env.sh && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
|
||||
- name: make firmware patch (bcm43455c0), RPi5
|
||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
|
||||
- name: copy modified driver, RPi5
|
||||
copy:
|
||||
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
|
||||
dest: "/usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
|
||||
- name: Delete the modified driver, RPi5
|
||||
file:
|
||||
state: absent
|
||||
path: '/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko'
|
||||
|
||||
- name: backup original driver, RPi5
|
||||
command: "mv /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
|
||||
|
||||
- name: load brcmfmac drivers
|
||||
command: "/sbin/depmod {{ kernel.full_pi5 }}"
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
||||
|
||||
- name: Delete nexmon content & directory
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/nexmon/
|
||||
|
||||
# NOW WE BUILD DRIVERS FOR RPi4, RPizero2w and RPi3
|
||||
|
||||
- name: clone nexmon repository
|
||||
git:
|
||||
repo: https://github.com/DrSchottky/nexmon.git
|
||||
dest: /usr/local/src/nexmon
|
||||
|
||||
- name: make firmware, RPi4
|
||||
shell: "source ./setup_env.sh && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full }}"
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
|
||||
- name: make firmware patch (bcm43455c0), RPi4
|
||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full }}"
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
|
||||
- name: install new firmware (bcm43455c0), RPi4 RPi5
|
||||
copy:
|
||||
src: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/brcmfmac43455-sdio.bin
|
||||
dest: /usr/lib/firmware/brcm/brcmfmac43455-sdio.bin
|
||||
follow: true
|
||||
|
||||
# NOW WE BUILD DRIVERS FOR RPiZero2W, RPi 3
|
||||
|
||||
- name: make firmware patch (bcm43436b0)
|
||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full }}"
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
|
||||
- name: install new firmware (bcm43436b0)
|
||||
copy:
|
||||
src: /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/brcmfmac43436-sdio.bin
|
||||
dest: /usr/lib/firmware/brcm/brcmfmac43436-sdio.bin
|
||||
follow: true
|
||||
|
||||
- name: make firmware patch (bcm43430a1)
|
||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/ && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full }}"
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
|
||||
- name: copy modified driver, RPi4
|
||||
copy:
|
||||
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
|
||||
dest: "/usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full }}"
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
|
||||
- name: install new firmware (bcm43430a1)
|
||||
copy:
|
||||
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
|
||||
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
||||
follow: true
|
||||
|
||||
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
|
||||
copy:
|
||||
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
||||
dest: /usr/lib/firmware/brcm/brcmfmac43436s-sdio.bin
|
||||
follow: true
|
||||
|
||||
# delete blob files that make nexmon sad
|
||||
- name: Delete the firmware blob files to avoid some nexmon crashing
|
||||
file:
|
||||
state: absent
|
||||
path: '{{ item }}'
|
||||
loop:
|
||||
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43430b0-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43455-sdio.clm_blob
|
||||
|
||||
- name: backup original driver
|
||||
command: "mv /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
|
||||
|
||||
- name: load brcmfmac drivers
|
||||
command: "/sbin/depmod {{ kernel.full }}"
|
||||
environment:
|
||||
QEMU_UNAME: "{{ kernel.full }}"
|
||||
|
||||
# To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation
|
||||
- name: Delete nexmon content & directory
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/nexmon/
|
||||
|
||||
- name: Create custom plugin directory
|
||||
file:
|
||||
path: /usr/local/share/pwnagotchi/custom-plugins/
|
||||
state: directory
|
||||
|
||||
- name: Create custom config directory
|
||||
file:
|
||||
path: /etc/pwnagotchi/conf.d/
|
||||
state: directory
|
||||
|
||||
- name: clone pwnagotchi repository
|
||||
git:
|
||||
repo: https://github.com/jayofelony/pwnagotchi.git
|
||||
dest: /usr/local/src/pwnagotchi
|
||||
|
||||
- name: build pwnagotchi wheel
|
||||
command: "pip3 install . --no-cache-dir --break-system-packages"
|
||||
args:
|
||||
chdir: /usr/local/src/pwnagotchi
|
||||
|
||||
- name: remove pwnagotchi folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/pwnagotchi
|
||||
|
||||
- name: create /usr/local/share/pwnagotchi/ folder
|
||||
file:
|
||||
path: /usr/local/share/pwnagotchi/
|
||||
state: directory
|
||||
|
||||
- name: Install go-1.21
|
||||
unarchive:
|
||||
src: https://go.dev/dl/go1.21.5.linux-arm64.tar.gz
|
||||
dest: /usr/local
|
||||
remote_src: yes
|
||||
register: golang
|
||||
|
||||
- name: Update .bashrc for go-1.21
|
||||
blockinfile:
|
||||
dest: /home/pi/.bashrc
|
||||
state: present
|
||||
block: |
|
||||
export GOPATH=$HOME/go
|
||||
export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
|
||||
alias custom='cd /usr/local/share/pwnagotchi/custom-plugins/'
|
||||
alias config='sudo nano /etc/pwnagotchi/config.toml'
|
||||
alias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered "s/,[[:digit:]]\\{3\\}\\]//g" | cut -d " " -f 2-'
|
||||
alias pwnver='python3 -c "import pwnagotchi as p; print(p.__version__)"'
|
||||
alias pwnkill='sudo killall -USR1 pwnagotchi'
|
||||
when: golang.changed
|
||||
|
||||
- name: download pwngrid
|
||||
git:
|
||||
repo: "{{ packages.pwngrid.source }}"
|
||||
dest: /usr/local/src/pwngrid
|
||||
|
||||
- name: install pwngrid
|
||||
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/pwngrid
|
||||
|
||||
- name: remove pwngrid folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/pwngrid
|
||||
|
||||
- name: download bettercap
|
||||
git:
|
||||
repo: "{{ packages.bettercap.source }}"
|
||||
dest: /usr/local/src/bettercap
|
||||
|
||||
- name: install bettercap 2.32.4
|
||||
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/bettercap
|
||||
|
||||
- name: remove bettercap folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/bettercap
|
||||
|
||||
#- name: download and install bettercap
|
||||
# unarchive:
|
||||
# src: "{{ packages.bettercap.url }}"
|
||||
# dest: /usr/local/bin
|
||||
# remote_src: yes
|
||||
# exclude:
|
||||
# - README.md
|
||||
# - LICENSE.md
|
||||
# mode: 0755
|
||||
|
||||
- name: clone bettercap caplets
|
||||
git:
|
||||
repo: "{{ packages.caplets.source }}"
|
||||
dest: /tmp/caplets
|
||||
register: capletsgit
|
||||
|
||||
- name: install bettercap caplets
|
||||
make:
|
||||
chdir: /tmp/caplets
|
||||
target: install
|
||||
when: capletsgit.changed
|
||||
|
||||
- name: download and install bettercap ui
|
||||
unarchive:
|
||||
src: "{{ packages.bettercap.ui }}"
|
||||
dest: /usr/local/share/bettercap/
|
||||
remote_src: yes
|
||||
mode: 0755
|
||||
|
||||
# to always have the bettercap webui available (because why not?)
|
||||
- name: copy pwnagotchi-manual over pwnagotchi-auto caplet
|
||||
ansible.builtin.copy:
|
||||
src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap
|
||||
dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap
|
||||
force: true
|
||||
ignore_errors: true
|
||||
|
||||
- name: create /etc/pwnagotchi folder
|
||||
file:
|
||||
path: /etc/pwnagotchi
|
||||
state: directory
|
||||
|
||||
- name: check if user configuration exists
|
||||
stat:
|
||||
path: /etc/pwnagotchi/config.toml
|
||||
register: user_config
|
||||
|
||||
- name: create /etc/pwnagotchi/config.toml
|
||||
copy:
|
||||
dest: /etc/pwnagotchi/config.toml
|
||||
content: |
|
||||
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
|
||||
# Example:
|
||||
# ui.display.enabled = true
|
||||
# ui.display.type = "waveshare_4"
|
||||
when: not user_config.stat.exists
|
||||
|
||||
- name: Delete motd
|
||||
file:
|
||||
state: absent
|
||||
path: /etc/motd
|
||||
|
||||
- name: Delete motd 10-uname
|
||||
file:
|
||||
state: absent
|
||||
path: /etc/update-motd.d/10-uname
|
||||
|
||||
- name: add firmware packages to hold
|
||||
dpkg_selections:
|
||||
name: "{{ item }}"
|
||||
selection: hold
|
||||
with_items: "{{ packages.apt.hold }}"
|
||||
|
||||
- name: disable unnecessary services
|
||||
systemd:
|
||||
name: "{{ item }}"
|
||||
state: stopped
|
||||
enabled: no
|
||||
with_items: "{{ services.disable }}"
|
||||
|
||||
- name: enable services
|
||||
systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: true
|
||||
state: stopped
|
||||
with_items: "{{ services.enable }}"
|
||||
register: enabled
|
||||
|
||||
- name: make /root readable, becauase that's where all the files are
|
||||
file:
|
||||
path: /root
|
||||
mode: '755'
|
||||
|
||||
- name: fix permissions on /home/pi
|
||||
file:
|
||||
path: /home/pi
|
||||
owner: pi
|
||||
group: pi
|
||||
recurse: true
|
||||
|
||||
- name: remove pre-collected packages zip
|
||||
file:
|
||||
path: /root/go_pkgs.tgz
|
||||
state: absent
|
||||
|
||||
- name: remove /root/go folder
|
||||
file:
|
||||
state: absent
|
||||
path: /root/go
|
||||
|
||||
- name: remove /usr/local/go folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/go
|
||||
|
||||
- name: remove pip cache
|
||||
file:
|
||||
state: absent
|
||||
path: /root/.cache/pip
|
||||
|
||||
- name: remove ssh keys
|
||||
file:
|
||||
state: absent
|
||||
path: "{{ item }}"
|
||||
with_fileglob:
|
||||
- "/etc/ssh/ssh_host*_key*"
|
||||
|
||||
- name: regenerate ssh keys
|
||||
shell: "dpkg-reconfigure openssh-server"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
|
||||
# Now we remove packages
|
||||
- name: remove unnecessary apt packages
|
||||
apt:
|
||||
name: "{{ packages.apt.remove }}"
|
||||
state: absent
|
||||
purge: yes
|
||||
register: removed
|
||||
|
||||
- name: remove dependencies that are no longer required
|
||||
apt:
|
||||
autoremove: yes
|
||||
when: removed.changed
|
||||
|
||||
- name: clean apt cache
|
||||
apt:
|
||||
autoclean: true
|
||||
when: removed.changed
|
||||
|
||||
handlers:
|
||||
- name: reload systemd services
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
when: enabled.changed
|
@ -1,15 +0,0 @@
|
||||
client_config_backend: file
|
||||
client_config_file: /root/client_secrets.json
|
||||
client_config:
|
||||
client_id: <YOUR CLIENT ID>
|
||||
client_secret: <YOUR CLIENT SECRET>
|
||||
|
||||
save_credentials: True
|
||||
save_credentials_backend: file
|
||||
save_credentials_file: /root/credentials.json
|
||||
|
||||
get_refresh_token: True
|
||||
|
||||
oauth_scope:
|
||||
- https://www.googleapis.com/auth/drive
|
||||
- https://www.googleapis.com/auth/drive.install
|
@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
|
||||
# we need to decrypt something
|
||||
if is_crypted_mode; then
|
||||
while ! is_decrypted; do
|
||||
echo "Waiting for decryption..."
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
# start mon0
|
||||
start_monitor_interface
|
||||
|
||||
if is_auto_mode_no_delete; then
|
||||
/usr/local/bin/bettercap -no-colors -caplet pwnagotchi-auto -iface wlan0mon
|
||||
else
|
||||
/usr/local/bin/bettercap -no-colors -caplet pwnagotchi-manual -iface wlan0mon
|
||||
fi
|
@ -1,148 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
from urllib.parse import parse_qsl
|
||||
|
||||
|
||||
_HTML_FORM_TEMPLATE = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Decryption</title>
|
||||
<style>
|
||||
body {{ text-align: center; padding: 150px; }}
|
||||
h1 {{ font-size: 50px; }}
|
||||
body {{ font: 20px Helvetica, sans-serif; color: #333; }}
|
||||
article {{ display: block; text-align: center; width: 650px; margin: 0 auto;}}
|
||||
input {{
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ccc;
|
||||
}}
|
||||
input[type=password] {{
|
||||
width: 75%;
|
||||
font-size: 24px;
|
||||
}}
|
||||
input[type=submit] {{
|
||||
cursor: pointer;
|
||||
width: 75%;
|
||||
}}
|
||||
input[type=submit]:hover {{
|
||||
background-color: #d9d9d9;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<h1>Decryption</h1>
|
||||
<p>Some of your files are encrypted.</p>
|
||||
<p>Please provide the decryption password.</p>
|
||||
<div>
|
||||
<form action="/set-password" method="POST">
|
||||
{password_fields}
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
POST_RESPONSE = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
/* Center the loader */
|
||||
#loader {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
z-index: 1;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: -75px 0 0 -75px;
|
||||
border: 16px solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
border-top: 16px solid #3498db;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
-webkit-animation: spin 2s linear infinite;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
#myDiv {
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
|
||||
function checkPwnagotchi() {
|
||||
var target = 'http://' + document.location.hostname + ':8080/';
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', target);
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200 || xhr.status == 401) {
|
||||
window.location.replace(target);
|
||||
}else{
|
||||
setTimeout(checkPwnagotchi, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
setTimeout(checkPwnagotchi, 1000);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body style="margin:0;">
|
||||
|
||||
<div id="loader"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
HTML_FORM = None
|
||||
|
||||
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(HTML_FORM.encode())
|
||||
|
||||
def do_POST(self):
|
||||
content_length = int(self.headers['Content-Length'])
|
||||
body = self.rfile.read(content_length)
|
||||
for mapping, password in parse_qsl(body.decode('UTF-8')):
|
||||
with open('/tmp/.pwnagotchi-secret-{}'.format(mapping), 'wt') as pwfile:
|
||||
pwfile.write(password)
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(POST_RESPONSE.encode())
|
||||
|
||||
|
||||
with open('/root/.pwnagotchi-crypted') as crypted_file:
|
||||
mappings = [line.split()[0] for line in crypted_file.readlines()]
|
||||
fields = ''.join(['<label for="{m}">Passphrase for {m}:</label>\n<input type="password" id="{m}" name="{m}" value=""><br>'.format(m=m)
|
||||
for m in mappings])
|
||||
HTML_FORM = _HTML_FORM_TEMPLATE.format(password_fields=fields)
|
||||
|
||||
httpd = HTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler)
|
||||
httpd.serve_forever()
|
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
sudo /usr/bin/tvservice -o
|
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
sudo /usr/bin/tvservice -p
|
@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
start_monitor_interface
|
@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
stop_monitor_interface
|
@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
|
||||
# we need to decrypt something
|
||||
if is_crypted_mode; then
|
||||
while ! is_decrypted; do
|
||||
echo "Waiting for decryption..."
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
if is_auto_mode; then
|
||||
/usr/local/bin/pwnagotchi
|
||||
systemctl restart bettercap
|
||||
else
|
||||
/usr/local/bin/pwnagotchi --manual
|
||||
fi
|
@ -1,191 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# well ... it blinks the led
|
||||
blink_led() {
|
||||
# shellcheck disable=SC2034
|
||||
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
|
||||
}
|
||||
|
||||
# reload mod
|
||||
reload_brcm() {
|
||||
if ! modprobe -r brcmfmac; then
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
if ! modprobe brcmfmac; then
|
||||
return 1
|
||||
fi
|
||||
sleep 2
|
||||
iw dev wlan0 set power_save off
|
||||
return 0
|
||||
}
|
||||
|
||||
# starts mon0
|
||||
start_monitor_interface() {
|
||||
rfkill unblock all
|
||||
ifconfig wlan0 up
|
||||
sleep 3
|
||||
iw dev wlan0 set power_save off
|
||||
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor
|
||||
sleep 2
|
||||
rfkill unblock all
|
||||
ifconfig wlan0 down
|
||||
ifconfig wlan0mon up
|
||||
iw dev wlan0mon set power_save off
|
||||
}
|
||||
|
||||
# stops mon0
|
||||
stop_monitor_interface() {
|
||||
ifconfig wlan0mon down && iw dev wlan0mon del
|
||||
reload_brcm
|
||||
ifconfig wlan0 up
|
||||
}
|
||||
|
||||
# returns 0 if the specified network interface is up
|
||||
is_interface_up() {
|
||||
if grep -qi 'up' /sys/class/net/"$1"/operstate; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# returns 0 if conditions for AUTO mode are met
|
||||
is_auto_mode() {
|
||||
# check override file first
|
||||
if [ -f /root/.pwnagotchi-manual ]; then
|
||||
# remove the override file if found
|
||||
rm -rf /root/.pwnagotchi-manual
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check override file first
|
||||
if [ -f /root/.pwnagotchi-auto ]; then
|
||||
# remove the override file if found
|
||||
rm -rf /root/.pwnagotchi-auto
|
||||
return 0
|
||||
fi
|
||||
|
||||
# if usb0 is up, we're in MANU
|
||||
if is_interface_up usb0; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# if eth0 is up (for other boards), we're in MANU
|
||||
if is_interface_up eth0; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# no override, but none of the interfaces is up -> AUTO
|
||||
return 0
|
||||
}
|
||||
|
||||
# returns 0 if conditions for AUTO mode are met
|
||||
is_auto_mode_no_delete() {
|
||||
# check override file first
|
||||
if [ -f /root/.pwnagotchi-manual ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check override file first
|
||||
if [ -f /root/.pwnagotchi-auto ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# if usb0 is up, we're in MANU
|
||||
if is_interface_up usb0; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# if eth0 is up (for other boards), we're in MANU
|
||||
if is_interface_up eth0; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# no override, but none of the interfaces is up -> AUTO
|
||||
return 0
|
||||
}
|
||||
|
||||
# check if we need to decrypt something
|
||||
is_crypted_mode() {
|
||||
if [ -f /root/.pwnagotchi-crypted ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# decryption loop
|
||||
is_decrypted() {
|
||||
while read -r mapping container mount; do
|
||||
# mapping = name the device or file will be mapped to
|
||||
# container = the luks encrypted device or file
|
||||
# mount = the mountpoint
|
||||
|
||||
# fail if not mounted
|
||||
if ! mountpoint -q "$mount" >/dev/null 2>&1; then
|
||||
if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then
|
||||
</tmp/.pwnagotchi-secret-"$mapping" read -r SECRET
|
||||
if ! test -b /dev/disk/by-id/dm-uuid-*"$(cryptsetup luksUUID "$container" | tr -d -)"*; then
|
||||
if echo -n "$SECRET" | cryptsetup luksOpen -d- "$container" "$mapping" >/dev/null 2>&1; then
|
||||
echo "Container decrypted!"
|
||||
fi
|
||||
fi
|
||||
|
||||
if mount /dev/mapper/"$mapping" "$mount" >/dev/null 2>&1; then
|
||||
echo "Mounted /dev/mapper/$mapping to $mount"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! ip -4 addr show wlan0 | grep inet >/dev/null 2>&1; then
|
||||
>/dev/null 2>&1 ip addr add 192.168.0.10/24 dev wlan0
|
||||
fi
|
||||
|
||||
if ! pgrep -f decryption-webserver >/dev/null 2>&1; then
|
||||
>/dev/null 2>&1 decryption-webserver &
|
||||
fi
|
||||
|
||||
if ! pgrep wpa_supplicant >/dev/null 2>&1; then
|
||||
>/tmp/wpa_supplicant.conf cat <<EOF
|
||||
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
||||
update_config=1
|
||||
ap_scan=2
|
||||
|
||||
network={
|
||||
ssid="DECRYPT-ME"
|
||||
mode=2
|
||||
key_mgmt=WPA-PSK
|
||||
psk="pwnagotchi"
|
||||
frequency=2437
|
||||
}
|
||||
EOF
|
||||
>/dev/null 2>&1 wpa_supplicant -u -s -O -D nl80211 -i wlan0 -c /tmp/wpa_supplicant.conf &
|
||||
fi
|
||||
|
||||
if ! pgrep dnsmasq >/dev/null 2>&1; then
|
||||
>/dev/null 2>&1 dnsmasq -k -p 53 -h -O "6,192.168.0.10" -A "/#/192.168.0.10" -i wlan0 -K -F 192.168.0.50,192.168.0.60,255.255.255.0,24h &
|
||||
fi
|
||||
|
||||
return 1
|
||||
fi
|
||||
done </root/.pwnagotchi-crypted
|
||||
|
||||
# overwrite passwords
|
||||
python3 -c 'print("A"*4096)' | tee /tmp/.pwnagotchi-secret-* >/dev/null
|
||||
# delete
|
||||
rm /tmp/.pwnagotchi-secret-*
|
||||
sync # flush
|
||||
|
||||
pkill wpa_supplicant
|
||||
pkill dnsmasq
|
||||
pid="$(pgrep -f "decryption-webserver")"
|
||||
[[ -n "$pid" ]] && kill "$pid"
|
||||
|
||||
return 0
|
||||
}
|
@ -9,7 +9,6 @@ _name = None
|
||||
config = None
|
||||
_cpu_stats = {}
|
||||
|
||||
|
||||
def set_name(new_name):
|
||||
if new_name is None:
|
||||
return
|
||||
|
@ -1 +1 @@
|
||||
__version__ = '2.8.7.1'
|
||||
__version__ = '2.9.5.4'
|
||||
|
@ -4,7 +4,9 @@ import os
|
||||
import re
|
||||
import logging
|
||||
import asyncio
|
||||
import _thread
|
||||
#import _thread
|
||||
import threading
|
||||
import subprocess
|
||||
|
||||
import pwnagotchi
|
||||
import pwnagotchi.utils as utils
|
||||
@ -14,12 +16,11 @@ from pwnagotchi.automata import Automata
|
||||
from pwnagotchi.log import LastSession
|
||||
from pwnagotchi.bettercap import Client
|
||||
from pwnagotchi.mesh.utils import AsyncAdvertiser
|
||||
from pwnagotchi.ai.train import AsyncTrainer
|
||||
|
||||
RECOVERY_DATA_FILE = '/root/.pwnagotchi-recovery'
|
||||
|
||||
|
||||
class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
class Agent(Client, Automata, AsyncAdvertiser):
|
||||
def __init__(self, view, config, keypair):
|
||||
Client.__init__(self,
|
||||
"127.0.0.1" if "hostname" not in config['bettercap'] else config['bettercap']['hostname'],
|
||||
@ -29,7 +30,6 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
"pwnagotchi" if "password" not in config['bettercap'] else config['bettercap']['password'])
|
||||
Automata.__init__(self, config, view)
|
||||
AsyncAdvertiser.__init__(self, config, view, keypair)
|
||||
AsyncTrainer.__init__(self, config)
|
||||
|
||||
self._started_at = time.time()
|
||||
self._current_channel = 0
|
||||
@ -129,7 +129,6 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
time.sleep(1)
|
||||
|
||||
def start(self):
|
||||
self.start_ai()
|
||||
self._wait_bettercap()
|
||||
self.setup_events()
|
||||
self.set_starting()
|
||||
@ -254,7 +253,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
txt = '%d (%d)' % (len(self._handshakes), tot)
|
||||
|
||||
if self._last_pwnd is not None:
|
||||
txt += ' [%s]' % self._last_pwnd[:11] # So it doesn't overlap with fix_brcmfmac_plugin
|
||||
txt += ' [%s]' % self._last_pwnd
|
||||
|
||||
self._view.set('shakes', txt)
|
||||
|
||||
@ -269,9 +268,9 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
self._save_recovery_data()
|
||||
pwnagotchi.reboot()
|
||||
|
||||
def _restart(self):
|
||||
def _restart(self, mode='AUTO'):
|
||||
self._save_recovery_data()
|
||||
pwnagotchi.restart("AUTO")
|
||||
pwnagotchi.restart(mode)
|
||||
|
||||
def _save_recovery_data(self):
|
||||
logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE)
|
||||
@ -304,7 +303,8 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
raise
|
||||
|
||||
def start_session_fetcher(self):
|
||||
_thread.start_new_thread(self._fetch_stats, ())
|
||||
#_thread.start_new_thread(self._fetch_stats, ())
|
||||
threading.Thread(target=self._fetch_stats, args=(), name="Session Fetcher", daemon=True).start()
|
||||
|
||||
def _fetch_stats(self):
|
||||
while True:
|
||||
@ -387,7 +387,8 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
def start_event_polling(self):
|
||||
# start a thread and pass in the mainloop
|
||||
_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
||||
#_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
||||
threading.Thread(target=self._event_poller, args=(asyncio.get_event_loop(),), name="Event Polling", daemon=True).start()
|
||||
|
||||
def is_module_running(self, module):
|
||||
s = self.session()
|
||||
|
@ -1,74 +0,0 @@
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
|
||||
# https://stackoverflow.com/questions/40426502/is-there-a-way-to-suppress-the-messages-tensorflow-prints/40426709
|
||||
# os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # or any {'0', '1', '2'}
|
||||
|
||||
|
||||
def load(config, agent, epoch, from_disk=True):
|
||||
config = config['ai']
|
||||
if not config['enabled']:
|
||||
logging.info("ai disabled")
|
||||
return False
|
||||
|
||||
try:
|
||||
begin = time.time()
|
||||
|
||||
logging.info("[AI] bootstrapping dependencies ...")
|
||||
|
||||
start = time.time()
|
||||
SB_BACKEND = "stable_baselines3"
|
||||
|
||||
from stable_baselines3 import A2C
|
||||
logging.debug("[AI] A2C imported in %.2fs" % (time.time() - start))
|
||||
|
||||
# remove invalid ai.parameters leftover from tensor_flow, if present
|
||||
for key in [ 'alpha', 'epsilon', 'lr_schedule' ]:
|
||||
if key in config['params']:
|
||||
logging.info("Removing legacy ai parameter %s" % key);
|
||||
del config['params'][key]
|
||||
|
||||
start = time.time()
|
||||
from stable_baselines3.a2c import MlpPolicy
|
||||
logging.debug("[AI] MlpPolicy imported in %.2fs" % (time.time() - start))
|
||||
SB_A2C_POLICY = MlpPolicy
|
||||
|
||||
start = time.time()
|
||||
from stable_baselines3.common.vec_env import DummyVecEnv
|
||||
logging.debug("[AI] DummyVecEnv imported in %.2fs" % (time.time() - start))
|
||||
|
||||
start = time.time()
|
||||
import pwnagotchi.ai.gym as wrappers
|
||||
logging.debug("[AI] gym wrapper imported in %.2fs" % (time.time() - start))
|
||||
|
||||
env = wrappers.Environment(agent, epoch)
|
||||
env = DummyVecEnv([lambda: env])
|
||||
|
||||
logging.info("[AI] creating model ...")
|
||||
|
||||
start = time.time()
|
||||
a2c = A2C(SB_A2C_POLICY, env, **config['params'])
|
||||
logging.debug("[AI] A2C created in %.2fs" % (time.time() - start))
|
||||
|
||||
if from_disk and os.path.exists(config['path']):
|
||||
logging.info("[AI] loading %s ..." % config['path'])
|
||||
start = time.time()
|
||||
a2c.load(config['path'], env)
|
||||
logging.debug("[AI] A2C loaded in %.2fs" % (time.time() - start))
|
||||
else:
|
||||
logging.info("[AI] model created:")
|
||||
for key, value in config['params'].items():
|
||||
logging.info(" %s: %s" % (key, value))
|
||||
|
||||
logging.debug("[AI] total loading time is %.2fs" % (time.time() - begin))
|
||||
|
||||
return a2c
|
||||
except Exception as e:
|
||||
logging.info("[AI] Error while starting AI")
|
||||
logging.debug("[AI] error while starting AI (%s)", e)
|
||||
logging.info("[AI] Deleting brain and restarting.")
|
||||
os.system("rm /root/brain.nn && service pwnagotchi restart")
|
||||
|
||||
logging.warning("[AI] AI not loaded!")
|
||||
return False
|
@ -1,61 +0,0 @@
|
||||
import numpy as np
|
||||
|
||||
import pwnagotchi.mesh.wifi as wifi
|
||||
|
||||
MAX_EPOCH_DURATION = 1024
|
||||
|
||||
|
||||
histogram_size = wifi.NumChannels
|
||||
|
||||
shape = (1,
|
||||
# aps per channel
|
||||
histogram_size +
|
||||
# clients per channel
|
||||
histogram_size +
|
||||
# peers per channel
|
||||
histogram_size +
|
||||
# duration
|
||||
1 +
|
||||
# inactive
|
||||
1 +
|
||||
# active
|
||||
1 +
|
||||
# missed
|
||||
1 +
|
||||
# hops
|
||||
1 +
|
||||
# deauths
|
||||
1 +
|
||||
# assocs
|
||||
1 +
|
||||
# handshakes
|
||||
1)
|
||||
|
||||
|
||||
def featurize(state, step):
|
||||
tot_epochs = step + 1e-10
|
||||
tot_interactions = (state['num_deauths'] + state['num_associations']) + 1e-10
|
||||
return np.concatenate((
|
||||
# aps per channel
|
||||
state['aps_histogram'],
|
||||
# clients per channel
|
||||
state['sta_histogram'],
|
||||
# peers per channel
|
||||
state['peers_histogram'],
|
||||
# duration
|
||||
[np.clip(state['duration_secs'] / MAX_EPOCH_DURATION, 0.0, 1.0)],
|
||||
# inactive
|
||||
[state['inactive_for_epochs'] / tot_epochs],
|
||||
# active
|
||||
[state['active_for_epochs'] / tot_epochs],
|
||||
# missed
|
||||
[state['missed_interactions'] / tot_interactions],
|
||||
# hops
|
||||
[state['num_hops'] / wifi.NumChannels],
|
||||
# deauths
|
||||
[state['num_deauths'] / tot_interactions],
|
||||
# assocs
|
||||
[state['num_associations'] / tot_interactions],
|
||||
# handshakes
|
||||
[state['num_handshakes'] / tot_interactions],
|
||||
))
|
@ -1,148 +0,0 @@
|
||||
import logging
|
||||
import gym
|
||||
from gym import spaces
|
||||
import numpy as np
|
||||
|
||||
import pwnagotchi.ai.featurizer as featurizer
|
||||
import pwnagotchi.ai.reward as reward
|
||||
from pwnagotchi.ai.parameter import Parameter
|
||||
|
||||
|
||||
class Environment(gym.Env):
|
||||
render_mode = "human"
|
||||
metadata = {'render_modes': ['human']}
|
||||
params = [
|
||||
Parameter('min_rssi', min_value=-200, max_value=-50),
|
||||
Parameter('ap_ttl', min_value=30, max_value=600),
|
||||
Parameter('sta_ttl', min_value=60, max_value=300),
|
||||
|
||||
Parameter('recon_time', min_value=5, max_value=60),
|
||||
Parameter('max_inactive_scale', min_value=3, max_value=10),
|
||||
Parameter('recon_inactive_multiplier', min_value=1, max_value=3),
|
||||
Parameter('hop_recon_time', min_value=5, max_value=60),
|
||||
Parameter('min_recon_time', min_value=1, max_value=30),
|
||||
Parameter('max_interactions', min_value=1, max_value=25),
|
||||
Parameter('max_misses_for_recon', min_value=3, max_value=10),
|
||||
Parameter('excited_num_epochs', min_value=5, max_value=30),
|
||||
Parameter('bored_num_epochs', min_value=5, max_value=30),
|
||||
Parameter('sad_num_epochs', min_value=5, max_value=30),
|
||||
]
|
||||
|
||||
def __init__(self, agent, epoch):
|
||||
super(Environment, self).__init__()
|
||||
self._agent = agent
|
||||
self._epoch = epoch
|
||||
self._epoch_num = 0
|
||||
self._last_render = None
|
||||
|
||||
channels = agent.supported_channels()
|
||||
|
||||
Environment.params += [
|
||||
Parameter('_channel_%d' % ch, min_value=0, max_value=1, meta=ch + 1) for ch in
|
||||
range(featurizer.histogram_size) if ch + 1 in channels
|
||||
]
|
||||
|
||||
self.last = {
|
||||
'reward': 0.0,
|
||||
'observation': None,
|
||||
'policy': None,
|
||||
'params': {},
|
||||
'state': None,
|
||||
'state_v': None
|
||||
}
|
||||
|
||||
self.action_space = spaces.MultiDiscrete([p.space_size() for p in Environment.params if p.trainable])
|
||||
self.observation_space = spaces.Box(low=0, high=1, shape=featurizer.shape, dtype=np.float32)
|
||||
self.reward_range = reward.range
|
||||
|
||||
@staticmethod
|
||||
def policy_size():
|
||||
return len(list(p for p in Environment.params if p.trainable))
|
||||
|
||||
@staticmethod
|
||||
def policy_to_params(policy):
|
||||
num = len(policy)
|
||||
params = {}
|
||||
|
||||
assert len(Environment.params) == num
|
||||
|
||||
channels = []
|
||||
|
||||
for i in range(num):
|
||||
param = Environment.params[i]
|
||||
|
||||
if '_channel' not in param.name:
|
||||
params[param.name] = param.to_param_value(policy[i])
|
||||
else:
|
||||
has_chan = param.to_param_value(policy[i])
|
||||
# print("%s policy:%s bool:%s" % (param.name, policy[i], has_chan))
|
||||
chan = param.meta
|
||||
if has_chan:
|
||||
channels.append(chan)
|
||||
|
||||
params['channels'] = channels
|
||||
|
||||
return params
|
||||
|
||||
def _next_epoch(self):
|
||||
logging.debug("[ai] waiting for epoch to finish ...")
|
||||
return self._epoch.wait_for_epoch_data()
|
||||
|
||||
def _apply_policy(self, policy):
|
||||
new_params = Environment.policy_to_params(policy)
|
||||
self.last['policy'] = policy
|
||||
self.last['params'] = new_params
|
||||
self._agent.on_ai_policy(new_params)
|
||||
|
||||
def step(self, policy):
|
||||
# create the parameters from the policy and update
|
||||
# them in the algorithm
|
||||
self._apply_policy(policy)
|
||||
self._epoch_num += 1
|
||||
|
||||
# wait for the algorithm to run with the new parameters
|
||||
state = self._next_epoch()
|
||||
|
||||
self.last['reward'] = state['reward']
|
||||
self.last['state'] = state
|
||||
self.last['state_v'] = featurizer.featurize(state, self._epoch_num)
|
||||
|
||||
self._agent.on_ai_step()
|
||||
|
||||
return self.last['state_v'], self.last['reward'], not self._agent.is_training(), {}
|
||||
|
||||
def reset(self):
|
||||
# logging.info("[ai] resetting environment ...")
|
||||
self._epoch_num = 0
|
||||
state = self._next_epoch()
|
||||
self.last['state'] = state
|
||||
self.last['state_v'] = featurizer.featurize(state, 1)
|
||||
return self.last['state_v']
|
||||
|
||||
def _render_histogram(self, hist):
|
||||
for ch in range(featurizer.histogram_size):
|
||||
if hist[ch]:
|
||||
logging.info(" CH %d: %s" % (ch + 1, hist[ch]))
|
||||
|
||||
def render(self, mode='human', close=False, force=False):
|
||||
# when using a vectorialized environment, render gets called twice
|
||||
# avoid rendering the same data
|
||||
if self._last_render == self._epoch_num:
|
||||
return
|
||||
|
||||
if not self._agent.is_training() and not force:
|
||||
return
|
||||
|
||||
self._last_render = self._epoch_num
|
||||
|
||||
logging.info("[AI] --- training epoch %d/%d ---" % (self._epoch_num, self._agent.training_epochs()))
|
||||
logging.info("[AI] REWARD: %f" % self.last['reward'])
|
||||
|
||||
logging.debug(
|
||||
"[AI] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items()))
|
||||
|
||||
logging.info("[AI] observation:")
|
||||
for name, value in self.last['state'].items():
|
||||
if 'histogram' in name:
|
||||
logging.info(" %s" % name.replace('_histogram', ''))
|
||||
self._render_histogram(value)
|
@ -1,30 +0,0 @@
|
||||
from gym import spaces
|
||||
|
||||
|
||||
class Parameter(object):
|
||||
def __init__(self, name, value=0.0, min_value=0, max_value=2, meta=None, trainable=True):
|
||||
self.name = name
|
||||
self.trainable = trainable
|
||||
self.meta = meta
|
||||
self.value = value
|
||||
self.min_value = min_value
|
||||
self.max_value = max_value + 1
|
||||
|
||||
# gymnasium.space.Discrete is within [0, 1, 2, ..., n-1]
|
||||
if self.min_value < 0:
|
||||
self.scale_factor = abs(self.min_value)
|
||||
elif self.min_value > 0:
|
||||
self.scale_factor = -self.min_value
|
||||
else:
|
||||
self.scale_factor = 0
|
||||
|
||||
def space_size(self):
|
||||
return self.max_value + self.scale_factor
|
||||
|
||||
def space(self):
|
||||
return spaces.Discrete(self.max_value + self.scale_factor)
|
||||
|
||||
def to_param_value(self, policy_v):
|
||||
self.value = policy_v - self.scale_factor
|
||||
assert self.min_value <= self.value <= self.max_value
|
||||
return int(self.value)
|
@ -1,27 +1,28 @@
|
||||
import pwnagotchi.mesh.wifi as wifi
|
||||
|
||||
range = (-.7, 1.02)
|
||||
fuck_zero = 1e-20
|
||||
range: tuple[float, float] = (-.7, 1.02)
|
||||
fuck_zero: float = 1e-20
|
||||
|
||||
|
||||
class RewardFunction(object):
|
||||
def __call__(self, epoch_n, state):
|
||||
tot_epochs = epoch_n + fuck_zero
|
||||
tot_interactions = max(state['num_deauths'] + state['num_associations'], state['num_handshakes']) + fuck_zero
|
||||
tot_channels = wifi.NumChannels
|
||||
def __call__(self, epoch_n: float, state: dict[str, float]) -> float:
|
||||
|
||||
h = state['num_handshakes'] / tot_interactions
|
||||
a = .2 * (state['active_for_epochs'] / tot_epochs)
|
||||
c = .1 * (state['num_hops'] / tot_channels)
|
||||
tot_epochs: float = epoch_n + fuck_zero
|
||||
tot_interactions: float = max(state['num_deauths'] + state['num_associations'], state['num_handshakes']) + fuck_zero
|
||||
tot_channels: int = wifi.NumChannels
|
||||
|
||||
b = -.3 * (state['blind_for_epochs'] / tot_epochs)
|
||||
m = -.3 * (state['missed_interactions'] / tot_interactions)
|
||||
i = -.2 * (state['inactive_for_epochs'] / tot_epochs)
|
||||
h: float = state['num_handshakes'] / tot_interactions
|
||||
a: float = .2 * (state['active_for_epochs'] / tot_epochs)
|
||||
c: float = .1 * (state['num_hops'] / tot_channels)
|
||||
|
||||
b: float = -.3 * (state['blind_for_epochs'] / tot_epochs)
|
||||
m: float = -.3 * (state['missed_interactions'] / tot_interactions)
|
||||
i: float = -.2 * (state['inactive_for_epochs'] / tot_epochs)
|
||||
|
||||
# include emotions if state >= 5 epochs
|
||||
_sad = state['sad_for_epochs'] if state['sad_for_epochs'] >= 5 else 0
|
||||
_bored = state['bored_for_epochs'] if state['bored_for_epochs'] >= 5 else 0
|
||||
s = -.2 * (_sad / tot_epochs)
|
||||
l = -.1 * (_bored / tot_epochs)
|
||||
_sad: float = state['sad_for_epochs'] if state['sad_for_epochs'] >= 5 else 0
|
||||
_bored: float = state['bored_for_epochs'] if state['bored_for_epochs'] >= 5 else 0
|
||||
s: float = -.2 * (_sad / tot_epochs)
|
||||
l: float = -.1 * (_bored / tot_epochs)
|
||||
|
||||
return h + a + c + b + i + m + s + l
|
||||
|
@ -1,197 +0,0 @@
|
||||
import _thread
|
||||
import threading
|
||||
import time
|
||||
import random
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
|
||||
import pwnagotchi.plugins as plugins
|
||||
import pwnagotchi.ai as ai
|
||||
|
||||
|
||||
class Stats(object):
|
||||
def __init__(self, path, events_receiver):
|
||||
self._lock = threading.Lock()
|
||||
self._receiver = events_receiver
|
||||
|
||||
self.path = path
|
||||
self.born_at = time.time()
|
||||
# total epochs lived (trained + just eval)
|
||||
self.epochs_lived = 0
|
||||
# total training epochs
|
||||
self.epochs_trained = 0
|
||||
|
||||
self.worst_reward = 0.0
|
||||
self.best_reward = 0.0
|
||||
|
||||
self.load()
|
||||
|
||||
def on_epoch(self, data, training):
|
||||
best_r = False
|
||||
worst_r = False
|
||||
with self._lock:
|
||||
reward = data['reward']
|
||||
if reward < self.worst_reward:
|
||||
self.worst_reward = reward
|
||||
worst_r = True
|
||||
|
||||
elif reward > self.best_reward:
|
||||
best_r = True
|
||||
self.best_reward = reward
|
||||
|
||||
self.epochs_lived += 1
|
||||
if training:
|
||||
self.epochs_trained += 1
|
||||
|
||||
self.save()
|
||||
|
||||
if best_r:
|
||||
self._receiver.on_ai_best_reward(reward)
|
||||
elif worst_r:
|
||||
self._receiver.on_ai_worst_reward(reward)
|
||||
|
||||
def load(self):
|
||||
with self._lock:
|
||||
if os.path.exists(self.path) and os.path.getsize(self.path) > 0:
|
||||
logging.info("[AI] loading %s" % self.path)
|
||||
with open(self.path, 'rt') as fp:
|
||||
obj = json.load(fp)
|
||||
|
||||
self.born_at = obj['born_at']
|
||||
self.epochs_lived, self.epochs_trained = obj['epochs_lived'], obj['epochs_trained']
|
||||
self.best_reward, self.worst_reward = obj['rewards']['best'], obj['rewards']['worst']
|
||||
|
||||
def save(self):
|
||||
with self._lock:
|
||||
logging.info("[AI] saving %s" % self.path)
|
||||
|
||||
data = json.dumps({
|
||||
'born_at': self.born_at,
|
||||
'epochs_lived': self.epochs_lived,
|
||||
'epochs_trained': self.epochs_trained,
|
||||
'rewards': {
|
||||
'best': self.best_reward,
|
||||
'worst': self.worst_reward
|
||||
}
|
||||
})
|
||||
|
||||
temp = "%s.tmp" % self.path
|
||||
back = "%s.bak" % self.path
|
||||
with open(temp, 'wt') as fp:
|
||||
fp.write(data)
|
||||
|
||||
if os.path.isfile(self.path):
|
||||
os.replace(self.path, back)
|
||||
os.replace(temp, self.path)
|
||||
|
||||
|
||||
class AsyncTrainer(object):
|
||||
def __init__(self, config):
|
||||
self._config = config
|
||||
self._model = None
|
||||
self._is_training = False
|
||||
self._training_epochs = 0
|
||||
self._nn_path = self._config['ai']['path']
|
||||
self._stats = Stats("%s.json" % os.path.splitext(self._nn_path)[0], self)
|
||||
|
||||
def set_training(self, training, for_epochs=0):
|
||||
self._is_training = training
|
||||
self._training_epochs = for_epochs
|
||||
|
||||
if training:
|
||||
plugins.on('ai_training_start', self, for_epochs)
|
||||
else:
|
||||
plugins.on('ai_training_end', self)
|
||||
|
||||
def is_training(self):
|
||||
return self._is_training
|
||||
|
||||
def training_epochs(self):
|
||||
return self._training_epochs
|
||||
|
||||
def start_ai(self):
|
||||
_thread.start_new_thread(self._ai_worker, ())
|
||||
|
||||
def _save_ai(self):
|
||||
logging.info("[AI] saving model to %s ..." % self._nn_path)
|
||||
temp = "%s.tmp" % self._nn_path
|
||||
self._model.save(temp)
|
||||
os.replace(temp, self._nn_path)
|
||||
|
||||
def on_ai_step(self):
|
||||
self._model.env.render()
|
||||
|
||||
if self._is_training:
|
||||
self._save_ai()
|
||||
|
||||
self._stats.on_epoch(self._epoch.data(), self._is_training)
|
||||
|
||||
def on_ai_training_step(self, _locals, _globals):
|
||||
self._model.env.render()
|
||||
plugins.on('ai_training_step', self, _locals, _globals)
|
||||
|
||||
def on_ai_policy(self, new_params):
|
||||
plugins.on('ai_policy', self, new_params)
|
||||
logging.info("[AI] setting new policy:")
|
||||
for name, value in new_params.items():
|
||||
if name in self._config['personality']:
|
||||
curr_value = self._config['personality'][name]
|
||||
if curr_value != value:
|
||||
logging.info("[AI] ! %s: %s -> %s" % (name, curr_value, value))
|
||||
self._config['personality'][name] = value
|
||||
else:
|
||||
logging.error("[AI] param %s not in personality configuration!" % name)
|
||||
|
||||
self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl'])
|
||||
self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl'])
|
||||
self.run('set wifi.rssi.min %d' % self._config['personality']['min_rssi'])
|
||||
|
||||
def on_ai_ready(self):
|
||||
self._view.on_ai_ready()
|
||||
plugins.on('ai_ready', self)
|
||||
|
||||
def on_ai_best_reward(self, r):
|
||||
logging.info("[AI] best reward so far: %s" % r)
|
||||
self._view.on_motivated(r)
|
||||
plugins.on('ai_best_reward', self, r)
|
||||
|
||||
def on_ai_worst_reward(self, r):
|
||||
logging.info("[AI] worst reward so far: %s" % r)
|
||||
self._view.on_demotivated(r)
|
||||
plugins.on('ai_worst_reward', self, r)
|
||||
|
||||
def _ai_worker(self):
|
||||
self._model = ai.load(self._config, self, self._epoch)
|
||||
|
||||
if self._model:
|
||||
self.on_ai_ready()
|
||||
|
||||
epochs_per_episode = self._config['ai']['epochs_per_episode']
|
||||
|
||||
obs = None
|
||||
while True:
|
||||
self._model.env.render()
|
||||
# enter in training mode?
|
||||
if random.random() > self._config['ai']['laziness']:
|
||||
logging.info("[AI] learning for %d epochs ..." % epochs_per_episode)
|
||||
try:
|
||||
self.set_training(True, epochs_per_episode)
|
||||
# back up brain file before starting new training set
|
||||
if os.path.isfile(self._nn_path):
|
||||
back = "%s.bak" % self._nn_path
|
||||
os.replace(self._nn_path, back)
|
||||
self._view.set("mode", " AI")
|
||||
self._model.learn(total_timesteps=epochs_per_episode, callback=self.on_ai_training_step)
|
||||
except Exception as e:
|
||||
logging.exception("[AI] error while training (%s)", e)
|
||||
finally:
|
||||
self.set_training(False)
|
||||
obs = self._model.env.reset()
|
||||
# init the first time
|
||||
elif obs is None:
|
||||
obs = self._model.env.reset()
|
||||
|
||||
# run the inference
|
||||
action, _ = self._model.predict(obs)
|
||||
obs, _, _, _ = self._model.env.step(action)
|
@ -1,16 +0,0 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
def normalize(v, min_v, max_v):
|
||||
return (v - min_v) / (max_v - min_v)
|
||||
|
||||
|
||||
def as_batches(x, y, batch_size, shuffle=True):
|
||||
x_size = len(x)
|
||||
assert x_size == len(y)
|
||||
|
||||
indices = np.random.permutation(x_size) if shuffle else None
|
||||
|
||||
for offset in range(0, x_size - batch_size + 1, batch_size):
|
||||
excerpt = indices[offset:offset + batch_size] if shuffle else slice(offset, offset + batch_size)
|
||||
yield x[excerpt], y[excerpt]
|
@ -2,6 +2,7 @@ import logging
|
||||
|
||||
import pwnagotchi.plugins as plugins
|
||||
from pwnagotchi.ai.epoch import Epoch
|
||||
import os
|
||||
|
||||
|
||||
# basic mood system
|
||||
@ -136,7 +137,6 @@ class Automata(object):
|
||||
self.set_grateful()
|
||||
|
||||
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
|
||||
|
||||
if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']:
|
||||
logging.critical("%d epochs without visible access points -> restarting ...", self._epoch.blind_for)
|
||||
self._restart()
|
||||
|
@ -100,7 +100,6 @@ class Client(object):
|
||||
await asyncio.sleep(sleep_time)
|
||||
continue
|
||||
except OSError:
|
||||
sleep_time = min_sleep + max_sleep * random.random()
|
||||
logging.warning('connection to the bettercap endpoint failed...')
|
||||
pwnagotchi.restart("AUTO")
|
||||
|
||||
|
78
bin/pwnagotchi → pwnagotchi/cli.py
Executable file → Normal file
78
bin/pwnagotchi → pwnagotchi/cli.py
Executable file → Normal file
@ -1,10 +1,9 @@
|
||||
#!/usr/bin/python3
|
||||
import logging
|
||||
import argparse
|
||||
import time
|
||||
import signal
|
||||
import sys
|
||||
import toml
|
||||
import tomlkit
|
||||
import requests
|
||||
import os
|
||||
import re
|
||||
@ -14,9 +13,8 @@ from pwnagotchi import utils
|
||||
from pwnagotchi.google import cmd as google_cmd
|
||||
from pwnagotchi.plugins import cmd as plugins_cmd
|
||||
from pwnagotchi import log
|
||||
from pwnagotchi import restart
|
||||
from pwnagotchi import fs
|
||||
from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple
|
||||
from pwnagotchi.utils import parse_version as version_to_tuple
|
||||
|
||||
|
||||
def pwnagotchi_cli():
|
||||
@ -50,6 +48,7 @@ def pwnagotchi_cli():
|
||||
logging.info("entering auto mode ...")
|
||||
|
||||
agent.mode = 'auto'
|
||||
agent.last_session.parse(agent.view(), args.skip_session) # show stats in AUTO
|
||||
agent.start()
|
||||
|
||||
while True:
|
||||
@ -60,7 +59,7 @@ def pwnagotchi_cli():
|
||||
channels = agent.get_access_points_by_channel()
|
||||
# for each channel
|
||||
for ch, aps in channels:
|
||||
time.sleep(0.2)
|
||||
time.sleep(1)
|
||||
agent.set_channel(ch)
|
||||
|
||||
if not agent.is_stale() and agent.any_activity():
|
||||
@ -188,12 +187,14 @@ def pwnagotchi_cli():
|
||||
if pwn_name == "":
|
||||
pwn_name = "Pwnagotchi"
|
||||
print("I shall go by Pwnagotchi from now on!")
|
||||
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
||||
pwn_name = (f"[main]\n"
|
||||
f"name = \"{pwn_name}\"\n")
|
||||
f.write(pwn_name)
|
||||
else:
|
||||
if is_valid_hostname(pwn_name):
|
||||
print(f"I shall go by {pwn_name} from now on!")
|
||||
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
||||
pwn_name = (f"[main]\n"
|
||||
f"name = \"{pwn_name}\"\n")
|
||||
f.write(pwn_name)
|
||||
else:
|
||||
print("You have chosen an invalid name. Please start over.")
|
||||
@ -204,54 +205,64 @@ def pwnagotchi_cli():
|
||||
"Be sure to use digits as your answer.\n\n"
|
||||
"Amount of networks: ")
|
||||
if int(pwn_whitelist) > 0:
|
||||
f.write("main.whitelist = [\n")
|
||||
f.write("whitelist = [\n")
|
||||
for x in range(int(pwn_whitelist)):
|
||||
ssid = input("SSID (Name): ")
|
||||
bssid = input("BSSID (MAC): ")
|
||||
f.write(f"\t\"{ssid}\",\n")
|
||||
f.write(f"\t\"{bssid}\",\n")
|
||||
if bssid != "":
|
||||
f.write(f"\t\"{bssid}\",\n")
|
||||
f.write("]\n")
|
||||
# set bluetooth tether
|
||||
pwn_bluetooth = input("Do you want to enable BT-Tether?\n\n"
|
||||
"[Y/N] ")
|
||||
if pwn_bluetooth.lower() in ('y', 'yes'):
|
||||
f.write("main.plugins.bt-tether.enabled = true\n\n")
|
||||
pwn_bluetooth_device = input("What device do you use? Android or iOS?\n\n"
|
||||
f.write("[main.plugins.bt-tether]\n"
|
||||
"enabled = true\n\n")
|
||||
pwn_bluetooth_phone_name = input("What name uses your phone, check settings?\n\n")
|
||||
if pwn_bluetooth_phone_name != "":
|
||||
f.write(f"phone-name = \"{pwn_bluetooth_phone_name}\"\n")
|
||||
pwn_bluetooth_device = input("What device do you use? android or ios?\n\n"
|
||||
"Device: ")
|
||||
if pwn_bluetooth_device.lower() == "android":
|
||||
f.write("main.plugins.bt-tether.devices.android-phone.enabled = true\n")
|
||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
||||
"MAC: ")
|
||||
if pwn_bluetooth_mac != "":
|
||||
f.write(f"main.plugins.bt-tether.devices.android-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
||||
elif pwn_bluetooth_device.lower() == "ios":
|
||||
f.write("main.plugins.bt-tether.devices.ios-phone.enabled = true\n")
|
||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
||||
"MAC: ")
|
||||
if pwn_bluetooth_mac != "":
|
||||
f.write(f"main.plugins.bt-tether.devices.ios-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
||||
if pwn_bluetooth_device != "":
|
||||
if pwn_bluetooth_device != "android" and pwn_bluetooth_device != "ios":
|
||||
print("You have chosen an invalid device. Please start over.")
|
||||
exit()
|
||||
f.write(f"phone = \"{pwn_bluetooth_device.lower()}\"\n")
|
||||
if pwn_bluetooth_device == "android":
|
||||
f.write("ip = \"192.168.44.44\"\n")
|
||||
elif pwn_bluetooth_device == "ios":
|
||||
f.write("ip = \"172.20.10.6\"\n")
|
||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
||||
"MAC: ")
|
||||
if pwn_bluetooth_mac != "":
|
||||
f.write(f"mac = \"{pwn_bluetooth_mac}\"\n")
|
||||
# set up display settings
|
||||
pwn_display_enabled = input("Do you want to enable a display?\n\n"
|
||||
"[Y/N]: ")
|
||||
if pwn_display_enabled.lower() in ('y', 'yes'):
|
||||
f.write("ui.display.enabled = true\n")
|
||||
f.write("[ui.display]\n"
|
||||
"enabled = true\n")
|
||||
pwn_display_type = input("What display do you use?\n\n"
|
||||
"Be sure to check for the correct display type @ \n"
|
||||
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L431\n\n"
|
||||
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L501\n\n"
|
||||
"Display type: ")
|
||||
if pwn_display_type != "":
|
||||
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
|
||||
f.write(f"type = \"{pwn_display_type}\"\n")
|
||||
pwn_display_invert = input("Do you want to invert the display colors?\n"
|
||||
"N = Black background\n"
|
||||
"Y = White background\n\n"
|
||||
"[Y/N]: ")
|
||||
if pwn_display_invert.lower() in ('y', 'yes'):
|
||||
f.write("ui.invert = true\n")
|
||||
f.write("[ui]\n"
|
||||
"invert = true\n")
|
||||
f.close()
|
||||
if pwn_bluetooth.lower() in ('y', 'yes'):
|
||||
if pwn_bluetooth_device.lower == "android":
|
||||
print("To visit the webui when connected with your phone, visit: http://192.168.44.44:8080\n"
|
||||
"Be sure to run `sudo bluetoothctl` to set-up the bluetooth connection for the first time. And read the wiki step 4.\n"
|
||||
"Your configuration is done, and I will restart in 5 seconds.")
|
||||
|
||||
elif pwn_bluetooth_device.lower == "ios":
|
||||
print("To visit the webui when connected with your phone, visit: http://172.20.10.6:8080\n"
|
||||
"Your configuration is done, and I will restart in 5 seconds.")
|
||||
@ -264,8 +275,7 @@ def pwnagotchi_cli():
|
||||
sys.exit(0)
|
||||
|
||||
if args.donate:
|
||||
print("Donations can made @ \n "
|
||||
"https://www.patreon.com/pwnagotchi_torch \n "
|
||||
print("Donations can be made @ \n "
|
||||
"https://github.com/sponsors/jayofelony \n\n"
|
||||
"But only if you really want to!")
|
||||
sys.exit(0)
|
||||
@ -278,8 +288,7 @@ def pwnagotchi_cli():
|
||||
local = version_to_tuple(pwnagotchi.__version__)
|
||||
remote = version_to_tuple(latest_ver)
|
||||
if remote > local:
|
||||
user_input = input("There is a new version available! Update from v%s to v%s?\n[Y/N] "
|
||||
% (pwnagotchi.__version__, latest_ver))
|
||||
user_input = input("There is a new version available! Update from v%s to v%s?\n[Y/N] " % (pwnagotchi.__version__, latest_ver))
|
||||
# input validation
|
||||
if user_input.lower() in ('y', 'yes'):
|
||||
if os.path.exists('/root/.auto-update'):
|
||||
@ -296,7 +305,7 @@ def pwnagotchi_cli():
|
||||
config = utils.load_config(args)
|
||||
|
||||
if args.print_config:
|
||||
print(toml.dumps(config, encoder=DottedTomlEncoder()))
|
||||
print(tomlkit.dumps(config))
|
||||
sys.exit(0)
|
||||
|
||||
from pwnagotchi.identity import KeyPair
|
||||
@ -324,8 +333,8 @@ def pwnagotchi_cli():
|
||||
agent = Agent(view=display, config=config, keypair=KeyPair(view=display))
|
||||
|
||||
def usr1_handler(*unused):
|
||||
logging.info('Received USR1 singal. Restart process ...')
|
||||
restart("MANU" if args.do_manual else "AUTO")
|
||||
logging.info('Received USR1 signal. Restart process ...')
|
||||
agent._restart("MANU" if args.do_manual else "AUTO")
|
||||
|
||||
signal.signal(signal.SIGUSR1, usr1_handler)
|
||||
|
||||
@ -334,5 +343,6 @@ def pwnagotchi_cli():
|
||||
else:
|
||||
do_auto_mode(agent)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnagotchi_cli()
|
@ -1,214 +1,257 @@
|
||||
main.name = "pwnagotchi"
|
||||
main.lang = "en"
|
||||
main.whitelist = [
|
||||
[main]
|
||||
name = "pwnagotchi"
|
||||
lang = "en"
|
||||
iface = "wlan0mon"
|
||||
mon_start_cmd = "/usr/bin/monstart"
|
||||
mon_stop_cmd = "/usr/bin/monstop"
|
||||
mon_max_blind_epochs = 5
|
||||
no_restart = false
|
||||
whitelist = [
|
||||
"EXAMPLE_NETWORK",
|
||||
"ANOTHER_EXAMPLE_NETWORK",
|
||||
"fo:od:ba:be:fo:od",
|
||||
"fo:od:ba"
|
||||
]
|
||||
main.confd = "/etc/pwnagotchi/conf.d/"
|
||||
main.custom_plugin_repos = [
|
||||
confd = "/etc/pwnagotchi/conf.d/"
|
||||
custom_plugin_repos = [
|
||||
"https://github.com/jayofelony/pwnagotchi-torch-plugins/archive/master.zip",
|
||||
"https://github.com/tisboyo/pwnagotchi-pisugar2-plugin/archive/master.zip",
|
||||
"https://github.com/nullm0ose/pwnagotchi-plugin-pisugar3/archive/master.zip",
|
||||
"https://github.com/Sniffleupagus/pwnagotchi_plugins/archive/master.zip",
|
||||
"https://github.com/NeonLightning/pwny/archive/master.zip",
|
||||
"https://github.com/marbasec/UPSLite_Plugin_1_3/archive/master.zip"
|
||||
"https://github.com/marbasec/UPSLite_Plugin_1_3/archive/master.zip",
|
||||
"https://github.com/wpa-2/Pwnagotchi-Plugins/archive/master.zip",
|
||||
"https://github.com/cyberartemio/wardriver-pwnagotchi-plugin/archive/main.zip"
|
||||
]
|
||||
custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/"
|
||||
|
||||
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/"
|
||||
[main.plugins.auto-tune]
|
||||
enabled = true
|
||||
|
||||
main.plugins.auto-update.enabled = true
|
||||
main.plugins.auto-update.install = true
|
||||
main.plugins.auto-update.interval = 1
|
||||
[main.plugins.auto_backup]
|
||||
enabled = false
|
||||
interval = "daily" # or "hourly", or a number (minutes)
|
||||
max_tries = 0
|
||||
backup_location = "/home/pi/"
|
||||
files = [
|
||||
"/root/settings.yaml",
|
||||
"/root/client_secrets.json",
|
||||
"/root/.api-report.json",
|
||||
"/root/.ssh",
|
||||
"/root/.bashrc",
|
||||
"/root/.profile",
|
||||
"/home/pi/handshakes",
|
||||
"/root/peers",
|
||||
"/etc/pwnagotchi/",
|
||||
"/usr/local/share/pwnagotchi/custom-plugins",
|
||||
"/etc/ssh/",
|
||||
"/home/pi/.bashrc",
|
||||
"/home/pi/.profile",
|
||||
"/home/pi/.wpa_sec_uploads"
|
||||
]
|
||||
exclude = [ "/etc/pwnagotchi/logs/*"]
|
||||
commands = [ "tar cf {backup_file} {files}"]
|
||||
|
||||
main.plugins.bt-tether.enabled = false
|
||||
[main.plugins.auto-update]
|
||||
enabled = true
|
||||
install = true
|
||||
interval = 1
|
||||
token = "" # Create a personal access token (classic) with scope set to public_repo to use the GitHub API
|
||||
|
||||
main.plugins.bt-tether.devices.android-phone.enabled = false
|
||||
main.plugins.bt-tether.devices.android-phone.search_order = 1
|
||||
main.plugins.bt-tether.devices.android-phone.mac = ""
|
||||
main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44"
|
||||
main.plugins.bt-tether.devices.android-phone.netmask = 24
|
||||
main.plugins.bt-tether.devices.android-phone.interval = 1
|
||||
main.plugins.bt-tether.devices.android-phone.scantime = 10
|
||||
main.plugins.bt-tether.devices.android-phone.max_tries = 10
|
||||
main.plugins.bt-tether.devices.android-phone.share_internet = true
|
||||
main.plugins.bt-tether.devices.android-phone.priority = 1
|
||||
[main.plugins.bt-tether]
|
||||
enabled = false
|
||||
phone-name = "" # name as shown on the phone i.e. "Pwnagotchi's Phone"
|
||||
mac = ""
|
||||
phone = "" # android or ios
|
||||
ip = "" # optional, default : 192.168.44.2 if android or 172.20.10.2 if ios
|
||||
dns = "8.8.8.8 1.1.1.1" # optional, default (google): "8.8.8.8 1.1.1.1". Consider using anonymous DNS like OpenNic :-)
|
||||
|
||||
main.plugins.bt-tether.devices.ios-phone.enabled = false
|
||||
main.plugins.bt-tether.devices.ios-phone.search_order = 2
|
||||
main.plugins.bt-tether.devices.ios-phone.mac = ""
|
||||
main.plugins.bt-tether.devices.ios-phone.ip = "172.20.10.6"
|
||||
main.plugins.bt-tether.devices.ios-phone.netmask = 24
|
||||
main.plugins.bt-tether.devices.ios-phone.interval = 5
|
||||
main.plugins.bt-tether.devices.ios-phone.scantime = 20
|
||||
main.plugins.bt-tether.devices.ios-phone.max_tries = 0
|
||||
main.plugins.bt-tether.devices.ios-phone.share_internet = true
|
||||
main.plugins.bt-tether.devices.ios-phone.priority = 999
|
||||
[main.plugins.fix_services]
|
||||
enabled = true
|
||||
|
||||
main.plugins.fix_services.enabled = true
|
||||
[main.plugins.cache]
|
||||
enabled = true
|
||||
|
||||
main.plugins.gdrivesync.enabled = false
|
||||
main.plugins.gdrivesync.backupfiles = ['']
|
||||
main.plugins.gdrivesync.backup_folder = "PwnagotchiBackups"
|
||||
main.plugin.gdrivesync.interval = 1
|
||||
[main.plugins.gdrivesync]
|
||||
enabled = false
|
||||
backupfiles = [""]
|
||||
backup_folder = "PwnagotchiBackups"
|
||||
interval = 1
|
||||
|
||||
main.plugins.gpio_buttons.enabled = false
|
||||
[main.plugins.gpio_buttons]
|
||||
enabled = false
|
||||
|
||||
main.plugins.gps.enabled = false
|
||||
main.plugins.gps.speed = 19200
|
||||
main.plugins.gps.device = "/dev/ttyUSB0" # for GPSD: "localhost:2947"
|
||||
[main.plugins.gps]
|
||||
enabled = false
|
||||
speed = 19200
|
||||
device = "/dev/ttyUSB0" # for GPSD: "localhost:2947"
|
||||
|
||||
main.plugins.grid.enabled = true
|
||||
main.plugins.grid.report = true
|
||||
[main.plugins.gps_listener]
|
||||
enabled = false
|
||||
|
||||
main.plugins.logtail.enabled = false
|
||||
main.plugins.logtail.max-lines = 10000
|
||||
[main.plugins.grid]
|
||||
enabled = true
|
||||
report = true
|
||||
|
||||
main.plugins.memtemp.enabled = false
|
||||
main.plugins.memtemp.scale = "celsius"
|
||||
main.plugins.memtemp.orientation = "horizontal"
|
||||
[main.plugins.logtail]
|
||||
enabled = false
|
||||
max-lines = 10000
|
||||
|
||||
main.plugins.net-pos.enabled = false
|
||||
main.plugins.net-pos.api_key = "test"
|
||||
[main.plugins.memtemp]
|
||||
enabled = false
|
||||
scale = "celsius"
|
||||
orientation = "horizontal"
|
||||
|
||||
main.plugins.onlinehashcrack.enabled = false
|
||||
main.plugins.onlinehashcrack.email = ""
|
||||
main.plugins.onlinehashcrack.dashboard = ""
|
||||
main.plugins.onlinehashcrack.single_files = false
|
||||
[main.plugins.ohcapi]
|
||||
enabled = false
|
||||
api_key = "sk_your_api_key_here"
|
||||
receive_email = "yes"
|
||||
|
||||
main.plugins.pisugar2.enabled = false
|
||||
main.plugins.pisugar2.shutdown = 5
|
||||
main.plugins.pisugar2.sync_rtc_on_boot = false
|
||||
[main.plugins.pwndroid]
|
||||
enabled = false
|
||||
display = false # show coords on display
|
||||
display_altitude = false # show altitude on display
|
||||
|
||||
main.plugins.session-stats.enabled = true
|
||||
main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/"
|
||||
[main.plugins.pisugarx]
|
||||
enabled = false
|
||||
rotation = false
|
||||
default_display = "percentage"
|
||||
lowpower_shutdown = true
|
||||
lowpower_shutdown_level = 10 # battery percent at which the device will turn off
|
||||
max_charge_voltage_protection = false #It will limit the battery voltage to about 80% to extend battery life
|
||||
|
||||
main.plugins.ups_hat_c.enabled = false
|
||||
main.plugins.ups_hat_c.label_on = true # show BAT label or just percentage
|
||||
main.plugins.ups_hat_c.shutdown = 5 # battery percent at which the device will turn off
|
||||
main.plugins.ups_hat_c.bat_x_coord = 140
|
||||
main.plugins.ups_hat_c.bat_y_coord = 0
|
||||
[main.plugins.pwncrack]
|
||||
enabled = false
|
||||
key = ""
|
||||
|
||||
main.plugins.ups_lite.enabled = false
|
||||
main.plugins.ups_lite.shutdown = 2
|
||||
[main.plugins.session-stats]
|
||||
enabled = false
|
||||
save_directory = "/var/tmp/pwnagotchi/sessions/"
|
||||
|
||||
main.plugins.webcfg.enabled = true
|
||||
[main.plugins.ups_hat_c]
|
||||
enabled = false
|
||||
label_on = true # show BAT label or just percentage
|
||||
shutdown = 5 # battery percent at which the device will turn off
|
||||
bat_x_coord = 140
|
||||
bat_y_coord = 0
|
||||
|
||||
main.plugins.webgpsmap.enabled = false
|
||||
[main.plugins.ups_lite]
|
||||
enabled = false
|
||||
shutdown = 2
|
||||
|
||||
main.plugins.wigle.enabled = false
|
||||
main.plugins.wigle.api_key = ""
|
||||
main.plugins.wigle.donate = false
|
||||
[main.plugins.webcfg]
|
||||
enabled = true
|
||||
|
||||
main.plugins.wpa-sec.enabled = false
|
||||
main.plugins.wpa-sec.api_key = ""
|
||||
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
|
||||
main.plugins.wpa-sec.download_results = false
|
||||
[main.plugins.webgpsmap]
|
||||
enabled = false
|
||||
|
||||
main.iface = "wlan0mon"
|
||||
main.mon_start_cmd = "/usr/bin/monstart"
|
||||
main.mon_stop_cmd = "/usr/bin/monstop"
|
||||
main.mon_max_blind_epochs = 50
|
||||
main.no_restart = false
|
||||
[main.plugins.wigle]
|
||||
enabled = false
|
||||
api_key = "" # mandatory
|
||||
cvs_dir = "/tmp" # optionnal, is set, the CVS is written to this directory
|
||||
donate = false # default: off
|
||||
timeout = 30 # default: 30
|
||||
position = [7, 85] # optionnal
|
||||
|
||||
main.filter = ""
|
||||
[main.plugins.wpa-sec]
|
||||
enabled = false
|
||||
api_key = ""
|
||||
api_url = "https://wpa-sec.stanev.org"
|
||||
download_results = false
|
||||
show_pwd = false
|
||||
|
||||
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
|
||||
main.log.rotation.enabled = true
|
||||
main.log.rotation.size = "10M"
|
||||
[main.log]
|
||||
path = "/etc/pwnagotchi/log/pwnagotchi.log"
|
||||
path-debug = "/etc/pwnagotchi/log/pwnagotchi-debug.log"
|
||||
|
||||
ai.enabled = true
|
||||
ai.path = "/root/brain.nn"
|
||||
ai.laziness = 0.1
|
||||
ai.epochs_per_episode = 50
|
||||
[main.log.rotation]
|
||||
enabled = true
|
||||
size = "10M"
|
||||
|
||||
ai.params.gamma = 0.99
|
||||
ai.params.n_steps = 1
|
||||
ai.params.vf_coef = 0.25
|
||||
ai.params.ent_coef = 0.01
|
||||
ai.params.max_grad_norm = 0.5
|
||||
ai.params.learning_rate = 0.001
|
||||
ai.params.verbose = 1
|
||||
[personality]
|
||||
advertise = true
|
||||
deauth = true
|
||||
associate = true
|
||||
channels = []
|
||||
min_rssi = -200
|
||||
ap_ttl = 120
|
||||
sta_ttl = 300
|
||||
recon_time = 30
|
||||
max_inactive_scale = 2
|
||||
recon_inactive_multiplier = 2
|
||||
hop_recon_time = 10
|
||||
min_recon_time = 5
|
||||
max_interactions = 3
|
||||
max_misses_for_recon = 5
|
||||
excited_num_epochs = 10
|
||||
bored_num_epochs = 15
|
||||
sad_num_epochs = 25
|
||||
bond_encounters_factor = 20000
|
||||
throttle_a = 0.4
|
||||
throttle_d = 0.9
|
||||
|
||||
personality.advertise = true
|
||||
personality.deauth = true
|
||||
personality.associate = true
|
||||
personality.channels = []
|
||||
personality.min_rssi = -200
|
||||
personality.ap_ttl = 120
|
||||
personality.sta_ttl = 300
|
||||
personality.recon_time = 30
|
||||
personality.max_inactive_scale = 2
|
||||
personality.recon_inactive_multiplier = 2
|
||||
personality.hop_recon_time = 10
|
||||
personality.min_recon_time = 5
|
||||
personality.max_interactions = 3
|
||||
personality.max_misses_for_recon = 5
|
||||
personality.excited_num_epochs = 10
|
||||
personality.bored_num_epochs = 15
|
||||
personality.sad_num_epochs = 25
|
||||
personality.bond_encounters_factor = 20000
|
||||
personality.throttle_a = 0.4
|
||||
personality.throttle_d = 0.9
|
||||
[ui]
|
||||
invert = false # false = black background, true = white background
|
||||
cursor = true
|
||||
fps = 0.0
|
||||
|
||||
personality.clear_on_exit = true # clear display when shutting down cleanly
|
||||
[ui.font]
|
||||
name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
|
||||
size_offset = 0 # will be added to the font size
|
||||
|
||||
ui.invert = false # false = black background, true = white background
|
||||
[ui.faces]
|
||||
look_r = "( ⚆_⚆)"
|
||||
look_l = "(☉_☉ )"
|
||||
look_r_happy = "( ◕‿◕)"
|
||||
look_l_happy = "(◕‿◕ )"
|
||||
sleep = "(⇀‿‿↼)"
|
||||
sleep2 = "(≖‿‿≖)"
|
||||
awake = "(◕‿‿◕)"
|
||||
bored = "(-__-)"
|
||||
intense = "(°▃▃°)"
|
||||
cool = "(⌐■_■)"
|
||||
happy = "(•‿‿•)"
|
||||
excited = "(ᵔ◡◡ᵔ)"
|
||||
grateful = "(^‿‿^)"
|
||||
motivated = "(☼‿‿☼)"
|
||||
demotivated = "(≖__≖)"
|
||||
smart = "(✜‿‿✜)"
|
||||
lonely = "(ب__ب)"
|
||||
sad = "(╥☁╥ )"
|
||||
angry = "(-_-')"
|
||||
friend = "(♥‿‿♥)"
|
||||
broken = "(☓‿‿☓)"
|
||||
debug = "(#__#)"
|
||||
upload = "(1__0)"
|
||||
upload1 = "(1__1)"
|
||||
upload2 = "(0__1)"
|
||||
png = false
|
||||
position_x = 0
|
||||
position_y = 34
|
||||
|
||||
ui.fps = 0.0
|
||||
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
|
||||
ui.font.size_offset = 0 # will be added to the font size
|
||||
[ui.web]
|
||||
enabled = true
|
||||
address = "::" # listening on both ipv4 and ipv6 - switch to 0.0.0.0 to listen on just ipv4
|
||||
auth = false
|
||||
username = "changeme" # if auth is true
|
||||
password = "changeme" # if auth is true
|
||||
origin = ""
|
||||
port = 8080
|
||||
on_frame = ""
|
||||
|
||||
ui.faces.look_r = "( ⚆_⚆)"
|
||||
ui.faces.look_l = "(☉_☉ )"
|
||||
ui.faces.look_r_happy = "( ◕‿◕)"
|
||||
ui.faces.look_l_happy = "(◕‿◕ )"
|
||||
ui.faces.sleep = "(⇀‿‿↼)"
|
||||
ui.faces.sleep2 = "(≖‿‿≖)"
|
||||
ui.faces.awake = "(◕‿‿◕)"
|
||||
ui.faces.bored = "(-__-)"
|
||||
ui.faces.intense = "(°▃▃°)"
|
||||
ui.faces.cool = "(⌐■_■)"
|
||||
ui.faces.happy = "(•‿‿•)"
|
||||
ui.faces.excited = "(ᵔ◡◡ᵔ)"
|
||||
ui.faces.grateful = "(^‿‿^)"
|
||||
ui.faces.motivated = "(☼‿‿☼)"
|
||||
ui.faces.demotivated = "(≖__≖)"
|
||||
ui.faces.smart = "(✜‿‿✜)"
|
||||
ui.faces.lonely = "(ب__ب)"
|
||||
ui.faces.sad = "(╥☁╥ )"
|
||||
ui.faces.angry = "(-_-')"
|
||||
ui.faces.friend = "(♥‿‿♥)"
|
||||
ui.faces.broken = "(☓‿‿☓)"
|
||||
ui.faces.debug = "(#__#)"
|
||||
ui.faces.upload = "(1__0)"
|
||||
ui.faces.upload1 = "(1__1)"
|
||||
ui.faces.upload2 = "(0__1)"
|
||||
ui.faces.png = false
|
||||
ui.faces.position_x = 0
|
||||
ui.faces.position_y = 34
|
||||
[ui.display]
|
||||
enabled = false
|
||||
rotation = 180
|
||||
type = "waveshare_4"
|
||||
|
||||
ui.web.enabled = true
|
||||
ui.web.address = "::" # listening on both ipv4 and ipv6 - switch to 0.0.0.0 to listen on just ipv4
|
||||
ui.web.username = "changeme"
|
||||
ui.web.password = "changeme"
|
||||
ui.web.origin = ""
|
||||
ui.web.port = 8080
|
||||
ui.web.on_frame = ""
|
||||
|
||||
ui.display.enabled = false
|
||||
ui.display.rotation = 180
|
||||
ui.display.type = "waveshare_4"
|
||||
|
||||
bettercap.handshakes = "/root/handshakes"
|
||||
bettercap.silence = [
|
||||
[bettercap]
|
||||
handshakes = "/home/pi/handshakes"
|
||||
silence = [
|
||||
"ble.device.new",
|
||||
"ble.device.lost",
|
||||
"ble.device.disconnected",
|
||||
"ble.device.connected",
|
||||
"ble.device.service.discovered",
|
||||
"ble.device.characteristic.discovered",
|
||||
"ble.device.disconnected",
|
||||
"ble.device.connected",
|
||||
"ble.connection.timeout",
|
||||
"wifi.client.new",
|
||||
"wifi.client.lost",
|
||||
"wifi.client.probe",
|
||||
@ -217,17 +260,21 @@ bettercap.silence = [
|
||||
"mod.started"
|
||||
]
|
||||
|
||||
fs.memory.enabled = true
|
||||
fs.memory.mounts.log.enabled = true
|
||||
fs.memory.mounts.log.mount = "/etc/pwnagotchi/log/"
|
||||
fs.memory.mounts.log.size = "50M"
|
||||
fs.memory.mounts.log.sync = 60
|
||||
fs.memory.mounts.log.zram = true
|
||||
fs.memory.mounts.log.rsync = true
|
||||
[fs.memory]
|
||||
enabled = true
|
||||
|
||||
fs.memory.mounts.data.enabled = true
|
||||
fs.memory.mounts.data.mount = "/var/tmp/pwnagotchi"
|
||||
fs.memory.mounts.data.size = "10M"
|
||||
fs.memory.mounts.data.sync = 3600
|
||||
fs.memory.mounts.data.zram = true
|
||||
fs.memory.mounts.data.rsync = true
|
||||
[fs.memory.mounts.log]
|
||||
enabled = true
|
||||
mount = "/etc/pwnagotchi/log/"
|
||||
size = "50M"
|
||||
sync = 60
|
||||
zram = true
|
||||
rsync = true
|
||||
|
||||
[fs.memory.mounts.data]
|
||||
enabled = true
|
||||
mount = "/var/tmp/pwnagotchi"
|
||||
size = "10M"
|
||||
sync = 3600
|
||||
zram = true
|
||||
rsync = true
|
||||
|
@ -3,7 +3,8 @@ import re
|
||||
import tempfile
|
||||
import contextlib
|
||||
import shutil
|
||||
import _thread
|
||||
#import _thread
|
||||
import threading
|
||||
import logging
|
||||
|
||||
from time import sleep
|
||||
@ -85,7 +86,8 @@ def setup_mounts(config):
|
||||
if interval:
|
||||
logging.debug("[FS] Starting thread to sync %s (interval: %d)",
|
||||
options['mount'], interval)
|
||||
_thread.start_new_thread(m.daemonize, (interval,))
|
||||
threading.Thread(target=m.daemonize, args=(interval,),name="File Sys", daemon=True).start()
|
||||
#_thread.start_new_thread(m.daemonize, (interval,))
|
||||
else:
|
||||
logging.debug("[FS] Not syncing %s, because interval is 0",
|
||||
options['mount'])
|
||||
|
@ -13,7 +13,8 @@ def is_connected():
|
||||
try:
|
||||
# check DNS
|
||||
host = 'https://api.opwngrid.xyz/api/v1/uptime'
|
||||
r = requests.get(host, headers=None, timeout=(30.0, 60.0))
|
||||
headers = {'user-agent': f'pwnagotchi/{pwnagotchi.__version__}'}
|
||||
r = requests.get(host, headers=headers, timeout=(30.0, 60.0))
|
||||
if r.json().get('isUp'):
|
||||
return True
|
||||
except:
|
||||
@ -70,10 +71,9 @@ def update_data(last_session):
|
||||
enabled = [name for name, options in pwnagotchi.config['main']['plugins'].items() if
|
||||
'enabled' in options and options['enabled']]
|
||||
language = pwnagotchi.config['main']['lang']
|
||||
ai = pwnagotchi.config['ai']['enabled']
|
||||
|
||||
data = {
|
||||
'ai': ai,
|
||||
'ai': "No AI!",
|
||||
'session': {
|
||||
'duration': last_session.duration,
|
||||
'epochs': last_session.epochs,
|
||||
@ -87,12 +87,11 @@ def update_data(last_session):
|
||||
'peers': last_session.peers,
|
||||
},
|
||||
'uname': subprocess.getoutput("uname -a"),
|
||||
'brain': brain,
|
||||
'version': pwnagotchi.__version__,
|
||||
'build': "Pwnagotchi by Jayofelony",
|
||||
'plugins': enabled,
|
||||
'language': language,
|
||||
'bettercap': subprocess.getoutput("bettercap -version").split(".\n\n")[1],
|
||||
'bettercap': subprocess.getoutput("bettercap -version"),
|
||||
'opwngrid': subprocess.getoutput("pwngrid -version")
|
||||
}
|
||||
|
||||
|
@ -50,10 +50,6 @@ class KeyPair(object):
|
||||
with open(self.fingerprint_path, 'w+t') as fp:
|
||||
fp.write(self.fingerprint)
|
||||
|
||||
# no exception, keys loaded correctly.
|
||||
self._view.on_starting()
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
# if we're here, loading the keys broke something ...
|
||||
logging.exception("error loading keys, maybe corrupted, deleting and regenerating ...")
|
||||
@ -63,6 +59,9 @@ class KeyPair(object):
|
||||
except:
|
||||
pass
|
||||
|
||||
# no exception, keys loaded correctly.
|
||||
self._view.on_starting()
|
||||
return
|
||||
def sign(self, message):
|
||||
hasher = SHA256.new(message.encode("ascii"))
|
||||
signer = PKCS1_PSS.new(self.priv_key, saltLen=16)
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1.5.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: MatthewNunu https://github.com/MatthewNunu\n"
|
||||
"Language-Team: \n"
|
||||
@ -28,11 +28,11 @@ msgstr "Nuwe dag, nuwe jag, nuwe pwns!"
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hack die wêreld!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI gereed."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Die neurale netwerk is gereed."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Genereer wagwoord, moenie afskakel nie ..."
|
||||
@ -257,3 +257,9 @@ msgstr "minuut"
|
||||
|
||||
msgid "second"
|
||||
msgstr "tweede"
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "AI gereed."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Die neurale netwerk is gereed."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
Binary file not shown.
@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Pwnagotchi Belarusian translation v 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: <https://github.com/andreifinski>\n"
|
||||
"Language-Team: \n"
|
||||
@ -33,11 +33,11 @@ msgstr "Новы дзень, новае паляванне, новыя ўзло
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Узламай гэту Планету!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "A.I. гатовы."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Нейронная сетка гатова."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Генерацыя ключоў, не выключайце..."
|
||||
@ -263,6 +263,12 @@ msgstr "хвіліну"
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "A.I. гатовы."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Нейронная сетка гатова."
|
||||
|
||||
#, python-brace-format
|
||||
#~ msgid "Unit {name} is nearby! {name}"
|
||||
#~ msgstr "Мэта {name} побач!"
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: 2019-10-23 20:56+0200\n"
|
||||
"Last-Translator: Georgi Koemdzhiev <https://github.com/georgikoemdzhiev>\n"
|
||||
"Language-Team: \n"
|
||||
@ -28,11 +28,11 @@ msgstr "Нов ден, нов лов, нови pwns!"
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Хакни планетата!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI готов."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Невронната мрежа е готова."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Генериране на ключове, не изключвай ..."
|
||||
@ -258,3 +258,9 @@ msgstr "минута"
|
||||
|
||||
msgid "second"
|
||||
msgstr "секунда"
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "AI готов."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Невронната мрежа е готова."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\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"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user