rex 2.0.9 → 2.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rex/exploitation/cmdstager/bourne.rb +14 -8
  3. data/lib/rex/exploitation/cmdstager/echo.rb +3 -3
  4. data/lib/rex/exploitation/js/memory.rb +1 -1
  5. data/lib/rex/java/serialization/model/contents.rb +1 -1
  6. data/lib/rex/mime/message.rb +1 -1
  7. data/lib/rex/parser/acunetix_nokogiri.rb +2 -0
  8. data/lib/rex/parser/appscan_nokogiri.rb +1 -1
  9. data/lib/rex/parser/burp_issue_nokogiri.rb +139 -0
  10. data/lib/rex/parser/burp_session_nokogiri.rb +1 -1
  11. data/lib/rex/parser/fs/bitlocker.rb +233 -0
  12. data/lib/rex/parser/fusionvm_nokogiri.rb +2 -2
  13. data/lib/rex/parser/ini.rb +1 -8
  14. data/lib/rex/parser/nokogiri_doc_mixin.rb +5 -0
  15. data/lib/rex/payloads/meterpreter/config.rb +23 -4
  16. data/lib/rex/post/meterpreter/channel.rb +8 -3
  17. data/lib/rex/post/meterpreter/client.rb +1 -0
  18. data/lib/rex/post/meterpreter/client_core.rb +2 -2
  19. data/lib/rex/post/meterpreter/extensions/android/android.rb +86 -1
  20. data/lib/rex/post/meterpreter/extensions/android/tlv.rb +29 -0
  21. data/lib/rex/post/meterpreter/extensions/extapi/wmi/wmi.rb +1 -1
  22. data/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb +75 -89
  23. data/lib/rex/post/meterpreter/extensions/stdapi/sys/event_log.rb +8 -2
  24. data/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb +10 -5
  25. data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/registry_key.rb +7 -2
  26. data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/remote_registry_key.rb +10 -5
  27. data/lib/rex/post/meterpreter/extensions/stdapi/sys/thread.rb +8 -2
  28. data/lib/rex/post/meterpreter/extensions/stdapi/webcam/webcam.rb +1 -1
  29. data/lib/rex/post/meterpreter/packet.rb +38 -0
  30. data/lib/rex/post/meterpreter/packet_dispatcher.rb +101 -108
  31. data/lib/rex/post/meterpreter/packet_parser.rb +14 -6
  32. data/lib/rex/post/meterpreter/packet_response_waiter.rb +42 -21
  33. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb +54 -4
  34. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +39 -13
  35. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +8 -0
  36. data/lib/rex/proto/adb.rb +7 -0
  37. data/lib/rex/proto/adb/client.rb +39 -0
  38. data/lib/rex/proto/adb/message.rb +164 -0
  39. data/lib/rex/proto/dcerpc/svcctl/packet.rb +9 -9
  40. data/lib/rex/proto/http/client_request.rb +2 -1
  41. data/lib/rex/proto/http/response.rb +1 -1
  42. data/lib/rex/proto/kademlia/bootstrap_response.rb +2 -2
  43. data/lib/rex/proto/ntp/modes.rb +17 -0
  44. data/lib/rex/text.rb +12 -0
  45. data/lib/rex/zip/blocks.rb +1 -1
  46. data/lib/rex/zip/entry.rb +1 -1
  47. data/rex.gemspec +28 -1
  48. metadata +106 -3
@@ -12,6 +12,11 @@ module Meterpreter
12
12
  ###
13
13
  class PacketParser
14
14
 
15
+ # 4 byte xor
16
+ # 4 byte length
17
+ # 4 byte type
18
+ HEADER_SIZE = 12
19
+
15
20
  #
16
21
  # Initializes the packet parser context with an optional cipher.
17
22
  #
@@ -26,7 +31,7 @@ class PacketParser
26
31
  #
27
32
  def reset
28
33
  self.raw = ''
29
- self.hdr_length_left = 8
34
+ self.hdr_length_left = HEADER_SIZE
30
35
  self.payload_length_left = 0
31
36
  end
32
37
 
@@ -34,6 +39,9 @@ class PacketParser
34
39
  # Reads data from the wire and parse as much of the packet as possible.
35
40
  #
36
41
  def recv(sock)
42
+ # Create a typeless packet
43
+ packet = Packet.new(0)
44
+
37
45
  if (self.hdr_length_left > 0)
38
46
  buf = sock.read(self.hdr_length_left)
39
47
 
@@ -49,7 +57,10 @@ class PacketParser
49
57
  # payload length left to the number of bytes
50
58
  # specified in the length
51
59
  if (self.hdr_length_left == 0)
52
- self.payload_length_left = raw.unpack("N")[0] - 8
60
+ xor_key = raw[0, 4].unpack('N')[0]
61
+ length_bytes = packet.xor_bytes(xor_key, raw[4, 4])
62
+ # header size doesn't include the xor key, which is always tacked on the front
63
+ self.payload_length_left = length_bytes.unpack("N")[0] - (HEADER_SIZE - 4)
53
64
  end
54
65
  elsif (self.payload_length_left > 0)
55
66
  buf = sock.read(self.payload_length_left)
@@ -67,14 +78,11 @@ class PacketParser
67
78
  if ((self.hdr_length_left == 0) &&
68
79
  (self.payload_length_left == 0))
69
80
 
70
- # Create a typeless packet
71
- packet = Packet.new(0)
72
-
73
81
  # TODO: cipher decryption
74
82
  if (cipher)
75
83
  end
76
84
 
77
- # Serialize the packet from the raw buffer
85
+ # Deserialize the packet from the raw buffer
78
86
  packet.from_r(self.raw)
79
87
 
80
88
  # Reset our state
@@ -15,6 +15,30 @@ module Meterpreter
15
15
  ###
16
16
  class PacketResponseWaiter
17
17
 
18
+ # Arbitrary argument to {#completion_routine}
19
+ #
20
+ # @return [Object,nil]
21
+ attr_accessor :completion_param
22
+
23
+ # A callback to be called when this waiter is notified of a packet's
24
+ # arrival. If not nil, this will be called with the response packet as first
25
+ # parameter and {#completion_param} as the second.
26
+ #
27
+ # @return [Proc,nil]
28
+ attr_accessor :completion_routine
29
+
30
+ # @return [ConditionVariable]
31
+ attr_accessor :cond
32
+
33
+ # @return [Mutex]
34
+ attr_accessor :mutex
35
+
36
+ # @return [Packet]
37
+ attr_accessor :response
38
+
39
+ # @return [Fixnum] request ID to wait for
40
+ attr_accessor :rid
41
+
18
42
  #
19
43
  # Initializes a response waiter instance for the supplied request
20
44
  # identifier.
@@ -27,7 +51,8 @@ class PacketResponseWaiter
27
51
  self.completion_routine = completion_routine
28
52
  self.completion_param = completion_param
29
53
  else
30
- self.done = false
54
+ self.mutex = Mutex.new
55
+ self.cond = ConditionVariable.new
31
56
  end
32
57
  end
33
58
 
@@ -42,41 +67,37 @@ class PacketResponseWaiter
42
67
  #
43
68
  # Notifies the waiter that the supplied response packet has arrived.
44
69
  #
70
+ # @param response [Packet]
71
+ # @return [void]
45
72
  def notify(response)
46
- self.response = response
47
-
48
73
  if (self.completion_routine)
74
+ self.response = response
49
75
  self.completion_routine.call(response, self.completion_param)
50
76
  else
51
- self.done = true
77
+ self.mutex.synchronize do
78
+ self.response = response
79
+ self.cond.signal
80
+ end
52
81
  end
53
82
  end
54
83
 
55
84
  #
56
- # Waits for a given time interval for the response packet to arrive.
57
- # If the interval is -1 we can wait forever.
85
+ # Wait for a given time interval for the response packet to arrive.
58
86
  #
87
+ # @param interval [Fixnum,nil] number of seconds to wait, or nil to wait
88
+ # forever
89
+ # @return [Packet,nil] the response, or nil if the interval elapsed before
90
+ # receiving one
59
91
  def wait(interval)
60
- if( interval and interval == -1 )
61
- while(not self.done)
62
- ::IO.select(nil, nil, nil, 0.1)
63
- end
64
- else
65
- begin
66
- Timeout.timeout(interval) {
67
- while(not self.done)
68
- ::IO.select(nil, nil, nil, 0.1)
69
- end
70
- }
71
- rescue Timeout::Error
72
- self.response = nil
92
+ interval = nil if interval and interval == -1
93
+ self.mutex.synchronize do
94
+ if self.response.nil?
95
+ self.cond.wait(self.mutex, interval)
73
96
  end
74
97
  end
75
98
  return self.response
76
99
  end
77
100
 
78
- attr_accessor :rid, :done, :response # :nodoc:
79
- attr_accessor :completion_routine, :completion_param # :nodoc:
80
101
  end
81
102
 
82
103
  end; end; end
@@ -29,7 +29,9 @@ class Console::CommandDispatcher::Android
29
29
  'device_shutdown' => 'Shutdown device',
30
30
  'send_sms' => 'Sends SMS from target session',
31
31
  'wlan_geolocate' => 'Get current lat-long using WLAN information',
32
- 'interval_collect' => 'Manage interval collection capabilities'
32
+ 'interval_collect' => 'Manage interval collection capabilities',
33
+ 'activity_start' => 'Start an Android activity from a Uri string',
34
+ 'set_audio_mode' => 'Set Ringer Mode'
33
35
  }
34
36
 
35
37
  reqs = {
@@ -41,7 +43,9 @@ class Console::CommandDispatcher::Android
41
43
  'device_shutdown' => ['device_shutdown'],
42
44
  'send_sms' => ['send_sms'],
43
45
  'wlan_geolocate' => ['wlan_geolocate'],
44
- 'interval_collect' => ['interval_collect']
46
+ 'interval_collect' => ['interval_collect'],
47
+ 'activity_start' => ['activity_start'],
48
+ 'set_audio_mode' => ['set_audio_mode']
45
49
  }
46
50
 
47
51
  # Ensure any requirements of the command are met
@@ -151,6 +155,36 @@ class Console::CommandDispatcher::Android
151
155
  end
152
156
  end
153
157
 
158
+ def cmd_set_audio_mode(*args)
159
+ help = false
160
+ mode = 1
161
+ set_audio_mode_opts = Rex::Parser::Arguments.new(
162
+ '-h' => [ false, "Help Banner" ],
163
+ '-m' => [ true, "Set Mode - (0 - Off, 1 - Normal, 2 - Max) (Default: '#{mode}')"]
164
+ )
165
+
166
+ set_audio_mode_opts.parse(args) do |opt, _idx, val|
167
+ case opt
168
+ when '-h'
169
+ help = true
170
+ when '-m'
171
+ mode = val.to_i
172
+ else
173
+ help = true
174
+ end
175
+ end
176
+
177
+ if help || mode < 0 || mode > 2
178
+ print_line('Usage: set_audio_mode [options]')
179
+ print_line('Set Ringer mode.')
180
+ print_line(set_audio_mode_opts.usage)
181
+ return
182
+ end
183
+
184
+ client.android.set_audio_mode(mode)
185
+ print_status("Ringer mode was changed to #{mode}!")
186
+ end
187
+
154
188
  def cmd_dump_sms(*args)
155
189
  path = "sms_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
156
190
  dump_sms_opts = Rex::Parser::Arguments.new(
@@ -457,7 +491,7 @@ class Console::CommandDispatcher::Android
457
491
  end
458
492
  end
459
493
 
460
- if dest.blank? || body.blank?
494
+ if dest.to_s.empty? || body.to_s.empty?
461
495
  print_error("You must enter both a destination address -d and the SMS text body -t")
462
496
  print_error('e.g. send_sms -d +351961234567 -t "GREETINGS PROFESSOR FALKEN."')
463
497
  print_line(send_sms_opts.usage)
@@ -509,7 +543,7 @@ class Console::CommandDispatcher::Android
509
543
  wlan_list << [mac, ssid, ss.to_s]
510
544
  end
511
545
 
512
- if wlan_list.blank?
546
+ if wlan_list.to_s.empty?
513
547
  print_error("Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.")
514
548
  return
515
549
  end
@@ -528,6 +562,22 @@ class Console::CommandDispatcher::Android
528
562
  end
529
563
  end
530
564
 
565
+ def cmd_activity_start(*args)
566
+ if (args.length < 1)
567
+ print_line("Usage: activity_start <uri>\n")
568
+ print_line("Start an Android activity from a uri")
569
+ return
570
+ end
571
+
572
+ uri = args[0]
573
+ result = client.android.activity_start(uri)
574
+ if result.nil?
575
+ print_status("Intent started")
576
+ else
577
+ print_error("Error: #{result}")
578
+ end
579
+ end
580
+
531
581
  #
532
582
  # Name for this dispatcher
533
583
  #
@@ -816,16 +816,18 @@ class Console::CommandDispatcher::Core
816
816
  end
817
817
 
818
818
  @@migrate_opts = Rex::Parser::Arguments.new(
819
- '-p' => [true, 'Writable path - Linux only (eg. /tmp).'],
820
- '-t' => [true, 'The number of seconds to wait for migration to finish (default: 60).'],
821
- '-h' => [false, 'Help menu.']
819
+ '-P' => [true, 'PID to migrate to.'],
820
+ '-N' => [true, 'Process name to migrate to.'],
821
+ '-p' => [true, 'Writable path - Linux only (eg. /tmp).'],
822
+ '-t' => [true, 'The number of seconds to wait for migration to finish (default: 60).'],
823
+ '-h' => [false, 'Help menu.']
822
824
  )
823
825
 
824
826
  def cmd_migrate_help
825
827
  if client.platform =~ /linux/
826
- print_line('Usage: migrate <pid> [-p writable_path] [-t timeout]')
828
+ print_line('Usage: migrate <<pid> | -P <pid> | -N <name>> [-p writable_path] [-t timeout]')
827
829
  else
828
- print_line('Usage: migrate <pid> [-t timeout]')
830
+ print_line('Usage: migrate <<pid> | -P <pid> | -N <name>> [-t timeout]')
829
831
  end
830
832
  print_line
831
833
  print_line('Migrates the server instance to another process.')
@@ -840,29 +842,53 @@ class Console::CommandDispatcher::Core
840
842
  # platforms a path for the unix domain socket used for IPC.
841
843
  # @return [void]
842
844
  def cmd_migrate(*args)
843
- if args.length == 0 || args.include?('-h')
845
+ if args.length == 0 || args.any? { |arg| %w(-h --pid --name).include? arg }
844
846
  cmd_migrate_help
845
847
  return true
846
848
  end
847
849
 
848
- pid = args[0].to_i
849
- if pid == 0
850
- print_error('A process ID must be specified, not a process name')
851
- return
852
- end
853
-
850
+ pid = nil
854
851
  writable_dir = nil
855
852
  opts = {
856
853
  timeout: nil
857
854
  }
858
855
 
859
- @@transport_opts.parse(args) do |opt, idx, val|
856
+ @@migrate_opts.parse(args) do |opt, idx, val|
860
857
  case opt
861
858
  when '-t'
862
859
  opts[:timeout] = val.to_i
863
860
  when '-p'
864
861
  writable_dir = val
862
+ when '-P'
863
+ unless val =~ /^\d+$/
864
+ print_error("Not a PID: #{val}")
865
+ return
866
+ end
867
+ pid = val.to_i
868
+ when '-N'
869
+ if val.to_s.empty?
870
+ print_error("No process name provided")
871
+ return
872
+ end
873
+ # this will migrate to the first process with a matching name
874
+ unless (process = client.sys.process.processes.find { |p| p['name'] == val })
875
+ print_error("Could not find process name #{val}")
876
+ return
877
+ end
878
+ pid = process['pid']
879
+ end
880
+ end
881
+
882
+ unless pid
883
+ unless (pid = args.first)
884
+ print_error('A process ID or name argument must be provided')
885
+ return
886
+ end
887
+ unless pid =~ /^\d+$/
888
+ print_error("Not a PID: #{pid}")
889
+ return
865
890
  end
891
+ pid = pid.to_i
866
892
  end
867
893
 
868
894
  begin
@@ -52,6 +52,7 @@ class Console::CommandDispatcher::Stdapi::Fs
52
52
  'cat' => 'Read the contents of a file to the screen',
53
53
  'cd' => 'Change directory',
54
54
  'del' => 'Delete the specified file',
55
+ 'dir' => 'List files (alias for ls)',
55
56
  'download' => 'Download a file or directory',
56
57
  'edit' => 'Edit a file',
57
58
  'getlwd' => 'Print local working directory',
@@ -73,6 +74,7 @@ class Console::CommandDispatcher::Stdapi::Fs
73
74
  'cat' => [],
74
75
  'cd' => ['stdapi_fs_chdir'],
75
76
  'del' => ['stdapi_fs_rm'],
77
+ 'dir' => ['stdapi_fs_stat', 'stdapi_fs_ls'],
76
78
  'download' => [],
77
79
  'edit' => [],
78
80
  'getlwd' => [],
@@ -598,6 +600,12 @@ class Console::CommandDispatcher::Stdapi::Fs
598
600
  return true
599
601
  end
600
602
 
603
+ #
604
+ # Alias the ls command to dir, for those of us who have windows muscle-memory
605
+ #
606
+ alias cmd_dir cmd_ls
607
+
608
+
601
609
  #
602
610
  # Make one or more directory.
603
611
  #
@@ -0,0 +1,7 @@
1
+ # -*- coding: binary -*-
2
+ #
3
+ # Support for the ADB android debugging protocol
4
+ #
5
+
6
+ require 'rex/proto/adb/client'
7
+ require 'rex/proto/adb/message'
@@ -0,0 +1,39 @@
1
+ # -*- coding: binary -*-
2
+
3
+ ##
4
+ # ADB protocol support
5
+ ##
6
+
7
+ require 'rex/proto/adb/message'
8
+
9
+ module Rex
10
+ module Proto
11
+ module ADB
12
+
13
+ class Client
14
+
15
+ def initialize(sock, opts = {})
16
+ @sock = sock
17
+ @opts = opts
18
+ @local_id_counter = 0x0a
19
+ end
20
+
21
+ def connect
22
+ ADB::Message::Connect.new.send_recv(@sock)
23
+ end
24
+
25
+ def exec_cmd(cmd)
26
+ local_id = @local_id_counter += 1
27
+ response = ADB::Message::Open.new(local_id, "shell:"+cmd).send_recv(@sock)
28
+ ADB::Message::Close.new(local_id, response.arg0).send_recv(@sock)
29
+ end
30
+
31
+ def read_message
32
+ ADB::Message.read(@sock)
33
+ end
34
+
35
+ end # Client
36
+
37
+ end # ADB
38
+ end # Proto
39
+ end # Rex
@@ -0,0 +1,164 @@
1
+ # -*- coding: binary -*-
2
+
3
+ ##
4
+ # ADB protocol support
5
+ ##
6
+
7
+ module Rex
8
+ module Proto
9
+ module ADB
10
+
11
+ # A Message for the ADB protocol. For documentation see:
12
+ # https://android.googlesource.com/platform/system/core/+/master/adb/protocol.txt
13
+ class Message
14
+
15
+ WORD_WIDTH = 4 # bytes
16
+ WORD_PACK = 'L<'
17
+
18
+ attr_accessor :command
19
+ attr_accessor :arg0
20
+ attr_accessor :arg1
21
+ attr_accessor :data
22
+
23
+ def initialize(arg0, arg1, data)
24
+ self.command = self.class::COMMAND if defined?(self.class::COMMAND)
25
+ self.arg0 = arg0
26
+ self.arg1 = arg1
27
+ self.data = data + "\0"
28
+ end
29
+
30
+ def data_check
31
+ # this check is implemented in adb/transport.cpp, in the send_packet method.
32
+ # it is not crc32 as the docs make it appear, it is just a 32bit sum.
33
+ data.bytes.inject(&:+) & 0xffffffff
34
+ end
35
+
36
+ def magic
37
+ command_word ^ 0xffffffff
38
+ end
39
+
40
+ def command_word
41
+ command.unpack(WORD_PACK)[0]
42
+ end
43
+
44
+ def send_recv(socket)
45
+ socket.print self.serialize
46
+ Message.read socket
47
+ end
48
+
49
+ def serialize
50
+ [
51
+ command_word,
52
+ arg0,
53
+ arg1,
54
+ data.bytes.length,
55
+ data_check,
56
+ magic
57
+ ].pack(WORD_PACK+'*') + data
58
+ end
59
+
60
+ def to_s
61
+ [
62
+ "command=#{command}",
63
+ "arg0=0x#{arg0.to_s(16)}",
64
+ "arg1=0x#{arg1.to_s(16)}",
65
+ "data=#{data}"
66
+ ].join("\n")
67
+ end
68
+
69
+ def self.read(socket)
70
+ header = socket.recvfrom(6 * WORD_WIDTH)[0]
71
+ command = header[0, WORD_WIDTH]
72
+ arg0 = header[WORD_WIDTH, WORD_WIDTH].unpack(WORD_PACK)[0]
73
+ arg1 = header[WORD_WIDTH*2, WORD_WIDTH].unpack(WORD_PACK)[0]
74
+ payload_len = header[WORD_WIDTH*3, WORD_WIDTH].unpack(WORD_PACK)[0]
75
+ payload = socket.recvfrom(payload_len)[0]
76
+
77
+ klass = MESSAGE_TYPES.find { |klass| klass::COMMAND == command }
78
+ if klass.nil?
79
+ raise "Invalid adb command: #{command}"
80
+ end
81
+
82
+ message = klass.allocate
83
+ message.command = command
84
+ message.arg0 = arg0
85
+ message.arg1 = arg1
86
+ message.data = payload
87
+ message
88
+ end
89
+
90
+ #
91
+ # Subclasses inside Message:: namespace for specific message types
92
+ #
93
+
94
+ class Connect < Message
95
+ COMMAND = "CNXN"
96
+ DEFAULT_VERSION = 0x01000000
97
+ DEFAULT_MAXDATA = 4096
98
+ DEFAULT_IDENTITY = "host::"
99
+
100
+ def initialize(version=DEFAULT_VERSION,
101
+ maxdata=DEFAULT_MAXDATA,
102
+ system_identity_string=DEFAULT_IDENTITY)
103
+ super
104
+ end
105
+ end
106
+
107
+ class Auth < Message
108
+ COMMAND = "AUTH"
109
+ TYPE_TOKEN = 1
110
+ TYPE_SIGNATURE = 2
111
+
112
+ def initialize(type, data)
113
+ super(type, 0, data)
114
+ end
115
+ end
116
+
117
+ class Open < Message
118
+ COMMAND = "OPEN"
119
+
120
+ def initialize(local_id, destination)
121
+ super(local_id, 0, destination)
122
+ end
123
+ end
124
+
125
+ class Ready < Message
126
+ COMMAND = "OKAY"
127
+
128
+ def initialize(local_id, remote_id)
129
+ super(local_id, remote_id, "")
130
+ end
131
+ end
132
+
133
+ class Write < Message
134
+ COMMAND = "WRTE"
135
+
136
+ def initialize(local_id, remote_id, data)
137
+ super
138
+ end
139
+ end
140
+
141
+ class Close < Message
142
+ COMMAND = "CLSE"
143
+
144
+ def initialize(local_id, remote_id)
145
+ super(local_id, remote_id, "")
146
+ end
147
+ end
148
+
149
+ class Sync < Message
150
+ COMMAND = "SYNC"
151
+
152
+ def initialize(online, sequence)
153
+ super(online, sequence, "")
154
+ end
155
+ end
156
+
157
+ # Avoid a dependency on Rails's nice Class#subclasses
158
+ MESSAGE_TYPES = [Connect, Auth, Open, Ready, Write, Close, Sync]
159
+
160
+ end # Message
161
+
162
+ end # ADB
163
+ end # Proto
164
+ end # Rex