librex 0.0.32 → 0.0.33
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.
- data/README.markdown +1 -1
- data/lib/rex/exploitation/egghunter.rb +90 -1
- data/lib/rex/exploitation/javascriptosdetect.rb +9 -1
- data/lib/rex/io/ring_buffer.rb.ut.rb +134 -0
- data/lib/rex/io/stream_server.rb +11 -2
- data/lib/rex/parser/nessus_xml.rb +4 -2
- data/lib/rex/parser/nexpose_xml.rb +45 -15
- data/lib/rex/parser/nmap_nokogiri.rb +385 -0
- data/lib/rex/parser/nmap_xml.rb +71 -51
- data/lib/rex/parser/nokogiri_doc_mixin.rb +99 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/file_stat.rb +1 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +1 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb.ut.rb +4 -128
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/mock_magic.rb +146 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb.ut.rb +63 -0
- data/lib/rex/post/meterpreter/packet_dispatcher.rb +8 -2
- data/lib/rex/proto/http/handler/proc.rb +1 -1
- data/lib/rex/proto/http/server.rb +15 -10
- data/lib/rex/socket/parameters.rb +16 -0
- data/lib/rex/socket/ssl_tcp_server.rb +47 -38
- data/lib/rex/ui/text/dispatcher_shell.rb +49 -29
- data/lib/rex/zip.rb +3 -0
- data/lib/rex/zip/archive.rb +13 -89
- data/lib/rex/zip/entry.rb +11 -1
- data/lib/rex/zip/jar.rb +224 -0
- metadata +9 -3
data/README.markdown
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
A non-official re-packaging of the Rex library as a gem for easy of usage of the Metasploit REX framework in a non Metasploit application. I received permission from HDM to create this package.
|
4
4
|
|
5
5
|
Currently based on:
|
6
|
-
SVN Revision:
|
6
|
+
SVN Revision: 12724
|
7
7
|
|
8
8
|
# Credits
|
9
9
|
The Metasploit development team <http://www.metasploit.com>
|
@@ -20,6 +20,7 @@ module Exploitation
|
|
20
20
|
# Checksum code merged to Egghunter by jduck
|
21
21
|
# Conversion to use Metasm by jduck
|
22
22
|
# Startreg code added by corelanc0d3r
|
23
|
+
# Added routine to disable DEP for discovered egg (for win, added by corelanc0d3r)
|
23
24
|
#
|
24
25
|
###
|
25
26
|
class Egghunter
|
@@ -57,7 +58,95 @@ class Egghunter
|
|
57
58
|
end
|
58
59
|
startstub << "\n\t" if startstub.length > 0
|
59
60
|
|
61
|
+
getpointer = ''
|
62
|
+
getsize = ''
|
63
|
+
getpc = ''
|
64
|
+
jmppayload = "jmp edi"
|
65
|
+
|
66
|
+
apireg = (opts[:depreg] || 'esi').downcase
|
67
|
+
apidest = opts[:depdest]
|
68
|
+
depsize = opts[:depsize]
|
69
|
+
|
70
|
+
freeregs = [ "esi", "ebp", "ecx", "ebx" ]
|
71
|
+
|
72
|
+
if opts[:depmethod]
|
73
|
+
|
74
|
+
if freeregs.index(apireg) == nil
|
75
|
+
getpointer << "mov #{freeregs[0]},#{apireg}\n\t"
|
76
|
+
apireg = freeregs[0]
|
77
|
+
end
|
78
|
+
freeregs.delete(apireg)
|
79
|
+
|
80
|
+
if opts[:depmethod].downcase == "copy" || opts[:depmethod].downcase == "copy_size"
|
81
|
+
if apidest
|
82
|
+
if freeregs.index(apidest) == nil
|
83
|
+
getpointer << "mov #{freeregs[0]},#{apidest}\n\t"
|
84
|
+
apidest = freeregs[0]
|
85
|
+
end
|
86
|
+
else
|
87
|
+
getpc = "fldpi\n\tfstenv [esp-0xc]\n\tpop #{freeregs[0]}\n\t"
|
88
|
+
apidest = freeregs[0]
|
89
|
+
end
|
90
|
+
freeregs.delete(apidest)
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
sizereg = freeregs[0]
|
95
|
+
|
96
|
+
if not depsize
|
97
|
+
depsize = payload.length * 2
|
98
|
+
if opts[:depmethod]
|
99
|
+
if opts[:depmethod].downcase == "copy_size"
|
100
|
+
depsize = payload.length
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
blockcnt = 0
|
107
|
+
vpsize = 0
|
108
|
+
blocksize = depsize
|
109
|
+
while blocksize >= 127
|
110
|
+
blocksize = blocksize / 2
|
111
|
+
blockcnt += 1
|
112
|
+
end
|
113
|
+
if blockcnt > 0
|
114
|
+
getsize << "xor #{sizereg},#{sizereg}\n\tadd #{sizereg},0x%02x\n\t" % blocksize
|
115
|
+
vpsize = blocksize
|
116
|
+
depblockcnt = 0
|
117
|
+
while depblockcnt < blockcnt
|
118
|
+
getsize << "add #{sizereg},#{sizereg}\n\t"
|
119
|
+
vpsize += vpsize
|
120
|
+
depblockcnt += 1
|
121
|
+
end
|
122
|
+
delta = depsize - vpsize
|
123
|
+
if delta > 0
|
124
|
+
getsize << "add #{sizereg},0x%02x\n\t" % delta
|
125
|
+
end
|
126
|
+
else
|
127
|
+
getsize << "xor #{sizereg},#{sizereg}\n\tadd #{sizereg},0x%02x\n\t" % depsize
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
case opts[:depmethod].downcase
|
132
|
+
when "virtualprotect"
|
133
|
+
jmppayload = "push esp\n\tpush 0x40\n\t"
|
134
|
+
jmppayload << getsize
|
135
|
+
jmppayload << "push #{sizereg}\n\tpush edi\n\tpush edi\n\tpush #{apireg}\n\tret"
|
136
|
+
when "copy"
|
137
|
+
jmppayload = getpc
|
138
|
+
jmppayload << "push edi\n\tpush #{apidest}\n\tpush #{apidest}\n\tpush #{apireg}\n\tmov edi,#{apidest}\n\tret"
|
139
|
+
when "copy_size"
|
140
|
+
jmppayload = getpc
|
141
|
+
jmppayload << getsize
|
142
|
+
jmppayload << "push #{sizereg}\n\tpush edi\n\tpush #{apidest}\n\tpush #{apidest}\n\tpush #{apireg}\n\tmov edi,#{apidest}\n\tret"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
jmppayload << "\n" if jmppayload.length > 0
|
147
|
+
|
60
148
|
assembly = <<EOS
|
149
|
+
#{getpointer}
|
61
150
|
#{startstub}
|
62
151
|
check_readable:
|
63
152
|
or dx,0xfff
|
@@ -84,7 +173,7 @@ check_for_tag:
|
|
84
173
|
#{checksum}
|
85
174
|
|
86
175
|
; jump to the payload
|
87
|
-
|
176
|
+
#{jmppayload}
|
88
177
|
EOS
|
89
178
|
|
90
179
|
assembled_code = Metasm::Shellcode.assemble(Metasm::Ia32.new, assembly).encode_string
|
@@ -636,6 +636,12 @@ function getVersion(){
|
|
636
636
|
os_flavor = "XP";
|
637
637
|
os_sp = "SP3";
|
638
638
|
break;
|
639
|
+
case "576000":
|
640
|
+
// IE 7.0.6000.16386, Vista Ultimate SP0 English
|
641
|
+
ua_version = "7.0";
|
642
|
+
os_flavor = "Vista";
|
643
|
+
os_sp = "SP0";
|
644
|
+
break;
|
639
645
|
case "5822960":
|
640
646
|
// IE 8.0.6001.18702, XP Professional SP3 Greek
|
641
647
|
case "5818702":
|
@@ -650,6 +656,8 @@ function getVersion(){
|
|
650
656
|
case "5816385":
|
651
657
|
// IE 8.0.7600.16385, Windows 7 English
|
652
658
|
case "5816475":
|
659
|
+
case "5816762":
|
660
|
+
// IE 8.0.7600.16385, Windows 7 English
|
653
661
|
ua_version = "8.0";
|
654
662
|
os_flavor = "7";
|
655
663
|
os_sp = "SP0";
|
@@ -816,7 +824,7 @@ function ua_ver_cmp(ver_a, ver_b) {
|
|
816
824
|
|
817
825
|
a = ver_a.split(".");
|
818
826
|
b = ver_b.split(".");
|
819
|
-
for (i = 0; i < Math.max(a.length, b.length); i++) {
|
827
|
+
for (var i = 0; i < Math.max(a.length, b.length); i++) {
|
820
828
|
// 3.0 == 3
|
821
829
|
if (!b[i]) { b[i] = "0"; }
|
822
830
|
if (!a[i]) { a[i] = "0"; }
|
@@ -0,0 +1,134 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', '..'))
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
require 'rex/socket'
|
6
|
+
require 'rex/io/ring_buffer'
|
7
|
+
|
8
|
+
#
|
9
|
+
# TODO: Mock up the socket so this test doesn't take so long
|
10
|
+
#
|
11
|
+
class Rex::IO::RingBuffer::UnitTest < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def setup
|
14
|
+
server = Rex::Socket.create_tcp_server('LocalPort' => 0)
|
15
|
+
lport = server.getsockname[2]
|
16
|
+
@client = Rex::Socket.create_tcp('PeerHost' => '127.0.0.1', 'PeerPort' => lport)
|
17
|
+
conn = server.accept
|
18
|
+
#server.close
|
19
|
+
|
20
|
+
@r = Rex::IO::RingBuffer.new(conn, {:size => 64})
|
21
|
+
@r.start_monitor
|
22
|
+
end
|
23
|
+
|
24
|
+
def teardown
|
25
|
+
begin
|
26
|
+
@client.close
|
27
|
+
@r.stop_monitor
|
28
|
+
rescue ::Exception
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_single_read_data
|
33
|
+
@client.put("123")
|
34
|
+
@r.wait(0)
|
35
|
+
s,d = @r.read_data
|
36
|
+
|
37
|
+
assert_equal("123", d)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_sequential_read_data
|
41
|
+
@r.clear_data
|
42
|
+
|
43
|
+
s = nil
|
44
|
+
0.upto(10) do |num|
|
45
|
+
@client.put(num.to_s)
|
46
|
+
@r.wait(s)
|
47
|
+
s,d = @r.read_data(s)
|
48
|
+
assert_equal(num.to_s, d)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_wrap
|
53
|
+
@r.clear_data
|
54
|
+
0.upto(@r.size - 1) {
|
55
|
+
@client.put("a")
|
56
|
+
# Need to sleep so the socket doesn't get all the data in one read()
|
57
|
+
sleep 0.05
|
58
|
+
}
|
59
|
+
s,d = @r.read_data
|
60
|
+
|
61
|
+
@client.put("b")
|
62
|
+
sleep 0.01
|
63
|
+
s,d = @r.read_data(s)
|
64
|
+
|
65
|
+
assert_equal("b", d)
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
=begin
|
71
|
+
client.put("4")
|
72
|
+
client.put("5")
|
73
|
+
client.put("6")
|
74
|
+
s,d = r.read_data(s)
|
75
|
+
|
76
|
+
client.put("7")
|
77
|
+
client.put("8")
|
78
|
+
client.put("9")
|
79
|
+
s,d = r.read_data(s)
|
80
|
+
|
81
|
+
client.put("0")
|
82
|
+
s,d = r.read_data(s)
|
83
|
+
|
84
|
+
test_counter = 11
|
85
|
+
1.upto(100) do
|
86
|
+
client.put( "X" )
|
87
|
+
test_counter += 1
|
88
|
+
end
|
89
|
+
|
90
|
+
sleep(1)
|
91
|
+
|
92
|
+
s,d = r.read_data
|
93
|
+
p s
|
94
|
+
p d
|
95
|
+
|
96
|
+
fdata = ''
|
97
|
+
File.open("/bin/ls", "rb") do |fd|
|
98
|
+
fdata = fd.read(fd.stat.size)
|
99
|
+
fdata = fdata * 10
|
100
|
+
client.put(fdata)
|
101
|
+
end
|
102
|
+
|
103
|
+
sleep(1)
|
104
|
+
|
105
|
+
s,vdata = r.read_data(s)
|
106
|
+
|
107
|
+
if vdata != fdata
|
108
|
+
puts "DATA FAILED"
|
109
|
+
else
|
110
|
+
puts "DATA VERIFIED"
|
111
|
+
end
|
112
|
+
|
113
|
+
r.clear_data
|
114
|
+
|
115
|
+
a = r.create_stream
|
116
|
+
b = r.create_stream
|
117
|
+
|
118
|
+
client.put("ABC123")
|
119
|
+
sleep(1)
|
120
|
+
|
121
|
+
p a.read
|
122
|
+
p b.read
|
123
|
+
|
124
|
+
client.put("$$$$$$")
|
125
|
+
sleep(1)
|
126
|
+
|
127
|
+
p a.read
|
128
|
+
p b.read
|
129
|
+
|
130
|
+
c = r.create_stream
|
131
|
+
p c.read
|
132
|
+
|
133
|
+
end
|
134
|
+
=end
|
data/lib/rex/io/stream_server.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
1
3
|
module Rex
|
2
4
|
module IO
|
3
5
|
|
@@ -61,6 +63,7 @@ module StreamServer
|
|
61
63
|
#
|
62
64
|
def start
|
63
65
|
self.clients = []
|
66
|
+
self.client_waiter = ::Queue.new
|
64
67
|
|
65
68
|
self.listener_thread = Rex::ThreadFactory.spawn("StreamServerListener", false) {
|
66
69
|
monitor_listener
|
@@ -128,6 +131,7 @@ module StreamServer
|
|
128
131
|
|
129
132
|
attr_accessor :clients # :nodoc:
|
130
133
|
attr_accessor :listener_thread, :clients_thread # :nodoc:
|
134
|
+
attr_accessor :client_waiter
|
131
135
|
|
132
136
|
protected
|
133
137
|
|
@@ -151,6 +155,9 @@ protected
|
|
151
155
|
|
152
156
|
# Initialize the connection processing
|
153
157
|
on_client_connect(cli)
|
158
|
+
|
159
|
+
# Notify the client monitor
|
160
|
+
self.client_waiter.push(cli)
|
154
161
|
|
155
162
|
# Skip exceptions caused by accept() [ SSL ]
|
156
163
|
rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
|
@@ -170,12 +177,14 @@ protected
|
|
170
177
|
#
|
171
178
|
def monitor_clients
|
172
179
|
begin
|
180
|
+
|
181
|
+
# Wait for a notify if our client list is empty
|
173
182
|
if (clients.length == 0)
|
174
|
-
|
183
|
+
self.client_waiter.pop
|
175
184
|
next
|
176
185
|
end
|
177
186
|
|
178
|
-
sd = Rex::ThreadSafe.select(clients)
|
187
|
+
sd = Rex::ThreadSafe.select(clients, nil, nil, nil)
|
179
188
|
|
180
189
|
sd[0].each { |cfd|
|
181
190
|
begin
|
@@ -17,7 +17,8 @@ class NessusXMLStreamParser
|
|
17
17
|
def reset_state
|
18
18
|
@host = {'hname' => nil, 'addr' => nil, 'mac' => nil, 'os' => nil, 'ports' => [
|
19
19
|
'port' => {'port' => nil, 'svc_name' => nil, 'proto' => nil, 'severity' => nil,
|
20
|
-
'nasl' => nil, '
|
20
|
+
'nasl' => nil, 'nasl_name' => nil, 'description' => nil,
|
21
|
+
'cve' => [], 'bid' => [], 'xref' => [], 'msf' => nil } ] }
|
21
22
|
@state = :generic_state
|
22
23
|
end
|
23
24
|
|
@@ -46,7 +47,8 @@ class NessusXMLStreamParser
|
|
46
47
|
@bid = Array.new
|
47
48
|
@xref = Array.new
|
48
49
|
@x = Hash.new
|
49
|
-
@x['nasl'] =
|
50
|
+
@x['nasl'] = attributes['pluginID']
|
51
|
+
@x['nasl_name'] = attributes['pluginName']
|
50
52
|
@x['port'] = attributes['port']
|
51
53
|
@x['proto'] = attributes['protocol']
|
52
54
|
@x['svc_name'] = attributes['svc_name']
|
@@ -13,8 +13,16 @@ class NexposeXMLStreamParser
|
|
13
13
|
|
14
14
|
def reset_state
|
15
15
|
@state = :generic_state
|
16
|
-
@
|
17
|
-
@
|
16
|
+
@only_vuln_states_needed = true
|
17
|
+
@current_vuln_id = nil
|
18
|
+
@vulnerable_markers = ['vulnerable-exploited', 'vulnerable-version', 'potential']
|
19
|
+
@host = {"status" => nil, "endpoints" => [], "names" => [], "vulns" => {}}
|
20
|
+
@vuln = {"refs" => [], "description" => [], "solution" => []}
|
21
|
+
end
|
22
|
+
|
23
|
+
# If all vuln states are required set this to false
|
24
|
+
def parse_vulnerable_states_only only_vuln_states_needed
|
25
|
+
@only_vuln_states_needed = only_vuln_states_needed
|
18
26
|
end
|
19
27
|
|
20
28
|
def tag_start(name, attributes)
|
@@ -48,19 +56,31 @@ class NexposeXMLStreamParser
|
|
48
56
|
if @state == :in_service
|
49
57
|
@host["endpoints"].last.merge!(attributes)
|
50
58
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
@host["
|
56
|
-
|
59
|
+
when "test"
|
60
|
+
if (not @only_vuln_states_needed) or (@vulnerable_markers.include? attributes["status"].to_s.chomp and @only_vuln_states_needed)
|
61
|
+
@state = :in_test
|
62
|
+
@current_vuln_id = attributes["id"]
|
63
|
+
@host["vulns"][@current_vuln_id] = attributes.dup
|
64
|
+
# Append the endpoint info for how the vuln was discovered
|
65
|
+
unless @host["endpoints"].empty?
|
66
|
+
@host["vulns"][@current_vuln_id].merge!("endpoint_data" => @host["endpoints"].last)
|
67
|
+
end
|
68
|
+
if attributes["key"]
|
69
|
+
@host["notes"] ||= []
|
70
|
+
@host["notes"] << [@current_vuln_id, attributes["key"]]
|
71
|
+
end
|
57
72
|
end
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
73
|
+
when "vulnerability"
|
74
|
+
@vuln.merge! attributes
|
75
|
+
when "reference"
|
76
|
+
@state = :in_reference
|
77
|
+
@vuln["refs"].push attributes
|
78
|
+
when "solution"
|
79
|
+
@state = :in_solution
|
80
|
+
when "description"
|
81
|
+
@state = :in_description
|
82
|
+
when "URLLink"
|
83
|
+
@vuln["solution"] << attributes
|
64
84
|
end
|
65
85
|
end
|
66
86
|
|
@@ -70,7 +90,17 @@ class NexposeXMLStreamParser
|
|
70
90
|
@host["names"].push str
|
71
91
|
when :in_reference
|
72
92
|
@vuln["refs"].last["value"] = str
|
73
|
-
|
93
|
+
when :in_solution
|
94
|
+
@vuln["solution"] << str
|
95
|
+
when :in_description
|
96
|
+
@vuln["description"] << str
|
97
|
+
when :in_test
|
98
|
+
if @host["vulns"][@current_vuln_id]
|
99
|
+
proof = @host["vulns"][@current_vuln_id]["proof"] || []
|
100
|
+
proof << str
|
101
|
+
@host["vulns"][@current_vuln_id]["proof"] = proof
|
102
|
+
end
|
103
|
+
end
|
74
104
|
end
|
75
105
|
|
76
106
|
def tag_end(name)
|
@@ -0,0 +1,385 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)),"nokogiri_doc_mixin")
|
2
|
+
|
3
|
+
module Rex
|
4
|
+
module Parser
|
5
|
+
|
6
|
+
# If Nokogiri is available, define Nmap document class.
|
7
|
+
load_nokogiri && class NmapDocument < Nokogiri::XML::SAX::Document
|
8
|
+
|
9
|
+
include NokogiriDocMixin
|
10
|
+
|
11
|
+
def determine_port_state(v)
|
12
|
+
case v
|
13
|
+
when "open"
|
14
|
+
Msf::ServiceState::Open
|
15
|
+
when "closed"
|
16
|
+
Msf::ServiceState::Closed
|
17
|
+
when "filtered"
|
18
|
+
Msf::ServiceState::Filtered
|
19
|
+
when "unknown"
|
20
|
+
Msf::ServiceState::Unknown
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Compare OS fingerprinting data
|
25
|
+
def better_os_match(orig_hash,new_hash)
|
26
|
+
return false unless new_hash.has_key? "accuracy"
|
27
|
+
return true unless orig_hash.has_key? "accuracy"
|
28
|
+
new_hash["accuracy"].to_i > orig_hash["accuracy"].to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
# Triggered every time a new element is encountered. We keep state
|
32
|
+
# ourselves with the @state variable, turning things on when we
|
33
|
+
# get here (and turning things off when we exit in end_element()).
|
34
|
+
def start_element(name=nil,attrs=[])
|
35
|
+
attrs = normalize_attrs(attrs)
|
36
|
+
block = @block
|
37
|
+
@state[:current_tag][name] = true
|
38
|
+
case name
|
39
|
+
when "host"
|
40
|
+
@state[:in_host] = true
|
41
|
+
when "os"
|
42
|
+
if @state[:in_host]
|
43
|
+
@state[:in_os] = true
|
44
|
+
end
|
45
|
+
when "status"
|
46
|
+
record_host_status(attrs)
|
47
|
+
when "address"
|
48
|
+
record_address(attrs)
|
49
|
+
when "osclass"
|
50
|
+
record_host_osclass(attrs)
|
51
|
+
when "osmatch"
|
52
|
+
record_host_osmatch(attrs)
|
53
|
+
when "uptime"
|
54
|
+
record_host_uptime(attrs)
|
55
|
+
when "hostname"
|
56
|
+
record_hostname(attrs)
|
57
|
+
when "port"
|
58
|
+
record_port(attrs)
|
59
|
+
when "state"
|
60
|
+
record_port_state(attrs)
|
61
|
+
when "service"
|
62
|
+
record_port_service(attrs)
|
63
|
+
when "script" # Not actually used in import?
|
64
|
+
record_port_script(attrs)
|
65
|
+
record_host_script(attrs)
|
66
|
+
# Ignoring post scripts completely
|
67
|
+
when "trace"
|
68
|
+
record_host_trace(attrs)
|
69
|
+
when "hop"
|
70
|
+
record_host_hop(attrs)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# We can certainly get fancier with self.send() magic, but
|
75
|
+
# leaving this pretty simple for now.
|
76
|
+
|
77
|
+
def record_host_hop(attrs)
|
78
|
+
return unless @state[:in_host]
|
79
|
+
return unless @state[:in_trace]
|
80
|
+
hops = attr_hash(attrs)
|
81
|
+
hops["name"] = hops.delete "host"
|
82
|
+
@state[:trace][:hops] << hops
|
83
|
+
end
|
84
|
+
|
85
|
+
def record_host_trace(attrs)
|
86
|
+
return unless @state[:in_host]
|
87
|
+
@state[:in_trace] = true
|
88
|
+
@state[:trace] = attr_hash(attrs)
|
89
|
+
@state[:trace][:hops] = []
|
90
|
+
end
|
91
|
+
|
92
|
+
def record_host_uptime(attrs)
|
93
|
+
return unless @state[:in_host]
|
94
|
+
@state[:uptime] = attr_hash(attrs)
|
95
|
+
end
|
96
|
+
|
97
|
+
def record_host_osmatch(attrs)
|
98
|
+
return unless @state[:in_host]
|
99
|
+
return unless @state[:in_os]
|
100
|
+
temp_hash = attr_hash(attrs)
|
101
|
+
if temp_hash["accuracy"].to_i == 100
|
102
|
+
@state[:os]["osmatch"] = temp_hash["name"]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def record_host_osclass(attrs)
|
107
|
+
return unless @state[:in_host]
|
108
|
+
return unless @state[:in_os]
|
109
|
+
@state[:os] ||= {}
|
110
|
+
temp_hash = attr_hash(attrs)
|
111
|
+
if better_os_match(@state[:os],temp_hash)
|
112
|
+
@state[:os] = temp_hash
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def record_hostname(attrs)
|
117
|
+
return unless @state[:in_host]
|
118
|
+
if attr_hash(attrs)["type"] == "PTR"
|
119
|
+
@state[:hostname] = attr_hash(attrs)["name"]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def record_host_script(attrs)
|
124
|
+
return unless @state[:in_host]
|
125
|
+
return if @state[:in_port]
|
126
|
+
temp_hash = attr_hash(attrs)
|
127
|
+
@state[:hostscripts] ||= {}
|
128
|
+
@state[:hostscripts].merge! temp_hash
|
129
|
+
temp_hash[:addresses] = @state[:addresses]
|
130
|
+
db.emit(:host_script,temp_hash,&block) if block
|
131
|
+
end
|
132
|
+
|
133
|
+
def record_port_script(attrs)
|
134
|
+
return unless @state[:in_host]
|
135
|
+
return unless @state[:in_port]
|
136
|
+
temp_hash = attr_hash(attrs)
|
137
|
+
@state[:portscripts] ||= {}
|
138
|
+
@state[:portscripts].merge! temp_hash
|
139
|
+
temp_hash[:addresses] = @state[:addresses]
|
140
|
+
temp_hash[:port] = @state[:port]
|
141
|
+
db.emit(:port_script,temp_hash,&block) if block
|
142
|
+
end
|
143
|
+
|
144
|
+
def record_port_service(attrs)
|
145
|
+
return unless @state[:in_host]
|
146
|
+
return unless @state[:in_port]
|
147
|
+
svc = attr_hash(attrs)
|
148
|
+
if svc["name"] && @args[:fix_services]
|
149
|
+
svc["name"] = db.nmap_msf_service_map(svc["name"])
|
150
|
+
end
|
151
|
+
@state[:port] = @state[:port].merge(svc)
|
152
|
+
end
|
153
|
+
|
154
|
+
def record_port_state(attrs)
|
155
|
+
return unless @state[:in_host]
|
156
|
+
return unless @state[:in_port]
|
157
|
+
temp_hash = attr_hash(attrs)
|
158
|
+
@state[:port] = @state[:port].merge(temp_hash)
|
159
|
+
end
|
160
|
+
|
161
|
+
def record_port(attrs)
|
162
|
+
return unless @state[:in_host]
|
163
|
+
@state[:in_port] = true
|
164
|
+
@state[:port] ||= {}
|
165
|
+
svc = attr_hash(attrs)
|
166
|
+
@state[:port] = @state[:port].merge(svc)
|
167
|
+
end
|
168
|
+
|
169
|
+
def record_host_status(attrs)
|
170
|
+
return unless @state[:in_host]
|
171
|
+
attrs.each do |k,v|
|
172
|
+
next unless k == "state"
|
173
|
+
@state[:host_alive] = (v == "up")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def record_address(attrs)
|
178
|
+
return unless @state[:in_host]
|
179
|
+
@state[:addresses] ||= {}
|
180
|
+
address = nil
|
181
|
+
type = nil
|
182
|
+
attrs.each do |k,v|
|
183
|
+
if k == "addr"
|
184
|
+
address = v
|
185
|
+
elsif k == "addrtype"
|
186
|
+
type = v
|
187
|
+
end
|
188
|
+
end
|
189
|
+
@state[:addresses][type] = address
|
190
|
+
end
|
191
|
+
|
192
|
+
# When we exit a tag, this is triggered.
|
193
|
+
def end_element(name=nil)
|
194
|
+
block = @block
|
195
|
+
@state[:current_tag].delete name
|
196
|
+
case name
|
197
|
+
when "os"
|
198
|
+
collect_os_data
|
199
|
+
@state[:in_os] = false
|
200
|
+
@state[:os] = {}
|
201
|
+
when "port"
|
202
|
+
collect_port_data
|
203
|
+
@state[:in_port] = false
|
204
|
+
@state[:port] = {}
|
205
|
+
when "script"
|
206
|
+
if @state[:in_host]
|
207
|
+
if @state[:in_port]
|
208
|
+
@state[:portscripts] = {}
|
209
|
+
else
|
210
|
+
@state[:hostscripts] = {}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
when "trace"
|
214
|
+
@state[:in_trace] = false
|
215
|
+
when "host" # Roll everything up now
|
216
|
+
collect_host_data
|
217
|
+
host_object = report_host &block
|
218
|
+
if host_object
|
219
|
+
db.report_import_note(@args[:wspace],host_object)
|
220
|
+
report_services(host_object,&block)
|
221
|
+
report_fingerprint(host_object)
|
222
|
+
report_uptime(host_object)
|
223
|
+
report_traceroute(host_object)
|
224
|
+
end
|
225
|
+
@state.delete_if {|k| k != :current_tag}
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def end_document
|
230
|
+
block = @block
|
231
|
+
unless @state[:current_tag].empty?
|
232
|
+
missing_ends = @state[:current_tag].keys.map {|x| "'#{x}'"}.join(", ")
|
233
|
+
msg = "Warning, the provided file is incomplete, and there may be missing\n"
|
234
|
+
msg << "data. The following tags were not closed: #{missing_ends}."
|
235
|
+
db.emit(:warning,msg,&block)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def collect_os_data
|
240
|
+
return unless @state[:in_host]
|
241
|
+
if @state[:os]
|
242
|
+
@report_data[:os_fingerprint] = {
|
243
|
+
:type => "host.os.nmap_fingerprint",
|
244
|
+
:data => {
|
245
|
+
:os_vendor => @state[:os]["vendor"],
|
246
|
+
:os_family => @state[:os]["osfamily"],
|
247
|
+
:os_version => @state[:os]["osgen"],
|
248
|
+
:os_accuracy => @state[:os]["accuracy"].to_i
|
249
|
+
}
|
250
|
+
}
|
251
|
+
if @state[:os].has_key? "osmatch"
|
252
|
+
@report_data[:os_fingerprint][:data][:os_match] = @state[:os]["osmatch"]
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def collect_host_data
|
258
|
+
if @state[:host_alive]
|
259
|
+
@report_data[:state] = Msf::HostState::Alive
|
260
|
+
else
|
261
|
+
@report_data[:state] = Msf::HostState::Dead
|
262
|
+
end
|
263
|
+
if @state[:addresses]
|
264
|
+
if @state[:addresses].has_key? "ipv4"
|
265
|
+
@report_data[:host] = @state[:addresses]["ipv4"]
|
266
|
+
elsif @state[:addresses].has_key? "ipv6"
|
267
|
+
@report_data[:host] = @state[:addresses]["ipv6"]
|
268
|
+
end
|
269
|
+
end
|
270
|
+
if @state[:addresses] and @state[:addresses].has_key?("mac")
|
271
|
+
@report_data[:mac] = @state[:addresses]["mac"]
|
272
|
+
end
|
273
|
+
if @state[:hostname]
|
274
|
+
@report_data[:name] = @state[:hostname]
|
275
|
+
end
|
276
|
+
if @state[:uptime]
|
277
|
+
@report_data[:last_boot] = @state[:uptime]["lastboot"]
|
278
|
+
end
|
279
|
+
if @state[:trace] and @state[:trace].has_key?(:hops)
|
280
|
+
@report_data[:traceroute] = @state[:trace]
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def collect_port_data(&block)
|
285
|
+
return unless @state[:in_host]
|
286
|
+
if @args[:fix_services]
|
287
|
+
if @state[:port]["state"] == "filtered"
|
288
|
+
return
|
289
|
+
end
|
290
|
+
end
|
291
|
+
@report_data[:ports] ||= []
|
292
|
+
port_hash = {}
|
293
|
+
extra = []
|
294
|
+
@state[:port].each do |k,v|
|
295
|
+
case k
|
296
|
+
when "protocol"
|
297
|
+
port_hash[:protocol] = v
|
298
|
+
when "portid"
|
299
|
+
port_hash[:port] = v
|
300
|
+
when "state"
|
301
|
+
port_hash[:state] = determine_port_state(v)
|
302
|
+
when "name"
|
303
|
+
port_hash[:name] = v
|
304
|
+
when "reason"
|
305
|
+
port_hash[:reason] = v
|
306
|
+
when "product"
|
307
|
+
extra[0] = v
|
308
|
+
when "version"
|
309
|
+
extra[1] = v
|
310
|
+
when "extrainfo"
|
311
|
+
extra[2] = v
|
312
|
+
end
|
313
|
+
end
|
314
|
+
port_hash[:info] = extra.compact.join(" ") unless extra.empty?
|
315
|
+
# Skip localhost port results when they're unknown
|
316
|
+
if( port_hash[:reason] == "localhost-response" &&
|
317
|
+
port_hash[:state] == Msf::ServiceState::Unknown )
|
318
|
+
@report_data[:ports]
|
319
|
+
else
|
320
|
+
@report_data[:ports] << port_hash
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def report_traceroute(host_object)
|
325
|
+
return unless host_object.kind_of? ::Msf::DBManager::Host
|
326
|
+
return unless @report_data[:traceroute]
|
327
|
+
db.report_note(
|
328
|
+
:workspace => host_object.workspace,
|
329
|
+
:host => host_object,
|
330
|
+
:type => "host.nmap.traceroute",
|
331
|
+
:data => {
|
332
|
+
'port' => @report_data[:traceroute]["port"].to_i,
|
333
|
+
'proto' => @report_data[:traceroute]["proto"].to_s,
|
334
|
+
'hops' => @report_data[:traceroute][:hops]
|
335
|
+
}
|
336
|
+
)
|
337
|
+
end
|
338
|
+
|
339
|
+
def report_uptime(host_object)
|
340
|
+
return unless host_object.kind_of? ::Msf::DBManager::Host
|
341
|
+
return unless @report_data[:last_boot]
|
342
|
+
db.report_note(
|
343
|
+
:workspace => host_object.workspace,
|
344
|
+
:host => host_object,
|
345
|
+
:type => "host.last_boot",
|
346
|
+
:data => { :time => @report_data[:last_boot] }
|
347
|
+
)
|
348
|
+
end
|
349
|
+
|
350
|
+
def report_fingerprint(host_object)
|
351
|
+
return unless host_object.kind_of? ::Msf::DBManager::Host
|
352
|
+
return unless @report_data[:os_fingerprint]
|
353
|
+
db.report_note(
|
354
|
+
@report_data[:os_fingerprint].merge(
|
355
|
+
:workspace => host_object.workspace,
|
356
|
+
:host => host_object
|
357
|
+
)
|
358
|
+
)
|
359
|
+
end
|
360
|
+
|
361
|
+
def report_host(&block)
|
362
|
+
if host_is_okay
|
363
|
+
host_object = db.report_host( @report_data.merge(
|
364
|
+
:workspace => @args[:wspace] ) )
|
365
|
+
db.emit(:address,@report_data[:host],&block) if block
|
366
|
+
host_object
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def report_services(host_object,&block)
|
371
|
+
return unless host_object.kind_of? ::Msf::DBManager::Host
|
372
|
+
return unless @report_data[:ports]
|
373
|
+
return if @report_data[:ports].empty?
|
374
|
+
reported = []
|
375
|
+
@report_data[:ports].each do |svc|
|
376
|
+
reported << db.report_service(svc.merge(:host => host_object))
|
377
|
+
end
|
378
|
+
reported
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|