Skip to content

Commit a3e196e

Browse files
committed
Support arbitrary external command_stager exploits
So much done, so much more to do.
1 parent 1306065 commit a3e196e

File tree

5 files changed

+231
-75
lines changed

5 files changed

+231
-75
lines changed

lib/msf/core/modules/external.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# -*- coding: binary -*-
2+
# Namespace for loading external Metasploit modules
3+
4+
module Msf::Modules::External
5+
6+
end
7+
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# -*- coding: binary -*-
2+
require 'msf/core/modules/external'
3+
require 'msf/core/modules/external/message'
4+
require 'open3'
5+
6+
class Msf::Modules::External::Bridge
7+
8+
attr_reader :path, :running
9+
10+
def meta
11+
@meta ||= describe
12+
end
13+
14+
def run(datastore)
15+
unless self.running
16+
m = Msf::Modules::External::Message.new(:run)
17+
m.params = datastore.dup
18+
send(m)
19+
self.running = true
20+
end
21+
end
22+
23+
def get_status
24+
if self.running
25+
n = receive_notification
26+
if n && n['params']
27+
n['params']
28+
else
29+
close_ios
30+
self.running = false
31+
n['response'] if n
32+
end
33+
end
34+
end
35+
36+
def initialize(module_path)
37+
self.running = false
38+
self.path = module_path
39+
end
40+
41+
protected
42+
43+
attr_writer :path, :running
44+
attr_accessor :ios
45+
46+
def describe
47+
resp = send_receive(Msf::Modules::External::Message.new(:describe))
48+
close_ios
49+
resp['response']
50+
end
51+
52+
# XXX TODO non-blocking writes, check write lengths, non-blocking JSON parse loop read
53+
54+
def send_receive(message)
55+
send(message)
56+
read_json(message.id, self.ios[1])
57+
end
58+
59+
def send(message)
60+
input, output, status = ::Open3.popen3([self.path, self.path])
61+
self.ios = [input, output, status]
62+
case Rex::ThreadSafe.select(nil, [input], nil, 0.1)
63+
when nil
64+
raise "Cannot run module #{self.path}"
65+
when [[], [input], []]
66+
m = message.to_json
67+
write_message(input, m)
68+
else
69+
raise "Error running module #{self.path}"
70+
end
71+
end
72+
73+
def receive_notification
74+
input, output, status = self.ios
75+
case Rex::ThreadSafe.select([output], nil, nil, 10)
76+
when nil
77+
nil
78+
when [[output], [], []]
79+
read_json(nil, output)
80+
end
81+
end
82+
83+
def write_message(fd, json)
84+
fd.write(json)
85+
end
86+
87+
def read_json(id, fd)
88+
resp = fd.readpartial(10_000)
89+
JSON.parse(resp)
90+
end
91+
92+
def close_ios
93+
input, output, status = self.ios
94+
[input, output].each {|fd| fd.close rescue nil} # Yeah, yeah. I know.
95+
end
96+
end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# -*- coding: binary -*-
2+
require 'msf/core/modules/external'
3+
require 'base64'
4+
require 'json'
5+
6+
class Msf::Modules::External::Message
7+
8+
attr_reader :method, :id
9+
attr_accessor :params
10+
11+
def initialize(m)
12+
self.method = m
13+
self.params = {}
14+
self.id = Base64.strict_encode64(SecureRandom.random_bytes(16))
15+
end
16+
17+
def to_json
18+
JSON.generate({jsonrpc: '2.0', id: self.id, method: self.method, params: self.params.to_h})
19+
end
20+
21+
protected
22+
23+
attr_writer :method, :id
24+
end

lib/msf/core/modules/external/shim.rb

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# -*- coding: binary -*-
2+
require 'msf/core/modules/external'
3+
require 'msf/core/modules/external/bridge'
4+
5+
class Msf::Modules::External::Shim
6+
def self.generate(module_path)
7+
mod = Msf::Modules::External::Bridge.new(module_path)
8+
case mod.meta['type']
9+
when 'remote_exploit.cmd_stager.wget'
10+
s = remote_exploit_cmd_stager(mod)
11+
File.open('/tmp/module', 'w') {|f| f.write(s)}
12+
s
13+
end
14+
end
15+
16+
def self.remote_exploit_cmd_stager(mod)
17+
%Q|
18+
require 'msf/core/modules/external/bridge'
19+
20+
class MetasploitModule < Msf::Exploit::Remote
21+
Rank = ExcellentRanking
22+
23+
include Msf::Exploit::CmdStager
24+
25+
def initialize(info = {})
26+
super(update_info(info,
27+
'Name' => #{mod.meta['name'].dump},
28+
'Description' => #{mod.meta['description'].dump},
29+
'Author' =>
30+
[
31+
#{mod.meta['authors'].map(&:dump).join(', ')}
32+
],
33+
'License' => MSF_LICENSE,
34+
'References' =>
35+
[
36+
#{mod.meta['references'].map do |r|
37+
"[#{r['type'].upcase.dump}, #{r['ref'].dump}]"
38+
end.join(', ')}
39+
],
40+
'DisclosureDate' => #{mod.meta['date'].dump},
41+
'Privileged' => #{mod.meta['privileged'].inspect},
42+
'Platform' => [#{mod.meta['targets'].map{|t| t['platform'].dump}.uniq.join(', ')}],
43+
'Payload' =>
44+
{
45+
'DisableNops' => true
46+
},
47+
'Targets' =>
48+
[
49+
#{mod.meta['targets'].map do |t|
50+
%Q^[#{t['platform'].dump} + ' ' + #{t['arch'].dump},
51+
{'Arch' => ARCH_#{t['arch'].upcase}, 'Platform' => #{t['platform'].dump} }]^
52+
end.join(', ')}
53+
],
54+
'DefaultTarget' => 0,
55+
'DefaultOptions' => { 'WfsDelay' => 5 }
56+
))
57+
58+
register_options([
59+
#{mod.meta['options'].map do |n, o|
60+
"Opt#{o['type'].capitalize}.new(#{n.dump},
61+
[#{o['required']}, #{o['description'].dump}, #{o['default'].inspect}])"
62+
end.join(', ')}
63+
], self.class)
64+
end
65+
66+
def execute_command(cmd, opts)
67+
mod = Msf::Modules::External::Bridge.new(#{mod.path.dump})
68+
mod.run(datastore.merge(command: cmd))
69+
wait_status(mod)
70+
true
71+
end
72+
73+
def exploit
74+
print_status("Exploiting...")
75+
execute_cmdstager({:flavor => :wget})
76+
end
77+
78+
def wait_status(mod)
79+
while mod.running
80+
m = mod.get_status
81+
if m
82+
case m['level']
83+
when 'error'
84+
print_error m['message']
85+
when 'warning'
86+
print_warning m['message']
87+
when 'good'
88+
print_good m['message']
89+
when 'info'
90+
print_status m['message']
91+
when 'debug'
92+
vprint_status m['message']
93+
else
94+
print_status m['message']
95+
end
96+
end
97+
end
98+
end
99+
end
100+
|
101+
end
102+
end

lib/msf/core/modules/loader/executable.rb

Lines changed: 2 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require 'msf/core/modules/loader'
44
require 'msf/core/modules/loader/base'
5+
require 'msf/core/modules/external/shim'
56

67
# Concerns loading executables from a directory as modules
78
class Msf::Modules::Loader::Executable < Msf::Modules::Loader::Base
@@ -81,80 +82,6 @@ def read_module_content(parent_path, type, module_reference_name)
8182
load_error(full_path, Errno::ENOENT.new)
8283
return ''
8384
end
84-
%Q|
85-
require 'msf/core'
86-
87-
class MetasploitModule < Msf::Exploit::Remote
88-
Rank = ExcellentRanking
89-
90-
include Msf::Exploit::CmdStager
91-
92-
def initialize(info = {})
93-
super(update_info(info,
94-
'Name' => 'Haraka Remote Command Injection',
95-
'Description' => %q{
96-
Some Linksys E-Series Routers are vulnerable to an unauthenticated OS command
97-
injection. This vulnerability was used from the so-called "TheMoon" worm. There
98-
are many Linksys systems that are potentially vulnerable, including E4200, E3200, E3000,
99-
E2500, E2100L, E2000, E1550, E1500, E1200, E1000, and E900. This module was tested
100-
successfully against an E1500 v1.0.5.
101-
},
102-
'Author' =>
103-
[
104-
'Johannes Ullrich', #worm discovery
105-
'Rew', # original exploit
106-
'infodox', # another exploit
107-
'Michael Messner <devnull[at]s3cur1ty.de>', # Metasploit module
108-
'juan vazquez' # minor help with msf module
109-
],
110-
'License' => MSF_LICENSE,
111-
'References' =>
112-
[
113-
[ 'EDB', '31683' ],
114-
[ 'BID', '65585' ],
115-
[ 'OSVDB', '103321' ],
116-
[ 'PACKETSTORM', '125253' ],
117-
[ 'PACKETSTORM', '125252' ],
118-
[ 'URL', 'https://isc.sans.edu/diary/Linksys+Worm+%22TheMoon%22+Summary%3A+What+we+know+so+far/17633' ],
119-
[ 'URL', 'https://isc.sans.edu/forums/diary/Linksys+Worm+TheMoon+Captured/17630' ]
120-
],
121-
'DisclosureDate' => 'Feb 13 2014',
122-
'Privileged' => true,
123-
'Platform' => %w{ linux unix },
124-
'Payload' =>
125-
{
126-
'DisableNops' => true
127-
},
128-
'Targets' =>
129-
[
130-
[ 'Linux x64 Payload',
131-
{
132-
'Arch' => ARCH_X64,
133-
'Platform' => 'linux'
134-
}
135-
]
136-
],
137-
'DefaultTarget' => 0,
138-
'DefaultOptions' => { 'WfsDelay' => 5 }
139-
))
140-
end
141-
142-
def execute_command(cmd, opts)
143-
to = 'admin@arnold'
144-
rhost = '192.168.244.130'
145-
`#{module_path(parent_path, type, module_reference_name)} -c "\#{cmd}" -t \#{to} -m \#{rhost}`
146-
true
147-
end
148-
149-
def exploit
150-
print_status("Trying to access the vulnerable URL...")
151-
152-
print_status("Exploiting...")
153-
execute_cmdstager({:flavor => :wget})
154-
end
155-
156-
157-
end
158-
|
85+
Msf::Modules::External::Shim.generate(full_path)
15986
end
16087
end

0 commit comments

Comments
 (0)