Module: Msf::Auxiliary::Login

Included in:
Metasploit::Framework::Telnet::Client, Exploit::Remote::Telnet
Defined in:
lib/msf/core/auxiliary/login.rb

Overview

This module exposes methods that may be useful to exploits that deal with servers that require authentication via /bin/login

Constant Summary collapse

NULL =
"\000"
CR =
"\r"
LF =
"\n"
EOL =
CR + LF

Instance Method Summary collapse

Instance Method Details

#busy_message?Boolean

Returns:

  • (Boolean)

127
128
129
130
131
132
133
134
# File 'lib/msf/core/auxiliary/login.rb', line 127

def busy_message?
  recvn = @recvd.gsub(@busy_regex, '')
  if(recvn != @recvd)
    @recvd = recvn.strip
    return true
  end
  false
end

#command_echo?(cmd) ⇒ Boolean

Returns:

  • (Boolean)

109
110
111
112
113
114
115
116
# File 'lib/msf/core/auxiliary/login.rb', line 109

def command_echo?(cmd)
  recvn = @recvd.gsub(/^(\s*#{cmd}\r?\n\s*|\s*\*+\s*)/, '')
  if(recvn != @recvd)
    @recvd = recvn
    return true
  end
  false
end

#create_login_ivarsObject


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/msf/core/auxiliary/login.rb', line 27

def 
  # Appended to by each read and gets reset after each send.  Doing it
  # this way lets us deal with partial reads in the middle of expect
  # strings, e.g., the first recv returns "Pa" and the second returns
  # "ssword: "
  @recvd = ''
  @trace = ''

  #
  # Some of these regexes borrowed from NeXpose, others added from datasets
  #
  @login_regex = /(?:log[io]n( name|)|user( ?name|id|))\s*\:/i
  @password_regex = /(?:password|passwd)\s*\:/i
  @false_failure_regex = /(?:(^\s*last)\ login *\:|allows only\ .*\ Telnet\ Client\ License)/i
  @failure_regex = /(?:
      Incorrect | Unknown  | Fail      | Invalid  |
      Login     | Password | Passwd    | Username |
      Unable    | Error    | Denied    | Reject   |
      Refuse    | Close    | Closing   | %\ Bad   |
      Sorry     |
      ^http | html |
      Not\ on\ system\ console |
      Enter\ username\ and\ password |
      Auto\ Apply\ On |
      YOU\ LOGGED\ IN\ USING\ ALL\ UPPERCASE\ CHARACTERS|
      \n\*$ |
      (Login ?|User ?)(name|): |
      ^\s*\<[a-f0-9]+\>\s*$ |
      ^\s*220.*FTP|
      not\ allowed\ to\ log\ in
    )/mix

  @waiting_regex = /(?:
    .*please\ wait.* |
    .*one\ minute.*
  )/mix

  @busy_regex = /(?:
    Another\ telnet\ session\ is\ in\ progress | Disconnecting\.\.\.
  )/mix

  @success_regex = /(?:
      list\ of\ built-in     |
      sh.*[\#\$]\s*$         |
      \[\/\]\s*$             |
      or\ the\ MENU\ system  |
      Password\ is\ not\ set |
      logging\ in\ as\ visitor |
      Login\ successful
    )/mix
end

#initialize(info = {}) ⇒ Object

Creates an instance of a login negotiation module.


21
22
23
24
25
# File 'lib/msf/core/auxiliary/login.rb', line 21

def initialize(info = {})
  super

  
end

#login_failed?Boolean

Returns:

  • (Boolean)

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/msf/core/auxiliary/login.rb', line 144

def 
  # Naively, failure means matching the failure regex.
  #
  # However, this leads to problems with false positives in the case of
  # "login:" because unix systems commonly show "Last login: Sat Jan  3
  # 20:22:52" upon successful login, so check against a false-positive
  # regex, also.
  #

  # Empty strings should not count
  if @recvd.strip.length == 0
    return true
  end

  # If we have not seen a newline, this is likely an echo'd prompt
  if ! @recvd.index("\n")
    return true
  end

  # We do have a set of highly-accurate success patterns
  if (@recvd =~ @success_regex)
    return false
  end

  if @recvd =~ @failure_regex
    if @recvd !~ @false_failure_regex
      return true
    end
  end
  return false
end

#login_prompt?Boolean

Returns:

  • (Boolean)

104
105
106
107
# File 'lib/msf/core/auxiliary/login.rb', line 104

def 
  return true if @recvd =~ @login_regex
  return false
end

#login_succeeded?Boolean

Returns:

  • (Boolean)

176
177
178
179
180
181
# File 'lib/msf/core/auxiliary/login.rb', line 176

def 
  # Much easier to test for failure than success because a few key words
  # mean failure whereas all kinds of crap is used for success, much of
  # which also shows up in failure messages.
  return (not )
end

#password_prompt?(username = nil) ⇒ Boolean

Returns:

  • (Boolean)

136
137
138
139
140
141
142
# File 'lib/msf/core/auxiliary/login.rb', line 136

def password_prompt?(username=nil)
  return true if(@recvd =~ @password_regex)
  if username
    return true if !(username.empty?) and @recvd.to_s.include?("#{username}'s")
  end
  return false
end

#raw_send(cmd, nsock = self.sock) ⇒ Object

This method transmits a telnet command and does not wait for a response

Resets the @recvd buffer


221
222
223
224
225
# File 'lib/msf/core/auxiliary/login.rb', line 221

def raw_send(cmd, nsock = self.sock)
  @recvd = ''
  @trace << cmd
  nsock.put(cmd)
end

#recv(fd = self.sock, timeout = 10) ⇒ Object

Appends to the @recvd buffer which is used to tell us whether we're at a login prompt, a password prompt, or a working shell.


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/msf/core/auxiliary/login.rb', line 83

def recv(fd=self.sock, timeout=10)

  data = ''

  begin
    data = fd.get_once(-1, timeout)
    return nil if not data or data.length == 0

    # combine EOL into "\n"
    data.gsub!(/#{EOL}/no, "\n")

    @trace << data
    @recvd << data
    fd.flush

  rescue ::EOFError, ::Errno::EPIPE
  end

  data
end

#recv_all(nsock = self.sock, timeout = 10) ⇒ Object


211
212
213
214
# File 'lib/msf/core/auxiliary/login.rb', line 211

def recv_all(nsock = self.sock, timeout = 10)
  # Make sure we read something in
  wait_for(/./)
end

#send_pass(pass, nsock = self.sock) ⇒ Object

This method completes user authentication by sending the supplied password


197
198
199
200
201
202
203
# File 'lib/msf/core/auxiliary/login.rb', line 197

def send_pass(pass, nsock = self.sock)
  got_prompt = wait_for(@password_regex)
  if not got_prompt
    print_error("#{rhost} - Something is wrong, didn't get a password prompt")
  end
  return send_recv("#{pass}\r\n")
end

#send_recv(msg, nsock = self.sock) ⇒ Object


205
206
207
208
209
# File 'lib/msf/core/auxiliary/login.rb', line 205

def send_recv(msg, nsock = self.sock)
  raw_send(msg, nsock)
  recv_all(nsock)
  return @recvd
end

#send_user(user, nsock = self.sock) ⇒ Object

This method logs in as the supplied user by transmitting the username


186
187
188
189
190
191
192
# File 'lib/msf/core/auxiliary/login.rb', line 186

def send_user(user, nsock = self.sock)
  got_prompt = wait_for(@login_regex)
  if not got_prompt
    print_error("#{rhost} - Something is wrong, didn't get a login prompt")
  end
  return send_recv("#{user}\r\n")
end

#wait_for(expect, nsock = self.sock) ⇒ Object

Wait for the supplied string (or Regexp) to show up on the socket, or a timeout


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/msf/core/auxiliary/login.rb', line 231

def wait_for(expect, nsock = self.sock)
  if expect.kind_of? Regexp
    regx = expect
  else
    regx = /#{Regexp.quote(expect)}/i
  end
  return true if @recvd =~ regx

  resp = ''
  while (resp and not @recvd =~ regx)
    resp = recv(nsock)
  end

  return (@recvd =~ regx)
end

#waiting_message?Boolean

Returns:

  • (Boolean)

118
119
120
121
122
123
124
125
# File 'lib/msf/core/auxiliary/login.rb', line 118

def waiting_message?
  recvn = @recvd.gsub(@waiting_regex, '')
  if(recvn != @recvd)
    @recvd = recvn.strip
    return true
  end
  false
end