pwn 0.5.506 → 0.5.507
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +2 -2
- data/README.md +3 -3
- data/bin/pwn_gqrx_scanner +16 -16
- data/bin/pwn_serial_son_micro_sm132_rfid +11 -11
- data/lib/pwn/ai.rb +1 -1
- data/lib/pwn/aws.rb +1 -1
- data/lib/pwn/banner.rb +1 -1
- data/lib/pwn/blockchain.rb +1 -1
- data/lib/pwn/ffi.rb +1 -1
- data/lib/pwn/plugins/burp_suite.rb +2 -2
- data/lib/pwn/plugins.rb +1 -7
- data/lib/pwn/reports.rb +1 -1
- data/lib/pwn/sast.rb +2 -2
- data/lib/pwn/sdr/decoder/gsm.rb +200 -0
- data/lib/pwn/sdr/decoder.rb +19 -0
- data/lib/pwn/{plugins → sdr}/flipper_zero.rb +5 -5
- data/lib/pwn/sdr/frequency_allocation.rb +372 -0
- data/lib/pwn/sdr/gqrx.rb +656 -0
- data/lib/pwn/{plugins → sdr}/rfidler.rb +2 -2
- data/lib/pwn/{plugins → sdr}/son_micro_rfid.rb +12 -12
- data/lib/pwn/sdr.rb +21 -0
- data/lib/pwn/version.rb +1 -1
- data/lib/pwn/www.rb +1 -1
- data/lib/pwn.rb +1 -0
- data/spec/lib/pwn/sdr/decoder/gsm_spec.rb +15 -0
- data/spec/lib/pwn/sdr/decoder_spec.rb +10 -0
- data/spec/lib/pwn/{plugins → sdr}/flipper_zero_spec.rb +3 -3
- data/spec/lib/pwn/sdr/frequency_allocation_spec.rb +15 -0
- data/spec/lib/pwn/{plugins → sdr}/gqrx_spec.rb +3 -3
- data/spec/lib/pwn/{plugins → sdr}/rfidler_spec.rb +3 -3
- data/spec/lib/pwn/{plugins → sdr}/son_micro_rfid_spec.rb +3 -3
- data/spec/lib/pwn/sdr_spec.rb +10 -0
- data/third_party/pwn_rdoc.jsonl +3 -1
- metadata +21 -13
- data/lib/pwn/plugins/gqrx.rb +0 -757
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5f3b2e3e72d554719b28120d3ccb558d563b5d0d70ca79425994eaf20977bc7
|
|
4
|
+
data.tar.gz: 2a59f3fb5bbe1b66192ebd4c4cd7c6528ead94ffb06770089aaff9806dc83130
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 846169897acd7a88a03c71c65f08e1502ad13ab98538000cf613ad9ff0d43844631510ffb75b32f186e6aca75df0761a92cb030c7014466f7c93e3826d1dae1d
|
|
7
|
+
data.tar.gz: 4974d662081fdc274fc8332265fcc41c5b51a8fcd9de3794641f46719002db226c1659bea12d19969bb5b1ef3ab3d93d088a16885de6a337ee6ab1cff86a9224
|
data/Gemfile
CHANGED
|
@@ -38,7 +38,7 @@ gem 'gem-wrappers', '1.4.0'
|
|
|
38
38
|
gem 'geocoder', '1.8.6'
|
|
39
39
|
gem 'gist', '6.0.0'
|
|
40
40
|
gem 'gruff', '0.29.0'
|
|
41
|
-
gem 'htmlentities', '4.
|
|
41
|
+
gem 'htmlentities', '4.4.2'
|
|
42
42
|
gem 'ipaddress', '0.8.3'
|
|
43
43
|
gem 'jenkins_api_client2', '1.9.0'
|
|
44
44
|
gem 'js-beautify', '0.1.8'
|
|
@@ -90,7 +90,7 @@ gem 'rvm', '1.11.3.9'
|
|
|
90
90
|
gem 'savon', '2.15.1'
|
|
91
91
|
gem 'selenium-devtools', '0.142.0'
|
|
92
92
|
gem 'selenium-webdriver', '4.38.0'
|
|
93
|
-
gem 'slack-ruby-client', '3.
|
|
93
|
+
gem 'slack-ruby-client', '3.1.0'
|
|
94
94
|
gem 'socksify', '1.8.1'
|
|
95
95
|
gem 'spreadsheet', '1.3.4'
|
|
96
96
|
gem 'sqlite3', '2.8.0'
|
data/README.md
CHANGED
|
@@ -37,7 +37,7 @@ $ cd /opt/pwn
|
|
|
37
37
|
$ ./install.sh
|
|
38
38
|
$ ./install.sh ruby-gem
|
|
39
39
|
$ pwn
|
|
40
|
-
pwn[v0.5.
|
|
40
|
+
pwn[v0.5.507]:001 >>> PWN.help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
[](https://youtu.be/G7iLUY4FzsI)
|
|
@@ -52,7 +52,7 @@ $ rvm use ruby-3.4.4@pwn
|
|
|
52
52
|
$ gem uninstall --all --executables pwn
|
|
53
53
|
$ gem install --verbose pwn
|
|
54
54
|
$ pwn
|
|
55
|
-
pwn[v0.5.
|
|
55
|
+
pwn[v0.5.507]:001 >>> PWN.help
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
If you're using a multi-user install of RVM do:
|
|
@@ -62,7 +62,7 @@ $ rvm use ruby-3.4.4@pwn
|
|
|
62
62
|
$ rvmsudo gem uninstall --all --executables pwn
|
|
63
63
|
$ rvmsudo gem install --verbose pwn
|
|
64
64
|
$ pwn
|
|
65
|
-
pwn[v0.5.
|
|
65
|
+
pwn[v0.5.507]:001 >>> PWN.help
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
|
data/bin/pwn_gqrx_scanner
CHANGED
|
@@ -9,8 +9,8 @@ PWN::Driver::Parser.new do |options|
|
|
|
9
9
|
opts[:profile] = p
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
options.on('-l', '--list-profiles', '<Optional - List supported profiles and exit>') do |l|
|
|
13
|
-
opts[:
|
|
12
|
+
options.on('-l', '--list-scan-profiles', '<Optional - List supported scan profiles and exit>') do |l|
|
|
13
|
+
opts[:list_scan_profiles] = l
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
options.on('-tFREQ', '--target-freq=FREQ', '<Required if "--assume-profile" is Nil - Frequency to Conclude Scanning (e.g. 900.000.000 == 900 mHz>') do |e|
|
|
@@ -74,15 +74,15 @@ begin
|
|
|
74
74
|
pwn_provider = 'ruby-gem'
|
|
75
75
|
pwn_provider = ENV.fetch('PWN_PROVIDER') if ENV.keys.any? { |s| s == 'PWN_PROVIDER' }
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
if
|
|
79
|
-
profiles_available = PWN::
|
|
77
|
+
list_scan_profiles = opts[:list_scan_profiles]
|
|
78
|
+
if list_scan_profiles
|
|
79
|
+
profiles_available = PWN::SDR::GQRX.list_scan_profiles
|
|
80
80
|
puts JSON.pretty_generate(profiles_available)
|
|
81
81
|
exit 0
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
profile = opts[:profile]
|
|
85
|
-
opts = PWN::
|
|
85
|
+
opts = PWN::SDR::GQRX.assume_profile(profile: profile) unless profile.nil?
|
|
86
86
|
|
|
87
87
|
target_freq = opts[:target_freq]
|
|
88
88
|
target_freq = target_freq.to_s.delete('.') unless target_freq.nil?
|
|
@@ -93,13 +93,13 @@ begin
|
|
|
93
93
|
port = opts[:port]
|
|
94
94
|
|
|
95
95
|
puts "Connecting to GQRX at #{host}:#{port}..."
|
|
96
|
-
gqrx_sock = PWN::
|
|
96
|
+
gqrx_sock = PWN::SDR::GQRX.connect(target: host, port: port)
|
|
97
97
|
|
|
98
98
|
start_freq = opts[:start_freq]
|
|
99
99
|
start_freq = start_freq.to_s.delete('.') unless start_freq.nil?
|
|
100
100
|
start_freq = start_freq.to_i
|
|
101
101
|
if start_freq.zero?
|
|
102
|
-
start_freq = PWN::
|
|
102
|
+
start_freq = PWN::SDR::GQRX.gqrx_cmd(
|
|
103
103
|
gqrx_sock: gqrx_sock, cmd: 'f',
|
|
104
104
|
resp_ok: 'RPRT 0'
|
|
105
105
|
).to_i
|
|
@@ -115,7 +115,7 @@ begin
|
|
|
115
115
|
|
|
116
116
|
puts "Setting demodulator mode to #{demodulator_mode} and bandwidth to #{bandwidth}..."
|
|
117
117
|
bandwidth = bandwidth.to_s.delete('.').to_i unless bandwidth.nil?
|
|
118
|
-
demod_resp = PWN::
|
|
118
|
+
demod_resp = PWN::SDR::GQRX.gqrx_cmd(
|
|
119
119
|
gqrx_sock: gqrx_sock,
|
|
120
120
|
cmd: "M #{demodulator_mode} #{bandwidth}",
|
|
121
121
|
resp_ok: 'RPRT 0'
|
|
@@ -123,7 +123,7 @@ begin
|
|
|
123
123
|
|
|
124
124
|
audio_gain_db = opts[:audio_gain_db] ||= 1.0
|
|
125
125
|
audio_gain_db = audio_gain_db.to_f
|
|
126
|
-
audio_gain_db_resp = PWN::
|
|
126
|
+
audio_gain_db_resp = PWN::SDR::GQRX.gqrx_cmd(
|
|
127
127
|
gqrx_sock: gqrx_sock,
|
|
128
128
|
cmd: "L AF #{audio_gain_db}",
|
|
129
129
|
resp_ok: 'RPRT 0'
|
|
@@ -131,7 +131,7 @@ begin
|
|
|
131
131
|
|
|
132
132
|
squelch = opts[:squelch] ||= -63.0
|
|
133
133
|
squelch = squelch.to_f
|
|
134
|
-
squelch_resp = PWN::
|
|
134
|
+
squelch_resp = PWN::SDR::GQRX.gqrx_cmd(
|
|
135
135
|
gqrx_sock: gqrx_sock,
|
|
136
136
|
cmd: "L SQL #{squelch}",
|
|
137
137
|
resp_ok: 'RPRT 0'
|
|
@@ -149,7 +149,7 @@ begin
|
|
|
149
149
|
|
|
150
150
|
rf_gain = opts[:rf_gain] ||= 0.0
|
|
151
151
|
rf_gain = rf_gain.to_f
|
|
152
|
-
rf_gain_resp = PWN::
|
|
152
|
+
rf_gain_resp = PWN::SDR::GQRX.gqrx_cmd(
|
|
153
153
|
gqrx_sock: gqrx_sock,
|
|
154
154
|
cmd: "L RF_GAIN #{rf_gain}",
|
|
155
155
|
resp_ok: 'RPRT 0'
|
|
@@ -157,7 +157,7 @@ begin
|
|
|
157
157
|
|
|
158
158
|
intermediate_gain = opts[:intermediate_gain] ||= 32.0
|
|
159
159
|
intermediate_gain = intermediate_gain.to_f
|
|
160
|
-
intermediate_resp = PWN::
|
|
160
|
+
intermediate_resp = PWN::SDR::GQRX.gqrx_cmd(
|
|
161
161
|
gqrx_sock: gqrx_sock,
|
|
162
162
|
cmd: "L IF_GAIN #{intermediate_gain}",
|
|
163
163
|
resp_ok: 'RPRT 0'
|
|
@@ -165,7 +165,7 @@ begin
|
|
|
165
165
|
|
|
166
166
|
baseband_gain = opts[:baseband_gain] ||= 10.0
|
|
167
167
|
baseband_gain = baseband_gain.to_f
|
|
168
|
-
baseband_resp = PWN::
|
|
168
|
+
baseband_resp = PWN::SDR::GQRX.gqrx_cmd(
|
|
169
169
|
gqrx_sock: gqrx_sock,
|
|
170
170
|
cmd: "L BB_GAIN #{baseband_gain}",
|
|
171
171
|
resp_ok: 'RPRT 0'
|
|
@@ -175,7 +175,7 @@ begin
|
|
|
175
175
|
t_freq_pretty = target_freq.to_s.chars.insert(-4, '.').insert(-8, '.').join
|
|
176
176
|
puts "*** Scanning from #{s_freq_pretty} to #{t_freq_pretty}\n\n\n"
|
|
177
177
|
|
|
178
|
-
PWN::
|
|
178
|
+
PWN::SDR::GQRX.scan_range(
|
|
179
179
|
gqrx_sock: gqrx_sock,
|
|
180
180
|
demodulator_mode: demodulator_mode,
|
|
181
181
|
bandwidth: bandwidth,
|
|
@@ -192,5 +192,5 @@ rescue StandardError => e
|
|
|
192
192
|
rescue Interrupt, SystemExit
|
|
193
193
|
puts "\nGoodbye."
|
|
194
194
|
ensure
|
|
195
|
-
gqrx_sock = PWN::
|
|
195
|
+
gqrx_sock = PWN::SDR::GQRX.disconnect(gqrx_sock: gqrx_sock) unless gqrx_sock.nil?
|
|
196
196
|
end
|
|
@@ -44,7 +44,7 @@ begin
|
|
|
44
44
|
parity = opts[:parity]
|
|
45
45
|
flow_control = opts[:flow_control]
|
|
46
46
|
|
|
47
|
-
son_micro_rfid_obj = PWN::
|
|
47
|
+
son_micro_rfid_obj = PWN::SDR::SonMicroRFID.connect(
|
|
48
48
|
block_dev: block_dev,
|
|
49
49
|
baud: baud,
|
|
50
50
|
data_bits: data_bits,
|
|
@@ -60,7 +60,7 @@ begin
|
|
|
60
60
|
puts "Signals: #{son_micro_rfid_obj[:serial_conn].signals}"
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
exec_resp = PWN::
|
|
63
|
+
exec_resp = PWN::SDR::SonMicroRFID.exec(
|
|
64
64
|
son_micro_rfid_obj: son_micro_rfid_obj,
|
|
65
65
|
cmd: :firmware
|
|
66
66
|
)
|
|
@@ -76,7 +76,7 @@ begin
|
|
|
76
76
|
menu_msg = ''
|
|
77
77
|
loop do
|
|
78
78
|
unless menu_msg.include?('ERROR')
|
|
79
|
-
exec_resp = PWN::
|
|
79
|
+
exec_resp = PWN::SDR::SonMicroRFID.exec(
|
|
80
80
|
son_micro_rfid_obj: son_micro_rfid_obj,
|
|
81
81
|
cmd: :antenna_power,
|
|
82
82
|
params: :on
|
|
@@ -100,7 +100,7 @@ begin
|
|
|
100
100
|
when :R
|
|
101
101
|
menu_msg = 'READ TAG'
|
|
102
102
|
begin
|
|
103
|
-
rfid_data = PWN::
|
|
103
|
+
rfid_data = PWN::SDR::SonMicroRFID.read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
|
|
104
104
|
puts "\nRFID Data:"
|
|
105
105
|
puts "Tag ID: #{rfid_data[:tag_id]}"
|
|
106
106
|
puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
|
|
@@ -110,7 +110,7 @@ begin
|
|
|
110
110
|
when :B
|
|
111
111
|
menu_msg = 'BACKUP TAG TO FILE'
|
|
112
112
|
begin
|
|
113
|
-
rfid_data = PWN::
|
|
113
|
+
rfid_data = PWN::SDR::SonMicroRFID.backup_tag(son_micro_rfid_obj: son_micro_rfid_obj)
|
|
114
114
|
puts "\nBackup successful. RFID Data:"
|
|
115
115
|
puts "Tag ID: #{rfid_data[:tag_id]}"
|
|
116
116
|
puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
|
|
@@ -123,7 +123,7 @@ begin
|
|
|
123
123
|
next unless gets.chomp.strip.upcase == 'Y'
|
|
124
124
|
|
|
125
125
|
begin
|
|
126
|
-
rfid_data = PWN::
|
|
126
|
+
rfid_data = PWN::SDR::SonMicroRFID.clone_tag(son_micro_rfid_obj: son_micro_rfid_obj)
|
|
127
127
|
puts "\nClone successful. RFID Data:"
|
|
128
128
|
puts "Tag ID: #{rfid_data[:tag_id]}"
|
|
129
129
|
puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
|
|
@@ -136,7 +136,7 @@ begin
|
|
|
136
136
|
next unless gets.chomp.strip.upcase == 'Y'
|
|
137
137
|
|
|
138
138
|
begin
|
|
139
|
-
rfid_data = PWN::
|
|
139
|
+
rfid_data = PWN::SDR::SonMicroRFID.load_tag_from_file(son_micro_rfid_obj: son_micro_rfid_obj)
|
|
140
140
|
puts "\nLoad successful. RFID Data:"
|
|
141
141
|
puts "Tag ID: #{rfid_data[:tag_id]}"
|
|
142
142
|
puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
|
|
@@ -149,7 +149,7 @@ begin
|
|
|
149
149
|
next unless gets.chomp.strip.upcase == 'Y'
|
|
150
150
|
|
|
151
151
|
begin
|
|
152
|
-
rfid_data = PWN::
|
|
152
|
+
rfid_data = PWN::SDR::SonMicroRFID.update_tag(son_micro_rfid_obj: son_micro_rfid_obj)
|
|
153
153
|
puts "\nUpdate successful. RFID Data:"
|
|
154
154
|
puts "Tag ID: #{rfid_data[:tag_id]}"
|
|
155
155
|
puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
|
|
@@ -159,7 +159,7 @@ begin
|
|
|
159
159
|
when :W
|
|
160
160
|
menu_msg = 'WARM RESET'
|
|
161
161
|
begin
|
|
162
|
-
exec_resp = PWN::
|
|
162
|
+
exec_resp = PWN::SDR::SonMicroRFID.exec(
|
|
163
163
|
son_micro_rfid_obj: son_micro_rfid_obj,
|
|
164
164
|
cmd: :reset
|
|
165
165
|
)
|
|
@@ -171,7 +171,7 @@ begin
|
|
|
171
171
|
exit
|
|
172
172
|
else
|
|
173
173
|
menu_msg = '****** ERROR: Invalid Menu Option Selected ******'
|
|
174
|
-
exec_resp = PWN::
|
|
174
|
+
exec_resp = PWN::SDR::SonMicroRFID.exec(
|
|
175
175
|
son_micro_rfid_obj: son_micro_rfid_obj,
|
|
176
176
|
cmd: :antenna_power,
|
|
177
177
|
params: :off
|
|
@@ -184,5 +184,5 @@ rescue StandardError => e
|
|
|
184
184
|
rescue SystemExit, Interrupt
|
|
185
185
|
puts "\nGoodbye."
|
|
186
186
|
ensure
|
|
187
|
-
PWN::
|
|
187
|
+
PWN::SDR::SonMicroRFID.disconnect(son_micro_rfid_obj: son_micro_rfid_obj) if son_micro_rfid_obj
|
|
188
188
|
end
|
data/lib/pwn/ai.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
# This file, using the autoload directive loads
|
|
4
|
+
# This file, using the autoload directive loads AI modules
|
|
5
5
|
# into memory only when they're needed. For more information, see:
|
|
6
6
|
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
7
|
module AI
|
data/lib/pwn/aws.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
# This file, using the autoload directive loads
|
|
4
|
+
# This file, using the autoload directive loads AWS modules
|
|
5
5
|
# into memory only when they're needed. For more information, see:
|
|
6
6
|
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
7
|
module AWS
|
data/lib/pwn/banner.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
# This file, using the autoload directive loads
|
|
4
|
+
# This file, using the autoload directive loads Banner modules
|
|
5
5
|
# into memory only when they're needed. For more information, see:
|
|
6
6
|
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
7
|
module Banner
|
data/lib/pwn/blockchain.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
# This file, using the autoload directive loads
|
|
4
|
+
# This file, using the autoload directive loads Blockchain modules
|
|
5
5
|
# into memory only when they're needed. For more information, see:
|
|
6
6
|
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
7
|
module Blockchain
|
data/lib/pwn/ffi.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
# This file, using the autoload directive loads
|
|
4
|
+
# This file, using the autoload directive loads FFI modules
|
|
5
5
|
# into memory only when they're needed. For more information, see:
|
|
6
6
|
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
7
|
module FFI
|
|
@@ -265,13 +265,13 @@ module PWN
|
|
|
265
265
|
end
|
|
266
266
|
end
|
|
267
267
|
rescue Errno::ECONNREFUSED
|
|
268
|
-
puts
|
|
268
|
+
puts "BurpSuite:#{type} AI Introspection Thread >>> Terminating API Calls..."
|
|
269
269
|
rescue StandardError => e
|
|
270
270
|
puts "BurpSuite AI Introspection Thread Error: #{e}"
|
|
271
271
|
puts e.backtrace
|
|
272
272
|
raise e
|
|
273
273
|
ensure
|
|
274
|
-
puts
|
|
274
|
+
puts "BurpSuite:#{type} AI Introspection Thread >>> Goodbye."
|
|
275
275
|
end
|
|
276
276
|
|
|
277
277
|
burp_obj[:introspection_threads] = introspection_thread_arr.push(introspection_thread)
|
data/lib/pwn/plugins.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
# This file, using the autoload directive loads
|
|
4
|
+
# This file, using the autoload directive loads Plugins modules
|
|
5
5
|
# into memory only when they're needed. For more information, see:
|
|
6
6
|
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
7
|
module Plugins
|
|
@@ -25,11 +25,9 @@ module PWN
|
|
|
25
25
|
autoload :DetectOS, 'pwn/plugins/detect_os'
|
|
26
26
|
autoload :EIN, 'pwn/plugins/ein'
|
|
27
27
|
autoload :FileFu, 'pwn/plugins/file_fu'
|
|
28
|
-
autoload :FlipperZero, 'pwn/plugins/flipper_zero'
|
|
29
28
|
autoload :Fuzz, 'pwn/plugins/fuzz'
|
|
30
29
|
autoload :Git, 'pwn/plugins/git'
|
|
31
30
|
autoload :Github, 'pwn/plugins/github'
|
|
32
|
-
autoload :GQRX, 'pwn/plugins/gqrx'
|
|
33
31
|
autoload :HackerOne, 'pwn/plugins/hacker_one'
|
|
34
32
|
autoload :Hunter, 'pwn/plugins/hunter'
|
|
35
33
|
autoload :IPInfo, 'pwn/plugins/ip_info'
|
|
@@ -47,8 +45,6 @@ module PWN
|
|
|
47
45
|
autoload :NmapIt, 'pwn/plugins/nmap_it'
|
|
48
46
|
autoload :OAuth2, 'pwn/plugins/oauth2'
|
|
49
47
|
autoload :OCR, 'pwn/plugins/ocr'
|
|
50
|
-
autoload :Ollama, 'pwn/plugins/ollama'
|
|
51
|
-
autoload :OpenAI, 'pwn/plugins/open_ai'
|
|
52
48
|
autoload :OpenAPI, 'pwn/plugins/open_api'
|
|
53
49
|
autoload :OpenVAS, 'pwn/plugins/openvas'
|
|
54
50
|
autoload :Packet, 'pwn/plugins/packet'
|
|
@@ -57,13 +53,11 @@ module PWN
|
|
|
57
53
|
autoload :PS, 'pwn/plugins/ps'
|
|
58
54
|
autoload :RabbitMQ, 'pwn/plugins/rabbit_mq'
|
|
59
55
|
autoload :REPL, 'pwn/plugins/repl'
|
|
60
|
-
autoload :RFIDler, 'pwn/plugins/rfidler'
|
|
61
56
|
autoload :ScannableCodes, 'pwn/plugins/scannable_codes'
|
|
62
57
|
autoload :Serial, 'pwn/plugins/serial'
|
|
63
58
|
autoload :Shodan, 'pwn/plugins/shodan'
|
|
64
59
|
autoload :SlackClient, 'pwn/plugins/slack_client'
|
|
65
60
|
autoload :Sock, 'pwn/plugins/sock'
|
|
66
|
-
autoload :SonMicroRFID, 'pwn/plugins/son_micro_rfid'
|
|
67
61
|
autoload :Spider, 'pwn/plugins/spider'
|
|
68
62
|
autoload :SSN, 'pwn/plugins/ssn'
|
|
69
63
|
autoload :ThreadPool, 'pwn/plugins/thread_pool'
|
data/lib/pwn/reports.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
# This file, using the autoload directive loads
|
|
4
|
+
# This file, using the autoload directive loads Report modules
|
|
5
5
|
# into memory only when they're needed. For more information, see:
|
|
6
6
|
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
7
|
module Reports
|
data/lib/pwn/sast.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
# This file, using the autoload directive loads
|
|
5
|
-
#
|
|
4
|
+
# This file, using the autoload directive loads SAST modules
|
|
5
|
+
# into memory only when they're needed. For more information, see:
|
|
6
6
|
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
7
|
module SAST
|
|
8
8
|
# Zero False Negative SAST Modules
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PWN
|
|
4
|
+
module SDR
|
|
5
|
+
module Decoder
|
|
6
|
+
# SDR Decoder for GSM signals.
|
|
7
|
+
module GSM
|
|
8
|
+
POWER_THRESHOLD = 0.1
|
|
9
|
+
SLEEP_INTERVAL = 0.1
|
|
10
|
+
HEADER_SIZE = 44
|
|
11
|
+
BURST_DURATION_SEC = 0.000577
|
|
12
|
+
|
|
13
|
+
# TSC 0 binary sequence (26 bits): 00100101110000101010011011
|
|
14
|
+
TSC_0 = [0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1].freeze
|
|
15
|
+
|
|
16
|
+
# Starts the live decoding thread.
|
|
17
|
+
def self.start(opts = {})
|
|
18
|
+
gqrx_obj = opts[:gqrx_obj]
|
|
19
|
+
raise ':ERROR: :gqrx_obj is required' unless gqrx_obj.is_a?(Hash)
|
|
20
|
+
|
|
21
|
+
record_path = gqrx_obj[:record_path]
|
|
22
|
+
frequency = gqrx_obj[:frequency]
|
|
23
|
+
sample_rate = gqrx_obj[:bandwidth].to_i
|
|
24
|
+
gqrx_sock = gqrx_obj[:gqrx_sock]
|
|
25
|
+
|
|
26
|
+
gqrx_obj[:decoder_stop_flag] ||= [false]
|
|
27
|
+
|
|
28
|
+
sleep 0.1 until File.exist?(record_path)
|
|
29
|
+
|
|
30
|
+
header = File.binread(record_path, HEADER_SIZE)
|
|
31
|
+
raise 'Invalid WAV header' unless header.start_with?('RIFF') && header.include?('WAVE')
|
|
32
|
+
|
|
33
|
+
bytes_read = HEADER_SIZE
|
|
34
|
+
|
|
35
|
+
puts "GSM Decoder started for frequency: #{frequency}, sample_rate: #{sample_rate}"
|
|
36
|
+
|
|
37
|
+
Thread.new do
|
|
38
|
+
loop do
|
|
39
|
+
break if gqrx_obj[:decoder_stop_flag][0]
|
|
40
|
+
|
|
41
|
+
current_size = File.size(record_path)
|
|
42
|
+
if current_size > bytes_read
|
|
43
|
+
new_bytes = current_size - bytes_read
|
|
44
|
+
# Ensure full I/Q pairs (8 bytes)
|
|
45
|
+
new_bytes -= new_bytes % 8
|
|
46
|
+
data = File.binread(record_path, new_bytes, bytes_read)
|
|
47
|
+
process_chunk(
|
|
48
|
+
data: data,
|
|
49
|
+
sample_rate: sample_rate,
|
|
50
|
+
frequency: frequency
|
|
51
|
+
)
|
|
52
|
+
bytes_read = current_size
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
sleep SLEEP_INTERVAL
|
|
56
|
+
end
|
|
57
|
+
rescue StandardError => e
|
|
58
|
+
puts "Decoder error: #{e.message}"
|
|
59
|
+
ensure
|
|
60
|
+
cleanup(record_path: record_path)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Stops the decoding thread.
|
|
65
|
+
def self.stop(opts = {})
|
|
66
|
+
thread = opts[:thread]
|
|
67
|
+
gqrx_obj = opts[:gqrx_obj]
|
|
68
|
+
raise ':ERROR: :thread and :gqrx_obj are required' unless thread && gqrx_obj.is_a?(Hash)
|
|
69
|
+
|
|
70
|
+
gqrx_obj[:decoder_stop_flag][0] = true
|
|
71
|
+
thread.join(1.0)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class << self
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def process_chunk(opts = {})
|
|
78
|
+
data = opts[:data]
|
|
79
|
+
sample_rate = opts[:sample_rate]
|
|
80
|
+
frequency = opts[:frequency]
|
|
81
|
+
raise ':ERROR: :data, :sample_rate, and :frequency are required' unless data && sample_rate && frequency
|
|
82
|
+
|
|
83
|
+
samples = data.unpack('f< *')
|
|
84
|
+
return if samples.length.odd? # Skip incomplete
|
|
85
|
+
|
|
86
|
+
complex_samples = []
|
|
87
|
+
(0...samples.length).step(2) do |i|
|
|
88
|
+
complex_samples << Complex(samples[i], samples[i + 1])
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
window_size = [(sample_rate * BURST_DURATION_SEC).round, complex_samples.length].min
|
|
92
|
+
return if window_size <= 0
|
|
93
|
+
|
|
94
|
+
# Simplified power on sliding windows
|
|
95
|
+
powers = []
|
|
96
|
+
complex_samples.each_cons(window_size) do |window|
|
|
97
|
+
power = window.map { |c| c.abs**2 }.sum / window_size
|
|
98
|
+
powers << power
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
max_power = powers.max
|
|
102
|
+
return unless max_power > POWER_THRESHOLD
|
|
103
|
+
|
|
104
|
+
# Demod the entire chunk (assume burst-aligned roughly)
|
|
105
|
+
bits = demod_gmsk(complex_samples)
|
|
106
|
+
# Synchronize via TSC correlation
|
|
107
|
+
sync_offset = find_tsc_offset(bits, TSC_0)
|
|
108
|
+
return unless sync_offset >= 0
|
|
109
|
+
|
|
110
|
+
# Extract data bits from normal burst structure
|
|
111
|
+
burst_start = sync_offset - 58 # TSC starts at symbol 58 (0-index)
|
|
112
|
+
return unless burst_start >= 0 && burst_start + 148 <= bits.length
|
|
113
|
+
|
|
114
|
+
data_bits = extract_data_bits(bits, burst_start)
|
|
115
|
+
puts "Burst synchronized at offset #{sync_offset} for #{frequency} Hz (power: #{max_power.round(4)})"
|
|
116
|
+
decode_imsi(
|
|
117
|
+
data_bits: data_bits,
|
|
118
|
+
frequency: frequency
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def demod_gmsk(complex_samples)
|
|
123
|
+
return [] if complex_samples.length < 2
|
|
124
|
+
|
|
125
|
+
bits = []
|
|
126
|
+
(1...complex_samples.length).each do |i|
|
|
127
|
+
prod = complex_samples[i] * complex_samples[i - 1].conj
|
|
128
|
+
# Sign of imaginary part for quadrature differential
|
|
129
|
+
bit = (prod.imag >= 0 ? 0 : 1) # Or adjust polarity
|
|
130
|
+
bits << bit
|
|
131
|
+
end
|
|
132
|
+
bits
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def find_tsc_offset(bits, tsc)
|
|
136
|
+
max_corr = -1
|
|
137
|
+
best_offset = -1
|
|
138
|
+
tsc_length = tsc.length # 26
|
|
139
|
+
(0...(bits.length - tsc_length + 1)).each do |offset|
|
|
140
|
+
window = bits[offset, tsc_length]
|
|
141
|
+
corr = window.zip(tsc).count { |b1, b2| b1 == b2 }
|
|
142
|
+
if corr > max_corr
|
|
143
|
+
max_corr = corr
|
|
144
|
+
best_offset = offset
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
# Threshold: e.g., >20 matches for good sync
|
|
148
|
+
max_corr > 20 ? best_offset : -1
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Extract 114 data bits from normal burst (ignoring tails/guard)
|
|
152
|
+
def extract_data_bits(bits, burst_start)
|
|
153
|
+
data1_start = burst_start + 2
|
|
154
|
+
data2_start = burst_start + 88 # After TSC 26 + data1 57 = 85, +3? Wait, structure: tail2(0-1), data(2-58), tsc(59-84), data(85-141), tail(142-143)
|
|
155
|
+
data1 = bits[data1_start, 57]
|
|
156
|
+
data2 = bits[data2_start, 57]
|
|
157
|
+
data1 + data2
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def decode_imsi(opts = {})
|
|
161
|
+
data_bits = opts[:data_bits]
|
|
162
|
+
frequency = opts[:frequency]
|
|
163
|
+
raise ':ERROR: :data_bits and :frequency are required' unless data_bits && frequency
|
|
164
|
+
|
|
165
|
+
# Simplified "IMSI extraction": Interpret first ~60 bits as packed digits (4 bits per digit, BCD-like).
|
|
166
|
+
# In reality: Deinterleave (over bursts), Viterbi decode convolutional code (polys G0=10011b, G1=11011b),
|
|
167
|
+
# CRC check, parse L3 message (e.g., Paging Req Type 1 has IMSI IE at specific offset, packed BCD).
|
|
168
|
+
# Here: Raw data bits to 15-digit IMSI (first 60 bits -> 15 nibbles).
|
|
169
|
+
return unless data_bits.length >= 60
|
|
170
|
+
|
|
171
|
+
imsi_digits = []
|
|
172
|
+
data_bits[0, 60].each_slice(4) do |nibble|
|
|
173
|
+
digit = nibble.join.to_i(2)
|
|
174
|
+
imsi_digits << (digit % 10) # Mod 10 for digit-like, or keep as is for hex
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Format as 3(MCC)+3(MNC)+9(MSIN)
|
|
178
|
+
mcc = imsi_digits[0, 3].join
|
|
179
|
+
mnc = imsi_digits[3, 3].join
|
|
180
|
+
msin = imsi_digits[6, 9].join
|
|
181
|
+
imsi = "#{mcc.ljust(3, '0')}#{mnc.ljust(3, '0')}#{msin.ljust(9, '0')}"
|
|
182
|
+
|
|
183
|
+
puts "Decoded IMSI: #{imsi} at #{frequency} Hz"
|
|
184
|
+
# TODO: Integrate full L3 parser (e.g., from ruby-gsm gem or custom).
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def cleanup(opts = {})
|
|
188
|
+
record_path = opts[:record_path]
|
|
189
|
+
raise ':ERROR: :record_path is required' unless record_path
|
|
190
|
+
|
|
191
|
+
return unless File.exist?(record_path)
|
|
192
|
+
|
|
193
|
+
File.delete(record_path)
|
|
194
|
+
puts "Cleaned up recording: #{record_path}"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PWN
|
|
4
|
+
# This file, using the autoload directive loads SDR modules
|
|
5
|
+
# into memory only when they're needed. For more information, see:
|
|
6
|
+
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
|
+
module SDR
|
|
8
|
+
# Deocder Module for SDR signals.
|
|
9
|
+
module Decoder
|
|
10
|
+
autoload :GSM, 'pwn/sdr/decoder/gsm'
|
|
11
|
+
|
|
12
|
+
# Display a List of Every PWN::AI Module
|
|
13
|
+
|
|
14
|
+
public_class_method def self.help
|
|
15
|
+
constants.sort
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
module
|
|
4
|
+
module SDR
|
|
5
5
|
# This plugin is used for interacting with Bus Pirate v3.6
|
|
6
6
|
# This plugin may be compatible with other versions, however,
|
|
7
7
|
# has not been tested with anything other than v3.6.
|
|
8
8
|
module FlipperZero
|
|
9
9
|
# Supported Method Parameters::
|
|
10
|
-
# PWN::
|
|
10
|
+
# PWN::SDR::FlipperZero.connect_via_screen(
|
|
11
11
|
# block_dev: 'optional - serial block device path (defaults to /dev/ttyACM0)'
|
|
12
12
|
# )
|
|
13
13
|
|
|
@@ -34,7 +34,7 @@ module PWN
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Supported Method Parameters::
|
|
37
|
-
# flipper_zero_obj = PWN::
|
|
37
|
+
# flipper_zero_obj = PWN::SDR::FlipperZero.connect(
|
|
38
38
|
# block_dev: 'optional serial block device path (defaults to /dev/ttyACM0)',
|
|
39
39
|
# baud: 'optional (defaults to 9600)',
|
|
40
40
|
# data_bits: 'optional (defaults to 8)',
|
|
@@ -50,7 +50,7 @@ module PWN
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# Supported Method Parameters::
|
|
53
|
-
# response = PWN::
|
|
53
|
+
# response = PWN::SDR::FlipperZero.request(
|
|
54
54
|
# flipper_zero_obj: 'required - flipper_zero_obj returned from #connect method',
|
|
55
55
|
# payload: 'optional - payload to send to the device (defaults to help)'
|
|
56
56
|
# )
|
|
@@ -75,7 +75,7 @@ module PWN
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
# Supported Method Parameters::
|
|
78
|
-
# PWN::
|
|
78
|
+
# PWN::SDR::FlipperZero.disconnect(
|
|
79
79
|
# flipper_zero_obj: 'required - flipper_zero_obj returned from #connect method'
|
|
80
80
|
# )
|
|
81
81
|
|