pwn 0.5.620 → 0.5.622
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 +1 -1
- data/README.md +1 -1
- data/documentation/Agent-Tool-Registry.md +3 -3
- data/documentation/Configuration.md +292 -47
- data/documentation/Cron.md +14 -1
- data/documentation/Diagrams.md +1 -1
- data/documentation/Extrospection.md +71 -5
- data/documentation/Home.md +2 -2
- data/documentation/How-PWN-Works.md +1 -1
- data/documentation/Mistakes.md +2 -1
- data/documentation/Persistence.md +2 -1
- data/documentation/Skills-Memory-Learning.md +3 -2
- data/documentation/Transparent-Browser.md +18 -1
- data/documentation/diagrams/agent-tool-registry.svg +55 -54
- data/documentation/diagrams/ai-integration-tool-calling.svg +24 -24
- data/documentation/diagrams/aws-cloud-security.svg +25 -25
- data/documentation/diagrams/burp-vs-zap-preference.svg +12 -12
- data/documentation/diagrams/code-scanning-sast.svg +25 -25
- data/documentation/diagrams/cron-scheduling.svg +23 -23
- data/documentation/diagrams/dot/agent-tool-registry.dot +2 -2
- data/documentation/diagrams/dot/extrospection-world-awareness.dot +29 -8
- data/documentation/diagrams/dot/memory-skills-detailed.dot +4 -0
- data/documentation/diagrams/dot/overall-pwn-architecture.dot +2 -2
- data/documentation/diagrams/dot/persistence-filesystem.dot +2 -1
- data/documentation/diagrams/dot/pwn-ai-feedback-learning-loop.dot +11 -5
- data/documentation/diagrams/driver-framework.svg +13 -13
- data/documentation/diagrams/extrospection-world-awareness.svg +229 -99
- data/documentation/diagrams/fuzzing-workflow.svg +24 -24
- data/documentation/diagrams/hardware-hacking.svg +18 -18
- data/documentation/diagrams/history-to-drivers.svg +18 -18
- data/documentation/diagrams/memory-skills-detailed.svg +124 -101
- data/documentation/diagrams/mistakes-negative-feedback.svg +55 -55
- data/documentation/diagrams/network-infra-testing.svg +27 -27
- data/documentation/diagrams/overall-pwn-architecture.svg +85 -85
- data/documentation/diagrams/penetration-testing-workflow.svg +30 -30
- data/documentation/diagrams/persistence-filesystem.svg +94 -85
- data/documentation/diagrams/plugin-ecosystem.svg +35 -35
- data/documentation/diagrams/pwn-ai-feedback-learning-loop.svg +226 -180
- data/documentation/diagrams/pwn-repl-prototyping.svg +20 -20
- data/documentation/diagrams/reporting-pipeline.svg +18 -18
- data/documentation/diagrams/reverse-engineering-flow.svg +21 -21
- data/documentation/diagrams/sdr-radio-flow.svg +31 -31
- data/documentation/diagrams/sessions-cron-automation.svg +18 -18
- data/documentation/diagrams/swarm-multi-agent.svg +39 -39
- data/documentation/diagrams/web-application-testing.svg +26 -26
- data/documentation/diagrams/zero-day-research-flow.svg +25 -25
- data/documentation/pwn-ai-Agent.md +4 -4
- data/lib/pwn/ai/agent/assembly.rb +1 -1
- data/lib/pwn/ai/agent/btc.rb +1 -1
- data/lib/pwn/ai/agent/burp_suite.rb +1 -1
- data/lib/pwn/ai/agent/extrospection.rb +435 -6
- data/lib/pwn/ai/agent/gqrx.rb +2 -2
- data/lib/pwn/ai/agent/hacker_one.rb +1 -1
- data/lib/pwn/ai/agent/learning.rb +16 -16
- data/lib/pwn/ai/agent/loop.rb +1 -1
- data/lib/pwn/ai/agent/{introspection.rb → reflect.rb} +12 -12
- data/lib/pwn/ai/agent/sast.rb +1 -1
- data/lib/pwn/ai/agent/tools/extrospection.rb +89 -4
- data/lib/pwn/ai/agent/tools/learning.rb +7 -7
- data/lib/pwn/ai/agent/transparent_browser.rb +1 -1
- data/lib/pwn/ai/agent/vuln_gen.rb +2 -2
- data/lib/pwn/ai/agent.rb +1 -1
- data/lib/pwn/ai/open_ai.rb +10 -6
- data/lib/pwn/config.rb +5 -4
- data/lib/pwn/plugins/burp_suite.rb +2 -2
- data/lib/pwn/sast/pom_version.rb +2 -2
- data/lib/pwn/sast/test_case_engine.rb +1 -1
- data/lib/pwn/sdr/decoder/adsb.rb +14 -33
- data/lib/pwn/sdr/decoder/apt.rb +97 -36
- data/lib/pwn/sdr/decoder/base.rb +258 -171
- data/lib/pwn/sdr/decoder/bluetooth.rb +13 -20
- data/lib/pwn/sdr/decoder/dect.rb +13 -27
- data/lib/pwn/sdr/decoder/dsp.rb +396 -0
- data/lib/pwn/sdr/decoder/flex.rb +177 -210
- data/lib/pwn/sdr/decoder/gps.rb +12 -21
- data/lib/pwn/sdr/decoder/gsm.rb +21 -73
- data/lib/pwn/sdr/decoder/iridium.rb +11 -26
- data/lib/pwn/sdr/decoder/lora.rb +16 -23
- data/lib/pwn/sdr/decoder/lte.rb +11 -32
- data/lib/pwn/sdr/decoder/morse.rb +95 -21
- data/lib/pwn/sdr/decoder/p25.rb +19 -16
- data/lib/pwn/sdr/decoder/pager.rb +22 -38
- data/lib/pwn/sdr/decoder/pocsag.rb +175 -68
- data/lib/pwn/sdr/decoder/rfid.rb +24 -33
- data/lib/pwn/sdr/decoder/rtl433.rb +21 -30
- data/lib/pwn/sdr/decoder/rtty.rb +100 -27
- data/lib/pwn/sdr/decoder/wifi.rb +21 -42
- data/lib/pwn/sdr/decoder/zigbee.rb +18 -40
- data/lib/pwn/sdr/decoder.rb +8 -0
- data/lib/pwn/sdr/gqrx.rb +1 -1
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/ai/agent/{introspection_spec.rb → reflect_spec.rb} +3 -3
- data/spec/lib/pwn/sdr/decoder/dsp_spec.rb +15 -0
- data/third_party/pwn_rdoc.jsonl +75 -19
- metadata +7 -5
|
@@ -5,14 +5,14 @@ require 'json'
|
|
|
5
5
|
module PWN
|
|
6
6
|
module AI
|
|
7
7
|
module Agent
|
|
8
|
-
# PWN::AI::Agent::
|
|
8
|
+
# PWN::AI::Agent::Reflect is the inward-facing counterpart to
|
|
9
9
|
# PWN::AI::Agent::Extrospection. Where Extrospection looks OUTWARD at
|
|
10
10
|
# the world the agent operates in (host state, toolchain, network,
|
|
11
|
-
# threat-intel),
|
|
12
|
-
#
|
|
13
|
-
#
|
|
11
|
+
# threat-intel), Reflect looks INWARD - it lets pwn hand a request to
|
|
12
|
+
# the active AI engine and reflect on its own artifacts, transcripts,
|
|
13
|
+
# findings, code, or decisions.
|
|
14
14
|
#
|
|
15
|
-
# This module is gated by `PWN::Env[:ai][:
|
|
15
|
+
# This module is gated by `PWN::Env[:ai][:module_reflection]` so that
|
|
16
16
|
# potentially-sensitive local data is never shipped to a remote LLM
|
|
17
17
|
# unless the operator has explicitly opted in via pwn-vault / config.
|
|
18
18
|
#
|
|
@@ -21,16 +21,16 @@ module PWN
|
|
|
21
21
|
# through when it wants an LLM opinion on locally-produced data, and
|
|
22
22
|
# it is also what PWN::AI::Agent::Learning.reflect uses to distill
|
|
23
23
|
# session transcripts into durable PWN::Memory lessons.
|
|
24
|
-
module
|
|
24
|
+
module Reflect
|
|
25
25
|
# Supported Method Parameters::
|
|
26
|
-
# response = PWN::AI::Agent::
|
|
26
|
+
# response = PWN::AI::Agent::Reflect.on(
|
|
27
27
|
# request: 'required - String - What you want the AI to reflect on',
|
|
28
28
|
# system_role_content: 'optional - context to set up the model behavior for reflection',
|
|
29
29
|
# spinner: 'optional - Boolean - Display spinner during operation (default: false)',
|
|
30
30
|
# suppress_pii_warning: 'optional - Boolean - Suppress PII Warnings (default: false)'
|
|
31
31
|
# )
|
|
32
32
|
|
|
33
|
-
public_class_method def self.
|
|
33
|
+
public_class_method def self.on(opts = {})
|
|
34
34
|
request = opts[:request]
|
|
35
35
|
raise 'ERROR: request must be provided' if request.nil?
|
|
36
36
|
|
|
@@ -42,14 +42,14 @@ module PWN
|
|
|
42
42
|
|
|
43
43
|
response = nil
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
ai_module_reflection = PWN::Env[:ai][:module_reflection]
|
|
46
46
|
|
|
47
|
-
if
|
|
47
|
+
if ai_module_reflection && request.length.positive?
|
|
48
48
|
engine = PWN::Env[:ai][:active].to_s.downcase.to_sym
|
|
49
49
|
valid_ai_engines = PWN::AI.help.reject { |e| e.downcase == :agent }.map(&:downcase)
|
|
50
50
|
raise "ERROR: Unsupported AI engine. Supported engines are: #{valid_ai_engines}" unless valid_ai_engines.include?(engine)
|
|
51
51
|
|
|
52
|
-
warn "AI
|
|
52
|
+
warn "AI Reflection is enabled. Ensure #{engine} has been authorized for use and/or requests are sanitized properly." unless suppress_pii_warning
|
|
53
53
|
response = PWN::AI::Agent::Loop.run(
|
|
54
54
|
request: request.chomp,
|
|
55
55
|
system_role_content: system_role_content,
|
|
@@ -75,7 +75,7 @@ module PWN
|
|
|
75
75
|
|
|
76
76
|
public_class_method def self.help
|
|
77
77
|
puts "USAGE:
|
|
78
|
-
#{self}.
|
|
78
|
+
#{self}.on(
|
|
79
79
|
request: 'required - String - What you want the AI to reflect on',
|
|
80
80
|
system_role_content: 'optional - context to set up the model behavior for reflection',
|
|
81
81
|
spinner: 'optional - Boolean - Display spinner during operation (default: false)',
|
data/lib/pwn/ai/agent/sast.rb
CHANGED
|
@@ -16,7 +16,7 @@ module PWN
|
|
|
16
16
|
|
|
17
17
|
system_role_content = 'Your sole purpose is to analyze source code snippets and generate an Exploit Prediction Scoring System (EPSS) score between 0% - 100%. Just generate a score unless score is >= 75% in which a PoC and code fix should also be included.'
|
|
18
18
|
|
|
19
|
-
PWN::AI::Agent::
|
|
19
|
+
PWN::AI::Agent::Reflect.on(
|
|
20
20
|
system_role_content: system_role_content,
|
|
21
21
|
request: request,
|
|
22
22
|
suppress_pii_warning: true
|
|
@@ -29,7 +29,7 @@ PWN::AI::Agent::Registry.register(
|
|
|
29
29
|
type: 'object',
|
|
30
30
|
properties: {
|
|
31
31
|
persist: { type: 'boolean', default: true, description: 'Write to disk & rotate previous baseline.' },
|
|
32
|
-
sections: { type: 'array', items: { type: 'string', enum: %w[host net toolchain repo env rf] }, description: 'Subset of probes to run (default all).' }
|
|
32
|
+
sections: { type: 'array', items: { type: 'string', enum: %w[host net toolchain repo env rf web] }, description: 'Subset of probes to run (default all).' }
|
|
33
33
|
},
|
|
34
34
|
required: []
|
|
35
35
|
}
|
|
@@ -86,7 +86,7 @@ PWN::AI::Agent::Registry.register(
|
|
|
86
86
|
properties: {
|
|
87
87
|
source: { type: 'string', description: 'Where it came from (nmap, shodan, burp, cve, human, ...).' },
|
|
88
88
|
data: { type: 'string', description: 'The observation itself.' },
|
|
89
|
-
category: { type: 'string', enum: %w[recon vuln intel target network env rf misc], default: 'misc' },
|
|
89
|
+
category: { type: 'string', enum: %w[recon vuln intel target network env rf web misc], default: 'misc' },
|
|
90
90
|
target: { type: 'string', description: 'Host / IP / URL / asset the observation is about.' },
|
|
91
91
|
tags: { type: 'array', items: { type: 'string' } },
|
|
92
92
|
ttl: { type: 'integer', description: 'Seconds until stale (omit = forever).' }
|
|
@@ -187,7 +187,10 @@ PWN::AI::Agent::Registry.register(
|
|
|
187
187
|
'against toolchain drift / missing binaries, (b) Learning ' \
|
|
188
188
|
'failures against host/net/repo drift on the same day, ' \
|
|
189
189
|
'(c) recorded :intel observations against installed ' \
|
|
190
|
-
'component versions
|
|
190
|
+
'component versions, (d) :web DOM drift on watched targets ' \
|
|
191
|
+
'against Learning failures citing that host, (e) refuted ' \
|
|
192
|
+
'extro_verify claims against stale PWN::Memory :fact ' \
|
|
193
|
+
'entries. Returns actionable findings so the ' \
|
|
191
194
|
'agent can distinguish "I did it wrong" from "the world ' \
|
|
192
195
|
'changed under me".',
|
|
193
196
|
parameters: {
|
|
@@ -255,7 +258,7 @@ PWN::AI::Agent::Registry.register(
|
|
|
255
258
|
name: 'extro_auto_toggle',
|
|
256
259
|
description: 'Enable/disable end-of-run auto-extrospection ' \
|
|
257
260
|
'(PWN::Env[:ai][:agent][:auto_extrospect]). When enabled, ' \
|
|
258
|
-
'Learning.
|
|
261
|
+
'Learning.auto_introspect ALSO calls Extrospection.auto_' \
|
|
259
262
|
'extrospect after every final answer, so host drift is ' \
|
|
260
263
|
'captured continuously without an explicit extro_snapshot ' \
|
|
261
264
|
'call. Omit `enabled` to just read the current state.',
|
|
@@ -278,3 +281,85 @@ PWN::AI::Agent::Registry.register(
|
|
|
278
281
|
{ previous: prev, current: ai[:agent][:auto_extrospect] ? true : false }
|
|
279
282
|
}
|
|
280
283
|
)
|
|
284
|
+
|
|
285
|
+
PWN::AI::Agent::Registry.register(
|
|
286
|
+
name: 'extro_verify',
|
|
287
|
+
toolset: 'extrospection',
|
|
288
|
+
schema: {
|
|
289
|
+
name: 'extro_verify',
|
|
290
|
+
description: 'Browser-backed SELF FACT-CHECK. Drives ' \
|
|
291
|
+
'PWN::Plugins::TransparentBrowser (:headless) against a ' \
|
|
292
|
+
'canonical source for the claim class (NVD/CVE.org for ' \
|
|
293
|
+
':cve, rubygems/PyPI/GitHub for :version, the cited URL ' \
|
|
294
|
+
'for :doc, DuckDuckGo HTML for :generic), renders the DOM ' \
|
|
295
|
+
'with JS executed, and returns {claim:, kind:, verdict: ' \
|
|
296
|
+
':confirmed|:refuted|:unknown, confidence:, evidence:[], ' \
|
|
297
|
+
'action_taken:}. On :refuted → Mistakes.record(tool:' \
|
|
298
|
+
"'assumption') so KNOWN MISTAKES warns every future run; " \
|
|
299
|
+
'on :confirmed → observe(:intel, ttl:30d); on :unknown → ' \
|
|
300
|
+
'Learning.note_outcome(tags:[needs_human]). This is the ' \
|
|
301
|
+
'PROACTIVE trigger that catches the model being wrong ' \
|
|
302
|
+
'about the world before a human does — the extrospective ' \
|
|
303
|
+
'mirror of mistakes_record.',
|
|
304
|
+
parameters: {
|
|
305
|
+
type: 'object',
|
|
306
|
+
properties: {
|
|
307
|
+
claim: { type: 'string', description: 'Factual claim to fact-check (a CVE assertion, "latest X is v1.2.3", a cited URL + quoted snippet, or free text).' },
|
|
308
|
+
kind: { type: 'string', enum: %w[cve version doc generic], description: 'Force a verifier. Omit to auto-detect from the claim.' },
|
|
309
|
+
url: { type: 'string', description: 'Explicit URL the claim cites (forces kind: :doc).' },
|
|
310
|
+
commit: { type: 'boolean', default: true, description: 'Write Mistakes/observe/Learning on the verdict.' },
|
|
311
|
+
proxy: { type: 'string', description: 'Upstream proxy for TransparentBrowser (e.g. "tor" or http://127.0.0.1:8080).' }
|
|
312
|
+
},
|
|
313
|
+
required: %w[claim]
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
check: -> { defined?(PWN::AI::Agent::Extrospection) && PWN::AI::Agent::Extrospection.respond_to?(:verify) },
|
|
317
|
+
handler: lambda { |args|
|
|
318
|
+
o = { claim: args[:claim] }
|
|
319
|
+
o[:kind] = args[:kind] if args[:kind]
|
|
320
|
+
o[:url] = args[:url] if args[:url]
|
|
321
|
+
o[:commit] = args[:commit] if args.key?(:commit)
|
|
322
|
+
o[:proxy] = args[:proxy] if args[:proxy]
|
|
323
|
+
PWN::AI::Agent::Extrospection.verify(o)
|
|
324
|
+
}
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
PWN::AI::Agent::Registry.register(
|
|
328
|
+
name: 'extro_watch',
|
|
329
|
+
toolset: 'extrospection',
|
|
330
|
+
schema: {
|
|
331
|
+
name: 'extro_watch',
|
|
332
|
+
description: 'Passive change-detection on an external web artefact you ' \
|
|
333
|
+
'care about (a target /api/version, a vendor changelog, a ' \
|
|
334
|
+
'bug-bounty scope page). Renders the URL headlessly via ' \
|
|
335
|
+
'PWN::Plugins::TransparentBrowser, hashes the RENDERED DOM ' \
|
|
336
|
+
'text (JS-delivered changes count), captures title / TLS ' \
|
|
337
|
+
'cert fp / screenshot, and persists it as observe(' \
|
|
338
|
+
'category: :web). Re-running against the same URL returns ' \
|
|
339
|
+
'{changed: true|false, prior_sha:, current:{…}}; a ' \
|
|
340
|
+
'subsequent extro_snapshot(sections:["web"]) surfaces the ' \
|
|
341
|
+
'delta in drift() as ~web.<host>.dom_sha exactly like ' \
|
|
342
|
+
'~toolchain.nmap today.',
|
|
343
|
+
parameters: {
|
|
344
|
+
type: 'object',
|
|
345
|
+
properties: {
|
|
346
|
+
url: { type: 'string', description: 'URL to render, hash and watch.' },
|
|
347
|
+
selector: { type: 'string', description: 'CSS selector whose innerText to hash (default full body).' },
|
|
348
|
+
ttl: { type: 'integer', description: 'Seconds until this :web observation is stale (default 604800 = 7d).' },
|
|
349
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
350
|
+
proxy: { type: 'string', description: 'Upstream proxy for TransparentBrowser (e.g. "tor").' }
|
|
351
|
+
},
|
|
352
|
+
required: %w[url]
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
check: -> { defined?(PWN::AI::Agent::Extrospection) && PWN::AI::Agent::Extrospection.respond_to?(:watch) },
|
|
356
|
+
handler: lambda { |args|
|
|
357
|
+
PWN::AI::Agent::Extrospection.watch(
|
|
358
|
+
url: args[:url],
|
|
359
|
+
selector: args[:selector],
|
|
360
|
+
ttl: args[:ttl],
|
|
361
|
+
tags: args[:tags],
|
|
362
|
+
proxy: args[:proxy]
|
|
363
|
+
)
|
|
364
|
+
}
|
|
365
|
+
)
|
|
@@ -190,13 +190,13 @@ PWN::AI::Agent::Registry.register(
|
|
|
190
190
|
)
|
|
191
191
|
|
|
192
192
|
PWN::AI::Agent::Registry.register(
|
|
193
|
-
name: '
|
|
193
|
+
name: 'learning_auto_introspect_toggle',
|
|
194
194
|
toolset: 'learning',
|
|
195
195
|
schema: {
|
|
196
|
-
name: '
|
|
196
|
+
name: 'learning_auto_introspect_toggle',
|
|
197
197
|
description: 'Enable/disable end-of-run auto-reflection ' \
|
|
198
|
-
'(PWN::Env[:ai][:agent][:
|
|
199
|
-
'Loop.run calls Learning.
|
|
198
|
+
'(PWN::Env[:ai][:agent][:auto_introspect]). When enabled, ' \
|
|
199
|
+
'Loop.run calls Learning.auto_introspect on the session after ' \
|
|
200
200
|
'every final answer. Disable during noisy fuzzing loops; ' \
|
|
201
201
|
're-enable for the summary turn. Omit `enabled` to just ' \
|
|
202
202
|
'read the current state.',
|
|
@@ -214,8 +214,8 @@ PWN::AI::Agent::Registry.register(
|
|
|
214
214
|
raise 'PWN::Env[:ai] is unavailable or immutable' unless ai.is_a?(Hash) && !ai.frozen?
|
|
215
215
|
|
|
216
216
|
ai[:agent] = (ai[:agent] || {}).dup if ai[:agent].nil? || ai[:agent].frozen?
|
|
217
|
-
prev = ai[:agent][:
|
|
218
|
-
ai[:agent][:
|
|
219
|
-
{ previous: prev, current: ai[:agent][:
|
|
217
|
+
prev = ai[:agent][:auto_introspect] ? true : false
|
|
218
|
+
ai[:agent][:auto_introspect] = (args[:enabled] ? true : false) if args.key?(:enabled)
|
|
219
|
+
{ previous: prev, current: ai[:agent][:auto_introspect] ? true : false }
|
|
220
220
|
}
|
|
221
221
|
)
|
|
@@ -20,7 +20,7 @@ module PWN
|
|
|
20
20
|
|
|
21
21
|
system_role_content = "Being an expert penetration tester skilled in code analysis, debugging, and exploitation while stepping through JavaScript in a Chrome DevTools debugging session: 1. Your sole purpose is to analyze each JavaScript step and generate an Exploit Prediction Scoring System (EPSS) score between 0% - 100%. The step currently resides in this block of JavaScript:\n```\n#{source_to_review}\n```\n2. If the score is >= 75%, generate a JavaScript proof-of-concept that would allow a threat actor to directly exploit or target a user for exploitation (i.e. no self-exploit). 3. If the EPSS score is >= 75% also provide a code fix. *** If the EPSS score is < 75%, no explanations or summaries - just the EPSS score."
|
|
22
22
|
|
|
23
|
-
PWN::AI::Agent::
|
|
23
|
+
PWN::AI::Agent::Reflect.on(
|
|
24
24
|
system_role_content: system_role_content,
|
|
25
25
|
request: request,
|
|
26
26
|
suppress_pii_warning: true
|
|
@@ -12,7 +12,7 @@ module PWN
|
|
|
12
12
|
# 4. CVSS score, vector string, and first.org calculator URI
|
|
13
13
|
# 5. CWE category, brief description, and CWE URI
|
|
14
14
|
# 6. Relevant NIST 800-53 control
|
|
15
|
-
# It leverages the PWN::AI::Agent::
|
|
15
|
+
# It leverages the PWN::AI::Agent::Reflect.on method. Defaults to Jira for existing workflow compatibility.
|
|
16
16
|
module VulnGen
|
|
17
17
|
# Supported Method Parameters::
|
|
18
18
|
# ai_analysis = PWN::AI::Agent::VulnGen.analyze(
|
|
@@ -60,7 +60,7 @@ module PWN
|
|
|
60
60
|
6. NIST 800-53 Security Control that is impacted by this vulnerability.
|
|
61
61
|
"
|
|
62
62
|
|
|
63
|
-
analysis = PWN::AI::Agent::
|
|
63
|
+
analysis = PWN::AI::Agent::Reflect.on(
|
|
64
64
|
system_role_content: system_role_content,
|
|
65
65
|
request: request,
|
|
66
66
|
suppress_pii_warning: true
|
data/lib/pwn/ai/agent.rb
CHANGED
|
@@ -27,7 +27,7 @@ module PWN
|
|
|
27
27
|
autoload :Learning, 'pwn/ai/agent/learning'
|
|
28
28
|
autoload :Mistakes, 'pwn/ai/agent/mistakes'
|
|
29
29
|
autoload :Extrospection, 'pwn/ai/agent/extrospection'
|
|
30
|
-
autoload :
|
|
30
|
+
autoload :Reflect, 'pwn/ai/agent/reflect'
|
|
31
31
|
autoload :Swarm, 'pwn/ai/agent/swarm'
|
|
32
32
|
|
|
33
33
|
# Display a List of Every PWN::AI Module
|
data/lib/pwn/ai/open_ai.rb
CHANGED
|
@@ -250,17 +250,21 @@ module PWN
|
|
|
250
250
|
end
|
|
251
251
|
messages.push(user_role)
|
|
252
252
|
|
|
253
|
-
# `max_tokens`
|
|
254
|
-
#
|
|
253
|
+
# Wire config key `max_tokens` (kept for cross-engine naming parity with
|
|
254
|
+
# Anthropic/Gemini) to the OpenAI wire-format field `max_completion_tokens`.
|
|
255
|
+
# OpenAI deprecated the request-body key `max_tokens` on /v1/chat/completions
|
|
256
|
+
# in favour of `max_completion_tokens`, which works for every chat model
|
|
255
257
|
# including the reasoning family. Don't try to guess per-model caps —
|
|
256
|
-
# let the server clamp; default to a generous ceiling that the
|
|
257
|
-
#
|
|
258
|
-
|
|
258
|
+
# let the server clamp; default to a generous ceiling that the operator
|
|
259
|
+
# can override via PWN::Env[:ai][:openai][:max_tokens].
|
|
260
|
+
# Accept legacy :max_completion_tokens as a one-release alias so existing
|
|
261
|
+
# pwn.yaml files keep working without a silent clamp to the default.
|
|
262
|
+
max_tokens = (engine[:max_tokens] || engine[:max_completion_tokens] || 16_384).to_i
|
|
259
263
|
|
|
260
264
|
http_body = {
|
|
261
265
|
model: model,
|
|
262
266
|
messages: messages,
|
|
263
|
-
max_completion_tokens:
|
|
267
|
+
max_completion_tokens: max_tokens
|
|
264
268
|
}
|
|
265
269
|
# Reasoning models reject sampler params (temperature, top_p, etc.)
|
|
266
270
|
http_body[:temperature] = temp unless reasoning
|
data/lib/pwn/config.rb
CHANGED
|
@@ -29,7 +29,7 @@ module PWN
|
|
|
29
29
|
env = {
|
|
30
30
|
ai: {
|
|
31
31
|
active: 'grok',
|
|
32
|
-
|
|
32
|
+
module_reflection: false,
|
|
33
33
|
grok: {
|
|
34
34
|
base_uri: 'optional - Base URI for Grok - Use private base OR defaults to https://api.x.ai/v1',
|
|
35
35
|
key: 'required - xAI Grok API Key',
|
|
@@ -58,6 +58,7 @@ module PWN
|
|
|
58
58
|
model: 'optional - OpenAI model to use',
|
|
59
59
|
system_role_content: 'You are an ethically hacking OpenAI agent.',
|
|
60
60
|
temp: 'optional - OpenAI temperature',
|
|
61
|
+
max_tokens: 'optional - Max output tokens per response (default 16384). Mapped to OpenAI wire param max_completion_tokens.',
|
|
61
62
|
max_prompt_length: 128_000
|
|
62
63
|
},
|
|
63
64
|
ollama: {
|
|
@@ -90,9 +91,9 @@ module PWN
|
|
|
90
91
|
max_iters: 25,
|
|
91
92
|
# Swarm (agent_ask/agent_debate) sub-agent recursion cap
|
|
92
93
|
max_depth: 3,
|
|
93
|
-
# run PWN::AI::Agent::Learning.
|
|
94
|
-
|
|
95
|
-
# also run PWN::AI::Agent::Extrospection.auto_extrospect from
|
|
94
|
+
# run PWN::AI::Agent::Learning.auto_introspect after every final answer
|
|
95
|
+
auto_introspect: true,
|
|
96
|
+
# also run PWN::AI::Agent::Extrospection.auto_extrospect from auto_introspect
|
|
96
97
|
auto_extrospect: false,
|
|
97
98
|
toolsets: nil
|
|
98
99
|
# multi-agent personas : ~/.pwn/agents.yml (see PWN::AI::Agent::Swarm.help)
|
|
@@ -50,7 +50,7 @@ module PWN
|
|
|
50
50
|
# type: 'required - type of history to introspect (:sitemap, :proxy_history, :websocket_history)'
|
|
51
51
|
# )
|
|
52
52
|
private_class_method def self.init_introspection_thread(opts = {})
|
|
53
|
-
# if PWN::Env[:ai][:
|
|
53
|
+
# if PWN::Env[:ai][:module_reflection] is true,
|
|
54
54
|
# spin up Thread to:
|
|
55
55
|
# 1. Periodically call get_proxy_history(burp_obj: burp_obj) method
|
|
56
56
|
# 2. For each entry w/ empty comment,
|
|
@@ -65,7 +65,7 @@ module PWN
|
|
|
65
65
|
type = opts[:type]
|
|
66
66
|
raise "ERROR: type parameter is required and must be one of: #{valid_types.join(', ')}" unless valid_types.include?(type)
|
|
67
67
|
|
|
68
|
-
if PWN::Env[:ai][:
|
|
68
|
+
if PWN::Env[:ai][:module_reflection]
|
|
69
69
|
introspection_thread_arr = burp_obj[:introspection_threads] ||= []
|
|
70
70
|
introspection_thread = Thread.new do
|
|
71
71
|
get_highlight_color = lambda do |opts = {}|
|
data/lib/pwn/sast/pom_version.rb
CHANGED
|
@@ -19,8 +19,8 @@ module PWN
|
|
|
19
19
|
dir_path = opts[:dir_path]
|
|
20
20
|
git_repo_root_uri = opts[:git_repo_root_uri].to_s.scrub
|
|
21
21
|
result_arr = []
|
|
22
|
-
|
|
23
|
-
logger_results = "AI
|
|
22
|
+
ai_module_reflection = PWN::Env[:ai][:module_reflection]
|
|
23
|
+
logger_results = "AI Module Reflection => #{PWN::Env[:ai][:module_reflection]} => "
|
|
24
24
|
|
|
25
25
|
PWN::Plugins::FileFu.recurse_in_dir(dir_path: dir_path) do |entry|
|
|
26
26
|
if File.file?(entry) && File.basename(entry) == 'pom.xml' && entry !~ /test/i
|
|
@@ -63,7 +63,7 @@ module PWN
|
|
|
63
63
|
git_repo_root_uri = opts[:git_repo_root_uri].to_s.scrub
|
|
64
64
|
|
|
65
65
|
result_arr = []
|
|
66
|
-
logger_results = "AI
|
|
66
|
+
logger_results = "AI Module Reflection => #{PWN::Env[:ai][:module_reflection]} => "
|
|
67
67
|
|
|
68
68
|
PWN::Plugins::FileFu.recurse_in_dir(
|
|
69
69
|
dir_path: dir_path,
|
data/lib/pwn/sdr/decoder/adsb.rb
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'shellwords'
|
|
4
|
-
|
|
5
3
|
module PWN
|
|
6
4
|
module SDR
|
|
7
5
|
module Decoder
|
|
8
|
-
# ADS-B (
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
6
|
+
# Pure-Ruby ADS-B (1090 MHz Mode-S / 978 MHz UAT) activity detector.
|
|
7
|
+
#
|
|
8
|
+
# Mode-S Extended Squitter is 2 Mbit/s PPM with 8/120 μs frames on a
|
|
9
|
+
# 2 MHz-wide channel — physically unrecoverable from GQRX's 48 kHz
|
|
10
|
+
# demodulated-audio tap. Rather than shell out to `dump1090`, this
|
|
11
|
+
# module runs Base.run_detector to characterise squitter density
|
|
12
|
+
# (bursts/sec, peak dBFS, floor) natively in Ruby. `parse_line` is
|
|
13
|
+
# retained for offline SBS-1 CSV analysis.
|
|
13
14
|
module ADSB
|
|
14
15
|
SBS_FIELDS = %i[
|
|
15
16
|
msg_type tx_type session_id aircraft_id icao24 flight_id
|
|
@@ -25,28 +26,13 @@ module PWN
|
|
|
25
26
|
|
|
26
27
|
public_class_method def self.decode(opts = {})
|
|
27
28
|
freq_obj = opts[:freq_obj]
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
gain = (freq_obj[:rf_gain] || 40).to_s.to_f
|
|
32
|
-
uat = hz.between?(977_000_000, 979_000_000)
|
|
33
|
-
|
|
34
|
-
direct_cmd =
|
|
35
|
-
if uat && PWN::SDR::Decoder::Base.bin_available?(bin: 'dump978-fa')
|
|
36
|
-
"bash -c #{Shellwords.escape("dump978-fa --sdr driver=rtlsdr --sdr-gain #{gain} --raw-stdout 2>/dev/null | uat2text")}"
|
|
37
|
-
elsif PWN::SDR::Decoder::Base.bin_available?(bin: 'dump1090-fa')
|
|
38
|
-
"dump1090-fa --device-type rtlsdr --gain #{gain} --net-sbs-stdout --quiet"
|
|
39
|
-
else
|
|
40
|
-
"dump1090 --gain #{gain} --net-sbs-stdout --quiet"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
PWN::SDR::Decoder::Base.run_pipeline(
|
|
29
|
+
hz = PWN::SDR.hz_to_i(freq: freq_obj[:freq])
|
|
30
|
+
uat = hz.between?(977_000_000, 979_000_000)
|
|
31
|
+
PWN::SDR::Decoder::Base.run_detector(
|
|
44
32
|
freq_obj: freq_obj,
|
|
45
33
|
protocol: uat ? 'ADSB-UAT978' : 'ADSB-1090ES',
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
line_match: /^(MSG,|-|\+)/,
|
|
49
|
-
parser: proc { |line| parse_line(line: line) }
|
|
34
|
+
note: '2 Mbit/s PPM squitters exceed the 48 kHz audio-tap Nyquist limit; native mode reports squitter-burst density only. Feed captured SBS-1 CSV to .parse_line for full field decode.',
|
|
35
|
+
describe: proc { |b| { modulation: 'PPM', frame_len_us: 120, classification: b[:duration_ms] < 5 ? 'squitter' : 'interrogation-train' } }
|
|
50
36
|
)
|
|
51
37
|
end
|
|
52
38
|
|
|
@@ -60,7 +46,6 @@ module PWN
|
|
|
60
46
|
f = line.split(',', -1)
|
|
61
47
|
out = { protocol: 'ADSB' }
|
|
62
48
|
SBS_FIELDS.each_with_index { |k, i| out[k] = f[i] unless f[i].to_s.empty? }
|
|
63
|
-
|
|
64
49
|
bits = []
|
|
65
50
|
bits << "ICAO=#{out[:icao24]}" if out[:icao24]
|
|
66
51
|
bits << "CS=#{out[:callsign].to_s.strip}" if out[:callsign]
|
|
@@ -81,15 +66,11 @@ module PWN
|
|
|
81
66
|
# Display Usage for this Module
|
|
82
67
|
|
|
83
68
|
public_class_method def self.help
|
|
84
|
-
puts "USAGE:
|
|
69
|
+
puts "USAGE (ruby-native detector, no external binaries):
|
|
85
70
|
#{self}.decode(
|
|
86
71
|
freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
|
|
87
72
|
)
|
|
88
73
|
|
|
89
|
-
NOTE: Requires `dump1090` (1090 MHz Mode-S/ES) or
|
|
90
|
-
`dump978-fa` + `uat2text` (978 MHz UAT). Owns the SDR
|
|
91
|
-
directly — set freq_obj[:sdr_args] if GQRX has device 0.
|
|
92
|
-
|
|
93
74
|
#{self}.parse_line(line: 'MSG,3,1,1,ABCDEF,1,...')
|
|
94
75
|
|
|
95
76
|
#{self}.authors
|
data/lib/pwn/sdr/decoder/apt.rb
CHANGED
|
@@ -1,16 +1,100 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'shellwords'
|
|
4
|
-
|
|
5
3
|
module PWN
|
|
6
4
|
module SDR
|
|
7
5
|
module Decoder
|
|
8
|
-
# NOAA APT (Automatic Picture Transmission) decoder for the
|
|
9
|
-
# polar-orbiting weather satellites (NOAA-15/18/19).
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
6
|
+
# Pure-Ruby NOAA APT (Automatic Picture Transmission) decoder for the
|
|
7
|
+
# 137 MHz polar-orbiting weather satellites (NOAA-15/18/19).
|
|
8
|
+
#
|
|
9
|
+
# APT is a 2400 Hz AM subcarrier inside a ~34 kHz-wide FM downlink
|
|
10
|
+
# carrying two 909-pixel image channels at 2 lines/second (4160
|
|
11
|
+
# words/line). This module envelope-demodulates the 2400 Hz carrier
|
|
12
|
+
# from GQRX's 48 kHz UDP audio, resamples to 4160 words/sec, aligns
|
|
13
|
+
# each line on the 7-pulse Sync-A pattern, and appends the resulting
|
|
14
|
+
# 8-bit greyscale rows to a Netpbm PGM (P5) file — all in Ruby.
|
|
15
|
+
# No `sox`, no `noaa-apt`.
|
|
13
16
|
module APT
|
|
17
|
+
WORDS_PER_LINE = 4160
|
|
18
|
+
LINES_PER_SEC = 2
|
|
19
|
+
WORD_RATE = WORDS_PER_LINE * LINES_PER_SEC # 8320 Hz
|
|
20
|
+
# Sync-A: 7 cycles of 1040 Hz square = 1 1 0 0 repeated 7 times
|
|
21
|
+
SYNC_A = ([1, 1, 0, 0] * 7).freeze
|
|
22
|
+
|
|
23
|
+
# Streaming APT demodulator fed by Base.run_native.
|
|
24
|
+
class Demod
|
|
25
|
+
def initialize(rate: 48_000, out_path: nil)
|
|
26
|
+
@rate = rate
|
|
27
|
+
@carrier = 2400.0
|
|
28
|
+
@env_win = (rate / @carrier).round # ≈20 samples per cycle
|
|
29
|
+
@stamp = Time.now.strftime('%Y%m%d_%H%M%S')
|
|
30
|
+
@pgm_path = out_path || "/tmp/apt_#{@stamp}.pgm"
|
|
31
|
+
@rows = []
|
|
32
|
+
@word_buf = []
|
|
33
|
+
@minv = 1.0
|
|
34
|
+
@maxv = 0.0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def feed(samples, &)
|
|
38
|
+
env = PWN::SDR::Decoder::DSP.envelope(samples: samples, window: @env_win)
|
|
39
|
+
words = PWN::SDR::Decoder::DSP.resample(samples: env, src_rate: @rate, dst_rate: WORD_RATE)
|
|
40
|
+
@word_buf.concat(words)
|
|
41
|
+
extract_lines(&)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def extract_lines
|
|
47
|
+
while @word_buf.length >= WORDS_PER_LINE * 2
|
|
48
|
+
off = sync_offset(@word_buf[0, WORDS_PER_LINE])
|
|
49
|
+
@word_buf.shift(off) if off.positive?
|
|
50
|
+
break if @word_buf.length < WORDS_PER_LINE
|
|
51
|
+
|
|
52
|
+
row = @word_buf.shift(WORDS_PER_LINE)
|
|
53
|
+
lo, hi = row.minmax
|
|
54
|
+
@minv = lo if lo < @minv
|
|
55
|
+
@maxv = hi if hi > @maxv
|
|
56
|
+
@rows << row
|
|
57
|
+
write_pgm if (@rows.length % 20).zero?
|
|
58
|
+
yield(
|
|
59
|
+
protocol: 'NOAA-APT',
|
|
60
|
+
lines: @rows.length,
|
|
61
|
+
seconds: @rows.length / LINES_PER_SEC,
|
|
62
|
+
pgm: @pgm_path,
|
|
63
|
+
summary: "APT line #{@rows.length} (#{@rows.length / LINES_PER_SEC}s) → #{@pgm_path}"
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def sync_offset(row)
|
|
69
|
+
mid = row.sum / row.length
|
|
70
|
+
best_off = 0
|
|
71
|
+
best_cor = -1.0
|
|
72
|
+
(0..(row.length - SYNC_A.length)).each do |o|
|
|
73
|
+
cor = 0.0
|
|
74
|
+
SYNC_A.each_with_index do |s, i|
|
|
75
|
+
cor += (row[o + i] - mid) * (s.zero? ? -1.0 : 1.0)
|
|
76
|
+
end
|
|
77
|
+
if cor > best_cor
|
|
78
|
+
best_cor = cor
|
|
79
|
+
best_off = o
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
best_off
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def write_pgm
|
|
86
|
+
span = @maxv - @minv
|
|
87
|
+
span = 1.0 if span <= 0
|
|
88
|
+
File.open(@pgm_path, 'wb') do |f|
|
|
89
|
+
f.write("P5\n#{WORDS_PER_LINE} #{@rows.length}\n255\n")
|
|
90
|
+
@rows.each do |row|
|
|
91
|
+
bytes = row.map { |v| (((v - @minv) / span) * 255).clamp(0, 255).round }
|
|
92
|
+
f.write(bytes.pack('C*'))
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
14
98
|
# Supported Method Parameters::
|
|
15
99
|
# PWN::SDR::Decoder::APT.decode(
|
|
16
100
|
# freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
|
|
@@ -18,35 +102,11 @@ module PWN
|
|
|
18
102
|
|
|
19
103
|
public_class_method def self.decode(opts = {})
|
|
20
104
|
freq_obj = opts[:freq_obj]
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
stamp = Time.now.strftime('%Y%m%d_%H%M%S')
|
|
24
|
-
wav_out = "/tmp/apt_#{stamp}.wav"
|
|
25
|
-
png_out = "/tmp/apt_#{stamp}.png"
|
|
26
|
-
|
|
27
|
-
# Record 11 025 Hz WAV (noaa-apt's native rate) until [ENTER],
|
|
28
|
-
# emitting a heartbeat line per second so the spinner stays live,
|
|
29
|
-
# then decode to PNG on exit.
|
|
30
|
-
inner = 'sox -t raw -e signed-integer -b 16 -r 22050 -c 1 - ' \
|
|
31
|
-
"-t wav -r 11025 #{Shellwords.escape(wav_out)} & SOXPID=$!; " \
|
|
32
|
-
'trap "kill $SOXPID 2>/dev/null" EXIT INT TERM; ' \
|
|
33
|
-
'while kill -0 $SOXPID 2>/dev/null; do ' \
|
|
34
|
-
"echo APT_REC seconds=$SECONDS file=#{wav_out}; sleep 1; done"
|
|
35
|
-
|
|
36
|
-
PWN::SDR::Decoder::Base.run_pipeline(
|
|
105
|
+
PWN::SDR::Decoder::Base.run_native(
|
|
37
106
|
freq_obj: freq_obj,
|
|
38
107
|
protocol: 'NOAA-APT',
|
|
39
|
-
|
|
40
|
-
decode_cmd: "bash -c #{Shellwords.escape(inner)}",
|
|
41
|
-
line_match: /^APT_REC/,
|
|
42
|
-
parser: proc { |line| { protocol: 'NOAA-APT', wav: wav_out, png: png_out, summary: line.strip } }
|
|
108
|
+
demod: Demod.new
|
|
43
109
|
)
|
|
44
|
-
|
|
45
|
-
return unless File.exist?(wav_out) && File.size(wav_out).positive?
|
|
46
|
-
|
|
47
|
-
system('noaa-apt', wav_out, '-o', png_out)
|
|
48
|
-
|
|
49
|
-
puts "[*] APT image written: #{png_out}" if File.exist?(png_out)
|
|
50
110
|
end
|
|
51
111
|
|
|
52
112
|
# Author(s):: 0day Inc. <support@0dayinc.com>
|
|
@@ -58,13 +118,14 @@ module PWN
|
|
|
58
118
|
# Display Usage for this Module
|
|
59
119
|
|
|
60
120
|
public_class_method def self.help
|
|
61
|
-
puts "USAGE:
|
|
121
|
+
puts "USAGE (ruby-native, no external binaries):
|
|
62
122
|
#{self}.decode(
|
|
63
123
|
freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
|
|
64
124
|
)
|
|
65
125
|
|
|
66
|
-
NOTE:
|
|
67
|
-
|
|
126
|
+
NOTE: Set GQRX to WFM (mono), ~34 kHz filter. Writes an 8-bit
|
|
127
|
+
greyscale Netpbm P5 image to /tmp/apt_<ts>.pgm every 10 s
|
|
128
|
+
of received pass. Both A/B channels are in one strip.
|
|
68
129
|
|
|
69
130
|
#{self}.authors
|
|
70
131
|
"
|