Class: Rex::Proto::SMB::SimpleClient

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/proto/smb/simple_client.rb,
lib/rex/proto/smb/simple_client/open_file.rb,
lib/rex/proto/smb/simple_client/open_pipe.rb

Direct Known Subclasses

SimpleClientPipe

Defined Under Namespace

Classes: OpenFile, OpenPipe

Constant Summary collapse

CONST =

Some short-hand class aliases

Rex::Proto::SMB::Constants
CRYPT =
Rex::Proto::SMB::Crypt
UTILS =
Rex::Proto::SMB::Utils
XCEPT =
Rex::Proto::SMB::Exceptions
EVADE =
Rex::Proto::SMB::Evasions
DEFAULT_VERSIONS =
[1, 2, 3].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(socket, direct = false, versions = DEFAULT_VERSIONS, always_encrypt: true, backend: nil, client: nil, msf_session: nil) ⇒ SimpleClient

Pass the socket object and a boolean indicating whether the socket is netbios or cifs



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
# File 'lib/rex/proto/smb/simple_client.rb', line 30

def initialize(socket, direct = false, versions = DEFAULT_VERSIONS, always_encrypt: true, backend: nil, client: nil, msf_session: nil)
  self.msf_session = msf_session
  session_lifetime do
    self.socket = socket
    self.direct = direct
    self.versions = versions
    self.shares = {}
    self.server_max_buffer_size = 1024 # 4356 (workstation) or 16644 (server) expected

    if !client.nil?
      self.client = client
    elsif (self.versions == [1] && backend.nil?) || backend == :rex
      self.client = Rex::Proto::SMB::Client.new(socket)
    elsif (backend.nil? || backend == :ruby_smb)
      self.client = RubySMB::Client.new(RubySMB::Dispatcher::Socket.new(self.socket, read_timeout: 60),
                                        username: '',
                                        password: '',
                                        smb1: self.versions.include?(1),
                                        smb2: self.versions.include?(2),
                                        smb3: self.versions.include?(3),
                                        always_encrypt: always_encrypt
                    )

      self.client.evasion_opts = {
        # Padding is performed between packet headers and data
        'pad_data' => EVADE::EVASION_NONE,
        # File path padding is performed on all open/create calls
        'pad_file' => EVADE::EVASION_NONE,
        # Modify the \PIPE\ string in trans_named_pipe calls
        'obscure_trans_pipe' => EVADE::EVASION_NONE,
      }
    end
    @address, @port = self.socket.peerinfo.split(':')
  end
end

Instance Attribute Details

#addressObject Also known as: peerhost

Returns the value of attribute address.



27
28
29
# File 'lib/rex/proto/smb/simple_client.rb', line 27

def address
  @address
end

#clientObject

Private accessors



25
26
27
# File 'lib/rex/proto/smb/simple_client.rb', line 25

def client
  @client
end

#directObject

Private accessors



25
26
27
# File 'lib/rex/proto/smb/simple_client.rb', line 25

def direct
  @direct
end

#last_errorObject

Public accessors



22
23
24
# File 'lib/rex/proto/smb/simple_client.rb', line 22

def last_error
  @last_error
end

#last_shareObject

Private accessors



25
26
27
# File 'lib/rex/proto/smb/simple_client.rb', line 25

def last_share
  @last_share
end

#msf_sessionObject

Private accessors



25
26
27
# File 'lib/rex/proto/smb/simple_client.rb', line 25

def msf_session
  @msf_session
end

#portObject

Returns the value of attribute port.



27
28
29
# File 'lib/rex/proto/smb/simple_client.rb', line 27

def port
  @port
end

#server_max_buffer_sizeObject

Public accessors



22
23
24
# File 'lib/rex/proto/smb/simple_client.rb', line 22

def server_max_buffer_size
  @server_max_buffer_size
end

#sharesObject

Private accessors



25
26
27
# File 'lib/rex/proto/smb/simple_client.rb', line 25

def shares
  @shares
end

#socketObject

Private accessors



25
26
27
# File 'lib/rex/proto/smb/simple_client.rb', line 25

def socket
  @socket
end

#versionsObject

Private accessors



25
26
27
# File 'lib/rex/proto/smb/simple_client.rb', line 25

def versions
  @versions
end

Instance Method Details

#connect(share) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/rex/proto/smb/simple_client.rb', line 186

def connect(share)
  session_lifetime do
    ok = self.client.tree_connect(share)

    if self.client.is_a?(RubySMB::Client)
      tree_id = ok.id
    else
      tree_id = ok['Payload']['SMB'].v['TreeID']
    end

    self.shares[share] = tree_id
    self.last_share = share
  end
end

#create_pipe(path, perm = 'o') ⇒ Object



258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/rex/proto/smb/simple_client.rb', line 258

def create_pipe(path, perm = 'o')
  session_lifetime do
    disposition = UTILS.create_mode_to_disposition(perm)
    ok = self.client.create_pipe(path, disposition)

    if self.client.is_a?(RubySMB::Client)
      file_id = ok
    else
      file_id = ok['Payload'].v['FileID']
    end

    fh = OpenPipe.new(self.client, path, self.client.last_tree_id, file_id, self.versions)
  end
end

#delete(*args) ⇒ Object



248
249
250
251
252
253
254
255
256
# File 'lib/rex/proto/smb/simple_client.rb', line 248

def delete(*args)
  session_lifetime do
    if self.client.is_a?(RubySMB::Client)
      self.client.delete(args[0])
    else
      self.client.delete(*args)
    end
  end
end

#disconnect(share) ⇒ Object



201
202
203
204
205
206
207
208
209
210
# File 'lib/rex/proto/smb/simple_client.rb', line 201

def disconnect(share)
  session_lifetime do
    if self.shares[share]
      ok = self.client.tree_disconnect(self.shares[share])
      self.shares.delete(share)
      return ok
    end
    false
  end
end

#login(name = '', user = '', pass = '', domain = '', verify_signature = false, usentlmv2 = false, usentlm2_session = true, send_lm = true, use_lanman_key = false, send_ntlm = true, native_os = 'Windows 2000 2195', native_lm = 'Windows 2000 5.0', spnopt = {}) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/rex/proto/smb/simple_client.rb', line 66

def (name = '', user = '', pass = '', domain = '',
    verify_signature = false, usentlmv2 = false, usentlm2_session = true,
    send_lm = true, use_lanman_key = false, send_ntlm = true,
    native_os = 'Windows 2000 2195', native_lm = 'Windows 2000 5.0', spnopt = {})

  session_lifetime do
    begin

      if (self.direct != true)
        self.client.session_request(name)
      end
      self.client.native_os = native_os
      self.client.native_lm = native_lm
      self.client.verify_signature = verify_signature
      self.client.use_ntlmv2 = usentlmv2
      self.client.usentlm2_session = usentlm2_session
      self.client.send_lm = send_lm
      self.client.use_lanman_key =  use_lanman_key
      self.client.send_ntlm = send_ntlm

      dlog("SMB version(s) to negotiate: #{self.versions}")
      ok = self.client.negotiate
      dlog("Negotiated SMB version: SMB#{negotiated_smb_version}")

      if self.client.is_a?(RubySMB::Client)
        self.server_max_buffer_size = self.client.server_max_buffer_size
      else
        self.server_max_buffer_size = ok['Payload'].v['MaxBuff']
      end

      # Disable NTLMv2 Session for Windows 2000 (breaks authentication on some systems)
      # XXX: This in turn breaks SMB auth for Windows 2000 configured to enforce NTLMv2
      # XXX: Tracked by ticket #4785#4785
      if self.client.native_lm =~ /Windows 2000 5\.0/ and usentlm2_session
      #	self.client.usentlm2_session = false
      end

      self.client.spnopt = spnopt

      # In case the user unsets the username or password option, we make sure this is
      # always a string
      user ||= ''
      pass ||= ''

      res = self.client.session_setup(user, pass, domain)
    rescue ::Interrupt
      raise $!
    rescue ::Exception => e
      elog(e)
      n = XCEPT::LoginError.new
      n.source = e
      if e.respond_to?('error_code') && e.respond_to?('get_error')
        n.error_code   = e.error_code
        n.error_reason = e.get_error(e.error_code)
      end
      raise n
    end

    # RubySMB does not raise any exception if the Session Setup fails
    if self.client.is_a?(RubySMB::Client) && res != WindowsError::NTStatus::STATUS_SUCCESS
      n = XCEPT::LoginError.new
      n.source       = res
      n.error_code   = res.value
      n.error_reason = res.name
      raise n
    end

    return true
  end
end

#login_split_next_ntlm1(user, domain, hash_lm, hash_nt) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/rex/proto/smb/simple_client.rb', line 166

def (user, domain, hash_lm, hash_nt)
  session_lifetime do
    begin
      ok = self.client.session_setup_no_ntlmssp_prehash(user, domain, hash_lm, hash_nt)
    rescue ::Interrupt
      raise $!
    rescue ::Exception => e
      n = XCEPT::LoginError.new
      n.source = e
      if(e.respond_to?('error_code'))
        n.error_code   = e.error_code
        n.error_reason = e.get_error(e.error_code)
      end
      raise n
    end

    return true
  end
end

#login_split_start_ntlm1(name = '') ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/rex/proto/smb/simple_client.rb', line 138

def (name = '')

  session_lifetime do
    begin

      if (self.direct != true)
        self.client.session_request(name)
      end

      # Disable extended security
      self.client.negotiate(false)
    rescue ::Interrupt
      raise $!
    rescue ::Exception => e
      n = XCEPT::LoginError.new
      n.source = e
      if(e.respond_to?('error_code'))
        n.error_code   = e.error_code
        n.error_reason = e.get_error(e.error_code)
      end
      raise n
    end

    return true
  end
end

#negotiated_smb_versionObject



279
280
281
282
# File 'lib/rex/proto/smb/simple_client.rb', line 279

def negotiated_smb_version
  return 1 if self.client.is_a?(Rex::Proto::SMB::Client)
  self.client.negotiated_smb_version || -1
end

#open(path, perm, chunk_size = 48000, read: true, write: false) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/rex/proto/smb/simple_client.rb', line 212

def open(path, perm, chunk_size = 48000, read: true, write: false)
  session_lifetime do
    if self.client.is_a?(RubySMB::Client)
      mode = 0
      if perm.include?('c')
        if perm.include?('o')
          mode = RubySMB::Dispositions::FILE_OPEN_IF
        elsif perm.include?('t')
          mode = RubySMB::Dispositions::FILE_OVERWRITE_IF
        else
          mode = RubySMB::Dispositions::FILE_CREATE
        end
      else
        if perm.include?('o')
          mode = RubySMB::Dispositions::FILE_OPEN
        elsif perm.include?('t')
          mode = RubySMB::Dispositions::FILE_OVERWRITE
        end
      end

      file_id = self.client.open(path, mode, read: true, write: write || perm.include?('w'))

    else
      mode = UTILS.open_mode_to_mode(perm)
      access = UTILS.open_mode_to_access(perm)

      ok = self.client.open(path, mode, access)
      file_id = ok['Payload'].v['FileID']
    end

    fh = OpenFile.new(self.client, path, self.client.last_tree_id, file_id, self.versions)
    fh.chunk_size = chunk_size
    fh
  end
end

#peerinfoObject



290
291
292
# File 'lib/rex/proto/smb/simple_client.rb', line 290

def peerinfo
  "#{peerhost}:#{peerport}"
end

#peerportObject



286
287
288
# File 'lib/rex/proto/smb/simple_client.rb', line 286

def peerport
  port.to_i
end

#trans_pipe(fid, data, no_response = nil) ⇒ Object



273
274
275
276
277
# File 'lib/rex/proto/smb/simple_client.rb', line 273

def trans_pipe(fid, data, no_response = nil)
  session_lifetime do
    client.trans_named_pipe(fid, data, no_response)
end
end