Skip to content

Commit 1f2313d

Browse files
committed
Land rapid7#4054, @jhart-r7's SunRPC lib and module cleanup
2 parents 4ecb892 + fb4b654 commit 1f2313d

File tree

8 files changed

+91
-62
lines changed

8 files changed

+91
-62
lines changed

lib/msf/core/exploit/sunrpc.rb

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,26 @@ def initialize(info = {})
3838

3939
register_advanced_options(
4040
[
41-
# XXX: Use portmapper to do call - Direct portmap to make the request to the program portmap_req
41+
OptInt.new('TIMEOUT', [true, 'Number of seconds to wait for responses to RPC calls', 5])
42+
# XXX: Use portmapper to do call - Direct portmap to make the request to the program portmap_req
4243
], Msf::Exploit::Remote::SunRPC)
4344

4445
register_options(
4546
[
46-
# XXX: XPORT
47+
# XXX: XPORT
4748
Opt::RHOST,
4849
Opt::RPORT(111),
4950
], Msf::Exploit::Remote::SunRPC
5051
)
5152
end
5253

53-
def sunrpc_create(protocol, program, version)
54+
def sunrpc_create(protocol, program, version, time_out = timeout)
5455
self.rpcobj = Rex::Proto::SunRPC::Client.new(
5556
:rhost => rhost,
5657
:rport => rport.to_i,
5758
:proto => protocol,
5859
:program => program,
60+
:timeout => time_out,
5961
:version => version,
6062
:context => {
6163
'Msf' => framework,
@@ -68,26 +70,23 @@ def sunrpc_create(protocol, program, version)
6870
end
6971

7072
ret = rpcobj.create
71-
return print_error("#{rhost}:#{rport} - SunRPC - No response to Portmap request") unless ret
73+
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - No response to Portmap request" unless ret
7274

7375
arr = XDR.decode!(ret, Integer, Integer, Integer, String, Integer, Integer)
7476
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
7577
err = "#{rhost}:#{rport} - SunRPC - Portmap request failed: "
7678
err << 'Message not accepted' if arr[1] != MSG_ACCEPTED
7779
err << 'RPC did not execute' if arr[4] != SUCCESS
7880
err << 'Program not available' if arr[5] == 0
79-
print_error(err)
80-
return nil
81+
raise ::Rex::Proto::SunRPC::RPCError, err
8182
end
8283

8384
rpcobj.pport = arr[5]
84-
#progname = progresolv(rpcobj.program)
85-
#print_status("#{rhost} - SunRPC Found #{progname} on #{protocol} port #{rpcobj.pport}")
8685
end
8786

88-
def sunrpc_call(proc, buf, timeout=20)
87+
def sunrpc_call(proc, buf, timeout = timeout)
8988
ret = rpcobj.call(proc, buf, timeout)
90-
return print_error("#{rhost}:#{rport} - SunRPC - No response to SunRPC call for procedure: #{proc}") unless ret
89+
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - No response to SunRPC call for procedure: #{proc}" unless ret
9190

9291
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
9392
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS
@@ -105,8 +104,7 @@ def sunrpc_call(proc, buf, timeout=20)
105104
else err << "Unknown Error"
106105
end
107106
end
108-
print_error("#{rhost}:#{rport} - SunRPC - #{err}")
109-
return nil
107+
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - #{err}"
110108
end
111109
return ret
112110
end
@@ -142,8 +140,7 @@ def portmap_qry()
142140
when GARBAGE_ARGS then err << "Garbage Arguments"
143141
else err << "Unknown Error"
144142
end
145-
print_error("#{rhost}:#{rport} - SunRPC - #{err}")
146-
return nil
143+
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - #{err}"
147144
end
148145

149146
return ret
@@ -162,6 +159,11 @@ def progresolv(number)
162159
return "UNKNOWN-#{number}"
163160
end
164161

162+
# Returns the time that this module will wait for RPC responses, in seconds
163+
def timeout
164+
datastore['TIMEOUT']
165+
end
166+
165167
# Used to track the last SunRPC context
166168
attr_accessor :rpcobj
167169
end

lib/rex/proto/sunrpc/client.rb

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,21 @@ module Rex
66
module Proto
77
module SunRPC
88

9+
class RPCError < ::StandardError
10+
def initialize(msg = 'RPC operation failed')
11+
super
12+
@msg = msg
13+
end
14+
15+
def to_s
16+
@msg
17+
end
18+
end
19+
920
class RPCTimeout < ::Interrupt
10-
def initialize(msg = 'Operation timed out.')
11-
@msg = msg
12-
end
21+
def initialize(msg = 'Operation timed out.')
22+
@msg = msg
23+
end
1324

1425
def to_s
1526
@msg

modules/auxiliary/admin/sunrpc/solaris_kcms_readfile.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ def run
127127
# done
128128
sunrpc_destroy
129129

130+
rescue Timeout::Error, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Rex::Proto::SunRPC::RPCError => e
131+
print_error(e.to_s)
130132
rescue ::Rex::Proto::SunRPC::RPCTimeout
131133
print_warning 'Warning: ' + $!
132134
print_warning 'Exploit may or may not have succeeded.'

modules/auxiliary/scanner/misc/sunrpc_portmapper.rb

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,74 +6,82 @@
66
require 'msf/core'
77

88
class Metasploit3 < Msf::Auxiliary
9-
109
include Msf::Exploit::Remote::SunRPC
1110
include Msf::Auxiliary::Report
1211
include Msf::Auxiliary::Scanner
1312

1413
def initialize
1514
super(
16-
'Name' => 'SunRPC Portmap Program Enumerator',
17-
'Description' => %q{
18-
This module calls the target portmap service and enumerates all
19-
program entries and their running port numbers.
20-
},
21-
'Author' => ['<tebo[at]attackresearch.com>'],
22-
'References' =>
15+
'Name' => 'SunRPC Portmap Program Enumerator',
16+
'Description' => '
17+
This module calls the target portmap service and enumerates all program
18+
entries and their running port numbers.
19+
',
20+
'Author' => ['<tebo[at]attackresearch.com>'],
21+
'References' =>
2322
[
24-
['URL', 'http://www.ietf.org/rfc/rfc1057.txt'],
23+
['URL', 'http://www.ietf.org/rfc/rfc1057.txt']
2524
],
2625
'License' => MSF_LICENSE
2726
)
28-
29-
register_options([], self.class)
3027
end
3128

3229
def run_host(ip)
33-
vprint_status "#{ip}:#{rport} - SunRPC - Enumerating programs"
30+
peer = "#{ip}:#{rport}"
31+
vprint_status "#{peer} - SunRPC - Enumerating programs"
3432

3533
begin
3634
program = 100000
3735
progver = 2
3836
procedure = 4
3937

4038
sunrpc_create('udp', program, progver)
41-
sunrpc_authnull()
39+
sunrpc_authnull
4240
resp = sunrpc_call(procedure, "")
4341

44-
progs = resp[3,1].unpack('C')[0]
42+
progs = resp[3, 1].unpack('C')[0]
4543
maps = []
4644
if (progs == 0x01)
47-
print_good("#{ip}:#{rport} - Programs available")
48-
while XDR.decode_int!(resp) == 1 do
49-
map = XDR.decode!(resp, Integer, Integer, Integer, Integer)
50-
maps << map
45+
while XDR.decode_int!(resp) == 1
46+
maps << XDR.decode!(resp, Integer, Integer, Integer, Integer)
5147
end
5248
end
5349
sunrpc_destroy
50+
return if maps.empty?
51+
vprint_good("#{peer} - Found #{maps.size} programs available")
52+
53+
table = Rex::Ui::Text::Table.new(
54+
'Header' => "SunRPC Programs for #{ip}",
55+
'Indent' => 1,
56+
'Columns' => %w(Name Number Version Port Protocol)
57+
)
5458

55-
lines = []
5659
maps.each do |map|
57-
prog, vers, prot, port = map[0,4]
58-
prot = if prot == 0x06; "tcp"
59-
elsif prot == 0x11; "udp"
60-
end
61-
lines << "\t#{progresolv(prog)} - #{port}/#{prot}"
60+
prog, vers, prot_num, port = map[0, 4]
61+
thing = "RPC Program ##{prog} v#{vers} on port #{port} w/ protocol #{prot_num}"
62+
if prot_num == 0x06
63+
proto = 'tcp'
64+
elsif prot_num == 0x11
65+
proto = 'udp'
66+
else
67+
print_error("#{peer}: unknown protocol number for #{thing}")
68+
next
69+
end
6270

71+
resolved = progresolv(prog)
72+
table << [ resolved, prog, vers, port, proto ]
6373
report_service(
64-
:host => ip,
65-
:port => port,
66-
:proto => prot,
67-
:name => progresolv(prog),
68-
:info => "Prog: #{prog} Version: #{vers} - via portmapper"
74+
host: ip,
75+
port: port,
76+
proto: proto,
77+
name: resolved,
78+
info: "Prog: #{prog} Version: #{vers} - via portmapper"
6979
)
7080
end
7181

72-
# So we don't print a line for every program version
73-
lines.uniq.each {|line| print_line(line)}
74-
75-
rescue ::Rex::Proto::SunRPC::RPCTimeout
82+
print_good(table.to_s)
83+
rescue ::Rex::Proto::SunRPC::RPCTimeout, ::Rex::Proto::SunRPC::RPCError => e
84+
vprint_error(e.to_s)
7685
end
7786
end
78-
7987
end

modules/auxiliary/scanner/nfs/nfsmount.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,12 @@ def run_host(ip)
7676
:update => :unique_data
7777
)
7878
elsif(exports == 0x00)
79-
print_status("#{ip} - No exported directories")
79+
vprint_status("#{ip} - No exported directories")
8080
end
8181

8282
sunrpc_destroy
83-
rescue ::Rex::Proto::SunRPC::RPCTimeout
83+
rescue ::Rex::Proto::SunRPC::RPCTimeout, ::Rex::Proto::SunRPC::RPCError => e
84+
vprint_error(e.to_s)
8485
end
8586
end
8687

modules/exploits/aix/rpc_cmsd_opcode21.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,7 @@ def brute_exploit(brute_target)
8080
print_status("Trying to exploit rpc.cmsd with address 0x%x ..." % brute_target['Ret'])
8181

8282
begin
83-
if (not sunrpc_create('udp', 100068, 4))
84-
fail_with(Failure::Unknown, 'sunrpc_create failed')
85-
end
83+
sunrpc_create('udp', 100068, 4)
8684

8785
# spray the heap a bit (work around powerpc cache issues)
8886
buf = make_nops(1024 - @aixpayload.length)
@@ -105,9 +103,11 @@ def brute_exploit(brute_target)
105103
sunrpc_destroy
106104

107105
rescue Rex::Proto::SunRPC::RPCTimeout
108-
# print_error('RPCTimeout')
106+
vprint_error('RPCTimeout')
107+
rescue Rex::Proto::SunRPC::RPCError => e
108+
vprint_error(e.to_s)
109109
rescue EOFError
110-
# print_error('EOFError')
110+
vprint_error('EOFError')
111111
end
112112
end
113113

modules/exploits/solaris/sunrpc/sadmind_adm_build_path.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,12 @@ def initialize(info = {})
9898
end
9999

100100
def brute_exploit(brute_target)
101-
sunrpc_create('udp', 100232, 10)
101+
begin
102+
sunrpc_create('udp', 100232, 10)
103+
rescue Rex::Proto::SunRPC::RPCTimeout, Rex::Proto::SunRPC::RPCError => e
104+
vprint_error(e.to_s)
105+
return
106+
end
102107

103108
unless @nops
104109
print_status('Creating nop block...')
@@ -145,6 +150,8 @@ def brute_exploit(brute_target)
145150
sunrpc_call(1, request, 2)
146151
rescue Rex::Proto::SunRPC::RPCTimeout
147152
print_status('Server did not respond, this is expected')
153+
rescue Rex::Proto::SunRPC::RPCError => e
154+
print_error(e.to_s)
148155
end
149156

150157
sunrpc_destroy

modules/exploits/windows/emc/networker_format_string.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@ def initialize(info = {})
7474
def exploit
7575

7676
begin
77-
if (not sunrpc_create('tcp', 0x5F3DD, 2))
78-
fail_with(Failure::Unknown, 'sunrpc_create failed')
79-
end
77+
sunrpc_create('tcp', 0x5F3DD, 2)
8078

8179
fs = "%n" * target['Offset']
8280
fs << [target.ret].pack("V") # push esp # ret from MSVCR71.dll

0 commit comments

Comments
 (0)