PostgreSQL 18.6 - High - Remote - File write - pg_basebackup follows absolute tar symlink leftover of CVE-2026-6475
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · postgres-basebackup-symlink
Class: File write Reach: Remote
PostgreSQL 18.6 (724edf9) - PostgreSQL Global Development Group
CVE-2026-6475 added path_is_safe_for_extraction for tar member names. Absolute symlink targets still skip that check so tablespaces work. astreamer_file.c then symlink()s the target and later fopen(..., "wb") follows it. Default pg_basebackup format is plain (-F p). pg_rewind create_target_symlink checks path and never link.
A hostile source that speaks BASE_BACKUP can plant an absolute symlink and overwrite any file the backup UID can write. Same victim model as 6475. This is not a re-file of the member-name checks.
| ID | no CVE yet |
| Class | File write (absolute tar symlink leftover of CVE-2026-6475) |
| Reach | Remote (UI:R client vs hostile PostgreSQL) |
| CWE | CWE-59, CWE-22 |
| CVSS | High: 8.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H |
| Product | PostgreSQL |
| Affected | 18.6 tag REL_18_6 (724edf9bde9d356724ad384a2e196edc3c9f80f7), in-tree pg_basebackup / pg_rewind |
| Auth | victim runs default pg_basebackup -D <dir> against an attacker source |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
Someone who can get an operator to run pg_basebackup -D against their PostgreSQL (the same UI:R model as CVE-2026-6475) can write outside -D.
| Privilege | Method / sink | Lab-proven or inferred |
|---|---|---|
Integrity: write a file outside -D | Hostile BASE_BACKUP tar: typeflag '2' member with a safe relative name (pg_tblspc/16384) and an absolute link target, then a regular member under that name. extract_link then create_file_for_extract fopen(..., "wb") with no O_NOFOLLOW | Lab-proven. Wrote POSTGRES-BASEBACKUP-SYMLINK-WITNESS at /work/oracle/. Dest holds the symlink. Control tar (no symlink) left the oracle empty and wrote PG_VERSION only inside -D |
Integrity: overwrite shell rc, authorized_keys, a systemd user unit, or crontab | Same sink, any path the client UID can write | Inferred. This lab does not touch those files |
| Availability: smash client config or keys | Same write | Inferred. Not demonstrated |
| Recon of the client disk | extract is write, not read | Closed |
Walk .. or absolute member names | path_is_safe_for_extraction | Closed. That is CVE-2026-6475 |
| Server-side unpriv SQL / unauth protocol | 18.6 default server | Closed. This leftover is the client |
| Tar-only backup | pg_basebackup -F t stores the tar without extracting | Closed |
RCE inside postgres | not this sink | Closed |
pg_rewind has the same create_target_symlink / open() leftover. This lab drives pg_basebackup only.
I pinned postgres/postgres REL_18_6 after the 18.6 wave (28 CVEs, plus the 18.4/18.2 set already on this tree). Twenty-two hunts. Default server, unpriv SQL, unauth protocol: none at this bar. CVE-2026-6475 was the sibling to re-read.
Member names are checked. Absolute symlink targets are not:
if (!is_absolute_path(linktarget) &&
!path_is_safe_for_extraction(member->linktarget))
{
pg_fatal("link target has unsafe path name: \"%s\"",
member->linktarget);
}
extract_link(mystreamer->filename, linktarget);
create_file_for_extract is still fopen(filename, "wb"). Comment at astreamer_file.c:351-356 says mapping is applied without checking the link is inside pg_tblspc.
Wrong turns already recorded: treating 6475 as closed and walking away; pointing -D at a live data directory; overwriting shell rc for theatre. An honest 18.6 server only emits pg_tblspc/<oid> symlinks and puts tablespace contents in a separate archive. A malicious source that speaks BASE_BACKUP can put the follow-up regular member in the same tar. That is the leftover.
python3 postgres-basebackup-symlink-Abraxas-Labs.py
or
cd lab
./run.sh
postgres:18.6 as the source image, running a BASE_BACKUP speaker. Client is in-tree pg_basebackup from the same tag (pg_basebackup (PostgreSQL) 18.6). Loopback 127.0.0.1:19370. Compose project postgres-basebackup-symlink. Do not point -D at a live data directory.
SUCCESS postgres-basebackup-symlink control-inside=yes oracle-outside=yes dest-is-symlink=yes image=postgres:18.6 how=pg_basebackup POSTGRES-BASEBACKUP-SYMLINK-WITNESS
Treat absolute symlink targets as unsafe unless they are rewritten by --tablespace-mapping. Open extract files with O_NOFOLLOW / lstat so a planted link cannot be followed. Apply path_is_safe_for_extraction to rewind link as well as path. Reject a regular member whose path walks through a symlink that left -D.
724edf9bde9d356724ad384a2e196edc3c9f80f7)src/fe_utils/astreamer_file.c · src/fe_utils/astreamer_tar.csrc/bin/pg_rewind/file_ops.cGNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.