rex 2.0.8 → 2.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rex.rb +1 -0
- data/lib/rex/arch.rb +5 -0
- data/lib/rex/arch/x86.rb +19 -5
- data/lib/rex/arch/zarch.rb +17 -0
- data/lib/rex/compat.rb +5 -4
- data/lib/rex/constants.rb +3 -1
- data/lib/rex/encoder/alpha2/alpha_mixed.rb +70 -9
- data/lib/rex/encoder/alpha2/alpha_upper.rb +67 -8
- data/lib/rex/exploitation/cmdstager.rb +1 -0
- data/lib/rex/exploitation/cmdstager/certutil.rb +115 -0
- data/lib/rex/exploitation/cmdstager/echo.rb +6 -3
- data/lib/rex/exploitation/egghunter.rb +1 -1
- data/lib/rex/google/geolocation.rb +68 -0
- data/lib/rex/io/bidirectional_pipe.rb +0 -4
- data/lib/rex/java/serialization.rb +2 -0
- data/lib/rex/java/serialization/decode_error.rb +11 -0
- data/lib/rex/java/serialization/encode_error.rb +11 -0
- data/lib/rex/java/serialization/model.rb +2 -0
- data/lib/rex/java/serialization/model/annotation.rb +3 -3
- data/lib/rex/java/serialization/model/block_data.rb +3 -3
- data/lib/rex/java/serialization/model/block_data_long.rb +3 -3
- data/lib/rex/java/serialization/model/class_desc.rb +6 -6
- data/lib/rex/java/serialization/model/contents.rb +17 -10
- data/lib/rex/java/serialization/model/field.rb +12 -11
- data/lib/rex/java/serialization/model/long_utf.rb +3 -3
- data/lib/rex/java/serialization/model/new_array.rb +22 -23
- data/lib/rex/java/serialization/model/new_class.rb +57 -0
- data/lib/rex/java/serialization/model/new_class_desc.rb +15 -16
- data/lib/rex/java/serialization/model/new_enum.rb +5 -5
- data/lib/rex/java/serialization/model/new_object.rb +22 -17
- data/lib/rex/java/serialization/model/proxy_class_desc.rb +109 -0
- data/lib/rex/java/serialization/model/reference.rb +4 -4
- data/lib/rex/java/serialization/model/stream.rb +7 -7
- data/lib/rex/java/serialization/model/utf.rb +3 -3
- data/lib/rex/json_hash_file.rb +94 -0
- data/lib/rex/logging/log_sink.rb +1 -0
- data/lib/rex/logging/sinks/timestamp_flatfile.rb +21 -0
- data/lib/rex/parser/appscan_nokogiri.rb +13 -23
- data/lib/rex/parser/fs/ntfs.rb +10 -5
- data/lib/rex/parser/nmap_nokogiri.rb +3 -1
- data/lib/rex/parser/openvas_nokogiri.rb +70 -73
- data/lib/rex/parser/winscp.rb +108 -0
- data/lib/rex/parser/x509_certificate.rb +92 -0
- data/lib/rex/payloads.rb +0 -1
- data/lib/rex/payloads/meterpreter/config.rb +154 -0
- data/lib/rex/payloads/meterpreter/uri_checksum.rb +136 -0
- data/lib/rex/post/meterpreter.rb +1 -1
- data/lib/rex/post/meterpreter/client.rb +26 -3
- data/lib/rex/post/meterpreter/client_core.rb +387 -75
- data/lib/rex/post/meterpreter/extensions/android/android.rb +127 -37
- data/lib/rex/post/meterpreter/extensions/android/tlv.rb +46 -25
- data/lib/rex/post/meterpreter/extensions/extapi/extapi.rb +4 -0
- data/lib/rex/post/meterpreter/extensions/extapi/ntds/ntds.rb +39 -0
- data/lib/rex/post/meterpreter/extensions/extapi/pageant/pageant.rb +44 -0
- data/lib/rex/post/meterpreter/extensions/extapi/tlv.rb +9 -0
- data/lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb +16 -1
- data/lib/rex/post/meterpreter/extensions/priv/priv.rb +1 -1
- data/lib/rex/post/meterpreter/extensions/python/python.rb +114 -0
- data/lib/rex/post/meterpreter/extensions/python/tlv.rb +21 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb +17 -14
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb +33 -12
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/mount.rb +57 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +3 -3
- data/lib/rex/post/meterpreter/extensions/stdapi/stdapi.rb +3 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb +2 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb +16 -3
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb +29 -6
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/registry_key.rb +5 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/tlv.rb +18 -6
- data/lib/rex/post/meterpreter/extensions/stdapi/ui.rb +2 -2
- data/lib/rex/post/meterpreter/extensions/stdapi/webcam/webcam.rb +34 -36
- data/lib/rex/post/meterpreter/packet.rb +29 -0
- data/lib/rex/post/meterpreter/packet_dispatcher.rb +20 -7
- data/lib/rex/post/meterpreter/ui/console.rb +1 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb +230 -72
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +544 -34
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb +188 -57
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb +115 -93
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/lanattacks/dhcp.rb +1 -1
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/mimikatz.rb +1 -1
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/elevate.rb +49 -15
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/timestomp.rb +11 -2
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/python.rb +187 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +324 -133
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb +52 -2
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +68 -65
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/ui.rb +9 -1
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/webcam.rb +113 -118
- data/lib/rex/post/meterpreter/ui/console/interactive_channel.rb +3 -0
- data/lib/rex/powershell.rb +62 -0
- data/lib/rex/powershell/command.rb +359 -0
- data/lib/rex/{exploitation/powershell → powershell}/function.rb +0 -2
- data/lib/rex/{exploitation/powershell → powershell}/obfu.rb +0 -2
- data/lib/rex/{exploitation/powershell → powershell}/output.rb +11 -5
- data/lib/rex/{exploitation/powershell → powershell}/param.rb +0 -2
- data/lib/rex/powershell/parser.rb +182 -0
- data/lib/rex/powershell/payload.rb +78 -0
- data/lib/rex/{exploitation/powershell → powershell}/psh_methods.rb +16 -2
- data/lib/rex/{exploitation/powershell → powershell}/script.rb +2 -4
- data/lib/rex/proto/dcerpc/client.rb +6 -6
- data/lib/rex/proto/dcerpc/exceptions.rb +26 -0
- data/lib/rex/proto/http/client.rb +3 -3
- data/lib/rex/proto/http/client_request.rb +0 -5
- data/lib/rex/proto/http/response.rb +86 -0
- data/lib/rex/proto/ipmi/utils.rb +30 -26
- data/lib/rex/proto/kerberos/client.rb +1 -1
- data/lib/rex/proto/kerberos/model/kdc_request.rb +2 -2
- data/lib/rex/proto/rfb/client.rb +8 -3
- data/lib/rex/proto/rfb/constants.rb +1 -1
- data/lib/rex/proto/rmi.rb +2 -0
- data/lib/rex/proto/rmi/decode_error.rb +10 -0
- data/lib/rex/proto/rmi/exception.rb +10 -0
- data/lib/rex/proto/rmi/model.rb +5 -0
- data/lib/rex/proto/rmi/model/call.rb +4 -4
- data/lib/rex/proto/rmi/model/call_data.rb +137 -0
- data/lib/rex/proto/rmi/model/dgc_ack.rb +2 -2
- data/lib/rex/proto/rmi/model/element.rb +26 -11
- data/lib/rex/proto/rmi/model/output_header.rb +4 -4
- data/lib/rex/proto/rmi/model/ping.rb +2 -2
- data/lib/rex/proto/rmi/model/ping_ack.rb +2 -2
- data/lib/rex/proto/rmi/model/protocol_ack.rb +2 -2
- data/lib/rex/proto/rmi/model/return_data.rb +5 -5
- data/lib/rex/proto/rmi/model/return_value.rb +124 -0
- data/lib/rex/proto/rmi/model/unique_identifier.rb +77 -0
- data/lib/rex/proto/steam.rb +3 -0
- data/lib/rex/proto/steam/message.rb +125 -0
- data/lib/rex/proto/tftp/client.rb +35 -14
- data/lib/rex/random_identifier_generator.rb +2 -0
- data/lib/rex/ropbuilder.rb +1 -1
- data/lib/rex/socket/parameters.rb +9 -0
- data/lib/rex/socket/ssl_tcp.rb +25 -41
- data/lib/rex/socket/ssl_tcp_server.rb +10 -21
- data/lib/rex/sslscan/result.rb +20 -1
- data/lib/rex/text.rb +241 -55
- data/lib/rex/ui/output.rb +0 -3
- data/lib/rex/ui/subscriber.rb +0 -10
- data/lib/rex/ui/text/color.rb +9 -0
- data/lib/rex/ui/text/dispatcher_shell.rb +1 -0
- data/lib/rex/ui/text/output.rb +15 -4
- data/lib/rex/ui/text/output/file.rb +1 -0
- data/lib/rex/ui/text/output/stdio.rb +0 -16
- data/lib/rex/ui/text/shell.rb +3 -0
- data/lib/rex/ui/text/table.rb +85 -19
- data/lib/rex/user_agent.rb +118 -0
- data/rex.gemspec +2 -2
- metadata +41 -14
- data/lib/rex/exploitation/powershell.rb +0 -62
- data/lib/rex/exploitation/powershell/parser.rb +0 -183
- data/lib/rex/payloads/meterpreter.rb +0 -2
- data/lib/rex/payloads/meterpreter/patch.rb +0 -136
@@ -15,6 +15,12 @@ module Parser
|
|
15
15
|
attrs = normalize_attrs(attrs)
|
16
16
|
block = @block
|
17
17
|
@state[:current_tag][name] = true
|
18
|
+
|
19
|
+
unless @text.nil?
|
20
|
+
@state[:text_backup] = @text
|
21
|
+
@text = nil
|
22
|
+
end
|
23
|
+
|
18
24
|
case name
|
19
25
|
when "host"
|
20
26
|
@state[:has_text] = true
|
@@ -25,97 +31,90 @@ module Parser
|
|
25
31
|
def end_element(name=nil)
|
26
32
|
block = @block
|
27
33
|
case name
|
28
|
-
when
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
when
|
34
|
+
when 'name'
|
35
|
+
if in_tag('result')
|
36
|
+
@state[:has_text] = true
|
37
|
+
@state[:vuln_name] = @text.strip if @text
|
38
|
+
end
|
39
|
+
when 'description'
|
34
40
|
@state[:has_text] = true
|
35
41
|
@state[:vuln_desc] = @text.strip if @text
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@state[:has_text] = true
|
41
|
-
@state[:bid] = @text.strip if @text
|
42
|
-
@text = nil
|
43
|
-
when "cve"
|
44
|
-
return if not in_tag("result")
|
45
|
-
return if not in_tag("nvt")
|
46
|
-
@state[:has_text] = true
|
47
|
-
@state[:cves] = @text.strip if @text
|
48
|
-
@text = nil
|
49
|
-
when "risk_factor"
|
50
|
-
return if not in_tag("result")
|
51
|
-
return if not in_tag("nvt")
|
52
|
-
|
53
|
-
#we do this to clean out the buffer so to speak
|
54
|
-
#if we don't set text to nil now, the text will show up later
|
55
|
-
@state[:has_text] = true
|
56
|
-
@text = nil
|
57
|
-
when "cvss_base"
|
58
|
-
return if not in_tag("result")
|
59
|
-
return if not in_tag("nvt")
|
60
|
-
@state[:has_text] = true
|
61
|
-
@text = nil
|
62
|
-
when "subnet"
|
63
|
-
@state[:has_text] = true
|
64
|
-
@text = nil
|
65
|
-
when "result"
|
66
|
-
return if not in_tag("results")
|
67
|
-
record_vuln
|
68
|
-
when "threat"
|
69
|
-
return if not in_tag("ports")
|
70
|
-
return if not in_tag("port")
|
71
|
-
@state[:has_text] = true
|
72
|
-
|
73
|
-
if not @text.index('(')
|
74
|
-
@state[:name] = nil
|
75
|
-
@state[:port] = nil
|
76
|
-
@state[:proto] = nil
|
77
|
-
@text = nil
|
78
|
-
return
|
42
|
+
when 'bid'
|
43
|
+
if in_tag('result') && in_tag('nvt')
|
44
|
+
@state[:has_text] = true
|
45
|
+
@state[:bid] = @text.strip if @text
|
79
46
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
47
|
+
when 'cve'
|
48
|
+
if in_tag('result') && in_tag('nvt')
|
49
|
+
@state[:has_text] = true
|
50
|
+
@state[:cves] = @text.strip if @text
|
51
|
+
end
|
52
|
+
when 'risk_factor'
|
53
|
+
if in_tag('result') && in_tag('nvt')
|
54
|
+
#we do this to clean out the buffer so to speak
|
55
|
+
#if we don't set text to nil now, the text will show up later
|
56
|
+
@state[:has_text] = true
|
57
|
+
end
|
58
|
+
when 'cvss_base'
|
59
|
+
if in_tag('result') && in_tag('nvt')
|
60
|
+
@state[:has_text] = true
|
61
|
+
end
|
62
|
+
when 'subnet'
|
63
|
+
@state[:has_text] = true
|
64
|
+
when 'result'
|
65
|
+
record_vuln if in_tag('results')
|
66
|
+
when 'threat'
|
67
|
+
@state[:has_text] = true if in_tag('ports') && in_tag('port')
|
68
|
+
when 'host'
|
87
69
|
if in_tag('result')
|
88
70
|
@state[:has_text] = true
|
89
71
|
@state[:host] = @text.strip if @text
|
90
|
-
|
91
|
-
elsif in_tag('ports')
|
92
|
-
return if not in_tag('port')
|
72
|
+
elsif in_tag('ports') && in_tag('port')
|
93
73
|
@state[:has_text] = true
|
94
74
|
@state[:host] = @text.strip if @text
|
95
|
-
@text = nil
|
96
75
|
end
|
97
|
-
when
|
76
|
+
when 'port'
|
98
77
|
if in_tag('result')
|
99
78
|
@state[:has_text] = true
|
100
|
-
if
|
79
|
+
if @text && @text.index('(')
|
80
|
+
@state[:proto] = @text.split('(')[1].split('/')[1].gsub(/\)/, '')
|
81
|
+
@state[:port] = @text.split('(')[1].split('/')[0].gsub(/\)/, '')
|
82
|
+
elsif @text && @text.index('/')
|
83
|
+
@state[:proto] = @text.split('/')[1].strip
|
84
|
+
@state[:port] = @text.split('/')[0].strip
|
85
|
+
else
|
86
|
+
@state[:proto] = nil
|
87
|
+
@state[:port] = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
if @state[:port] && @state[:port] == 'general'
|
101
91
|
@state[:proto] = nil
|
102
92
|
@state[:port] = nil
|
103
|
-
@text = nil
|
104
|
-
return
|
105
93
|
end
|
106
|
-
@state[:proto] = @text.split('(')[0].strip if @text
|
107
|
-
@state[:port] = @text.split('(')[1].split('/')[0].gsub(/\)/, '') if @text
|
108
|
-
@text = nil
|
109
94
|
elsif in_tag('ports')
|
110
|
-
|
95
|
+
if @text && @text.index('(')
|
96
|
+
@state[:name] = @text.split(' ')[0]
|
97
|
+
@state[:port] = @text.split('(')[1].split('/')[0]
|
98
|
+
@state[:proto] = @text.split('(')[1].split('/')[1].split(')')[0]
|
99
|
+
record_service unless @state[:name].nil?
|
100
|
+
elsif @text && @text.index('/')
|
101
|
+
@state[:port] = @text.split('/')[0]
|
102
|
+
@state[:proto] = @text.split('/')[1]
|
103
|
+
record_service unless @state[:port] == 'general'
|
104
|
+
end
|
111
105
|
end
|
112
|
-
when
|
113
|
-
return if not in_tag(
|
106
|
+
when 'name'
|
107
|
+
return if not in_tag('result')
|
114
108
|
@state[:has_text] = true
|
115
|
-
|
109
|
+
end
|
110
|
+
|
111
|
+
if @state[:text_backup]
|
112
|
+
@text = @state[:text_backup]
|
113
|
+
@state[:text_backup] = nil
|
116
114
|
else
|
117
115
|
@text = nil
|
118
116
|
end
|
117
|
+
|
119
118
|
@state[:current_tag].delete name
|
120
119
|
end
|
121
120
|
|
@@ -153,8 +152,6 @@ module Parser
|
|
153
152
|
end
|
154
153
|
|
155
154
|
def record_service
|
156
|
-
return if not @state[:name]
|
157
|
-
|
158
155
|
service_info = {}
|
159
156
|
service_info[:host] = @state[:host]
|
160
157
|
service_info[:name] = @state[:name]
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'rex/parser/ini'
|
2
|
+
|
3
|
+
module Rex
|
4
|
+
module Parser
|
5
|
+
module WinSCP
|
6
|
+
PWDALG_SIMPLE_MAGIC = 0xA3
|
7
|
+
PWDALG_SIMPLE_FLAG = 0xFF
|
8
|
+
|
9
|
+
def read_and_parse_ini(filename)
|
10
|
+
file = File.read(filename)
|
11
|
+
return if file.to_s.empty?
|
12
|
+
parse_ini(file)
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse_protocol(fsprotocol)
|
16
|
+
return 'Unknown' if fsprotocol.nil?
|
17
|
+
|
18
|
+
case fsprotocol
|
19
|
+
when 5 then 'FTP'
|
20
|
+
when 0 then 'SSH'
|
21
|
+
else
|
22
|
+
'Unknown'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_ini(file)
|
27
|
+
results = []
|
28
|
+
raise RuntimeError, 'No data to parse' if file.nil? || file.empty?
|
29
|
+
|
30
|
+
ini = Rex::Parser::Ini.from_s(file)
|
31
|
+
|
32
|
+
if ini['Configuration\\Security']
|
33
|
+
# if a Master Password is in use we give up
|
34
|
+
if ini['Configuration\\Security']['UseMasterPassword'].to_i == 1
|
35
|
+
raise RuntimeError, 'Master Password Set, unable to recover saved passwords!'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Runs through each group in the ini file looking for all of the Sessions
|
40
|
+
ini.each_key do |group|
|
41
|
+
if group.include?('Sessions') && ini[group].has_key?('Password')
|
42
|
+
# Decrypt our password, and report on results
|
43
|
+
encrypted_password = ini[group]['Password']
|
44
|
+
user = ini[group]['UserName']
|
45
|
+
host = ini[group]['HostName']
|
46
|
+
sname = parse_protocol(ini[group]['FSProtocol'].to_i)
|
47
|
+
plaintext = decrypt_password(encrypted_password, "#{user}#{host}")
|
48
|
+
|
49
|
+
results << {
|
50
|
+
hostname: host,
|
51
|
+
password: plaintext,
|
52
|
+
portnumber: ini[group]['PortNumber'] || 22,
|
53
|
+
username: user,
|
54
|
+
protocol: sname
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
results
|
60
|
+
end
|
61
|
+
|
62
|
+
# Decrypts the next character in the password sequence
|
63
|
+
def decrypt_next_char(pwd)
|
64
|
+
if pwd.nil? || pwd.length <= 0
|
65
|
+
return 0, pwd
|
66
|
+
end
|
67
|
+
|
68
|
+
# Takes the first char from the encrypted password and then left shifts the returned index by 4 bits
|
69
|
+
a = pwd[0].hex << 4
|
70
|
+
|
71
|
+
# Takes the second char from the encrypted password
|
72
|
+
b = pwd[1].hex
|
73
|
+
|
74
|
+
# Adds the two results, XORs against 0xA3, NOTs it and then ANDs it with 0xFF
|
75
|
+
result = ~((a + b) ^ PWDALG_SIMPLE_MAGIC) & PWDALG_SIMPLE_FLAG
|
76
|
+
|
77
|
+
# Strips the first two chars off and returns our result
|
78
|
+
return result, pwd[2..-1]
|
79
|
+
end
|
80
|
+
|
81
|
+
def decrypt_password(pwd, key)
|
82
|
+
flag, pwd = decrypt_next_char(pwd)
|
83
|
+
|
84
|
+
if flag == PWDALG_SIMPLE_FLAG
|
85
|
+
_, pwd = decrypt_next_char(pwd)
|
86
|
+
length, pwd = decrypt_next_char(pwd)
|
87
|
+
else
|
88
|
+
length = flag
|
89
|
+
end
|
90
|
+
|
91
|
+
del, pwd = decrypt_next_char(pwd)
|
92
|
+
pwd = pwd[del*2..-1]
|
93
|
+
|
94
|
+
result = ""
|
95
|
+
length.times do
|
96
|
+
r, pwd = decrypt_next_char(pwd)
|
97
|
+
result << r.chr
|
98
|
+
end
|
99
|
+
|
100
|
+
if flag == PWDALG_SIMPLE_FLAG
|
101
|
+
result = result[key.length..-1]
|
102
|
+
end
|
103
|
+
|
104
|
+
result
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Rex
|
6
|
+
module Parser
|
7
|
+
|
8
|
+
###
|
9
|
+
#
|
10
|
+
# This class parses the contents of a PEM-encoded X509 certificate file containing
|
11
|
+
# a private key, a public key, and any appended glue certificates.
|
12
|
+
#
|
13
|
+
###
|
14
|
+
class X509Certificate
|
15
|
+
|
16
|
+
#
|
17
|
+
# Parse a certificate in unified PEM format that contains a private key and
|
18
|
+
# one or more certificates. The first certificate is the primary, while any
|
19
|
+
# additional certificates are treated as intermediary certificates. This emulates
|
20
|
+
# the behavior of web servers like nginx.
|
21
|
+
#
|
22
|
+
# @param [String] ssl_cert
|
23
|
+
# @return [String, String, Array]
|
24
|
+
def self.parse_pem(ssl_cert)
|
25
|
+
cert = nil
|
26
|
+
key = nil
|
27
|
+
chain = nil
|
28
|
+
|
29
|
+
certs = []
|
30
|
+
ssl_cert.scan(/-----BEGIN\s*[^\-]+-----+\r?\n[^\-]*-----END\s*[^\-]+-----\r?\n?/nm).each do |pem|
|
31
|
+
if pem =~ /PRIVATE KEY/
|
32
|
+
key = OpenSSL::PKey::RSA.new(pem)
|
33
|
+
elsif pem =~ /CERTIFICATE/
|
34
|
+
certs << OpenSSL::X509::Certificate.new(pem)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
cert = certs.shift
|
39
|
+
if certs.length > 0
|
40
|
+
chain = certs
|
41
|
+
end
|
42
|
+
|
43
|
+
[key, cert, chain]
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Parse a certificate in unified PEM format from a file
|
48
|
+
#
|
49
|
+
# @param [String] ssl_cert_file
|
50
|
+
# @return [String, String, Array]
|
51
|
+
def self.parse_pem_file(ssl_cert_file)
|
52
|
+
data = ''
|
53
|
+
::File.open(ssl_cert_file, 'rb') do |fd|
|
54
|
+
data << fd.read(fd.stat.size)
|
55
|
+
end
|
56
|
+
parse_pem(data)
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Parse a certificate in unified PEM format and retrieve
|
61
|
+
# the SHA1 hash.
|
62
|
+
#
|
63
|
+
# @param [String] ssl_cert
|
64
|
+
# @return [String]
|
65
|
+
def self.get_cert_hash(ssl_cert)
|
66
|
+
hcert = parse_pem(ssl_cert)
|
67
|
+
|
68
|
+
unless hcert and hcert[0] and hcert[1]
|
69
|
+
raise ArgumentError, "Could not parse a private key and certificate"
|
70
|
+
end
|
71
|
+
|
72
|
+
Rex::Text.sha1_raw(hcert[1].to_der)
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Parse a file that contains a certificate in unified PEM
|
77
|
+
# format and retrieve the SHA1 hash.
|
78
|
+
#
|
79
|
+
# @param [String] ssl_cert_file
|
80
|
+
# @return [String]
|
81
|
+
def self.get_cert_file_hash(ssl_cert_file)
|
82
|
+
data = ''
|
83
|
+
::File.open(ssl_cert_file, 'rb') do |fd|
|
84
|
+
data << fd.read(fd.stat.size)
|
85
|
+
end
|
86
|
+
get_cert_hash(data)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
data/lib/rex/payloads.rb
CHANGED
@@ -0,0 +1,154 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'msf/core/payload/uuid'
|
3
|
+
require 'msf/core/payload/windows'
|
4
|
+
require 'msf/core/reflective_dll_loader'
|
5
|
+
require 'rex/parser/x509_certificate'
|
6
|
+
|
7
|
+
class Rex::Payloads::Meterpreter::Config
|
8
|
+
|
9
|
+
include Msf::ReflectiveDLLLoader
|
10
|
+
|
11
|
+
URL_SIZE = 512
|
12
|
+
UA_SIZE = 256
|
13
|
+
PROXY_HOST_SIZE = 128
|
14
|
+
PROXY_USER_SIZE = 64
|
15
|
+
PROXY_PASS_SIZE = 64
|
16
|
+
CERT_HASH_SIZE = 20
|
17
|
+
|
18
|
+
def initialize(opts={})
|
19
|
+
@opts = opts
|
20
|
+
if opts[:ascii_str] == true
|
21
|
+
@to_str = self.method(:to_ascii)
|
22
|
+
else
|
23
|
+
@to_str = self.method(:to_wchar_t)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_b
|
28
|
+
config_block
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def is_x86?
|
34
|
+
@opts[:arch] == ARCH_X86
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_str(item, size)
|
38
|
+
@to_str.call(item, size)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_wchar_t(item, size)
|
42
|
+
to_ascii(item, size).unpack('C*').pack('v*')
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_ascii(item, size)
|
46
|
+
item.to_s.ljust(size, "\x00")
|
47
|
+
end
|
48
|
+
|
49
|
+
def session_block(opts)
|
50
|
+
uuid = opts[:uuid].to_raw
|
51
|
+
exit_func = Msf::Payload::Windows.exit_types[opts[:exitfunk]]
|
52
|
+
|
53
|
+
session_data = [
|
54
|
+
0, # comms socket, patched in by the stager
|
55
|
+
exit_func, # exit function identifer
|
56
|
+
opts[:expiration], # Session expiry
|
57
|
+
uuid # the UUID
|
58
|
+
]
|
59
|
+
|
60
|
+
session_data.pack('VVVA*')
|
61
|
+
end
|
62
|
+
|
63
|
+
def transport_block(opts)
|
64
|
+
# Build the URL from the given parameters, and pad it out to the
|
65
|
+
# correct size
|
66
|
+
lhost = opts[:lhost]
|
67
|
+
if lhost && opts[:scheme].start_with?('http') && Rex::Socket.is_ipv6?(lhost)
|
68
|
+
lhost = "[#{lhost}]"
|
69
|
+
end
|
70
|
+
|
71
|
+
url = "#{opts[:scheme]}://#{lhost}:#{opts[:lport]}"
|
72
|
+
url << "#{opts[:uri]}/" if opts[:uri]
|
73
|
+
url << "?#{opts[:scope_id]}" if opts[:scope_id]
|
74
|
+
|
75
|
+
# if the transport URI is for a HTTP payload we need to add a stack
|
76
|
+
# of other stuff
|
77
|
+
pack = 'A*VVV'
|
78
|
+
transport_data = [
|
79
|
+
to_str(url, URL_SIZE), # transport URL
|
80
|
+
opts[:comm_timeout], # communications timeout
|
81
|
+
opts[:retry_total], # retry total time
|
82
|
+
opts[:retry_wait] # retry wait time
|
83
|
+
]
|
84
|
+
|
85
|
+
if url.start_with?('http')
|
86
|
+
proxy_host = ''
|
87
|
+
if opts[:proxy_host] && opts[:proxy_port]
|
88
|
+
prefix = 'http://'
|
89
|
+
prefix = 'socks=' if opts[:proxy_type].downcase == 'socks'
|
90
|
+
proxy_host = "#{prefix}#{opts[:proxy_host]}:#{opts[:proxy_port]}"
|
91
|
+
end
|
92
|
+
proxy_host = to_str(proxy_host || '', PROXY_HOST_SIZE)
|
93
|
+
proxy_user = to_str(opts[:proxy_user] || '', PROXY_USER_SIZE)
|
94
|
+
proxy_pass = to_str(opts[:proxy_pass] || '', PROXY_PASS_SIZE)
|
95
|
+
ua = to_str(opts[:ua] || '', UA_SIZE)
|
96
|
+
|
97
|
+
cert_hash = "\x00" * CERT_HASH_SIZE
|
98
|
+
cert_hash = opts[:ssl_cert_hash] if opts[:ssl_cert_hash]
|
99
|
+
|
100
|
+
# add the HTTP specific stuff
|
101
|
+
transport_data << proxy_host # Proxy host name
|
102
|
+
transport_data << proxy_user # Proxy user name
|
103
|
+
transport_data << proxy_pass # Proxy password
|
104
|
+
transport_data << ua # HTTP user agent
|
105
|
+
transport_data << cert_hash # SSL cert hash for verification
|
106
|
+
|
107
|
+
# update the packing spec
|
108
|
+
pack << 'A*A*A*A*A*'
|
109
|
+
end
|
110
|
+
|
111
|
+
# return the packed transport information
|
112
|
+
transport_data.pack(pack)
|
113
|
+
end
|
114
|
+
|
115
|
+
def extension_block(ext_name, file_extension)
|
116
|
+
ext_name = ext_name.strip.downcase
|
117
|
+
ext, o = load_rdi_dll(MetasploitPayloads.meterpreter_path("ext_server_#{ext_name}",
|
118
|
+
file_extension))
|
119
|
+
|
120
|
+
extension_data = [ ext.length, ext ].pack('VA*')
|
121
|
+
end
|
122
|
+
|
123
|
+
def config_block
|
124
|
+
# start with the session information
|
125
|
+
config = session_block(@opts)
|
126
|
+
|
127
|
+
# then load up the transport configurations
|
128
|
+
(@opts[:transports] || []).each do |t|
|
129
|
+
config << transport_block(t)
|
130
|
+
end
|
131
|
+
|
132
|
+
# terminate the transports with NULL (wchar)
|
133
|
+
config << "\x00\x00"
|
134
|
+
|
135
|
+
# configure the extensions - this will have to change when posix comes
|
136
|
+
# into play.
|
137
|
+
file_extension = 'x86.dll'
|
138
|
+
file_extension = 'x64.dll' unless is_x86?
|
139
|
+
|
140
|
+
(@opts[:extensions] || []).each do |e|
|
141
|
+
config << extension_block(e, file_extension)
|
142
|
+
end
|
143
|
+
|
144
|
+
# terminate the extensions with a 0 size
|
145
|
+
if is_x86?
|
146
|
+
config << [0].pack('V')
|
147
|
+
else
|
148
|
+
config << [0].pack('Q<')
|
149
|
+
end
|
150
|
+
|
151
|
+
# and we're done
|
152
|
+
config
|
153
|
+
end
|
154
|
+
end
|