@@ -33,48 +33,91 @@ def run_host(ip)
33
33
begin
34
34
domain = nil
35
35
connect
36
- vprint_status ( "Connected to #{ ip } :#{ datastore [ 'RPORT' ] } " )
37
36
38
- # send a EHLO and parse the extensions returned
37
+ unless banner
38
+ vprint_error ( "#{ rhost } :#{ rport } No banner received, aborting..." )
39
+ return
40
+ end
41
+
42
+ vprint_status ( "#{ rhost } :#{ rport } Connected: #{ banner . strip . inspect } " )
43
+
44
+ # Report the last line of the banner as services information (typically the interesting one)
45
+ report_service ( host : rhost , port : rport , name : 'smtp' , proto : 'tcp' , info : banner . strip . split ( "\n " ) . last )
46
+
47
+ # Send a EHLO and parse the extensions returned
39
48
sock . puts ( "EHLO " + datastore [ 'EHLO_DOMAIN' ] + "\r \n " )
40
- exts = sock . get_once . to_s . split ( /\n / )
41
49
42
- # loop through all returned extensions related to NTLM
43
- exts . grep ( /NTLM/ ) . each do |ext |
50
+ # Find all NTLM references in the EHLO response
51
+ exts = sock . get_once . to_s . split ( /\n / ) . grep ( /NTLM/ )
52
+ if exts . length == 0
53
+ vprint_error ( "#{ rhost } :#{ rport } No NTLM extensions found" )
54
+ return
55
+ end
56
+
57
+ exts . each do |ext |
44
58
45
- # extract the reply minus the first 4 chars (response code + dash)
59
+ # Extract the reply minus the first 4 chars (response code + dash)
46
60
e = ext [ 4 ..-1 ] . chomp
47
61
48
- # try the usual AUTH NTLM approach if possible, otherwise echo the extension back to server
62
+ # Try the usual AUTH NTLM approach if possible, otherwise echo the extension back to server
49
63
if e =~ /AUTH.*NTLM/
50
64
sock . puts ( "AUTH NTLM\r \n " )
65
+ vprint_status ( "#{ rhost } :#{ rport } Sending AUTH NTLM" )
51
66
else
52
67
sock . puts ( e + "\r \n " )
68
+ vprint_status ( "#{ rhost } :#{ rport } Sending #{ e } " )
53
69
end
54
70
55
- # we expect a "334" code to go ahead with NTLM auth
56
- reply = sock . get_once
57
- if reply . include? ( "334" )
58
- # send the NTLM AUTH blob to tell the server we're ready to auth
71
+ # We expect a "334" code to go ahead with NTLM auth
72
+ reply = sock . get_once . to_s
73
+ if reply !~ /^334\s +/m
74
+ vprint_status ( "#{ rhost } :#{ rport } Expected a 334 response, received #{ reply . strip . inspect } aborting..." )
75
+ break
76
+ else
77
+ # Send the NTLM AUTH blob to tell the server we're ready to auth
59
78
blob = "TlRMTVNTUAABAAAAt4II4gAAAAAAAAAAAAAAAAAAAAAFAs4OAAAADw=="
60
79
sock . puts ( blob + "\r \n " )
61
80
62
- # capture the challenge sent by server
63
- challenge = sock . get_once . split . last
64
-
65
- # and extract the domain out of it
66
- domain = Rex ::Proto ::NTLM ::Message . parse ( Rex ::Text . decode_base64 ( challenge ) ) [ :target_name ] . value ( ) . gsub ( /\0 / , '' )
67
- print_good ( "Domain: #{ domain } " )
68
- else
69
- print_error ( "Error in response: expected '334', aborting" )
81
+ # Capture the challenge sent by server
82
+ challenge = sock . get_once . to_s . split ( /\s +/ ) . last
83
+
84
+ if challenge . length == 0
85
+ vprint_status ( "#{ rhost } :#{ rport } Empty challenge response, aborting..." )
86
+ break
87
+ end
88
+
89
+ begin
90
+ # Extract the domain out of the NTLM response
91
+ ntlm_reply = Rex ::Proto ::NTLM ::Message . parse ( Rex ::Text . decode_base64 ( challenge ) )
92
+ if ! ntlm_reply && ntlm_reply . has_key? ( :target_name )
93
+ vprint_status ( "#{ rhost } :#{ rport } Invalid challenge response, aborting..." )
94
+ break
95
+ end
96
+
97
+ # TODO: Extract the server name from :target_info as well
98
+ domain = ntlm_reply [ :target_name ] . value . to_s . gsub ( /\x00 / , '' )
99
+ if domain . to_s . length == 0
100
+ vprint_status ( "#{ rhost } :#{ rport } Invalid target name in challenge response, aborting..." )
101
+ break
102
+ end
103
+
104
+ print_good ( "#{ rhost } :#{ rport } Domain: #{ domain } " )
105
+ report_note ( host : rhost , port : rport , proto : 'tcp' , type : 'smtp.ntlm_auth_info' , data : { domain : domain } )
106
+ break
107
+
108
+ rescue ::Rex ::ArgumentError
109
+ vprint_status ( "#{ rhost } :#{ rport } Invalid challenge response message, aborting..." )
110
+ break
111
+ end
70
112
end
71
113
end
72
114
73
- print_error ( "#{ ip } : No NTLM extensions found" ) if domain . nil? or domain . empty?
115
+ if ! domain
116
+ vprint_error ( "#{ rhost } :#{ rport } No NTLM domain found" )
117
+ end
74
118
75
- rescue ::Rex ::ConnectionRefused , ::Rex ::HostUnreachable , ::Rex ::ConnectionTimeout
76
- rescue Timeout ::Error => err
77
- print_error ( err . message )
119
+ rescue ::Rex ::ConnectionRefused , ::Rex ::HostUnreachable , ::Rex ::ConnectionTimeout , ::Timeout ::Error
120
+ # Ignore common networking and response timeout errors
78
121
ensure
79
122
disconnect
80
123
end
0 commit comments