Module: Msf::Exploit::Remote::SMB::Client

Includes:
NTLM::Client, Tcp
Included in:
Authenticated, PipeAuditor
Defined in:
lib/msf/core/exploit/remote/smb/client.rb

Overview

This mixin provides utility methods for interacting with a SMB/CIFS service on a remote machine. These methods may generally be useful in the context of exploitation. This mixin extends the Tcp exploit mixin. Only one SMB service can be accessed at a time using this class.

Defined Under Namespace

Modules: Authenticated, KerberosAuthentication, LocalPaths, PipeAuditor, Psexec, Psexec_MS17_010, RemotePaths, WebExec

Constant Summary collapse

SIMPLE =

These constants are unused here, but may be used in some code that includes this. Local definitions should be preferred.

Rex::Proto::SMB::SimpleClient
XCEPT =
Rex::Proto::SMB::Exceptions
CONST =
Rex::Proto::SMB::Constants
DCERPCPacket =

Alias over the Rex DCERPC protocol modules

Rex::Proto::DCERPC::Packet
DCERPCClient =
Rex::Proto::DCERPC::Client
DCERPCResponse =
Rex::Proto::DCERPC::Response
DCERPCUUID =
Rex::Proto::DCERPC::UUID
NDR =
Rex::Encoder::NDR

Instance Attribute Summary collapse

Attributes included from Tcp

#sock

Instance Method Summary collapse

Methods included from Tcp

#chost, #cleanup, #connect_timeout, #cport, #disconnect, #handler, #lhost, #lport, #peer, #print_prefix, #proxies, #rhost, #rport, #set_tcp_evasions, #shutdown, #ssl, #ssl_cipher, #ssl_verify_mode, #ssl_version

Instance Attribute Details

#simpleRex::Proto::SMB::SimpleClient



896
897
898
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 896

def simple
  @simple
end

Instance Method Details

#connect(global = true, versions: [], backend: nil) ⇒ Object

Override Tcp#connect to setup an SMB connection and configure evasion options

Also populates #simple.



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
136
137
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 97

def connect(global=true, versions: [], backend: nil)
  if versions.nil? || versions.empty?
    versions = datastore['SMB::ProtocolVersion'].split(',').map(&:strip).reject(&:blank?).map(&:to_i)
    # if the user explicitly set the protocol version to 1, still use ruby_smb
    backend ||= :ruby_smb if versions == [1]
  end

  disconnect() if global

  s = super(global, {'SSL' => false})
  self.sock = s if global

  # Disable direct SMB when SMBDirect has not been set
  # and the destination port is configured as 139
  direct = smb_direct
  if(datastore.default?('SMBDirect') and rport.to_i == 139)
    direct = false
  end

  c = Rex::Proto::SMB::SimpleClient.new(s, direct, versions, always_encrypt: datastore['SMB::AlwaysEncrypt'], backend: backend)

  # setup pipe evasion foo
  if datastore['SMB::pipe_evasion']
    # XXX - insert code to change the instance of the read/write functions to do segmentation
  end

  if (datastore['SMB::pad_data_level'])
    c.client.evasion_opts['pad_data'] = datastore['SMB::pad_data_level']
  end

  if (datastore['SMB::pad_file_level'])
    c.client.evasion_opts['pad_file'] = datastore['SMB::pad_file_level']
  end

  if (datastore['SMB::obscure_trans_pipe_level'])
    c.client.evasion_opts['obscure_trans_pipe'] = datastore['SMB::obscure_trans_pipe_level']
  end

  self.simple = c if global
  c
end

#domainObject



232
233
234
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 232

def domain
  datastore['SMBDomain']
end

#domain_username_split(user) ⇒ Object

If the username contains a / slash, then split it as a domain/username. NOTE: this is predicated on forward slashes, and not Microsoft's backwards slash convention.



248
249
250
251
252
253
254
255
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 248

def domain_username_split(user)
  return user if(user.nil? || user.empty?)
  if !user[/\//] # Only /, not \!
    return [nil,user]
  else
    return user.split("/",2)
  end
end

#initialize(info = {}) ⇒ Object



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
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 29

def initialize(info = {})
  super

  register_evasion_options(
  [
    OptBool.new('SMB::pipe_evasion',     [ true, 'Enable segmented read/writes for SMB Pipes', false]),
    OptInt.new('SMB::pipe_write_min_size', [ true, 'Minimum buffer size for pipe writes',  1]),
    OptInt.new('SMB::pipe_write_max_size', [ true, 'Maximum buffer size for pipe writes', 1024]),
    OptInt.new('SMB::pipe_read_min_size',  [ true, 'Minimum buffer size for pipe reads',  1]),
    OptInt.new('SMB::pipe_read_max_size',  [ true, 'Maximum buffer size for pipe reads', 1024]),
    OptInt.new('SMB::pad_data_level',  [ true, 'Place extra padding between headers and data (level 0-3)', 0]),
    OptInt.new('SMB::pad_file_level',  [ true, 'Obscure path names used in open/create (level 0-3)', 0]),
    OptInt.new('SMB::obscure_trans_pipe_level',  [ true, 'Obscure PIPE string in TransNamedPipe (level 0-3)', 0]),

  ], Msf::Exploit::Remote::SMB::Client)

  register_advanced_options(
  [
    OptBool.new('SMBDirect', [ false, 'The target port is a raw SMB service (not NetBIOS)', true ]),
    OptString.new('SMBUser', [ false, 'The username to authenticate as', ''], fallbacks: ['USERNAME']),
    OptString.new('SMBPass', [ false, 'The password for the specified username', ''], fallbacks: ['PASSWORD']),
    OptString.new('SMBDomain',  [ false, 'The Windows domain to use for authentication', '.'], fallbacks: ['DOMAIN']),
    OptString.new('SMBName', [ true, 'The NetBIOS hostname (required for port 139 connections)', '*SMBSERVER']),
    OptBool.new('SMB::VerifySignature', [ true, "Enforces client-side verification of server response signatures", false]),
    OptInt.new('SMB::ChunkSize', [ true, 'The chunk size for SMB segments, bigger values will increase speed but break NT 4.0 and SMB signing', 500]),
    #
    # Control the identified operating system of the client
    #
    OptString.new('SMB::Native_OS', [ true, 'The Native OS to send during authentication', 'Windows 2000 2195']),
    OptString.new('SMB::Native_LM', [ true, 'The Native LM to send during authentication', 'Windows 2000 5.0']),
    OptString.new(
      'SMB::ProtocolVersion',
      [
        true,
        'One or a list of coma-separated SMB protocol versions to '\
        'negotiate (e.g. "1" or "1,2" or "2,3,1")', '1,2,3'
      ],
      regex: '^[123](?:,[123])*$'
    ),
    OptBool.new(
      'SMB::AlwaysEncrypt',
      [
        true,
        'Enforces encryption even if the server does not require it (SMB3.x only). '\
        'Note that when it is set to false, the SMB client will still '\
        'encrypt the communication if the server requires it',
        true
      ]
    )
  ], Msf::Exploit::Remote::SMB::Client)

  register_options(
  [
    Opt::RHOST,
    OptPort.new('RPORT', [ true, 'The SMB service port', 445])
  ], Msf::Exploit::Remote::SMB::Client)

  register_autofilter_ports([ 139, 445])
  register_autofilter_services(%W{ netbios-ssn microsoft-ds })
end

#smb_create(pipe) ⇒ Object

This method opens a handle to an IPC pipe



213
214
215
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 213

def smb_create(pipe)
  self.simple.create_pipe(pipe)
end

#smb_directObject



228
229
230
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 228

def smb_direct
  datastore['SMBDirect']
end

#smb_enumprinters(flags, name, level, blen) ⇒ Object

Calls the EnumPrinters() function of the spooler service



317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 317

def smb_enumprinters(flags, name, level, blen)
  stub =
    NDR.long(flags) +
    (name ? NDR.uwstring(name) : NDR.long(0)) +
    NDR.long(level) +
    NDR.long(rand(0xffffffff)+1)+
    NDR.long(blen) +
    "\x00" * blen +
    NDR.long(blen)

  handle = dcerpc_handle(
    '12345678-1234-abcd-ef00-0123456789ab', '1.0',
    'ncacn_np', ["\\SPOOLSS"]
  )

  begin
    dcerpc_bind(handle)
    dcerpc.call(0x00, stub)
    return dcerpc.last_response.stub_data
  rescue ::Interrupt
    raise $!
  rescue ::Exception => e
    return nil
  end
end

#smb_enumprintprovidersObject

This method dumps the print provider strings from the spooler



344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 344

def smb_enumprintproviders
  resp = smb_enumprinters(8, nil, 1, 0)
  return nil if not resp
  rptr, tmp, blen = resp.unpack("V*")

  resp = smb_enumprinters(8, nil, 1, blen)
  return nil if not resp

  bcnt,pcnt,stat = resp[-12, 12].unpack("VVV")
  return nil if stat != 0
  return nil if pcnt == 0
  return nil if bcnt > blen
  return nil if pcnt < 3

  #
  # The correct way, which leads to invalid offsets :-(
  #
  #providers = []
  #
  #0.upto(pcnt-1) do |i|
  # flags,desc_o,name_o,comm_o = resp[8 + (i*16), 16].unpack("VVVV")
  #
  # #desc = read_unicode(resp,8+desc_o).gsub("\x00", '')
  # #name = read_unicode(resp,8+name_o).gsub("\x00", '')
  # #comm = read_unicode(resp,8+comm_o).gsub("\x00", '')
  # #providers << [flags,desc,name,comm]
  #end
  #
  #providers

  return resp

end

#smb_file_exist?(file) ⇒ Boolean

Whether a remote file exists

Parameters:

  • file (String)

    Path to a file to remove, relative to the most-recently connected share

Returns:

  • (Boolean)

Raises:



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 271

def smb_file_exist?(file)
  begin
    fd = simple.open(file, 'o')
  rescue RubySMB::Error::UnexpectedStatusCode => e
    found = false
  rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
    # If attempting to open the file results in a "*_NOT_FOUND" error,
    # then we can be sure the file is not there.
    #
    # Copy-pasted from smb/exceptions.rb to avoid the gymnastics
    # required to pull them out of a giant inverted hash
    #
    # 0xC0000034 => "STATUS_OBJECT_NAME_NOT_FOUND",
    # 0xC000003A => "STATUS_OBJECT_PATH_NOT_FOUND",
    # 0xC0000225 => "STATUS_NOT_FOUND",
    error_is_not_found = [ 0xC0000034, 0xC000003A, 0xC0000225 ].include?(e.error_code)
    # If the server returns some other error, then there was a
    # permissions problem or some other difficulty that we can't
    # really account for and hope the caller can deal with it.
    raise e unless error_is_not_found
    found = !error_is_not_found
  else
    # There was no exception, so we know the file is openable
    fd.close
    found = true
  end

  found
end

#smb_file_rm(file) ⇒ void

This method returns an undefined value.

Remove remote file

Parameters:

  • file (String)

    Path to a file to remove, relative to the most-recently connected share



305
306
307
308
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 305

def smb_file_rm(file)
  fd = smb_open(file, 'ro')
  fd.delete
end

#smb_fingerprintObject

This method performs an extensive set of fingerprinting operations



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 379

def smb_fingerprint
  fprint = {}

  # Connect to the server if needed
  if not self.simple
    # native_lm/native_os is only available with SMB1
    vprint_status('Force SMB1 since SMB fingerprint needs native_lm/native_os information')
    connect(versions: [1])
    # The login method can throw any number of exceptions, we don't
    # care since we still get the native_lm/native_os.
    begin
      ()
    rescue ::Rex::Proto::SMB::Exceptions::NoReply,
           ::Rex::Proto::SMB::Exceptions::ErrorCode,
           ::Rex::Proto::SMB::Exceptions::LoginError => e
      dlog("Error with SMB fingerprint: #{e.message}")
    end
  end

  fprint['native_os'] = smb_peer_os()
  fprint['native_lm'] = smb_peer_lm()

  # Leverage Recog for SMB native OS fingerprinting
  fp_match = Recog::Nizer.match('smb.native_os', fprint['native_os']) || { }

  os = fp_match['os.product'] || 'Unknown'
  sp = fp_match['os.version'] || ''

  # Metasploit prefers 'Windows 2003' vs 'Windows Server 2003'
  if os =~ /^Windows Server/
    os = os.sub(/^Windows Server/, 'Windows')
  end

  if fp_match['os.edition']
    fprint['edition'] = fp_match['os.edition']
  end

  if fp_match['os.build']
    fprint['build'] = fp_match['os.build']
  end

  if sp == ''
    sp = smb_fingerprint_windows_sp(os)
  end

  lang = smb_fingerprint_windows_lang

  fprint['os']   = os
  fprint['sp']   = sp
  fprint['lang'] = lang

  fprint
end

#smb_fingerprint_windows_langObject

Determine the native language pack of a Windows system via SMB probes



541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 541

def smb_fingerprint_windows_lang

  #
  # Remote language detection via Print Providers
  # Credit: http://immunityinc.com/downloads/Remote_Language_Detection_in_Immunity_CANVAS.odt
  #

  lang = 'Unknown'

  sigs =
  {
    'English' =>
      [
        Rex::Text.to_unicode('Windows NT Remote Printers'),
        Rex::Text.to_unicode('LanMan Print Services')
      ],
    'Spanish' =>
      [
        Rex::Text.to_unicode('Impresoras remotas Windows NT'),
        Rex::Text.to_unicode('Impresoras remotas de Windows NT')
      ],
    'Italian' =>
      [
        Rex::Text.to_unicode('Stampanti remote di Windows NT'),
        Rex::Text.to_unicode('Servizi di stampa LanMan')
      ],
    'French' =>
      [
        Rex::Text.to_unicode('Imprimantes distantes NT'),
        Rex::Text.to_unicode('Imprimantes distantes pour Windows NT'),
        Rex::Text.to_unicode("Services d'impression LanMan")
      ],
    'German' =>
      [
        Rex::Text.to_unicode('Remotedrucker')
      ],
    'Portuguese - Brazilian' =>
      [
        Rex::Text.to_unicode('Impr. remotas Windows NT'),
        Rex::Text.to_unicode('Impressoras remotas do Windows NT')
      ],
    'Portuguese' =>
      [
        Rex::Text.to_unicode('Imp. remotas do Windows NT')
      ],
    'Hungarian' =>
      [
        Rex::Text.to_unicode("\x54\xe1\x76\x6f\x6c\x69\x20\x6e\x79\x6f\x6d\x74\x61\x74\xf3\x6b")
      ],
    'Finnish' =>
      [
        Rex::Text.to_unicode("\x45\x74\xe4\x74\x75\x6c\x6f\x73\x74\x69\x6d\x65\x74")
      ],
    'Dutch' =>
      [
        Rex::Text.to_unicode('Externe printers voor NT')
      ],
    'Danish' =>
      [
        Rex::Text.to_unicode('Fjernprintere')
      ],
    'Swedish' =>
      [
        Rex::Text.to_unicode("\x46\x6a\xe4\x72\x72\x73\x6b\x72\x69\x76\x61\x72\x65")
      ],
    'Polish' =>
      [
        Rex::Text.to_unicode('Zdalne drukarki')
      ],
    'Czech'   =>
      [
        Rex::Text.to_unicode("\x56\x7a\x64\xe1\x6c\x65\x6e\xe9\x20\x74\x69\x73\x6b\xe1\x72\x6e\x79")
      ],
    'Turkish' =>
      [
        "\x59\x00\x61\x00\x7a\x00\x31\x01\x63\x00\x31\x01\x6c\x00\x61\x00\x72\x00"
      ],
    'Japanese' =>
      [
        "\xea\x30\xe2\x30\xfc\x30\xc8\x30\x20\x00\xd7\x30\xea\x30\xf3\x30\xbf\x30"
      ],
    'Chinese - Traditional' =>
      [
        "\xdc\x8f\x0b\x7a\x53\x62\x70\x53\x3a\x67"
      ],
    'Chinese - Traditional / Taiwan' =>
      [
        "\x60\x90\xef\x7a\x70\x53\x68\x88\x5f\x6a",
      ],
    'Korean' =>
      [
        "\xd0\xc6\xa9\xac\x20\x00\x04\xd5\xb0\xb9\x30\xd1",
      ],
    'Russian' =>
      [
        "\x1f\x04\x40\x04\x38\x04\x3d\x04\x42\x04\x35\x04\x40\x04\x4b\x04\x20\x00\x43\x04\x34\x04\x30\x04\x3b\x04\x35\x04\x3d\x04\x3d\x04\x3e\x04\x33\x04\x3e\x04\x20\x00\x34\x04\x3e\x04\x41\x04\x42\x04\x43\x04\x3f\x04\x30\x04",
      ],

  }

  begin
    prov = smb_enumprintproviders()
    if(prov)
      sigs.each_key do |k|
        sigs[k].each do |s|
          if(prov.index(s))
            lang = k
            break
          end
          break if lang != 'Unknown'
        end
        break if lang != 'Unknown'
      end

      if(lang == 'Unknown')

        @fpcache ||= {}
        mhash = ::Digest::MD5.hexdigest(prov[4,prov.length-4])

        if(not @fpcache[mhash])

          buff = "\n"
          buff << "*** NEW FINGERPRINT: PLEASE SEND TO [ msfdev[at]metasploit.com ]\n"
          buff << " VERS: $Revision$\n"
          buff << " HOST: #{rhost}\n"
          buff << "   OS: #{os}\n"
          buff << "   SP: #{sp}\n"

          prov.unpack("H*")[0].scan(/.{64}|.*/).each do |line|
            next if line.length == 0
            buff << "   FP: #{line}\n"
          end

          prov.split(/\x00\x00+/n).each do |line|
            line.gsub!("\x00",'')
            line.strip!
            next if line.length < 6

            buff <<  "  TXT: #{line}\n"
          end

          buff << "*** END FINGERPRINT\n"

          print_line(buff)

          @fpcache[mhash] = true
        end

      end
    end
  rescue ::Interrupt
    raise $!
  rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
  end
  lang
end

#smb_fingerprint_windows_sp(os) ⇒ Object

Determine the service pack level of a Windows system via SMB probes



436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 436

def smb_fingerprint_windows_sp(os)
  sp = ''

  if (os == 'Windows XP')
    # SRVSVC was blocked in SP2
    begin
      smb_create("\\SRVSVC")
      sp = 'Service Pack 0 / 1'
    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
      if (e.error_code == 0xc0000022)
        sp = 'Service Pack 2+'
      end
    end
  end

  if (os == 'Windows 2000' and sp.length == 0)
    # LLSRPC was blocked in a post-SP4 update
    begin
      smb_create("\\LLSRPC")
      sp = 'Service Pack 0 - 4'
    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
      if (e.error_code == 0xc0000022)
        sp = 'Service Pack 4 with MS05-010+'
      end
    end
  end

  #
  # Perform granular XP SP checks if LSARPC is exposed
  #
  if (os == 'Windows XP')

    #
    # Service Pack 2 added a range(0,64000) to opnum 0x22 in SRVSVC
    # Credit to spoonm for first use of unbounded [out] buffers
    #
    handle = dcerpc_handle(
      '4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
      'ncacn_np', ["\\BROWSER"]
    )

    begin
      dcerpc_bind(handle)

      stub =
        NDR.uwstring(Rex::Text.rand_text_alpha(rand(10)+1)) +
        NDR.wstring(Rex::Text.rand_text_alpha(rand(10)+1))  +
        NDR.long(64001) +
        NDR.long(0) +
        NDR.long(0)

      dcerpc.call(0x22, stub)
      sp = "Service Pack 0 / 1"

    rescue ::Interrupt
      raise $!
    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
    rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
    rescue ::Rex::Proto::DCERPC::Exceptions::Fault
      sp = "Service Pack 2+"
    rescue ::Exception
    end


    #
    # Service Pack 3 fixed information leaks via [unique][out] pointers
    # Call SRVSVC::NetRemoteTOD() to return [out] [ref] [unique]
    # Credit:
    #   Pointer leak is well known, but Immunity also covered in a paper
    #   Silent fix of pointer leak in SP3 and detection method by Rhys Kidd
    #
    handle = dcerpc_handle(
      '4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
      'ncacn_np', ["\\BROWSER"]
    )

    begin
      dcerpc_bind(handle)

      stub = NDR.uwstring(Rex::Text.rand_text_alpha(rand(8)+1))
      resp = dcerpc.call(0x1c, stub)

      if(resp and resp[0,4] == "\x00\x00\x02\x00")
        sp = "Service Pack 3"
      else
        if(resp and sp =~ /Service Pack 2\+/)
          sp = "Service Pack 2"
        end
      end

    rescue ::Interrupt
      raise $!
    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
    rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
    rescue ::Exception
    end
  end

  sp
end

#smb_hostnameObject



224
225
226
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 224

def smb_hostname
  datastore['SMBName'] || '*SMBSERVER'
end

#smb_lanman_netshareenumallObject

Retrieve a list of shares via the NetShareEnumAll function in the LANMAN service This method can only return shares with names 12 bytes or less



852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 852

def smb_lanman_netshareenumall
  shares = []
  begin
    # XXX: #trans is not supported by RubySMB
    res = self.simple.client.trans(
      "\\PIPE\\LANMAN",
      (
        [0x00].pack('v') +
        "WrLeh\x00"   +
        "B13BWz\x00"  +
        [0x01, 65406].pack("vv")
      ))
  rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
    vprint_error("Could not enumerate shares via LANMAN")
    return []
  end
  if res.nil?
    vprint_error("Could not enumerate shares via LANMAN")
    return []
  end

  lerror, lconv, lentries, lcount = res['Payload'].to_s[
    res['Payload'].v['ParamOffset'],
    res['Payload'].v['ParamCount']
  ].unpack("v4")

  data = res['Payload'].to_s[
    res['Payload'].v['DataOffset'],
    res['Payload'].v['DataCount']
  ]

  0.upto(lentries - 1) do |i|
    sname,tmp = data[(i * 20) +  0, 14].split("\x00")
    stype     = data[(i * 20) + 14, 2].unpack('v')[0]
    scoff     = data[(i * 20) + 16, 2].unpack('v')[0]
    scoff -= lconv if lconv != 0
    scomm,tmp = data[scoff, data.length - scoff].split("\x00")
    shares << [ sname, smb_lookup_share_type(stype), scomm]
  end

  shares
end

#smb_login(simple_client = self.simple) ⇒ void

This method returns an undefined value.

Establishes an SMB session over the default socket and connects to the IPC$ share.

You should call #connect before calling this

Parameters:



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 151

def (simple_client = self.simple)
  # Override the default RubySMB capabilities with Kerberos authentication
  if datastore['SMB::Auth'] == Msf::Exploit::Remote::AuthOption::KERBEROS
    fail_with(Msf::Exploit::Failure::BadConfig, 'The Smb::Rhostname option is required when using Kerberos authentication.') if datastore['Smb::Rhostname'].blank?
    fail_with(Msf::Exploit::Failure::BadConfig, 'The SMBDomain option is required when using Kerberos authentication.') if datastore['SMBDomain'].blank?
    offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(datastore['Smb::KrbOfferedEncryptionTypes'])
    fail_with(Msf::Exploit::Failure::BadConfig, 'At least one encryption type is required when using Kerberos authentication.') if offered_etypes.empty?

    kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::SMB.new(
      host: datastore['DomainControllerRhost'].blank? ? nil : datastore['DomainControllerRhost'],
      hostname: datastore['Smb::Rhostname'],
      proxies: datastore['Proxies'],
      realm: datastore['SMBDomain'],
      username: datastore['SMBUser'],
      password: datastore['SMBPass'],
      framework: framework,
      framework_module: self,
      cache_file: datastore['Smb::Krb5Ccname'].blank? ? nil : datastore['Smb::Krb5Ccname'],
      ticket_storage: kerberos_ticket_storage,
      offered_etypes: offered_etypes
    )

    simple_client.client.extend(Msf::Exploit::Remote::SMB::Client::KerberosAuthentication)
    simple_client.client.kerberos_authenticator = kerberos_authenticator
  end

  simple_client.(
    datastore['SMBName'],
    datastore['SMBUser'],
    datastore['SMBPass'],
    datastore['SMBDomain'],
    datastore['SMB::VerifySignature'],
    datastore['NTLM::UseNTLMv2'],
    datastore['NTLM::UseNTLM2_session'],
    datastore['NTLM::SendLM'],
    datastore['NTLM::UseLMKey'],
    datastore['NTLM::SendNTLM'],
    datastore['SMB::Native_OS'],
    datastore['SMB::Native_LM'],
    {:use_spn => datastore['NTLM::SendSPN'], :name =>  self.rhost}
  )
  # XXX: Any reason to connect to the IPC$ share in this method?
  simple_client.connect("\\\\#{datastore['RHOST']}\\IPC$")
end

#smb_lookup_share_type(val) ⇒ Object

Map an integer share type to a human friendly descriptor



699
700
701
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 699

def smb_lookup_share_type(val)
  [ 'DISK', 'PRINTER', 'DEVICE', 'IPC', 'SPECIAL', 'TEMPORARY' ][val]
end

#smb_netshareenumallObject

Retrieve a list of all shares using any available method



759
760
761
762
763
764
765
766
767
768
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 759

def smb_netshareenumall
  begin
    return smb_srvsvc_netshareenumall
  rescue Rex::Proto::SMB::Exceptions::ErrorCode, RubySMB::Error::RubySMBError => e
    vprint_error("Warning: NetShareEnumAll failed via Server Service: #{e}")
    return [] if self.simple.client.is_a?(RubySMB::Client)
    vprint_error("Falling back to LANMAN")
    return smb_lanman_netshareenumall
  end
end

#smb_netsharegetinfo(share) ⇒ Object

Retrieve detailed information about a specific share using any available method



704
705
706
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 704

def smb_netsharegetinfo(share)
 smb_srvsvc_netsharegetinfo(share)
end

#smb_open(path, perm, read: true, write: false) ⇒ Object

the default chunk size of 48000 for OpenFile is not compatible when signing is enabled (and with some nt4 implementations) cause it looks like MS windows refuse to sign big packet and send STATUS_ACCESS_DENIED fd.chunk_size = 500 is better



220
221
222
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 220

def smb_open(path, perm, read: true, write: false)
  self.simple.open(path, perm, datastore['SMB::ChunkSize'], read: read, write: write)
end

#smb_peer_lmObject

This method returns the native lanman version of the peer



205
206
207
208
209
210
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 205

def smb_peer_lm
  unless self.simple.negotiated_smb_version == 1
    print_warning("peer_native_lm is only available with SMB1 (current version: SMB#{self.simple.negotiated_smb_version})")
  end
  self.simple.client.peer_native_lm
end

#smb_peer_osObject

This method returns the native operating system of the peer



197
198
199
200
201
202
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 197

def smb_peer_os
  unless self.simple.negotiated_smb_version == 1
    print_warning("peer_native_os is only available with SMB1 (current version: SMB#{self.simple.negotiated_smb_version})")
  end
  self.simple.client.peer_native_os
end

#smb_srvsvc_netshareenumallObject

Retrieve a list of shares via the NetShareEnumAll function in the Server Service



771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 771

def smb_srvsvc_netshareenumall
  shares = []
  simple.connect("\\\\#{rhost}\\IPC$")
  handle = dcerpc_handle('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0', 'ncacn_np', ["\\srvsvc"])
  begin
    dcerpc_bind(handle)
  rescue Rex::Proto::SMB::Exceptions::ErrorCode, RubySMB::Error::RubySMBError => e
    vprint_error(e.message)
    return []
  end

  stubdata =
    NDR.uwstring("\\\\#{rhost}") +
    NDR.long(1)  #level

  ref_id = stubdata[0,4].unpack("V")[0]
  ctr = [1, ref_id + 4 , 0, 0].pack("VVVV")

  stubdata << ctr
  stubdata << NDR.align(ctr)
  stubdata << ["FFFFFFFF"].pack("H*")
  stubdata << [ref_id + 8, 0].pack("VV")
  response = dcerpc.call(0x0f, stubdata)
  res = response.dup
  win_error = res.slice!(-4, 4).unpack("V")[0]

  if win_error != 0
    raise RuntimeError, "Invalid DCERPC response: win_error = #{win_error}"
  end

  # Remove unused data
  res.slice!(0,12) # level, CTR header, Reference ID of CTR
  share_count = res.slice!(0, 4).unpack("V")[0]
  res.slice!(0,4) # Reference ID of CTR1
  share_max_count = res.slice!(0, 4).unpack("V")[0]

  if share_max_count != share_count
    raise RuntimeError, "Invalid DCERPC response: count != count max (#{share_count}/#{share_max_count})"
  end

  # ReferenceID / Type / ReferenceID of Comment
  types = res.slice!(0, share_count * 12).scan(/.{12}/n).map{|a| a[4,2].unpack("v")[0]}

  share_count.times do |t|
    length, offset, max_length = res.slice!(0, 12).unpack("VVV")
    if offset != 0
      raise RuntimeError, "Invalid DCERPC response: offset != 0 (#{offset})"
    end

    if length != max_length
      raise RuntimeError, "Invalid DCERPC response: length !=max_length (#{length}/#{max_length})"
    end
    name = res.slice!(0, 2 * length).gsub('\x00','')
    res.slice!(0,2) if length % 2 == 1 # pad

    comment_length, comment_offset, comment_max_length = res.slice!(0, 12).unpack("VVV")

    if comment_offset != 0
      raise RuntimeError, "Invalid DCERPC response: comment_offset != 0 (#{comment_offset})"
    end

    if comment_length != comment_max_length
      raise RuntimeError, "Invalid DCERPC response: comment_length != comment_max_length (#{comment_length}/#{comment_max_length})"
    end

    comment = res.slice!(0, 2 * comment_length)

    res.slice!(0,2) if comment_length % 2 == 1 # pad

    name    = Rex::Text.to_ascii(name).gsub("\x00", "")
    s_type  = smb_lookup_share_type(types[t])
    comment = Rex::Text.to_ascii(comment).gsub("\x00", "")

    shares << [ name, s_type, comment ]
  end

  shares
end

#smb_srvsvc_netsharegetinfo(share) ⇒ Object

Retrieve detailed share dinformation via the NetShareGetInfo function in the Server Service



709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 709

def smb_srvsvc_netsharegetinfo(share)
  shares = []
  simple.connect("\\\\#{rhost}\\IPC$")
  handle = dcerpc_handle('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0', 'ncacn_np', ["\\srvsvc"])
  begin
    dcerpc_bind(handle)
  rescue Rex::Proto::SMB::Exceptions::ErrorCode, RubySMB::Error::RubySMBError => e
    vprint_error(e.message)
    return []
  end

  stubdata =
    NDR.uwstring("\\\\#{rhost}") +
    NDR.wstring(share) +
    NDR.long(2)

  response = dcerpc.call(0x10, stubdata)

  if ! response
    raise RuntimeError, "Invalid DCERPC response: <empty>"
  end

  head = response.slice!(0, 40)
  if head.length != 40
    raise RuntimeError, "Invalid DCERPC response: not enough data"
  end

  share_info = {
    share_type: head[12, 4].unpack('V').first,
    permissions: head[20, 4].unpack('V').first,
    max_users: head[24, 4].unpack('V').first,
  }

  idx = 0

  [:share, :comment, :path, :password].each do |field|
    field_info = response[idx, 12].unpack("V*")
    break if field_info.length == 0
    idx += 12

    field_text = response[idx, field_info.first * 2]
    share_info[ field ] = field_text.gsub("\x00", '')
    idx += (field_info.first * 2)
    idx += (idx % 4)
  end

  share_info
end

#smbhostObject



236
237
238
239
240
241
242
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 236

def smbhost
  if domain == "."
    "#{rhost}:#{rport}"
  else
    "#{rhost}:#{rport}|#{domain}"
  end
end

#splitname(uname) ⇒ Object



257
258
259
260
261
262
263
264
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 257

def splitname(uname)
  if datastore["PRESERVE_DOMAINS"]
    d,u = domain_username_split(uname)
    return u
  else
    return uname
  end
end

#unicode(str) ⇒ Object

Convert a standard ASCII string to 16-bit Unicode



140
141
142
# File 'lib/msf/core/exploit/remote/smb/client.rb', line 140

def unicode(str)
  Rex::Text.to_unicode(str)
end