pwn 0.5.480 → 0.5.481

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be16c14d474f80d808e7389475a17ec6d7dc52cb4da44395ef516810a7054ae2
4
- data.tar.gz: 4e33ef6a047b000581e6fa1aa94ac177f00324ef011f60b43ad0fa2a01435c11
3
+ metadata.gz: f8f0ace17ae36a1a8e60fd015ad3c0a37dcaa22fd3490806b4fc033bd974ae70
4
+ data.tar.gz: 4a3b5eb17658a0a7c63e6e49bd87cad5740112ed1c2d77207fd0cde9949a50d6
5
5
  SHA512:
6
- metadata.gz: 1c561adc55354180fc0d53ca294e83693ee8ddcd625fe75f99b357e5cfb697bc95fdb9326c01e939c22f2cf2f80ff1bc00fe8a4b111e3eb889bffa02992dd248
7
- data.tar.gz: 841188de9b7921592cf1baa653e74eab49a7039468506930d095cdbe6076bfcd55071c99554d010ce1642e88d9730bf623fa912d67ee05a30ae4902c4790d7ef
6
+ metadata.gz: d97493c22667ef54489045fd1bdd5ff8940701fa266e923849f8784feacc546be605154bc215a15823b9c424a4ba025e6bd95d3a1b3e86eba3d70987bc55c6c0
7
+ data.tar.gz: 0636daf478dff9780efe1b14478d40d06e109927c955d8d8c50f8c1c8e5c1bd7faaad9cfc8f98254084f4f384cdaa90c4698d563e57d386be38449193600c2e4
data/.rubocop.yml CHANGED
@@ -6,7 +6,7 @@ Layout/LineLength:
6
6
  Lint/UselessRescue:
7
7
  Enabled: false
8
8
  Metrics/AbcSize:
9
- Max: 537.6
9
+ Max: 552.3
10
10
  Metrics/BlockLength:
11
11
  Max: 292
12
12
  Metrics/BlockNesting:
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.480]:001 >>> PWN.help
40
+ pwn[v0.5.481]:001 >>> PWN.help
41
41
  ```
42
42
 
43
43
  [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](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.480]:001 >>> PWN.help
55
+ pwn[v0.5.481]: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.480]:001 >>> PWN.help
65
+ pwn[v0.5.481]: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:
@@ -64,19 +64,10 @@ module PWN
64
64
  end
65
65
  end
66
66
 
67
- if pi.config.pwn_mesh
68
- active_channel = PWN::Env[:plugins][:meshtastic][:channel][:active].to_s.to_sym
69
- region = PWN::Env[:plugins][:meshtastic][:channel][active_channel][:region]
70
- topic = PWN::Env[:plugins][:meshtastic][:channel][active_channel][:topic]
71
- channel_num = PWN::Env[:plugins][:meshtastic][:channel][active_channel][:channel_num]
67
+ ps1_proc = "#{name}[#{version}]:#{line_count} #{dchars} ".to_s.scrub
68
+ ps1_proc = '' if pi.config.pwn_mesh
72
69
 
73
- pi.config.prompt_name = "pwn.mesh:#{region}/#{topic}:#{channel_num}"
74
- name = "\001\e[1m\002\001\e[32m\002#{pi.config.prompt_name}\001\e[0m\002"
75
- dchars = "\001\e[32m\002>>>\001\e[33m\002"
76
- dchars = "\001\e[33m\002***\001\e[33m\002" if mode == :splat
77
- end
78
-
79
- "#{name}[#{version}]:#{line_count} #{dchars} ".to_s.scrub
70
+ ps1_proc
80
71
  end
81
72
  rescue StandardError => e
82
73
  raise e
@@ -472,24 +463,77 @@ module PWN
472
463
  # Init ncurses UI (idempotent) with separate RX (top) and TX (bottom) panes
473
464
  Curses.init_screen
474
465
  Curses.curs_set(0)
475
- # Curses.crmode
476
- # Curses.ESCDELAY = 0
466
+ Curses.noecho
467
+ Curses.cbreak
468
+ Curses.crmode
469
+ Curses.ESCDELAY = 0
477
470
  Curses.start_color
478
471
  Curses.use_default_colors
479
472
 
473
+ mesh_highlight_colors = [
474
+ { fg: Curses::COLOR_RED, bg: Curses::COLOR_WHITE },
475
+ { fg: Curses::COLOR_GREEN, bg: Curses::COLOR_BLACK },
476
+ { fg: Curses::COLOR_YELLOW, bg: Curses::COLOR_BLACK },
477
+ { fg: Curses::COLOR_BLUE, bg: Curses::COLOR_WHITE },
478
+ { fg: Curses::COLOR_CYAN, bg: Curses::COLOR_BLACK },
479
+ { fg: Curses::COLOR_MAGENTA, bg: Curses::COLOR_WHITE },
480
+ { fg: Curses::COLOR_WHITE, bg: Curses::COLOR_BLUE }
481
+ ]
482
+ mesh_highlight_colors.each_with_index do |hash, idx|
483
+ color_id = idx + 1
484
+ color_fg = hash[:fg]
485
+ color_bg = hash[:bg]
486
+ Curses.init_pair(color_id, color_fg, color_bg)
487
+ end
488
+ PWN.const_set(:MeshColors, (1..mesh_highlight_colors.length).to_a)
489
+ PWN.const_set(:MeshLastPair, PWN::MeshColors.sample)
490
+
491
+ mesh_ui_colors = []
492
+ mesh_highlight_colors.each_with_index do |hl_hash, idx|
493
+ ui_hash = {
494
+ color_id: idx + 10,
495
+ fg: hl_hash[:fg],
496
+ bg: -1
497
+ }
498
+ Curses.init_pair(ui_hash[:color_id], ui_hash[:fg], ui_hash[:bg])
499
+ mesh_ui_colors.push(ui_hash)
500
+ end
501
+
502
+ red = mesh_ui_colors[0][:color_id]
503
+ green = mesh_ui_colors[1][:color_id]
504
+ yellow = mesh_ui_colors[2][:color_id]
505
+ blue = mesh_ui_colors[3][:color_id]
506
+ cyan = mesh_ui_colors[4][:color_id]
507
+ magenta = mesh_ui_colors[5][:color_id]
508
+ white = mesh_ui_colors[6][:color_id]
509
+
480
510
  rx_height = Curses.lines - 4
481
- rx_win = Curses::Window.new(rx_height, Curses.cols, 2, 0)
511
+ rx_win = Curses::Window.new(rx_height, Curses.cols, 0, 0)
482
512
  rx_win.scrollok(true)
483
513
  rx_win.nodelay = true
484
- rx_win.box('|', "\u2500")
485
- rx_win.addstr("<<< #{host}:#{port} | #{region}/#{topic} | ch:#{channel_num} >>>\n")
514
+ rx_win.attron(Curses.color_pair(cyan) | Curses::A_BOLD)
515
+
516
+ # Make rx_header bold and green
517
+ rx_win.attron(Curses.color_pair(green) | Curses::A_BOLD)
518
+ rx_header = "<<< #{host}:#{port} | #{region}/#{topic} | ch:#{channel_num} >>>"
519
+ rx_header_len = rx_header.length
520
+ rx_header_pos = (Curses.cols / 2) - (rx_header_len / 2)
521
+ rx_win.setpos(1, rx_header_pos)
522
+ rx_win.addstr(rx_header)
523
+ rx_win.attroff(Curses.color_pair(green) | Curses::A_BOLD)
524
+ # Jump two lines below header before messages begin
525
+ rx_win.setpos(2, 1)
526
+ rx_win.attron(Curses.color_pair(cyan) | Curses::A_BOLD)
527
+ rx_header_bottom_line = "\u2014" * (Curses.cols - 2)
528
+ rx_header_bottom_line_pos = (Curses.cols / 2) - (rx_header_bottom_line.length / 2)
529
+ rx_win.addstr(rx_header_bottom_line)
530
+ rx_win.attroff(Curses.color_pair(cyan) | Curses::A_BOLD)
531
+
486
532
  rx_win.refresh
487
533
 
488
534
  tx_win = Curses::Window.new(4, Curses.cols, rx_height, 0)
489
535
  tx_win.scrollok(false)
490
536
  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
537
  tx_win.refresh
494
538
 
495
539
  PWN.const_set(:MeshRxWin, rx_win)
@@ -498,24 +542,61 @@ module PWN
498
542
  PWN.const_set(:MeshPi, pi)
499
543
 
500
544
  # Live typing echo thread (idempotent)
501
- tx_prompt = "#{region}/#{topic}:#{channel_num} >>> "
545
+ tx_prompt = "#{region}/#{topic} >>>"
502
546
  echo_thread = Thread.new do
547
+ last_drawn = nil
503
548
  loop do
504
549
  break unless pi.config.pwn_mesh
505
550
 
506
551
  tx_win = PWN.const_get(:MeshTxWin)
507
552
  mutex = PWN.const_get(:MeshMutex)
508
553
  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
554
+ ts = Time.now.strftime('%H:%M:%S%z')
555
+ cursor_pos = (Readline.respond_to?(:point) ? Readline.point : msg_input.length)
556
+ prefix = "[#{ts}] [TX] #{tx_prompt} "
557
+ base_line = "#{prefix}#{msg_input}"
558
+ cursor_abs_index = prefix.length + cursor_pos
559
+ current_line = base_line
560
+ if current_line != last_drawn
561
+ mutex.synchronize do
562
+ # Only show draft if after_read hook hasn't just written final TX (heuristic)
563
+ tx_win.clear
564
+ tx_win.attron(Curses.color_pair(red) | Curses::A_BOLD)
565
+ tx_header_bottom_line = "\u2014" * (Curses.cols - 2)
566
+ tx_header_bottom_line_pos = (Curses.cols / 2) - (rx_header_bottom_line.length / 2)
567
+ tx_win.addstr(rx_header_bottom_line)
568
+ tx_win.attroff(Curses.color_pair(red) | Curses::A_BOLD)
569
+ tx_win.attron(Curses.color_pair(yellow) | Curses::A_BOLD)
570
+
571
+ inner_width = Curses.cols - 2
572
+ segments = current_line.chars.each_slice(inner_width).map(&:join)
573
+ available_rows = tx_win.maxy - 2
574
+ segments.first(available_rows).each_with_index do |seg, idx|
575
+ tx_win.setpos(1 + idx, 1)
576
+ start_index = idx * inner_width
577
+ end_index = start_index + inner_width - 1
578
+ if cursor_abs_index.between?(start_index, end_index)
579
+ cursor_col = cursor_abs_index - start_index
580
+ (0..inner_width).each do |col|
581
+ ch = seg[col] || ' '
582
+ if col == cursor_col
583
+ tx_win.attron(Curses.color_pair(red) | Curses::A_REVERSE | Curses::A_BOLD)
584
+ tx_win.addch(ch)
585
+ tx_win.attroff(Curses.color_pair(red) | Curses::A_REVERSE | Curses::A_BOLD)
586
+ else
587
+ tx_win.addch(ch)
588
+ end
589
+ end
590
+ else
591
+ tx_win.addstr(seg.ljust(inner_width))
592
+ end
593
+ end
594
+ tx_win.attroff(Curses.color_pair(yellow) | Curses::A_BOLD)
595
+ tx_win.refresh
596
+ end
597
+ last_drawn = current_line
517
598
  end
518
- sleep 0.01
599
+ sleep 0.005
519
600
  end
520
601
  end
521
602
  echo_thread.abort_on_exception = false
@@ -545,22 +626,38 @@ module PWN
545
626
  mutex = PWN.const_get(:MeshMutex)
546
627
 
547
628
  from = packet[:node_id_from]
548
- absolute_topic = "#{region}/#{topic.gsub('#', from)}:#{channel_num}"
629
+ absolute_topic = "#{region}/#{topic.gsub('#', from)}"
549
630
  to = packet[:node_id_to]
550
631
  rx_text = decoded[:payload]
551
- ts = Time.now.strftime('%H:%M:%S')
632
+ ts = Time.now.strftime('%H:%M:%S%z')
633
+
634
+ # Select a random color pair different from the last used one
635
+ colors_arr = PWN.const_get(:MeshColors)
636
+ last_pair = PWN.const_get(:MeshLastPair)
637
+ PWN.send(:remove_const, :MeshLastPair)
638
+ pair_choices = colors_arr.reject { |c| c == last_pair }
639
+ pair = pair_choices.sample
640
+ PWN.const_set(:MeshLastPair, pair)
641
+ rx_win.attron(Curses.color_pair(pair) | Curses::A_REVERSE)
642
+
643
+ current_line = "\n[#{ts}] [RX] #{absolute_topic} >>> #{rx_text}"
644
+ current_line = "\n[#{ts}] [RX][DM INTERCEPTED] #{absolute_topic} to: #{to} >>> #{rx_text}" unless to == '!ffffffff'
645
+
552
646
  mutex.synchronize do
553
- if to == '!ffffffff'
554
- rx_win.addstr("[#{ts}] [RX] #{absolute_topic}: #{rx_text}\n")
555
- else
556
- rx_win.addstr("[#{ts}] [RX][DM INTERCEPTED] #{absolute_topic} >>> #{to}: #{rx_text}\n")
647
+ inner_width = Curses.cols - 2
648
+ content = current_line.sub(/\A\n/, '')
649
+ segments = content.scan(/.{1,#{inner_width}}/)
650
+ segments.each do |seg|
651
+ rx_win.addstr("\n")
652
+ rx_win.setpos(rx_win.cury, 1)
653
+ line = seg.ljust(inner_width)
654
+ rx_win.addstr(line)
557
655
  end
558
- # rx_win.addstr("#{msg.inspect}\n\n\n")
559
656
  rx_win.refresh
560
657
  end
658
+ rx_win.attroff(Curses.color_pair(pair) | Curses::A_REVERSE)
561
659
  end
562
660
  end
563
- PWN.const_set(:MqttSubThread, true)
564
661
  rescue StandardError => e
565
662
  raise e
566
663
  end
@@ -648,6 +745,8 @@ module PWN
648
745
  PWN.const_get(:MeshTxWin).close
649
746
  PWN.send(:remove_const, :MeshTxWin)
650
747
  end
748
+ PWN.send(:remove_const, :MeshColors) if PWN.const_defined?(:MeshColors)
749
+ PWN.send(:remove_const, :MeshLastPair) if PWN.const_defined?(:MeshLastPair)
651
750
  PWN.send(:remove_const, :MeshPi) if PWN.const_defined?(:MeshPi)
652
751
  PWN.send(:remove_const, :MeshMutex) if PWN.const_defined?(:MeshMutex)
653
752
  PWN.send(:remove_const, :MqttSubThread) if PWN.const_defined?(:MqttSubThread)
@@ -805,21 +904,6 @@ module PWN
805
904
  text: tx_text,
806
905
  psks: psks
807
906
  )
808
-
809
- # Update TX pane (bottom) with last sent message
810
- if PWN.const_defined?(:MeshTxWin)
811
- tx_win = PWN.const_get(:MeshTxWin)
812
- mutex = PWN.const_get(:MeshMutex)
813
- absolute_topic = "#{region}/#{topic.gsub('#', from)}:#{channel_num}"
814
- ts = Time.now.strftime('%H:%M:%S')
815
- mutex.synchronize do
816
- tx_win.clear
817
- tx_win.box('|', "\u2500")
818
- tx_win.setpos(1, 1)
819
- tx_win.addstr("[#{ts}] [TX] #{absolute_topic} >>> #{to}: #{tx_text}")
820
- tx_win.refresh
821
- end
822
- end
823
907
  end
824
908
  end
825
909
  rescue StandardError => e
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.480'
4
+ VERSION = '0.5.481'
5
5
  end
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.480
4
+ version: 0.5.481
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.