pwn 0.5.613 → 0.5.617

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.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -10
  3. data/README.md +117 -17
  4. data/bin/pwn +47 -2
  5. data/bin/pwn_gqrx_scanner +75 -18
  6. data/documentation/AI-Integration.md +36 -0
  7. data/documentation/BurpSuite.md +26 -0
  8. data/documentation/Contributing.md +31 -0
  9. data/documentation/Diagrams.md +140 -0
  10. data/documentation/Drivers.md +25 -0
  11. data/documentation/General-PWN-Usage.md +60 -0
  12. data/documentation/Home.md +78 -0
  13. data/documentation/How-PWN-Works.md +62 -0
  14. data/documentation/Installation.md +75 -0
  15. data/documentation/NmapIt.md +18 -0
  16. data/documentation/Plugins.md +59 -0
  17. data/documentation/Reporting.md +28 -0
  18. data/documentation/SAST.md +32 -0
  19. data/documentation/Skills-Memory-Learning.md +44 -0
  20. data/documentation/Transparent-Browser.md +32 -0
  21. data/documentation/Troubleshooting.md +38 -0
  22. data/documentation/What-is-PWN.md +53 -0
  23. data/documentation/Why-PWN.md +48 -0
  24. data/documentation/diagrams/ai-integration-tool-calling.svg +172 -0
  25. data/documentation/diagrams/build.sh +20 -0
  26. data/documentation/diagrams/burp-vs-zap-preference.svg +69 -0
  27. data/documentation/diagrams/code-scanning-sast.svg +172 -0
  28. data/documentation/diagrams/dot/_THEME.md +27 -0
  29. data/documentation/diagrams/dot/agent-tool-registry.dot +59 -0
  30. data/documentation/diagrams/dot/ai-integration-tool-calling.dot +50 -0
  31. data/documentation/diagrams/dot/aws-cloud-security.dot +48 -0
  32. data/documentation/diagrams/dot/burp-vs-zap-preference.dot +26 -0
  33. data/documentation/diagrams/dot/code-scanning-sast.dot +49 -0
  34. data/documentation/diagrams/dot/cron-scheduling.dot +40 -0
  35. data/documentation/diagrams/dot/driver-framework.dot +39 -0
  36. data/documentation/diagrams/dot/extrospection-world-awareness.dot +55 -0
  37. data/documentation/diagrams/dot/fuzzing-workflow.dot +47 -0
  38. data/documentation/diagrams/dot/history-to-drivers.dot +34 -0
  39. data/documentation/diagrams/dot/memory-skills-detailed.dot +60 -0
  40. data/documentation/diagrams/dot/network-infra-testing.dot +54 -0
  41. data/documentation/diagrams/dot/overall-pwn-architecture.dot +115 -0
  42. data/documentation/diagrams/dot/penetration-testing-workflow.dot +60 -0
  43. data/documentation/diagrams/dot/plugin-ecosystem.dot +45 -0
  44. data/documentation/diagrams/dot/pwn-ai-feedback-learning-loop.dot +98 -0
  45. data/documentation/diagrams/dot/pwn-repl-prototyping.dot +56 -0
  46. data/documentation/diagrams/dot/reporting-pipeline.dot +46 -0
  47. data/documentation/diagrams/dot/reverse-engineering-flow.dot +43 -0
  48. data/documentation/diagrams/dot/sdr-radio-flow.dot +45 -0
  49. data/documentation/diagrams/dot/sessions-cron-automation.dot +44 -0
  50. data/documentation/diagrams/dot/swarm-multi-agent.dot +70 -0
  51. data/documentation/diagrams/dot/web-application-testing.dot +49 -0
  52. data/documentation/diagrams/dot/zero-day-research-flow.dot +49 -0
  53. data/documentation/diagrams/driver-framework.svg +111 -0
  54. data/documentation/diagrams/fuzzing-workflow.svg +160 -0
  55. data/documentation/diagrams/history-to-drivers.svg +119 -0
  56. data/documentation/diagrams/memory-skills-detailed.svg +202 -0
  57. data/documentation/diagrams/network-infra-testing.svg +183 -0
  58. data/documentation/diagrams/overall-pwn-architecture.svg +405 -0
  59. data/documentation/diagrams/penetration-testing-workflow.svg +201 -0
  60. data/documentation/diagrams/plugin-ecosystem.svg +276 -0
  61. data/documentation/diagrams/pwn-ai-feedback-learning-loop.svg +306 -0
  62. data/documentation/diagrams/pwn-repl-prototyping.svg +168 -0
  63. data/documentation/diagrams/reporting-pipeline.svg +151 -0
  64. data/documentation/diagrams/reverse-engineering-flow.svg +134 -0
  65. data/documentation/diagrams/sessions-cron-automation.svg +136 -0
  66. data/documentation/diagrams/web-application-testing.svg +173 -0
  67. data/documentation/diagrams/zero-day-research-flow.svg +153 -0
  68. data/documentation/pwn-REPL.md +33 -0
  69. data/documentation/pwn-ai-Agent.md +43 -0
  70. data/git_commit.sh +1 -1
  71. data/lib/pwn/ai/agent/assembly.rb +1 -1
  72. data/lib/pwn/ai/agent/btc.rb +1 -1
  73. data/lib/pwn/ai/agent/burp_suite.rb +1 -1
  74. data/lib/pwn/ai/agent/extrospection.rb +618 -0
  75. data/lib/pwn/ai/agent/gqrx.rb +2 -2
  76. data/lib/pwn/ai/agent/hacker_one.rb +1 -1
  77. data/lib/pwn/ai/agent/introspection.rb +91 -0
  78. data/lib/pwn/ai/agent/learning.rb +379 -0
  79. data/lib/pwn/ai/agent/loop.rb +33 -3
  80. data/lib/pwn/ai/agent/metrics.rb +155 -0
  81. data/lib/pwn/ai/agent/prompt_builder.rb +32 -3
  82. data/lib/pwn/ai/agent/sast.rb +1 -1
  83. data/lib/pwn/ai/agent/swarm.rb +437 -0
  84. data/lib/pwn/ai/agent/tools/cron.rb +163 -0
  85. data/lib/pwn/ai/agent/tools/extrospection.rb +280 -0
  86. data/lib/pwn/ai/agent/tools/learning.rb +221 -0
  87. data/lib/pwn/ai/agent/tools/memory.rb +27 -0
  88. data/lib/pwn/ai/agent/tools/metrics.rb +61 -0
  89. data/lib/pwn/ai/agent/tools/sessions.rb +139 -0
  90. data/lib/pwn/ai/agent/tools/skills.rb +124 -8
  91. data/lib/pwn/ai/agent/tools/swarm.rb +229 -0
  92. data/lib/pwn/ai/agent/transparent_browser.rb +1 -1
  93. data/lib/pwn/ai/agent/vuln_gen.rb +2 -2
  94. data/lib/pwn/ai/agent.rb +5 -0
  95. data/lib/pwn/ai/anthropic.rb +19 -4
  96. data/lib/pwn/ai/grok.rb +249 -106
  97. data/lib/pwn/ai.rb +0 -1
  98. data/lib/pwn/config.rb +96 -36
  99. data/lib/pwn/cron.rb +16 -7
  100. data/lib/pwn/plugins/monkey_patch.rb +2 -2
  101. data/lib/pwn/plugins/repl.rb +158 -284
  102. data/lib/pwn/sdr/decoder/base.rb +251 -0
  103. data/lib/pwn/sdr/decoder/gsm.rb +84 -185
  104. data/lib/pwn/sdr/decoder/pocsag.rb +62 -217
  105. data/lib/pwn/sdr/decoder.rb +1 -0
  106. data/lib/pwn/sdr/gqrx.rb +446 -65
  107. data/lib/pwn/version.rb +1 -1
  108. data/spec/lib/pwn/ai/agent/extrospection_spec.rb +15 -0
  109. data/spec/lib/pwn/ai/agent/introspection_spec.rb +15 -0
  110. data/spec/lib/pwn/ai/agent/learning_spec.rb +68 -0
  111. data/spec/lib/pwn/ai/agent/metrics_spec.rb +27 -0
  112. data/spec/lib/pwn/ai/agent/swarm_spec.rb +34 -0
  113. data/spec/lib/pwn/ai/agent/tools/cron_spec.rb +40 -0
  114. data/spec/lib/pwn/ai/agent/tools/extrospection_spec.rb +40 -0
  115. data/spec/lib/pwn/ai/agent/tools/learning_spec.rb +30 -0
  116. data/spec/lib/pwn/ai/agent/tools/metrics_spec.rb +20 -0
  117. data/spec/lib/pwn/ai/agent/tools/sessions_spec.rb +35 -0
  118. data/spec/lib/pwn/ai/agent/tools/swarm_spec.rb +17 -0
  119. data/spec/lib/pwn/{ai/introspection_spec.rb → sdr/decoder/base_spec.rb} +3 -3
  120. data/third_party/pwn_rdoc.jsonl +178 -33
  121. metadata +110 -36
  122. data/documentation/PWN.png +0 -0
  123. data/documentation/PWN_Contributors_and_Users.png +0 -0
  124. data/documentation/fax-spectrogram.png +0 -0
  125. data/documentation/fax-waveform.png +0 -0
  126. data/documentation/frozen_string_literal_files_changed_from_false_to_true.txt +0 -54
  127. data/documentation/lifecycle_authz_replay.example.yaml +0 -27
  128. data/documentation/pwn_android_war_dialer_session.png +0 -0
  129. data/documentation/pwn_install.png +0 -0
  130. data/documentation/pwn_wallpaper.jpg +0 -0
  131. data/documentation/ringing-spectrogram.png +0 -0
  132. data/documentation/ringing-waveform.png +0 -0
  133. data/documentation/vulnerability_report_template.md +0 -37
  134. data/lib/pwn/ai/introspection.rb +0 -76
@@ -1,244 +1,87 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
- require 'open3'
5
- require 'tty-spinner'
6
- require 'io/wait'
7
-
8
3
  module PWN
9
4
  module SDR
10
5
  module Decoder
11
- # POCSAG Decoder Module for Pagers
6
+ # POCSAG (CCIR Radiopaging Code No. 1) decoder for pager networks.
7
+ #
8
+ # Pipeline: GQRX UDP audio → sox 48k→22.05k → multimon-ng POCSAG512/1200/2400.
9
+ #
10
+ # multimon-ng emits lines of the form:
11
+ # POCSAG1200: Address: 123456 Function: 3 Alpha: FIRE ALARM ZONE 4
12
+ # POCSAG512: Address: 000042 Function: 0 Numeric: 5551234
13
+ # POCSAG2400: Address: 987654 Function: 1 Skyper: ...
14
+ # POCSAG1200: Address: 123456 Function: 2 Alpha: <partial><EOT>
15
+ #
16
+ # This module parses those into { protocol, baud, address, function,
17
+ # function_desc, type, type_desc, message } and JSON-logs each page.
12
18
  module POCSAG
19
+ FUNCTION_DESC = {
20
+ 0 => 'Numeric (Tone/A)',
21
+ 1 => 'Tone only (B)',
22
+ 2 => 'Tone only (C)',
23
+ 3 => 'Alphanumeric (D)'
24
+ }.freeze
25
+
26
+ TYPE_DESC = {
27
+ 'Alpha' => 'Alphanumeric text (7-bit ASCII)',
28
+ 'Numeric' => 'Numeric-only (BCD)',
29
+ 'Skyper' => 'Skyper network encoded',
30
+ 'Tone' => 'Tone-only alert (no message body)'
31
+ }.freeze
32
+
13
33
  # Supported Method Parameters::
14
- # pocsag_resp = PWN::SDR::Decoder::POCSAG.decode(
15
- # freq_obj: 'required - GQRX socket object returned from #connect method'
34
+ # PWN::SDR::Decoder::POCSAG.decode(
35
+ # freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
16
36
  # )
17
37
 
18
38
  public_class_method def self.decode(opts = {})
19
39
  freq_obj = opts[:freq_obj]
20
- gqrx_sock = freq_obj[:gqrx_sock]
21
- udp_ip = freq_obj[:udp_ip] || '127.0.0.1'
22
- udp_port = freq_obj[:udp_port] || 7355
23
-
24
- freq_obj.delete(:gqrx_sock)
25
-
26
- skip_freq_char = "\n"
27
-
28
- puts JSON.pretty_generate(freq_obj)
29
- puts 'Press [ENTER] to Continue to next frequency...'
30
-
31
- # Spinner setup with dynamic terminal width awareness
32
- spinner = TTY::Spinner.new(
33
- '[:spinner] :status',
34
- format: :arrow_pulse,
35
- clear: true,
36
- hide_cursor: true
37
- )
38
-
39
- spinner_overhead = 12
40
- max_title_length = [TTY::Screen.width - spinner_overhead, 50].max
41
-
42
- initial_title = "INFO: Decoding #{self.to_s.split('::').last} on udp://#{udp_ip}:#{udp_port} ..."
43
- initial_title = initial_title[0...max_title_length] if initial_title.length > max_title_length
44
- spinner.update(status: initial_title)
45
- spinner.auto_spin
46
40
 
47
- skip_freq = false
48
-
49
- # === Replace netcat with PWN::Plugins::Sock.listen ===
50
- udp_listener = PWN::SDR::GQRX.listen_udp(
51
- udp_ip: udp_ip,
52
- udp_port: udp_port
41
+ PWN::SDR::Decoder::Base.run_pipeline(
42
+ freq_obj: freq_obj,
43
+ protocol: 'POCSAG',
44
+ required_bins: %w[sox multimon-ng],
45
+ decode_cmd: 'multimon-ng -q -t raw -e -u -p ' \
46
+ '-a POCSAG512 -a POCSAG1200 -a POCSAG2400 -f auto -',
47
+ line_match: /^POCSAG(?:512|1200|2400):/,
48
+ parser: proc { |line| parse_line(line: line) }
53
49
  )
50
+ end
54
51
 
55
- # Combined processing pipeline: sox → multimon-ng
56
- decode_cmd = '
57
- sox -t raw -e signed-integer -b 16 -r 48000 -c 1 - \
58
- -t raw -e signed-integer -b 16 -r 22050 -c 1 - | \
59
- multimon-ng -t raw \
60
- -a POCSAG512 \
61
- -a POCSAG1200 \
62
- -a POCSAG2400 \
63
- -
64
- '
65
-
66
- mm_stdin, mm_stdout, mm_stderr, mm_wait_thr = Open3.popen3(decode_cmd)
67
-
68
- current_title = 'Waiting for data frames...'
52
+ # Supported Method Parameters::
53
+ # PWN::SDR::Decoder::POCSAG.parse_line(line: 'POCSAG1200: Address: ...')
69
54
 
70
- # Thread: read from UDP listener and feed to sox|multimon pipeline
71
- receiver_thread = Thread.new do
72
- begin
73
- loop do
74
- data, _sender = udp_listener.recv(4096)
75
- next unless data.to_s.bytesize.positive?
55
+ public_class_method def self.parse_line(opts = {})
56
+ line = opts[:line].to_s
57
+ out = { protocol: 'POCSAG' }
76
58
 
77
- mm_stdin.write(data)
78
- mm_stdin.flush rescue nil
79
- end
80
- rescue IOError, Errno::EPIPE, EOFError, Errno::ECONNRESET
81
- # normal exit path when shutting down
82
- end
59
+ if (m = line.match(/^POCSAG(\d+):/))
60
+ out[:baud] = m[1].to_i
83
61
  end
84
-
85
- # Thread: read decoded output from multimon-ng and display it
86
- decoder_thread = Thread.new do
87
- # buffer = +''
88
- buffer = ''
89
-
90
- valid_types = %w[ALN BIN HEX NUM TON TONE UNK]
91
- loop do
92
- begin
93
- chunk = mm_stdout.readpartial(4096)
94
- # buffer << chunk
95
- buffer = "#{buffer}#{chunk}"
96
-
97
- while (line = buffer.slice!(/^.*\n/))
98
- line = line.chomp
99
- next if line.empty? || !line.start_with?('POCSAG')
100
-
101
- decoded_at = Time.now.strftime('%Y-%m-%d %H:%M:%S%z')
102
- dec_msg = { decoded_at: decoded_at }
103
- dec_msg[:raw_inspected] = line.inspect
104
-
105
- protocol = line[0..8]
106
- protocol = line[0..9] unless protocol == 'POCSAG512'
107
- dec_msg[:protocol] = protocol
108
-
109
- # ────────────────────────────── Detect format ──────────────────────────────
110
- # Sometimes POCSAG is space delimited, sometimes pipe delimited
111
- # FLEX_NEXT appears to always be pipe delimited
112
-
113
- delimiter = '|'
114
- space_delim = false
115
- if line.start_with?('FLEX: ')
116
- delimiter = ' '
117
- space_delim = true
118
- end
119
-
120
- flex_pipe_delim = false
121
- flex_pipe_delim = true if line.start_with?('FLEX|')
122
-
123
- parts = line.split(delimiter)
124
-
125
- # protocol index already used
126
- idx_already_used = [0]
127
- target_parts_idx = 1
128
- target_parts_idx += 1 if flex_pipe_delim
129
- target_parts_idx += 2 if space_delim
130
- dec_msg[:speed] = parts[target_parts_idx] if parts[target_parts_idx]
131
- idx_already_used.push(target_parts_idx)
132
-
133
- target_parts_idx += 2
134
- dec_msg[:capcode] = parts[target_parts_idx] if parts[target_parts_idx]
135
- idx_already_used.push(target_parts_idx)
136
-
137
- target_parts_idx -= 1
138
- dec_msg[:capcode_loc] = parts[target_parts_idx] if parts[target_parts_idx]
139
- idx_already_used.push(target_parts_idx)
140
-
141
- while target_parts_idx < parts.size
142
- if idx_already_used.include?(target_parts_idx)
143
- target_parts_idx += 1
144
- next
145
- end
146
-
147
- key = parts[target_parts_idx]
148
- key = 'long_sequence_number' if key == 'LS'
149
-
150
- if key && valid_types.include?(key)
151
- dec_msg[:type] = key
152
-
153
- dec_msg[:type_desc] = case key
154
- when 'ALN'
155
- 'Human-readable text'
156
- when 'BIN'
157
- 'Binary / data payload (typically 32 bit words)'
158
- when 'HEX'
159
- 'Raw hex representation of data'
160
- when 'NUM'
161
- 'Numbers only'
162
- when 'TON', 'TONE'
163
- 'Just alert tone, no message'
164
- when 'UNK'
165
- 'Decoded but type unknown / unsupported format'
166
- end
167
-
168
- target_parts_idx += 1
169
- payload_parts = parts[target_parts_idx..]
170
- dec_msg[:type_payload] = payload_parts.join(delimiter)
171
-
172
- break
173
- else
174
- target_parts_idx += 1
175
- dec_msg[key] = parts[target_parts_idx] if parts[target_parts_idx]
176
- end
177
-
178
- idx_already_used.push(target_parts_idx)
179
- target_parts_idx += 1
180
- end
181
-
182
- final_msg = freq_obj.merge(dec_msg)
183
- puts JSON.pretty_generate(final_msg)
184
- # TODO: Append dec_msg to a log file in a better way
185
- flex_log_file = "/tmp/flex_decoder_#{Time.now.strftime('%Y%m%d')}.log"
186
- File.open(flex_log_file, 'a') do |f|
187
- f.puts("#{JSON.generate(final_msg)},")
188
- end
189
- end
190
- rescue EOFError, IOError
191
- break
192
- end
193
- end
62
+ if (m = line.match(/Address:\s*(\d+)/i))
63
+ out[:address] = m[1].to_i
64
+ out[:capcode] = m[1]
194
65
  end
195
-
196
- loop do
197
- spinner.update(status: current_title)
198
-
199
- # Non-blocking ENTER check to exit gracefully
200
- next unless $stdin.wait_readable(0)
201
-
202
- begin
203
- char = $stdin.read_nonblock(1)
204
- next unless char == skip_freq_char
205
-
206
- skip_freq = true
207
- puts "\n[!] ENTER pressed → stopping decoder..."
208
-
209
- break
210
- rescue IO::WaitReadable, EOFError
211
- # ignore
212
- end
213
-
214
- break if skip_freq
66
+ if (m = line.match(/Function:\s*(\d+)/i))
67
+ fn = m[1].to_i
68
+ out[:function] = fn
69
+ out[:function_desc] = FUNCTION_DESC[fn] || 'Unknown'
215
70
  end
216
-
217
- spinner.success('Decoding stopped')
218
- rescue StandardError => e
219
- spinner.error("Decoding failed: #{e.message}") if defined?(spinner)
220
- raise
221
- ensure
222
- # Cleanup
223
- [receiver_thread, decoder_thread].each do |thread|
224
- thread.kill if thread.alive?
71
+ if (m = line.match(/\b(Alpha|Numeric|Skyper|Tone):\s*(.*)$/i))
72
+ type = m[1].capitalize
73
+ out[:type] = type
74
+ out[:type_desc] = TYPE_DESC[type] || 'Unknown'
75
+ out[:message] = m[2].to_s.gsub(/<[A-Z]{2,3}>/, '').strip
225
76
  end
226
-
227
- [mm_stdin, mm_stdout, mm_stderr].each { |io| io.close rescue nil }
228
-
229
- mm_wait_thr&.value rescue nil
230
-
231
- PWN::SDR::GQRX.disconnect_udp(udp_listener: udp_listener) if defined?(udp_listener) && udp_listener
232
-
233
- spinner.stop if defined?(spinner) && spinner
77
+ out[:summary] = "POCSAG#{out[:baud]} RIC=#{out[:capcode]} F#{out[:function]} #{out[:type]}: #{out[:message]}"
78
+ out
234
79
  end
235
80
 
236
81
  # Author(s):: 0day Inc. <support@0dayinc.com>
237
82
 
238
83
  public_class_method def self.authors
239
- "AUTHOR(S):
240
- 0day Inc. <support@0dayinc.com>
241
- "
84
+ "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
242
85
  end
243
86
 
244
87
  # Display Usage for this Module
@@ -246,9 +89,11 @@ module PWN
246
89
  public_class_method def self.help
247
90
  puts "USAGE:
248
91
  #{self}.decode(
249
- freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq method'
92
+ freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
250
93
  )
251
94
 
95
+ #{self}.parse_line(line: 'POCSAG1200: Address: 123456 Function: 3 Alpha: HELLO')
96
+
252
97
  #{self}.authors
253
98
  "
254
99
  end
@@ -7,6 +7,7 @@ module PWN
7
7
  module SDR
8
8
  # Deocder Module for SDR signals.
9
9
  module Decoder
10
+ autoload :Base, 'pwn/sdr/decoder/base'
10
11
  autoload :Flex, 'pwn/sdr/decoder/flex'
11
12
  autoload :GSM, 'pwn/sdr/decoder/gsm'
12
13
  autoload :POCSAG, 'pwn/sdr/decoder/pocsag'