pwn 0.5.783 → 0.5.785
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/.ruby-version +1 -1
- data/Gemfile +8 -8
- data/README.md +37 -0
- data/Rakefile +19 -1
- data/bin/pwn +2 -1
- data/bin/pwn_setup +1 -1
- data/bin/pwn_web_cache_deception +35 -7
- data/build_gem.sh +1 -1
- data/documentation/AI-Integration.md +7 -5
- data/documentation/Agent-Tool-Registry.md +2 -2
- data/documentation/Backup-Retention.md +82 -0
- data/documentation/Configuration.md +10 -6
- data/documentation/Contributing.md +1 -1
- data/documentation/Cron.md +64 -35
- data/documentation/Diagrams.md +2 -2
- data/documentation/General-PWN-Usage.md +4 -3
- data/documentation/Home.md +6 -5
- data/documentation/How-PWN-Works.md +7 -6
- data/documentation/Installation.md +254 -10
- data/documentation/Mission-Cancellation.md +134 -0
- data/documentation/Operability.md +17 -0
- data/documentation/Persistence.md +8 -6
- data/documentation/Plugin-Degradation.md +12 -0
- data/documentation/Plugins.md +3 -2
- data/documentation/Reinforcement-Learning.md +5 -6
- data/documentation/Runtime-Source-Protection.md +162 -0
- data/documentation/Transparent-Browser.md +11 -0
- data/documentation/Troubleshooting.md +3 -2
- data/documentation/What-is-PWN.md +2 -2
- data/documentation/YubiKey-Vaults.md +183 -0
- data/documentation/YubiKey.md +339 -0
- data/documentation/diagrams/cron-scheduling.svg +130 -128
- data/documentation/diagrams/dot/cron-scheduling.dot +5 -5
- data/documentation/diagrams/dot/persistence-filesystem.dot +5 -3
- data/documentation/diagrams/dot/plugin-ecosystem.dot +2 -2
- data/documentation/diagrams/persistence-filesystem.svg +172 -142
- data/documentation/diagrams/plugin-ecosystem.svg +64 -64
- data/documentation/pwn-ai-Agent.md +10 -4
- data/etc/default_cron/session_cleanup.rb +1245 -22
- data/etc/default_skills/pwn/ai/agent/mission_namespace/SKILL.md +21 -0
- data/etc/default_skills/pwn/ai/agent/tools/browser_act/SKILL.md +45 -0
- data/etc/default_skills/pwn/ai/agent/tools/loot_query/SKILL.md +27 -26
- data/etc/default_skills/pwn/ai/agent/tools/recon_run/SKILL.md +25 -15
- data/etc/default_skills/pwn/ai/agent/tools/workspace_tool/SKILL.md +24 -0
- data/etc/default_skills/pwn/ai/server_tools/SKILL.md +25 -0
- data/etc/default_skills/pwn/backups/SKILL.md +54 -0
- data/etc/default_skills/pwn/plugins/recon/SKILL.md +12 -1
- data/etc/default_skills/pwn/plugins/yubikey/SKILL.md +157 -0
- data/etc/default_skills/pwn/plugins/yubikey/fido/SKILL.md +189 -0
- data/etc/default_skills/pwn/plugins/yubikey/hsmauth/SKILL.md +83 -0
- data/etc/default_skills/pwn/plugins/yubikey/management/SKILL.md +80 -0
- data/etc/default_skills/pwn/plugins/yubikey/oath/SKILL.md +115 -0
- data/etc/default_skills/pwn/plugins/yubikey/openpgp/SKILL.md +144 -0
- data/etc/default_skills/pwn/plugins/yubikey/otp/SKILL.md +97 -0
- data/etc/default_skills/pwn/plugins/yubikey/piv/SKILL.md +110 -0
- data/etc/default_skills/pwn/plugins/yubikey/protocol/SKILL.md +21 -0
- data/etc/default_skills/pwn/plugins/yubikey/security_domain/SKILL.md +75 -0
- data/etc/default_skills/pwn/plugins/yubikey/security_domain/scp/SKILL.md +46 -0
- data/etc/default_skills/pwn/plugins/yubikey/transport/SKILL.md +98 -0
- data/etc/default_skills/pwn/redaction/SKILL.md +13 -1
- data/lib/pwn/ai/agent/dispatch.rb +26 -15
- data/lib/pwn/ai/agent/extrospection.rb +6 -7
- data/lib/pwn/ai/agent/learning.rb +62 -25
- data/lib/pwn/ai/agent/loop.rb +17 -1
- data/lib/pwn/ai/agent/mission_namespace.rb +331 -0
- data/lib/pwn/ai/agent/policy.rb +5 -0
- data/lib/pwn/ai/agent/policy_evaluation.rb +8 -8
- data/lib/pwn/ai/agent/prompt_builder.rb +13 -2
- data/lib/pwn/ai/agent/request_runtime.rb +122 -18
- data/lib/pwn/ai/agent/reward.rb +5 -3
- data/lib/pwn/ai/agent/skill_consolidation.rb +3 -2
- data/lib/pwn/ai/agent/skill_review.rb +9 -5
- data/lib/pwn/ai/agent/swarm.rb +35 -16
- data/lib/pwn/ai/agent/tools/browser_act.rb +32 -0
- data/lib/pwn/ai/agent/tools/loot_query.rb +11 -1
- data/lib/pwn/ai/agent/tools/recon_run.rb +11 -1
- data/lib/pwn/ai/agent/tools/workspace_tool.rb +29 -0
- data/lib/pwn/ai/agent/turn_finalizer.rb +6 -2
- data/lib/pwn/ai/agent.rb +1 -0
- data/lib/pwn/ai/anthropic.rb +8 -33
- data/lib/pwn/ai/cli.rb +34 -5
- data/lib/pwn/ai/gemini.rb +1 -0
- data/lib/pwn/ai/grok.rb +39 -28
- data/lib/pwn/ai/ollama.rb +4 -2
- data/lib/pwn/ai/open_ai.rb +11 -32
- data/lib/pwn/ai/open_web_ui.rb +62 -4
- data/lib/pwn/ai/router.rb +53 -3
- data/lib/pwn/ai/server_tools.rb +231 -0
- data/lib/pwn/ai.rb +1 -0
- data/lib/pwn/backups.rb +347 -0
- data/lib/pwn/config.rb +70 -39
- data/lib/pwn/cron.rb +120 -49
- data/lib/pwn/migrate.rb +150 -68
- data/lib/pwn/module_skills.rb +7 -0
- data/lib/pwn/plugins/ai_sandbox.rb +538 -1
- data/lib/pwn/plugins/authentication_helper.rb +74 -4
- data/lib/pwn/plugins/basic_auth.rb +2 -1
- data/lib/pwn/plugins/binary_parser.rb +5 -4
- data/lib/pwn/plugins/ghidra.rb +4 -3
- data/lib/pwn/plugins/google_workspace.rb +4 -21
- data/lib/pwn/plugins/jobs.rb +1 -1
- data/lib/pwn/plugins/log.rb +21 -3
- data/lib/pwn/plugins/preflight_checker.rb +34 -9
- data/lib/pwn/plugins/recon.rb +4 -1
- data/lib/pwn/plugins/repl/ai.rb +305 -27
- data/lib/pwn/plugins/repl/ai_console.rb +273 -89
- data/lib/pwn/plugins/repl/ai_swarm.rb +181 -62
- data/lib/pwn/plugins/repl/mesh.rb +3 -16
- data/lib/pwn/plugins/repl/vault.rb +4 -15
- data/lib/pwn/plugins/repl.rb +1 -216
- data/lib/pwn/plugins/sandbox/driver.rb +4 -0
- data/lib/pwn/plugins/transparent_browser.rb +215 -0
- data/lib/pwn/plugins/vault.rb +412 -135
- data/lib/pwn/plugins/yubikey/fido.rb +1288 -0
- data/lib/pwn/plugins/yubikey/hsmauth.rb +439 -0
- data/lib/pwn/plugins/yubikey/management.rb +686 -0
- data/lib/pwn/plugins/yubikey/oath.rb +766 -0
- data/lib/pwn/plugins/yubikey/openpgp.rb +1063 -0
- data/lib/pwn/plugins/yubikey/otp.rb +897 -0
- data/lib/pwn/plugins/yubikey/piv.rb +1315 -0
- data/lib/pwn/plugins/yubikey/protocol.rb +341 -0
- data/lib/pwn/plugins/yubikey/security_domain/scp.rb +482 -0
- data/lib/pwn/plugins/yubikey/security_domain.rb +464 -0
- data/lib/pwn/plugins/yubikey/transport.rb +1017 -0
- data/lib/pwn/plugins/yubikey.rb +844 -0
- data/lib/pwn/plugins.rb +1 -0
- data/lib/pwn/redaction.rb +69 -12
- data/lib/pwn/session_trace.rb +4 -0
- data/lib/pwn/sessions.rb +58 -16
- data/lib/pwn/setup.rb +123 -41
- data/lib/pwn/version.rb +1 -1
- data/lib/pwn/www/app_cobalt_io.rb +16 -5
- data/lib/pwn/www/bug_crowd.rb +16 -5
- data/lib/pwn/www/coinbase_pro.rb +16 -5
- data/lib/pwn/www/github.rb +30 -14
- data/lib/pwn/www/paypal.rb +15 -4
- data/lib/pwn/www/synack.rb +16 -5
- data/lib/pwn/www/twitter.rb +17 -6
- data/lib/pwn.rb +1 -0
- data/scripts/build_gate.rb +58 -0
- data/spec/documentation/installation_md_spec.rb +1 -1
- data/spec/fixtures/yubikey/apdu_frames.json +162 -0
- data/spec/integration/authentication_helper_oath_spec.rb +117 -0
- data/spec/integration/build_gate_spec.rb +73 -0
- data/spec/integration/controller_spill_spec.rb +48 -0
- data/spec/integration/harness_process_spec.rb +97 -0
- data/spec/integration/mission_cancel_spec.rb +79 -0
- data/spec/integration/mission_curses_pty_spec.rb +44 -0
- data/spec/integration/persistence_roundtrip_spec.rb +1 -7
- data/spec/integration/readme_security_spec.rb +47 -0
- data/spec/integration/web_cache_deception_yubikey_spec.rb +159 -0
- data/spec/lib/pwn/ai/agent/dispatch_spec.rb +6 -3
- data/spec/lib/pwn/ai/agent/dispatch_vault_safety_spec.rb +86 -0
- data/spec/lib/pwn/ai/agent/injection_guard_spec.rb +2 -0
- data/spec/lib/pwn/ai/agent/learning_spec.rb +4 -4
- data/spec/lib/pwn/ai/agent/loop_artifacts_spec.rb +5 -0
- data/spec/lib/pwn/ai/agent/loop_spec.rb +104 -29
- data/spec/lib/pwn/ai/agent/mission_namespace_spec.rb +138 -0
- data/spec/lib/pwn/ai/agent/policy_spec.rb +27 -0
- data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +48 -0
- data/spec/lib/pwn/ai/agent/shell_search_effect_spec.rb +44 -0
- data/spec/lib/pwn/ai/agent/solve_spec.rb +6 -4
- data/spec/lib/pwn/ai/agent/tools/browser_act_spec.rb +44 -0
- data/spec/lib/pwn/ai/agent/tools/loot_query_yubikey_spec.rb +55 -0
- data/spec/lib/pwn/ai/agent/tools/recon_run_yubikey_spec.rb +43 -0
- data/spec/lib/pwn/ai/agent/tools/workspace_tool_spec.rb +20 -0
- data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +7 -0
- data/spec/lib/pwn/ai/anthropic_vault_enrollment_spec.rb +37 -0
- data/spec/lib/pwn/ai/cli_spec.rb +6 -9
- data/spec/lib/pwn/ai/oauth_vault_persistence_spec.rb +195 -0
- data/spec/lib/pwn/ai/open_ai_spec.rb +2 -2
- data/spec/lib/pwn/ai/open_web_ui_workspace_spec.rb +36 -0
- data/spec/lib/pwn/ai/router_spec.rb +31 -3
- data/spec/lib/pwn/ai/server_tools_spec.rb +39 -0
- data/spec/lib/pwn/backups_spec.rb +114 -0
- data/spec/lib/pwn/config_redaction_yubikey_spec.rb +28 -0
- data/spec/lib/pwn/config_yubikey_spec.rb +92 -0
- data/spec/lib/pwn/cron_spec.rb +113 -6
- data/spec/lib/pwn/migrate_spec.rb +108 -5
- data/spec/lib/pwn/migrate_yubikey_spec.rb +128 -0
- data/spec/lib/pwn/module_skills_spec.rb +8 -0
- data/spec/lib/pwn/plugins/ai_sandbox_spec.rb +230 -0
- data/spec/lib/pwn/plugins/authentication_helper_native_yubikey_spec.rb +40 -0
- data/spec/lib/pwn/plugins/authentication_helper_yubikey_spec.rb +90 -0
- data/spec/lib/pwn/plugins/capability_broker/daemon_spec.rb +1 -1
- data/spec/lib/pwn/plugins/google_workspace_spec.rb +17 -35
- data/spec/lib/pwn/plugins/log_spec.rb +7 -0
- data/spec/lib/pwn/plugins/log_yubikey_spec.rb +47 -0
- data/spec/lib/pwn/plugins/preflight_checker_native_yubikey_spec.rb +19 -0
- data/spec/lib/pwn/plugins/preflight_checker_yubikey_spec.rb +55 -0
- data/spec/lib/pwn/plugins/recon_yubikey_spec.rb +43 -0
- data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +125 -78
- data/spec/lib/pwn/plugins/repl/ai_legacy_containment_spec.rb +200 -0
- data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +30 -16
- data/spec/lib/pwn/plugins/repl/ai_vault_edit_yubikey_spec.rb +103 -0
- data/spec/lib/pwn/plugins/repl/ai_yubikey_spec.rb +48 -0
- data/spec/lib/pwn/plugins/repl/mesh_vault_persistence_spec.rb +149 -0
- data/spec/lib/pwn/plugins/repl/vault_yaml_serialization_spec.rb +11 -0
- data/spec/lib/pwn/plugins/repl/vault_yubikey_spec.rb +39 -0
- data/spec/lib/pwn/plugins/repl_spec.rb +56 -5
- data/spec/lib/pwn/plugins/transparent_browser_jev_spec.rb +46 -0
- data/spec/lib/pwn/plugins/vault_native_yubikey_spec.rb +29 -0
- data/spec/lib/pwn/plugins/vault_publication_safety_spec.rb +258 -0
- data/spec/lib/pwn/plugins/vault_store_safety_spec.rb +241 -0
- data/spec/lib/pwn/plugins/vault_yubikey_safety_spec.rb +168 -0
- data/spec/lib/pwn/plugins/vault_yubikey_spec.rb +128 -0
- data/spec/lib/pwn/plugins/vault_yubikey_vector_spec.rb +45 -0
- data/spec/lib/pwn/plugins/yubikey/fido_spec.rb +854 -0
- data/spec/lib/pwn/plugins/yubikey/hsmauth_spec.rb +235 -0
- data/spec/lib/pwn/plugins/yubikey/management_spec.rb +277 -0
- data/spec/lib/pwn/plugins/yubikey/oath_spec.rb +349 -0
- data/spec/lib/pwn/plugins/yubikey/openpgp_spec.rb +810 -0
- data/spec/lib/pwn/plugins/yubikey/otp_spec.rb +337 -0
- data/spec/lib/pwn/plugins/yubikey/piv_spec.rb +646 -0
- data/spec/lib/pwn/plugins/yubikey/protocol_secure_spec.rb +28 -0
- data/spec/lib/pwn/plugins/yubikey/protocol_spec.rb +149 -0
- data/spec/lib/pwn/plugins/yubikey/protocol_vectors_spec.rb +18 -0
- data/spec/lib/pwn/plugins/yubikey/security_domain/scp_spec.rb +256 -0
- data/spec/lib/pwn/plugins/yubikey/security_domain/support.rb +153 -0
- data/spec/lib/pwn/plugins/yubikey/security_domain_spec.rb +296 -0
- data/spec/lib/pwn/plugins/yubikey/transport_spec.rb +760 -0
- data/spec/lib/pwn/plugins/yubikey_security_spec.rb +260 -0
- data/spec/lib/pwn/plugins/yubikey_spec.rb +209 -0
- data/spec/lib/pwn/plugins/yubikey_terminal_spec.rb +147 -0
- data/spec/lib/pwn/redaction_spec.rb +13 -10
- data/spec/lib/pwn/redaction_yubikey_spec.rb +61 -0
- data/spec/lib/pwn/session_cleanup_concurrency_spec.rb +201 -0
- data/spec/lib/pwn/session_cleanup_default_spec.rb +167 -2
- data/spec/lib/pwn/session_cleanup_legacy_spec.rb +101 -0
- data/spec/lib/pwn/session_cleanup_payload_spec.rb +335 -2
- data/spec/lib/pwn/session_cleanup_recovery_spec.rb +403 -0
- data/spec/lib/pwn/sessions_spec.rb +112 -0
- data/spec/lib/pwn/setup_native_yubikey_spec.rb +66 -0
- data/spec/lib/pwn/setup_spec.rb +3 -1
- data/spec/lib/pwn/setup_vault_yubikey_spec.rb +61 -0
- data/spec/lib/pwn/setup_yubikey_spec.rb +40 -0
- data/spec/lib/pwn/www/login_yubikey_spec.rb +273 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/gate_failure_formatter.rb +14 -0
- data/spec/support/harness/cli_faults.rb +13 -9
- data/spec/support/harness/controller_spill.rb +26 -0
- data/spec/support/harness/controller_state.rb +54 -0
- data/spec/support/harness/evaluate.rb +12 -1
- data/spec/support/harness/experiments.rb +1 -1
- data/spec/support/harness/fake_provider.rb +22 -6
- data/spec/support/harness/fault_preload.rb +3 -1
- data/spec/support/harness/mission_console.rb +54 -0
- data/spec/support/harness/mission_curses_pty.rb +41 -0
- data/spec/support/harness/mission_dispatch.rb +30 -0
- data/spec/support/harness/mission_fixture_preload.rb +24 -0
- data/spec/support/harness/mission_job_lifetime.rb +25 -0
- data/spec/support/harness/mission_legacy.rb +25 -0
- data/spec/support/harness/mission_normal_exit.rb +34 -0
- data/spec/support/harness/mission_swarm.rb +23 -0
- data/spec/support/harness/mission_unsupported.rb +29 -0
- data/spec/support/harness/package_acceptance.rb +33 -5
- data/spec/support/harness/session_acceptance.rb +4 -4
- data/spec/support/harness/swarm_acceptance.rb +1 -1
- data/spec/support/harness/swarm_control.rb +44 -0
- data/spec/support/mission_namespace.rb +14 -0
- data/spec/support/mission_pipe_queue.rb +33 -0
- data/third_party/pwn_rdoc.jsonl +599 -15
- metadata +140 -17
data/lib/pwn/version.rb
CHANGED
|
@@ -5,6 +5,8 @@ require 'yaml'
|
|
|
5
5
|
module PWN
|
|
6
6
|
module WWW
|
|
7
7
|
# This plugin supports app.cobalt.io actions.
|
|
8
|
+
# YubiKey OATH MFA requires explicit confirm: true because HOTP advances counters
|
|
9
|
+
# and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
8
10
|
module AppCobaltIO
|
|
9
11
|
# Supported Method Parameters::
|
|
10
12
|
# browser_obj = PWN::WWW::AppCobaltIO.open(
|
|
@@ -28,10 +30,13 @@ module PWN
|
|
|
28
30
|
# browser_obj: 'required - browser_obj returned from #open method',
|
|
29
31
|
# username: 'required - username',
|
|
30
32
|
# password: 'optional - passwd (will prompt if blank)',
|
|
31
|
-
# mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
33
|
+
# mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
34
|
+
# yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
32
35
|
# )
|
|
33
36
|
|
|
34
37
|
public_class_method def self.login(opts = {})
|
|
38
|
+
PWN::Plugins::AuthenticationHelper.validate_mfa_options(opts)
|
|
39
|
+
yubikey = opts[:yubikey]
|
|
35
40
|
browser_obj = opts[:browser_obj]
|
|
36
41
|
username = opts[:username].to_s.scrub.strip.chomp
|
|
37
42
|
password = opts[:password]
|
|
@@ -54,9 +59,9 @@ module PWN
|
|
|
54
59
|
# name: 'commit' doesn't work
|
|
55
60
|
browser.button(index: 6).click! # no name or id in button element
|
|
56
61
|
|
|
57
|
-
if mfa
|
|
62
|
+
if mfa || !yubikey.nil?
|
|
58
63
|
until browser.url == 'https://app.cobalt.io/dashboard'
|
|
59
|
-
browser.text_field(id: 'code').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token'))
|
|
64
|
+
browser.text_field(id: 'code').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token', yubikey: yubikey))
|
|
60
65
|
browser.button(name: 'commit').click!
|
|
61
66
|
sleep 3
|
|
62
67
|
end
|
|
@@ -117,12 +122,18 @@ module PWN
|
|
|
117
122
|
proxy: 'optional - scheme://proxy_host:port || tor'
|
|
118
123
|
)
|
|
119
124
|
|
|
120
|
-
#
|
|
125
|
+
# Log in with manual MFA or explicitly selected YubiKey OATH.
|
|
126
|
+
# YubiKey enables MFA even when mfa is false; failures never fall back to a prompt.
|
|
127
|
+
# Configure the service for an authenticator challenge, not SMS.
|
|
128
|
+
# Example yubikey: { account: 'EXACT_OATH_NAME', confirm: true, serial: '123456', timeout: 30 }
|
|
129
|
+
# Explicit consent is required: HOTP advances counters and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
130
|
+
# Reject conflicting explicit tokens before any login action.
|
|
121
131
|
#{self}.login(
|
|
122
132
|
browser_obj: 'required - browser_obj returned from #open method',
|
|
123
133
|
username: 'required - username value consumed by #login',
|
|
124
134
|
password: 'optional - passwd (will prompt if blank)',
|
|
125
|
-
mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
135
|
+
mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
136
|
+
yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
126
137
|
)
|
|
127
138
|
|
|
128
139
|
# Run logout and return its result
|
data/lib/pwn/www/bug_crowd.rb
CHANGED
|
@@ -5,6 +5,8 @@ require 'yaml'
|
|
|
5
5
|
module PWN
|
|
6
6
|
module WWW
|
|
7
7
|
# This plugin supports bugcrowd.com actions.
|
|
8
|
+
# YubiKey OATH MFA requires explicit confirm: true because HOTP advances counters
|
|
9
|
+
# and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
8
10
|
module BugCrowd
|
|
9
11
|
# Supported Method Parameters::
|
|
10
12
|
# browser_obj = PWN::WWW::BugCrowd.open(
|
|
@@ -28,10 +30,13 @@ module PWN
|
|
|
28
30
|
# browser_obj: 'required - browser_obj returned from #open method',
|
|
29
31
|
# username: 'required - username',
|
|
30
32
|
# password: 'optional - passwd (will prompt if blank)',
|
|
31
|
-
# mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
33
|
+
# mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
34
|
+
# yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
32
35
|
# )
|
|
33
36
|
|
|
34
37
|
public_class_method def self.login(opts = {})
|
|
38
|
+
PWN::Plugins::AuthenticationHelper.validate_mfa_options(opts)
|
|
39
|
+
yubikey = opts[:yubikey]
|
|
35
40
|
browser_obj = opts[:browser_obj]
|
|
36
41
|
username = opts[:username].to_s.scrub.strip.chomp
|
|
37
42
|
password = opts[:password]
|
|
@@ -51,9 +56,9 @@ module PWN
|
|
|
51
56
|
browser.text_field(id: 'user_password').wait_until(&:present?).set(password)
|
|
52
57
|
browser.button(name: 'button').click!
|
|
53
58
|
|
|
54
|
-
if mfa
|
|
59
|
+
if mfa || !yubikey.nil?
|
|
55
60
|
until browser.url == 'https://bugcrowd.com/programs'
|
|
56
|
-
browser.text_field(name: 'otp_attempt').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token'))
|
|
61
|
+
browser.text_field(name: 'otp_attempt').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token', yubikey: yubikey))
|
|
57
62
|
browser.button(name: 'commit').click!
|
|
58
63
|
sleep 3
|
|
59
64
|
end
|
|
@@ -114,12 +119,18 @@ module PWN
|
|
|
114
119
|
proxy: 'optional - scheme://proxy_host:port || tor'
|
|
115
120
|
)
|
|
116
121
|
|
|
117
|
-
#
|
|
122
|
+
# Log in with manual MFA or explicitly selected YubiKey OATH.
|
|
123
|
+
# YubiKey enables MFA even when mfa is false; failures never fall back to a prompt.
|
|
124
|
+
# Configure the service for an authenticator challenge, not SMS.
|
|
125
|
+
# Example yubikey: { account: 'EXACT_OATH_NAME', confirm: true, serial: '123456', timeout: 30 }
|
|
126
|
+
# Explicit consent is required: HOTP advances counters and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
127
|
+
# Reject conflicting explicit tokens before any login action.
|
|
118
128
|
#{self}.login(
|
|
119
129
|
browser_obj: 'required - browser_obj returned from #open method',
|
|
120
130
|
username: 'required - username value consumed by #login',
|
|
121
131
|
password: 'optional - passwd (will prompt if blank)',
|
|
122
|
-
mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
132
|
+
mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
133
|
+
yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
123
134
|
)
|
|
124
135
|
|
|
125
136
|
# Run logout and return its result
|
data/lib/pwn/www/coinbase_pro.rb
CHANGED
|
@@ -5,6 +5,8 @@ require 'yaml'
|
|
|
5
5
|
module PWN
|
|
6
6
|
module WWW
|
|
7
7
|
# This plugin supports tradingview.com actions.
|
|
8
|
+
# YubiKey OATH MFA requires explicit confirm: true because HOTP advances counters
|
|
9
|
+
# and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
8
10
|
module CoinbasePro
|
|
9
11
|
# Supported Method Parameters::
|
|
10
12
|
# browser_obj = PWN::WWW::CoinbasePro.open(
|
|
@@ -28,10 +30,13 @@ module PWN
|
|
|
28
30
|
# browser_obj: 'required - browser_obj returned from #open method',
|
|
29
31
|
# username: 'required - username',
|
|
30
32
|
# password: 'optional - passwd (will prompt if blank)',
|
|
31
|
-
# mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
33
|
+
# mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
34
|
+
# yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
32
35
|
# )
|
|
33
36
|
|
|
34
37
|
public_class_method def self.login(opts = {})
|
|
38
|
+
PWN::Plugins::AuthenticationHelper.validate_mfa_options(opts)
|
|
39
|
+
yubikey = opts[:yubikey]
|
|
35
40
|
browser_obj = opts[:browser_obj]
|
|
36
41
|
username = opts[:username].to_s.scrub.strip.chomp
|
|
37
42
|
password = opts[:password]
|
|
@@ -52,9 +57,9 @@ module PWN
|
|
|
52
57
|
browser.text_field(name: 'password').wait_until(&:present?).set(password)
|
|
53
58
|
browser.button(text: 'Sign In').click!
|
|
54
59
|
|
|
55
|
-
if mfa
|
|
60
|
+
if mfa || !yubikey.nil?
|
|
56
61
|
until browser.url.include?('https://pro.coinbase.com')
|
|
57
|
-
browser.text_field(name: 'token').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token'))
|
|
62
|
+
browser.text_field(name: 'token').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token', yubikey: yubikey))
|
|
58
63
|
browser.button(text: 'Verify').click!
|
|
59
64
|
sleep 3
|
|
60
65
|
end
|
|
@@ -114,12 +119,18 @@ module PWN
|
|
|
114
119
|
proxy: 'optional - scheme://proxy_host:port || tor'
|
|
115
120
|
)
|
|
116
121
|
|
|
117
|
-
#
|
|
122
|
+
# Log in with manual MFA or explicitly selected YubiKey OATH.
|
|
123
|
+
# YubiKey enables MFA even when mfa is false; failures never fall back to a prompt.
|
|
124
|
+
# Configure the service for an authenticator challenge, not SMS.
|
|
125
|
+
# Example yubikey: { account: 'EXACT_OATH_NAME', confirm: true, serial: '123456', timeout: 30 }
|
|
126
|
+
# Explicit consent is required: HOTP advances counters and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
127
|
+
# Reject conflicting explicit tokens before any login action.
|
|
118
128
|
#{self}.login(
|
|
119
129
|
browser_obj: 'required - browser_obj returned from #open method',
|
|
120
130
|
username: 'required - username value consumed by #login',
|
|
121
131
|
password: 'optional - passwd (will prompt if blank)',
|
|
122
|
-
mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
132
|
+
mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
133
|
+
yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
123
134
|
)
|
|
124
135
|
|
|
125
136
|
# Run logout and return its result
|
data/lib/pwn/www/github.rb
CHANGED
|
@@ -5,6 +5,8 @@ require 'yaml'
|
|
|
5
5
|
module PWN
|
|
6
6
|
module WWW
|
|
7
7
|
# This plugin supports github.com actions.
|
|
8
|
+
# YubiKey OATH MFA requires explicit confirm: true because HOTP advances counters
|
|
9
|
+
# and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
8
10
|
module GitHub
|
|
9
11
|
# Supported Method Parameters::
|
|
10
12
|
# browser_obj = PWN::WWW::GitHub.open(
|
|
@@ -29,10 +31,12 @@ module PWN
|
|
|
29
31
|
# username: 'required - username',
|
|
30
32
|
# password: 'optional - passwd (will prompt if blank)',
|
|
31
33
|
# mfa: 'optional - MFA / TOTP token string, or true to prompt (defaults to false)',
|
|
32
|
-
# mfa_token: 'optional - MFA / TOTP token used to reach a post-authenticated state'
|
|
34
|
+
# mfa_token: 'optional - MFA / TOTP token used to reach a post-authenticated state',
|
|
35
|
+
# yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
33
36
|
# )
|
|
34
37
|
|
|
35
38
|
public_class_method def self.login(opts = {})
|
|
39
|
+
PWN::Plugins::AuthenticationHelper.validate_mfa_options(opts)
|
|
36
40
|
browser_obj = opts[:browser_obj]
|
|
37
41
|
username = opts[:username].to_s.scrub.strip.chomp
|
|
38
42
|
password = opts[:password]
|
|
@@ -45,6 +49,7 @@ module PWN
|
|
|
45
49
|
password = opts[:password].to_s.scrub.strip.chomp
|
|
46
50
|
end
|
|
47
51
|
mfa = opts[:mfa]
|
|
52
|
+
yubikey = opts[:yubikey]
|
|
48
53
|
mfa_token = opts[:mfa_token].to_s.scrub.strip.chomp
|
|
49
54
|
mfa_token = mfa.to_s.scrub.strip.chomp if mfa_token.empty? && mfa.is_a?(String)
|
|
50
55
|
|
|
@@ -54,7 +59,7 @@ module PWN
|
|
|
54
59
|
browser.text_field(name: 'password').wait_until(&:present?).set(password)
|
|
55
60
|
browser.input(name: 'commit').click!
|
|
56
61
|
|
|
57
|
-
if mfa || !mfa_token.empty?
|
|
62
|
+
if mfa || !mfa_token.empty? || !yubikey.nil?
|
|
58
63
|
until browser.url == 'https://github.com/' ||
|
|
59
64
|
browser.url.include?('https://github.com/dashboard') ||
|
|
60
65
|
(
|
|
@@ -63,23 +68,28 @@ module PWN
|
|
|
63
68
|
!browser.url.include?('/session')
|
|
64
69
|
)
|
|
65
70
|
|
|
71
|
+
mfa_field = if browser.text_field(name: 'app_otp').exist?
|
|
72
|
+
browser.text_field(name: 'app_otp')
|
|
73
|
+
elsif browser.text_field(id: 'app_totp').exist?
|
|
74
|
+
browser.text_field(id: 'app_totp')
|
|
75
|
+
elsif browser.text_field(name: 'otp').exist?
|
|
76
|
+
browser.text_field(name: 'otp')
|
|
77
|
+
else
|
|
78
|
+
raise ArgumentError, 'YubiKey OATH requires an authenticator challenge, not SMS' unless yubikey.nil?
|
|
79
|
+
|
|
80
|
+
browser.text_field(name: 'sms_otp')
|
|
81
|
+
end
|
|
82
|
+
mfa_field.wait_until(&:present?)
|
|
83
|
+
|
|
66
84
|
token = if mfa_token.empty?
|
|
67
|
-
PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token')
|
|
85
|
+
PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token', yubikey: yubikey)
|
|
68
86
|
else
|
|
69
87
|
consumed = mfa_token
|
|
70
88
|
mfa_token = ''
|
|
71
89
|
consumed
|
|
72
90
|
end
|
|
73
91
|
|
|
74
|
-
|
|
75
|
-
browser.text_field(name: 'app_otp').wait_until(&:present?).set(token)
|
|
76
|
-
elsif browser.text_field(id: 'app_totp').exist?
|
|
77
|
-
browser.text_field(id: 'app_totp').wait_until(&:present?).set(token)
|
|
78
|
-
elsif browser.text_field(name: 'otp').exist?
|
|
79
|
-
browser.text_field(name: 'otp').wait_until(&:present?).set(token)
|
|
80
|
-
else
|
|
81
|
-
browser.text_field(name: 'sms_otp').wait_until(&:present?).set(token)
|
|
82
|
-
end
|
|
92
|
+
mfa_field.set(token)
|
|
83
93
|
|
|
84
94
|
if browser.input(name: 'commit').exist?
|
|
85
95
|
browser.input(name: 'commit').click!
|
|
@@ -151,13 +161,19 @@ module PWN
|
|
|
151
161
|
proxy: 'optional - scheme://proxy_host:port || tor'
|
|
152
162
|
)
|
|
153
163
|
|
|
154
|
-
#
|
|
164
|
+
# Log in with manual MFA or explicitly selected YubiKey OATH.
|
|
165
|
+
# YubiKey enables MFA even when mfa is false; failures never fall back to a prompt.
|
|
166
|
+
# Configure the service for an authenticator challenge, not SMS.
|
|
167
|
+
# Example yubikey: { account: 'EXACT_OATH_NAME', confirm: true, serial: '123456', timeout: 30 }
|
|
168
|
+
# Explicit consent is required: HOTP advances counters and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
169
|
+
# Reject conflicting explicit tokens before any login action.
|
|
155
170
|
#{self}.login(
|
|
156
171
|
browser_obj: 'required - browser_obj returned from #open method',
|
|
157
172
|
username: 'required - username value consumed by #login',
|
|
158
173
|
password: 'optional - passwd (will prompt if blank)',
|
|
159
174
|
mfa: 'optional - MFA / TOTP token string, or true to prompt (defaults to false)',
|
|
160
|
-
mfa_token: 'optional - MFA / TOTP token used to reach a post-authenticated state'
|
|
175
|
+
mfa_token: 'optional - MFA / TOTP token used to reach a post-authenticated state',
|
|
176
|
+
yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
161
177
|
)
|
|
162
178
|
|
|
163
179
|
# Run logout and return its result
|
data/lib/pwn/www/paypal.rb
CHANGED
|
@@ -5,6 +5,9 @@ require 'yaml'
|
|
|
5
5
|
module PWN
|
|
6
6
|
module WWW
|
|
7
7
|
# This plugin supports paypal.com actions.
|
|
8
|
+
# Its legacy login adapter selects SMS MFA; YubiKey OATH is unsupported even
|
|
9
|
+
# with confirm: true; it is rejected before login rather than submitting
|
|
10
|
+
# an authenticator code as SMS.
|
|
8
11
|
module Paypal
|
|
9
12
|
# Supported Method Parameters::
|
|
10
13
|
# browser_obj = PWN::WWW::Paypal.open(
|
|
@@ -86,10 +89,14 @@ module PWN
|
|
|
86
89
|
# browser_obj: 'required - browser_obj returned from #open method',
|
|
87
90
|
# username: 'required - username (email address)',
|
|
88
91
|
# password: 'optional - passwd (will prompt if blank)',
|
|
89
|
-
# mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
92
|
+
# mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
93
|
+
# yubikey: 'optional - rejected: exact OATH account Hash with :serial, :reader, :device_path, :connection, :timeout is unsupported even with confirm: true; this adapter is SMS-only; explicit mfa/mfa_token values conflict; omit to use manual SMS'
|
|
90
94
|
# )
|
|
91
95
|
|
|
92
96
|
public_class_method def self.login(opts = {})
|
|
97
|
+
PWN::Plugins::AuthenticationHelper.validate_mfa_options(opts)
|
|
98
|
+
raise ArgumentError, 'PayPal YubiKey OATH login is unsupported by this SMS-only browser adapter' unless opts[:yubikey].nil?
|
|
99
|
+
|
|
93
100
|
browser_obj = opts[:browser_obj]
|
|
94
101
|
username = opts[:username].to_s.scrub.strip.chomp
|
|
95
102
|
password = opts[:password]
|
|
@@ -110,7 +117,7 @@ module PWN
|
|
|
110
117
|
browser.button(id: 'btnLogin').click!
|
|
111
118
|
|
|
112
119
|
if mfa
|
|
113
|
-
# Send code to SMS
|
|
120
|
+
# Send code to SMS; this legacy adapter does not select an OATH authenticator.
|
|
114
121
|
browser.button(id: 'btnSelectSoftToken').wait_until(&:present?).click!
|
|
115
122
|
until browser.url == 'https://www.paypal.com/myaccount/home'
|
|
116
123
|
browser.text_field(id: 'security-code').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token'))
|
|
@@ -188,12 +195,16 @@ module PWN
|
|
|
188
195
|
mfa: 'optional - mfa value consumed by #signup'
|
|
189
196
|
)
|
|
190
197
|
|
|
191
|
-
#
|
|
198
|
+
# Log in using the existing manual SMS challenge.
|
|
199
|
+
# YubiKey OATH is unsupported even with confirm: true; this SMS-only browser adapter rejects it before login.
|
|
200
|
+
# Service-level authenticator availability does not establish a compatible adapter flow.
|
|
201
|
+
# Validate options and reject conflicting explicit tokens before any login action.
|
|
192
202
|
#{self}.login(
|
|
193
203
|
browser_obj: 'required - browser_obj returned from #open method',
|
|
194
204
|
username: 'required - username (email address)',
|
|
195
205
|
password: 'optional - passwd (will prompt if blank)',
|
|
196
|
-
mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
206
|
+
mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
207
|
+
yubikey: 'optional - rejected: exact OATH account Hash with :serial, :reader, :device_path, :connection, :timeout is unsupported even with confirm: true; this adapter is SMS-only; explicit mfa/mfa_token values conflict; omit to use manual SMS'
|
|
197
208
|
)
|
|
198
209
|
|
|
199
210
|
# Run logout and return its result
|
data/lib/pwn/www/synack.rb
CHANGED
|
@@ -5,6 +5,8 @@ require 'yaml'
|
|
|
5
5
|
module PWN
|
|
6
6
|
module WWW
|
|
7
7
|
# This plugin supports platform.synack.com actions.
|
|
8
|
+
# YubiKey OATH MFA requires explicit confirm: true because HOTP advances counters
|
|
9
|
+
# and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
8
10
|
module Synack
|
|
9
11
|
# Supported Method Parameters::
|
|
10
12
|
# browser_obj = PWN::WWW::Synack.open(
|
|
@@ -28,10 +30,13 @@ module PWN
|
|
|
28
30
|
# browser_obj: 'required - browser_obj returned from #open method',
|
|
29
31
|
# username: 'required - username',
|
|
30
32
|
# password: 'optional - passwd (will prompt if blank)',
|
|
31
|
-
# mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
33
|
+
# mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
34
|
+
# yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
32
35
|
# )
|
|
33
36
|
|
|
34
37
|
public_class_method def self.login(opts = {})
|
|
38
|
+
PWN::Plugins::AuthenticationHelper.validate_mfa_options(opts)
|
|
39
|
+
yubikey = opts[:yubikey]
|
|
35
40
|
browser_obj = opts[:browser_obj]
|
|
36
41
|
username = opts[:username].to_s.scrub.strip.chomp
|
|
37
42
|
password = opts[:password]
|
|
@@ -51,9 +56,9 @@ module PWN
|
|
|
51
56
|
browser.text_field(name: 'password').wait_until(&:present?).set(password)
|
|
52
57
|
browser.button(class: 'btn').click!
|
|
53
58
|
|
|
54
|
-
if mfa
|
|
59
|
+
if mfa || !yubikey.nil?
|
|
55
60
|
until browser.url.include?('https://platform.synack.com')
|
|
56
|
-
browser.text_field(name: 'authy_token').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token'))
|
|
61
|
+
browser.text_field(name: 'authy_token').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token', yubikey: yubikey))
|
|
57
62
|
browser.button(class: 'btn').click!
|
|
58
63
|
sleep 3
|
|
59
64
|
end
|
|
@@ -114,12 +119,18 @@ module PWN
|
|
|
114
119
|
proxy: 'optional - scheme://proxy_host:port || tor'
|
|
115
120
|
)
|
|
116
121
|
|
|
117
|
-
#
|
|
122
|
+
# Log in with manual MFA or explicitly selected YubiKey OATH.
|
|
123
|
+
# YubiKey enables MFA even when mfa is false; failures never fall back to a prompt.
|
|
124
|
+
# Configure the service for an authenticator challenge, not SMS.
|
|
125
|
+
# Example yubikey: { account: 'EXACT_OATH_NAME', confirm: true, serial: '123456', timeout: 30 }
|
|
126
|
+
# Explicit consent is required: HOTP advances counters and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
127
|
+
# Reject conflicting explicit tokens before any login action.
|
|
118
128
|
#{self}.login(
|
|
119
129
|
browser_obj: 'required - browser_obj returned from #open method',
|
|
120
130
|
username: 'required - username value consumed by #login',
|
|
121
131
|
password: 'optional - passwd (will prompt if blank)',
|
|
122
|
-
mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
132
|
+
mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
133
|
+
yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
123
134
|
)
|
|
124
135
|
|
|
125
136
|
# Run logout and return its result
|
data/lib/pwn/www/twitter.rb
CHANGED
|
@@ -5,6 +5,8 @@ require 'yaml'
|
|
|
5
5
|
module PWN
|
|
6
6
|
module WWW
|
|
7
7
|
# This plugin supports twitter.com actions.
|
|
8
|
+
# YubiKey OATH MFA requires explicit confirm: true because HOTP advances counters
|
|
9
|
+
# and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
8
10
|
module Twitter
|
|
9
11
|
# Supported Method Parameters::
|
|
10
12
|
# browser_obj = PWN::WWW::Twitter.open(
|
|
@@ -28,10 +30,13 @@ module PWN
|
|
|
28
30
|
# browser_obj: 'required - browser_obj returned from #open method',
|
|
29
31
|
# username: 'required - username',
|
|
30
32
|
# password: 'optional - passwd (will prompt if blank)',
|
|
31
|
-
# mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
33
|
+
# mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
34
|
+
# yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
32
35
|
# )
|
|
33
36
|
|
|
34
37
|
public_class_method def self.login(opts = {})
|
|
38
|
+
PWN::Plugins::AuthenticationHelper.validate_mfa_options(opts)
|
|
39
|
+
yubikey = opts[:yubikey]
|
|
35
40
|
browser_obj = opts[:browser_obj]
|
|
36
41
|
username = opts[:username].to_s.scrub.strip.chomp
|
|
37
42
|
password = opts[:password]
|
|
@@ -45,17 +50,17 @@ module PWN
|
|
|
45
50
|
end
|
|
46
51
|
mfa = opts[:mfa]
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
browser.goto('https://twitter.com/login')
|
|
49
54
|
|
|
50
55
|
browser.text_field(index: 2).wait_until(&:present?).set(username)
|
|
51
56
|
browser.text_field(index: 3).wait_until(&:present?).set(password)
|
|
52
57
|
browser.button(index: 1).click!
|
|
53
58
|
|
|
54
|
-
if mfa
|
|
59
|
+
if mfa || !yubikey.nil?
|
|
55
60
|
until browser.url == 'https://twitter.com/' ||
|
|
56
61
|
browser.url == 'https://twitter.com/home'
|
|
57
62
|
|
|
58
|
-
browser.text_field(id: 'challenge_response').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token'))
|
|
63
|
+
browser.text_field(id: 'challenge_response').wait_until(&:present?).set(PWN::Plugins::AuthenticationHelper.mfa(prompt: 'enter mfa token', yubikey: yubikey))
|
|
59
64
|
browser.button(id: 'email_challenge_submit').click!
|
|
60
65
|
sleep 3
|
|
61
66
|
end
|
|
@@ -116,12 +121,18 @@ module PWN
|
|
|
116
121
|
proxy: 'optional - scheme://proxy_host:port || tor'
|
|
117
122
|
)
|
|
118
123
|
|
|
119
|
-
#
|
|
124
|
+
# Log in with manual MFA or explicitly selected YubiKey OATH.
|
|
125
|
+
# YubiKey enables MFA even when mfa is false; failures never fall back to a prompt.
|
|
126
|
+
# Configure the service for an authenticator challenge, not SMS.
|
|
127
|
+
# Example yubikey: { account: 'EXACT_OATH_NAME', confirm: true, serial: '123456', timeout: 30 }
|
|
128
|
+
# Explicit consent is required: HOTP advances counters and protected OATH accounts may require authentication; no automatic repair or deletion occurs.
|
|
129
|
+
# Reject conflicting explicit tokens before any login action.
|
|
120
130
|
#{self}.login(
|
|
121
131
|
browser_obj: 'required - browser_obj returned from #open method',
|
|
122
132
|
username: 'required - username value consumed by #login',
|
|
123
133
|
password: 'optional - passwd (will prompt if blank)',
|
|
124
|
-
mfa: 'optional - if true prompt for mfa token (defaults to false)'
|
|
134
|
+
mfa: 'optional - if true prompt for mfa token (defaults to false)',
|
|
135
|
+
yubikey: 'optional - Hash selecting an exact OATH account (required :account and literal :confirm true); optional :serial exact decimal String, :reader or :device_path nonempty control-free String, :connection native exchange object, :timeout finite positive seconds (default 30), in-memory :password or callable :secret_provider; serial/reader and reader/device_path are mutually exclusive; no OTP slot; explicit mfa/mfa_token values conflict'
|
|
125
136
|
)
|
|
126
137
|
|
|
127
138
|
# Run logout and return its result
|
data/lib/pwn.rb
CHANGED
|
@@ -24,6 +24,7 @@ module PWN
|
|
|
24
24
|
autoload :Memory, 'pwn/memory'
|
|
25
25
|
autoload :MemoryIndex, 'pwn/memory_index'
|
|
26
26
|
autoload :Migrate, 'pwn/migrate'
|
|
27
|
+
autoload :Backups, 'pwn/backups'
|
|
27
28
|
autoload :ModuleSkills, 'pwn/module_skills'
|
|
28
29
|
autoload :Sessions, 'pwn/sessions'
|
|
29
30
|
autoload :SessionTrace, 'pwn/session_trace'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Run only the validation gate, never installation. Keep one bounded, private
|
|
4
|
+
# log even when a terminal disconnects or the child exits without a summary.
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require 'open3'
|
|
7
|
+
|
|
8
|
+
abort 'Usage: ruby scripts/build_gate.rb command [args...]' if ARGV.empty?
|
|
9
|
+
|
|
10
|
+
limit = 1_048_576
|
|
11
|
+
path = File.join(Dir.home, '.pwn', 'logs', 'build-gate.log')
|
|
12
|
+
FileUtils.mkdir_p(File.dirname(path), mode: 0o700)
|
|
13
|
+
File.open(path, File::RDWR | File::CREAT | File::NOFOLLOW, 0o600) do |log|
|
|
14
|
+
abort "Build gate already running; log: #{path}" unless log.flock(File::LOCK_EX | File::LOCK_NB)
|
|
15
|
+
|
|
16
|
+
log.chmod(0o600)
|
|
17
|
+
log.truncate(0)
|
|
18
|
+
log.sync = true
|
|
19
|
+
$stdout.sync = true
|
|
20
|
+
append = lambda do |bytes|
|
|
21
|
+
if log.size + bytes.bytesize > limit
|
|
22
|
+
log.rewind
|
|
23
|
+
retained = (log.read + bytes).byteslice(-limit, limit)
|
|
24
|
+
log.rewind
|
|
25
|
+
log.truncate(0)
|
|
26
|
+
log.write(retained)
|
|
27
|
+
else
|
|
28
|
+
log.seek(0, IO::SEEK_END)
|
|
29
|
+
log.write(bytes)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
emit = lambda do |bytes|
|
|
33
|
+
append.call(bytes)
|
|
34
|
+
begin
|
|
35
|
+
$stdout.write(bytes)
|
|
36
|
+
rescue Errno::EPIPE, IOError
|
|
37
|
+
# Continue draining the child into the durable log after stream loss.
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
emit.call("Build gate log: #{path} (last #{limit} bytes; replaced on next run)\n")
|
|
42
|
+
emit.call("Gate launcher: uid=#{Process.uid} ruby=#{RUBY_VERSION} cwd=#{Dir.pwd} HOME=#{Dir.home}\n")
|
|
43
|
+
status = nil
|
|
44
|
+
Open3.popen2e(*ARGV) do |input, output, waiter|
|
|
45
|
+
input.close
|
|
46
|
+
loop { emit.call(output.readpartial(16_384)) }
|
|
47
|
+
rescue EOFError
|
|
48
|
+
status = waiter.value
|
|
49
|
+
end
|
|
50
|
+
result = status.signaled? ? "signal=#{Signal.signame(status.termsig)}" : "exit=#{status.exitstatus}"
|
|
51
|
+
emit.call("\nBuild gate #{result}\n")
|
|
52
|
+
unless status.success?
|
|
53
|
+
log.rewind
|
|
54
|
+
tail = log.read.byteslice(-16_384, 16_384) || log.tap(&:rewind).read
|
|
55
|
+
warn "\nBuild gate failed (#{result}). Build gate log: #{path}\n#{tail}"
|
|
56
|
+
end
|
|
57
|
+
exit(status.exitstatus || (128 + status.termsig))
|
|
58
|
+
end
|
|
@@ -66,7 +66,7 @@ RSpec.describe 'documentation/Installation.md' do
|
|
|
66
66
|
it 'programmatic API section is truthful' do
|
|
67
67
|
expect(PWN::Setup).to respond_to(:check, :deps, :list_profiles, :pkg_manager, :migrate)
|
|
68
68
|
expect(PWN::Setup.check(io: StringIO.new).keys)
|
|
69
|
-
.to eq(%i[ok native_gems_missing toolchain_missing state pkg_manager os arch])
|
|
69
|
+
.to eq(%i[ok native_gems_missing toolchain_missing state pkg_manager os arch native_libraries_missing])
|
|
70
70
|
expect(PWN::Setup.pkg_manager.keys).to eq(%i[key install sudo])
|
|
71
71
|
expect(PWN::Setup.constants).to include(:NATIVE_GEMS, :TOOLCHAIN, :PROFILES)
|
|
72
72
|
end
|