# -*- coding:binary -*-
require 'spec_helper'

require 'msf/core'
require 'msf/core/exploit/smb/server/share'
require 'rex/proto/smb/constants'

describe Msf::Exploit::Remote::SMB::Server::Share do

  subject(:mod) do
    mod = Msf::Exploit.new
    mod.extend described_class
    mod.send(:initialize)

    mod
  end

  let(:client_string) { '' }
  let(:client) { StringIO.new(client_string) }

  let(:default_response_length) { 139 }
  let(:default_response) do
    "\x00\x00\x00\x87\xff\x53\x4d\x42\xa2\x00\x00\x00\x00\x88\x01\xc8" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" +
    "\x00\x00\x44\x43\x22\xff\x00\x00\x00\x03\x00\x00\x01\x00\x00\x00" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\xff\x01\x1f\x00\x00\x00\x00\x00\x00\x00"
  end

  let(:valid_request) do
    "\x00\x00\x00\x58\xff\x53\x4d\x42\xa2\x00\x00\x00\x00\x18\x07\xc8" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x58\x0c" +
    "\x00\x00\xc0\xfd\x18\xff\x00\xde\xde\x00\x02\x00\x10\x00\x00\x00" +
    "\x00\x00\x00\x00\x01\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x90\x00\x00\x00\x07\x00\x00\x00\x01\x00\x00\x00\x01\x40\x00\x00" +
    "\x02\x00\x00\x00\x00\x05\x00\x00\x5c\x00\x00\x00"
  end
  let(:valid_response) do
    "\x00\x00\x00\x87\xff\x53\x4d\x42\xa2\x00\x00\x00\x00\x88\x01\xc8" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" +
    "\x00\x00\x44\x43\x22\xff\x00\x00\x00\x03\xef\xbe\x01\x00\x00\x00" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x10\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\x00\x00\x00\x00\x00\x07\x00\x01\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\xff\x01\x1f\x00\x00\x00\x00\x00\x00\x00"
  end

  let(:non_existent_path_request) do
    "\x00\x00\x00\x68\xff\x53\x4d\x42\xa2\x00\x00\x00\x00\x18\x07\xc8" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x58\x0c" +
    "\x00\x00\x80\x00\x18\xff\x00\xde\xde\x00\x12\x00\x16\x00\x00\x00" +
    "\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00" +
    "\x02\x00\x00\x00\x00\x15\x00\x00\x5c\x00\x74\x00\x65\x00\x73\x00" +
    "\x74\x00\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00"
  end
  let(:smb_error_response) do
    "\x00\x00\x00\x23\xff\x53\x4d\x42\xa2\x34\x00\x00\xc0\x88\x01\xc8" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" +
    "\x00\x00\x44\x43\x00\x00\x00"
  end
  let(:smb_error_length) { 39 }

  before(:each) do
    mod.instance_variable_set('@state', {
      client => {
        :multiplex_id => 0x41424344,
        :process_id   => 0x45464748,
        :file_id      => 0xdead,
        :dir_id       => 0xbeef
      }
    })
    mod.lo = 0
    mod.hi = 0
    mod.share = 'test'
    mod.file_name = 'false.exe'
    mod.file_contents = 'metasploit'

    allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
      io.write(data)
    end
  end

  describe "#send_nt_create_andx_res" do
    it "returns the number of bytes sent" do
      expect(mod.send_nt_create_andx_res(client)).to eq(default_response_length)
    end

    it "sends a valid SMB_COM_NT_CREATE_ANDX response to the client" do
      mod.send_nt_create_andx_res(client)
      client.seek(0)
      res = client.read
      expect(res).to eq(default_response)
    end
  end

  describe "#smb_cmd_nt_create_andx" do
    context "when valid request" do
      it "returns the number of bytes answered" do
        expect(mod.smb_cmd_nt_create_andx(client, valid_request)).to eq(default_response_length)
      end

      it "sends a valid SMB_COM_NT_CREATE_ANDX response to the client" do
        mod.smb_cmd_nt_create_andx(client, valid_request)
        client.seek(0)
        res = client.read
        expect(res).to eq(valid_response)
      end
    end

    context "when non existent path create requests" do
      it "returns the number of bytes answered" do
        expect(mod.smb_cmd_nt_create_andx(client, non_existent_path_request)).to eq(smb_error_length)
      end

      it "sends a SMB_STATUS_OBJECT_NAME_NOT_FOUND error response to the client" do
        mod.smb_cmd_nt_create_andx(client, non_existent_path_request)
        client.seek(0)
        res = client.read
        expect(res).to eq(smb_error_response)
      end
    end
  end

end


