Module: Msf::Exploit::PgAdmin

Includes:
Remote::HttpClient
Defined in:
lib/msf/core/exploit/pgadmin.rb

Instance Attribute Summary

Attributes included from Remote::HttpClient

#client, #cookie_jar

Instance Method Summary collapse

Methods included from Remote::HttpClient

#basic_auth, #cleanup, #configure_http_login_scanner, #connect, #connect_ws, #deregister_http_client_options, #disconnect, #download, #full_uri, #handler, #http_fingerprint, #initialize, #lookup_http_fingerprints, #normalize_uri, #path_from_uri, #peer, #proxies, #reconfig_redirect_opts!, #request_opts_from_url, #request_url, #rhost, #rport, #send_request_cgi, #send_request_cgi!, #send_request_raw, #service_details, #setup, #ssl, #ssl_version, #sslkeylogfile, #strip_tags, #target_uri, #validate_fingerprint, #vhost

Methods included from Auxiliary::LoginScanner

#configure_login_scanner

Methods included from Auxiliary::Report

#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot

Methods included from Metasploit::Framework::Require

optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines

Instance Method Details

#auth_required?Boolean

Returns:

  • (Boolean)


13
14
15
16
17
18
19
# File 'lib/msf/core/exploit/pgadmin.rb', line 13

def auth_required?
  res = send_request_cgi('uri' => normalize_uri(target_uri.path), 'keep_cookies' => true)
  if res&.code == 302 && res.headers['Location']['login']
    return true
  end
  false
end

#authenticate(username, password) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/msf/core/exploit/pgadmin.rb', line 81

def authenticate(username, password)
  res = send_request_cgi({
    'uri' => normalize_uri(target_uri.path, 'authenticate/login'),
    'method' => 'POST',
    'keep_cookies' => true,
    'vars_post' => {
      'csrf_token' => csrf_token,
      'email' => username,
      'password' => password,
      'language' => 'en',
      'internal_button' => 'Login'
    }
  })

  unless res&.code == 302 && res&.headers&.[]('Location') != normalize_uri(target_uri.path, 'login')
    fail_with(Msf::Exploit::Failure::NoAccess, 'Failed to authenticate to pgAdmin')
  end

  print_good('Successfully authenticated to pgAdmin')
  res
end

#check_version(patched_version, low_bound = 0) ⇒ Object



38
39
40
41
42
43
44
# File 'lib/msf/core/exploit/pgadmin.rb', line 38

def check_version(patched_version, low_bound = 0)
  version = get_version
  return Msf::Exploit::CheckCode::Unknown('Unable to determine the target version') unless version
  return Msf::Exploit::CheckCode::Safe("pgAdmin version #{version} is not affected") if version >= Rex::Version.new(patched_version) || version < Rex::Version.new(low_bound)

  Msf::Exploit::CheckCode::Appears("pgAdmin version #{version} is affected")
end

#csrf_tokenObject



46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/msf/core/exploit/pgadmin.rb', line 46

def csrf_token
  return @csrf_token if @csrf_token

  if auth_required?
    res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'login'), 'keep_cookies' => true)
    (res)
  else
    res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'browser/js/utils.js'), 'keep_cookies' => true)
    set_csrf_token_from_config(res)
  end
  fail_with(Failure::UnexpectedReply, 'Failed to obtain the CSRF token') unless @csrf_token
  @csrf_token
end

#get_versionObject



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/msf/core/exploit/pgadmin.rb', line 21

def get_version
  if auth_required?
    res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'login'), 'keep_cookies' => true)
  else
    res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'browser/'), 'keep_cookies' => true)
  end
  html_document = res&.get_html_document
  return unless html_document&.xpath('//title').text == 'pgAdmin 4'

  # there's multiple links in the HTML that expose the version number in the [X]XYYZZ,
  # see: https://github.com/pgadmin-org/pgadmin4/blob/053b1e3d693db987d1c947e1cb34daf842e387b7/web/version.py#L27
  versioned_link = html_document.xpath('//link').find { |link| link['href'] =~ /\?ver=(\d?\d)(\d\d)(\d\d)/ }
  return unless versioned_link

  Rex::Version.new("#{Regexp.last_match(1).to_i}.#{Regexp.last_match(2).to_i}.#{Regexp.last_match(3).to_i}")
end

#set_csrf_token_from_config(res) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/msf/core/exploit/pgadmin.rb', line 60

def set_csrf_token_from_config(res)

  # The CSRF token should be inside a java script tag, inside a function called window.renderSecurityPage and should look like:
  # ImQzYTQ0YzAzOGMyY2YwZWNkMWRkY2Q4ODdhMTA5MGM3YzI5ZTYzY2Ii.Z_6Kdw.XP2eOIJ26MikqG5J8J8W1bDPMpQ
  if res&.code == 200 && res.body =~ /csrfToken": "([\w+.-]+)"/
    @csrf_token = Regexp.last_match(1)
    # at some point between v7.0 and 7.7 the token format changed
  else
    @csrf_token = res&.body.scan(/pgAdmin\['csrf_token'\]\s*=\s*'([^']+)'/)&.flatten&.first
  end
end

#set_csrf_token_from_login_page(res) ⇒ Object



72
73
74
75
76
77
78
79
# File 'lib/msf/core/exploit/pgadmin.rb', line 72

def (res)
  if res&.code == 200 && res.body =~ /csrfToken": "([\w+.-]+)"/
    @csrf_token = Regexp.last_match(1)
    # at some point between v7.0 and 7.7 the token format changed
  elsif (element = res.get_html_document.xpath("//input[@id='csrf_token']")&.first)
    @csrf_token = element['value']
  end
end