pwn 0.5.475 → 0.5.476
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 -0
- data/README.md +3 -3
- data/lib/pwn/plugins/jira_data_center.rb +32 -10
- data/lib/pwn/plugins/repl.rb +154 -14
- data/lib/pwn/version.rb +1 -1
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 27b283c37fc8b736e65e2c64b23a21333dad62b689ce43e5dd3e958f665d29ae
|
|
4
|
+
data.tar.gz: 4a80083f4126e1628992e3c7029cfc358ee9e4c257605045bfab5ba82623efad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d68f38ceda2d002c7c6ba6a8f4efc94d9b6b7b96e33988c5a7d37c06bd131d61140e2ed3b7c5af5fe7d7a2da5528c98d579a3d0ce4f3436439bd948e9bed5aa
|
|
7
|
+
data.tar.gz: d3c54e9f9fdb049ee1055df0287935f88f4f2f3f4bd61020983a9b379d68b7f8ed06d86a3ddf28e611c906f23314fb14febe2ca7f68d16fa2f5de6bb49e53fdb
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -37,7 +37,7 @@ $ cd /opt/pwn
|
|
|
37
37
|
$ ./install.sh
|
|
38
38
|
$ ./install.sh ruby-gem
|
|
39
39
|
$ pwn
|
|
40
|
-
pwn[v0.5.
|
|
40
|
+
pwn[v0.5.476]:001 >>> PWN.help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
[](https://youtu.be/G7iLUY4FzsI)
|
|
@@ -52,7 +52,7 @@ $ rvm use ruby-3.4.4@pwn
|
|
|
52
52
|
$ gem uninstall --all --executables pwn
|
|
53
53
|
$ gem install --verbose pwn
|
|
54
54
|
$ pwn
|
|
55
|
-
pwn[v0.5.
|
|
55
|
+
pwn[v0.5.476]:001 >>> PWN.help
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
If you're using a multi-user install of RVM do:
|
|
@@ -62,7 +62,7 @@ $ rvm use ruby-3.4.4@pwn
|
|
|
62
62
|
$ rvmsudo gem uninstall --all --executables pwn
|
|
63
63
|
$ rvmsudo gem install --verbose pwn
|
|
64
64
|
$ pwn
|
|
65
|
-
pwn[v0.5.
|
|
65
|
+
pwn[v0.5.476]:001 >>> PWN.help
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
|
|
@@ -530,16 +530,38 @@ module PWN
|
|
|
530
530
|
end
|
|
531
531
|
end
|
|
532
532
|
|
|
533
|
-
#
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
533
|
+
# Attempt create, dynamically omit unlicensed fields on failure
|
|
534
|
+
attempt = 1
|
|
535
|
+
max_attempts = 5
|
|
536
|
+
cloned_issue = nil
|
|
537
|
+
begin
|
|
538
|
+
cloned_issue = create_issue(
|
|
539
|
+
project_key: project_key,
|
|
540
|
+
summary: summary,
|
|
541
|
+
issue_type: issue_type,
|
|
542
|
+
epic_name: epic_name,
|
|
543
|
+
description: description,
|
|
544
|
+
additional_fields: additional_fields,
|
|
545
|
+
attachments: attachments
|
|
546
|
+
)
|
|
547
|
+
rescue RestClient::ExceptionWithResponse => e
|
|
548
|
+
raise e if attempt >= max_attempts || e.response.body.to_s.empty?
|
|
549
|
+
|
|
550
|
+
json = JSON.parse(e.response.body, symbolize_names: true)
|
|
551
|
+
errors_hash = json[:errors] if json.is_a?(Hash)
|
|
552
|
+
if errors_hash.is_a?(Hash)
|
|
553
|
+
unlicensed_field_keys = errors_hash.select { |_fk, msg| msg.to_s =~ /unlicensed/i }.keys
|
|
554
|
+
if unlicensed_field_keys.any?
|
|
555
|
+
unlicensed_field_keys.each do |fk|
|
|
556
|
+
additional_fields[:fields].delete(fk.to_sym)
|
|
557
|
+
additional_fields[:fields].delete(fk.to_s)
|
|
558
|
+
end
|
|
559
|
+
@@logger.warn("Omitting unlicensed fields: #{unlicensed_field_keys.join(', ')} (attempt #{attempt}/#{max_attempts}). Retrying clone.") if defined?(@@logger)
|
|
560
|
+
attempt += 1
|
|
561
|
+
retry
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
end
|
|
543
565
|
|
|
544
566
|
# Return the fully fetched cloned issue
|
|
545
567
|
get_issue(issue: cloned_issue[:key])
|
data/lib/pwn/plugins/repl.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'curses'
|
|
3
4
|
require 'fileutils'
|
|
4
5
|
require 'meshtastic'
|
|
5
6
|
require 'pry'
|
|
@@ -437,6 +438,14 @@ module PWN
|
|
|
437
438
|
pi.config.pwn_mesh = true
|
|
438
439
|
meshtastic_env = PWN::Env[:plugins][:meshtastic]
|
|
439
440
|
|
|
441
|
+
PWN.send(:remove_const, :MeshTxEchoThread) if PWN.const_defined?(:MeshTxEchoThread)
|
|
442
|
+
PWN.send(:remove_const, :MqttObj) if PWN.const_defined?(:MqttObj)
|
|
443
|
+
PWN.send(:remove_const, :MeshRxWin) if PWN.const_defined?(:MeshRxWin)
|
|
444
|
+
PWN.send(:remove_const, :MeshTxWin) if PWN.const_defined?(:MeshTxWin)
|
|
445
|
+
PWN.send(:remove_const, :MeshPi) if PWN.const_defined?(:MeshPi)
|
|
446
|
+
PWN.send(:remove_const, :MeshMutex) if PWN.const_defined?(:MeshMutex)
|
|
447
|
+
PWN.send(:remove_const, :MqttSubThread) if PWN.const_defined?(:MqttSubThread)
|
|
448
|
+
|
|
440
449
|
mqtt_env = meshtastic_env[:mqtt]
|
|
441
450
|
host = mqtt_env[:host]
|
|
442
451
|
port = mqtt_env[:port]
|
|
@@ -458,6 +467,101 @@ module PWN
|
|
|
458
467
|
psk = channel_env[:psk]
|
|
459
468
|
region = channel_env[:region]
|
|
460
469
|
topic = channel_env[:topic]
|
|
470
|
+
channel_num = channel_env[:channel_num]
|
|
471
|
+
|
|
472
|
+
# Init ncurses UI (idempotent) with separate RX (top) and TX (bottom) panes
|
|
473
|
+
Curses.init_screen
|
|
474
|
+
Curses.curs_set(0)
|
|
475
|
+
# Curses.crmode
|
|
476
|
+
# Curses.ESCDELAY = 0
|
|
477
|
+
Curses.start_color
|
|
478
|
+
Curses.use_default_colors
|
|
479
|
+
|
|
480
|
+
rx_height = Curses.lines - 4
|
|
481
|
+
rx_win = Curses::Window.new(rx_height, Curses.cols, 0, 0)
|
|
482
|
+
rx_win.scrollok(true)
|
|
483
|
+
rx_win.nodelay = true
|
|
484
|
+
rx_win.box('|', "\u2500")
|
|
485
|
+
rx_win.addstr("#{host}:#{port} >>> #{region}/#{topic}:#{channel_num} >>> Messages >>>\n")
|
|
486
|
+
rx_win.refresh
|
|
487
|
+
|
|
488
|
+
tx_win = Curses::Window.new(4, Curses.cols, rx_height, 0)
|
|
489
|
+
tx_win.scrollok(false)
|
|
490
|
+
tx_win.nodelay = true
|
|
491
|
+
tx_win.box('|', "\u2500")
|
|
492
|
+
tx_win.addstr("TX Pane (echo while typing, last sent shown after Enter)\n")
|
|
493
|
+
tx_win.refresh
|
|
494
|
+
|
|
495
|
+
PWN.const_set(:MeshRxWin, rx_win)
|
|
496
|
+
PWN.const_set(:MeshTxWin, tx_win)
|
|
497
|
+
PWN.const_set(:MeshMutex, Mutex.new)
|
|
498
|
+
PWN.const_set(:MeshPi, pi)
|
|
499
|
+
|
|
500
|
+
# Live typing echo thread (idempotent)
|
|
501
|
+
tx_prompt = "#{region}/#{topic}:#{channel_num} >>> "
|
|
502
|
+
echo_thread = Thread.new do
|
|
503
|
+
loop do
|
|
504
|
+
break unless pi.config.pwn_mesh
|
|
505
|
+
|
|
506
|
+
tx_win = PWN.const_get(:MeshTxWin)
|
|
507
|
+
mutex = PWN.const_get(:MeshMutex)
|
|
508
|
+
msg_input = pi.input.line_buffer.to_s
|
|
509
|
+
ts = Time.now.strftime('%H:%M:%S')
|
|
510
|
+
mutex.synchronize do
|
|
511
|
+
# Only show draft if after_read hook hasn't just written final TX (heuristic)
|
|
512
|
+
tx_win.clear
|
|
513
|
+
tx_win.box('|', "\u2500")
|
|
514
|
+
tx_win.setpos(1, 1)
|
|
515
|
+
tx_win.addstr("[#{ts}] [TX] #{tx_prompt} #{msg_input}")
|
|
516
|
+
tx_win.refresh
|
|
517
|
+
end
|
|
518
|
+
sleep 0.01
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
echo_thread.abort_on_exception = false
|
|
522
|
+
PWN.const_set(:MeshTxEchoThread, echo_thread)
|
|
523
|
+
|
|
524
|
+
# Start single subscriber thread (idempotent)
|
|
525
|
+
# {packet: {from: 2868848892, to: 4294967295, channel: 93, encrypted: :decrypted, id: 3144461425, hop_limit: 3, topic: "msh/US/UT/2/e/BroadSec/!aaff28fc", node_id_from: "!aaff28fc", node_id_to: "!ffffffff", decoded: {portnum: :TEXT_MESSAGE_APP, payload: "testy"}}, channel_id: "BroadSec", gateway_id: "!aaff28fc"}
|
|
526
|
+
psks = { active_channel => psk }
|
|
527
|
+
PWN::Plugins::ThreadPool.fill(
|
|
528
|
+
enumerable_array: [:mesh_sub],
|
|
529
|
+
max_threads: 1,
|
|
530
|
+
detach: true
|
|
531
|
+
) do |_|
|
|
532
|
+
Meshtastic::MQTT.subscribe(
|
|
533
|
+
mqtt_obj: mqtt_obj,
|
|
534
|
+
region: region,
|
|
535
|
+
topic: topic,
|
|
536
|
+
channel: channel_num,
|
|
537
|
+
psks: psks
|
|
538
|
+
) do |msg|
|
|
539
|
+
next unless msg.key?(:packet) && msg[:packet].key?(:decoded) && msg[:packet][:decoded].is_a?(Hash)
|
|
540
|
+
|
|
541
|
+
packet = msg[:packet]
|
|
542
|
+
decoded = packet[:decoded]
|
|
543
|
+
next unless decoded.key?(:portnum) && decoded[:portnum] == :TEXT_MESSAGE_APP
|
|
544
|
+
|
|
545
|
+
rx_win = PWN.const_get(:MeshRxWin)
|
|
546
|
+
mutex = PWN.const_get(:MeshMutex)
|
|
547
|
+
|
|
548
|
+
from = packet[:node_id_from]
|
|
549
|
+
absolute_topic = "#{region}/#{topic.gsub('#', from)}:#{channel_num}"
|
|
550
|
+
to = packet[:node_id_to]
|
|
551
|
+
rx_text = decoded[:payload]
|
|
552
|
+
ts = Time.now.strftime('%H:%M:%S')
|
|
553
|
+
mutex.synchronize do
|
|
554
|
+
if to == '!ffffffff'
|
|
555
|
+
rx_win.addstr("[#{ts}] [RX] #{absolute_topic}: #{rx_text}\n")
|
|
556
|
+
else
|
|
557
|
+
rx_win.addstr("[#{ts}] [RX][DM INTERCEPTED] #{absolute_topic} >>> #{to}: #{rx_text}\n")
|
|
558
|
+
end
|
|
559
|
+
rx_win.addstr("#{msg.inspect}\n\n\n")
|
|
560
|
+
rx_win.refresh
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
PWN.const_set(:MqttSubThread, true)
|
|
461
565
|
rescue StandardError => e
|
|
462
566
|
raise e
|
|
463
567
|
end
|
|
@@ -523,8 +627,32 @@ module PWN
|
|
|
523
627
|
pi.config.pwn_ai_debug = false if pi.config.pwn_ai_debug
|
|
524
628
|
pi.config.pwn_ai_speak = false if pi.config.pwn_ai_speak
|
|
525
629
|
pi.config.completer = Pry::InputCompleter
|
|
526
|
-
|
|
527
|
-
|
|
630
|
+
return unless pi.config.pwn_mesh
|
|
631
|
+
|
|
632
|
+
pi.config.pwn_mesh = false
|
|
633
|
+
# Stop echo thread
|
|
634
|
+
if PWN.const_defined?(:MeshTxEchoThread)
|
|
635
|
+
PWN.const_get(:MeshTxEchoThread).kill
|
|
636
|
+
PWN.send(:remove_const, :MeshTxEchoThread)
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
if PWN.const_defined?(:MqttObj)
|
|
640
|
+
Meshtastic::MQTT.disconnect(mqtt_obj: PWN.const_get(:MqttObj))
|
|
641
|
+
PWN.send(:remove_const, :MqttObj)
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
if PWN.const_defined?(:MeshRxWin)
|
|
645
|
+
PWN.const_get(:MeshRxWin).close
|
|
646
|
+
PWN.send(:remove_const, :MeshRxWin)
|
|
647
|
+
end
|
|
648
|
+
if PWN.const_defined?(:MeshTxWin)
|
|
649
|
+
PWN.const_get(:MeshTxWin).close
|
|
650
|
+
PWN.send(:remove_const, :MeshTxWin)
|
|
651
|
+
end
|
|
652
|
+
PWN.send(:remove_const, :MeshPi) if PWN.const_defined?(:MeshPi)
|
|
653
|
+
PWN.send(:remove_const, :MeshMutex) if PWN.const_defined?(:MeshMutex)
|
|
654
|
+
PWN.send(:remove_const, :MqttSubThread) if PWN.const_defined?(:MqttSubThread)
|
|
655
|
+
Curses.close_screen
|
|
528
656
|
end
|
|
529
657
|
end
|
|
530
658
|
rescue StandardError => e
|
|
@@ -656,21 +784,18 @@ module PWN
|
|
|
656
784
|
psks = {}
|
|
657
785
|
psks[active_channel] = psk
|
|
658
786
|
|
|
659
|
-
|
|
787
|
+
tx_text = pi.input.line_buffer.to_s
|
|
660
788
|
to = '!ffffffff'
|
|
661
|
-
# If text include @! with 8 byte length
|
|
662
|
-
|
|
663
|
-
|
|
789
|
+
# If text include @! with 8 byte length,
|
|
790
|
+
# send DM to that address
|
|
791
|
+
if tx_text.include?('@!')
|
|
792
|
+
to_raw = tx_text.split('@').last.chomp[0..8]
|
|
664
793
|
# If to_raw[1..-1] is hex than set to = to_raw
|
|
665
794
|
to = to_raw if to_raw[1..-1].match?(/^[a-fA-F0-9]{8}$/)
|
|
666
|
-
|
|
795
|
+
# Remove any spaces from beginning of to_raw
|
|
796
|
+
tx_text.gsub!("@#{to_raw}", '').strip!
|
|
667
797
|
end
|
|
668
|
-
|
|
669
|
-
puts "To: #{to}"
|
|
670
|
-
puts "Region: #{region}"
|
|
671
|
-
puts "Topic: #{topic}"
|
|
672
|
-
puts "Channel #: #{channel_num}"
|
|
673
|
-
puts "Text: #{text}\n\n"
|
|
798
|
+
|
|
674
799
|
Meshtastic::MQTT.send_text(
|
|
675
800
|
mqtt_obj: mqtt_obj,
|
|
676
801
|
from: from,
|
|
@@ -678,9 +803,24 @@ module PWN
|
|
|
678
803
|
region: region,
|
|
679
804
|
topic: topic,
|
|
680
805
|
channel: channel_num,
|
|
681
|
-
text:
|
|
806
|
+
text: tx_text,
|
|
682
807
|
psks: psks
|
|
683
808
|
)
|
|
809
|
+
|
|
810
|
+
# Update TX pane (bottom) with last sent message
|
|
811
|
+
if PWN.const_defined?(:MeshTxWin)
|
|
812
|
+
tx_win = PWN.const_get(:MeshTxWin)
|
|
813
|
+
mutex = PWN.const_get(:MeshMutex)
|
|
814
|
+
absolute_topic = "#{region}/#{topic.gsub('#', from)}:#{channel_num}"
|
|
815
|
+
ts = Time.now.strftime('%H:%M:%S')
|
|
816
|
+
mutex.synchronize do
|
|
817
|
+
tx_win.clear
|
|
818
|
+
tx_win.box('|', "\u2500")
|
|
819
|
+
tx_win.setpos(1, 1)
|
|
820
|
+
tx_win.addstr("[#{ts}] [TX] #{absolute_topic} >>> #{to}: #{tx_text}")
|
|
821
|
+
tx_win.refresh
|
|
822
|
+
end
|
|
823
|
+
end
|
|
684
824
|
end
|
|
685
825
|
end
|
|
686
826
|
rescue StandardError => e
|
data/lib/pwn/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pwn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.476
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 0day Inc.
|
|
@@ -205,6 +205,20 @@ dependencies:
|
|
|
205
205
|
- - '='
|
|
206
206
|
- !ruby/object:Gem::Version
|
|
207
207
|
version: 7.0.0
|
|
208
|
+
- !ruby/object:Gem::Dependency
|
|
209
|
+
name: curses
|
|
210
|
+
requirement: !ruby/object:Gem::Requirement
|
|
211
|
+
requirements:
|
|
212
|
+
- - '='
|
|
213
|
+
- !ruby/object:Gem::Version
|
|
214
|
+
version: 1.5.3
|
|
215
|
+
type: :runtime
|
|
216
|
+
prerelease: false
|
|
217
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
218
|
+
requirements:
|
|
219
|
+
- - '='
|
|
220
|
+
- !ruby/object:Gem::Version
|
|
221
|
+
version: 1.5.3
|
|
208
222
|
- !ruby/object:Gem::Dependency
|
|
209
223
|
name: diffy
|
|
210
224
|
requirement: !ruby/object:Gem::Requirement
|