Threat Overview
CVE-2026-6471 is a PostgreSQL core-server authorization flaw in logical decoding. A non-superuser holding REPLICATION privilege can select an arbitrary logical decoding plugin path, causing PostgreSQL to load a file visible to the operating-system account running the server. This can execute arbitrary code as that OS account. PostgreSQL fixed the issue in 18.6, 17.11, 16.15, 15.19, and 14.24. The vulnerability requires REPLICATION privilege and is therefore not an unauthenticated Internet-facing RCE by itself.
References
- PostgreSQL Security Advisory CVE-2026-6471 – August 13, 2026: https://www.postgresql.org/support/security/CVE-2026-6471/
Impacted Systems
- Vendor/product: PostgreSQL core server.
- Affected versions: versions before 18.6, 17.11, 16.15, 15.19, and 14.24 in supported major branches 14-18.
- Fixed versions: 18.6, 17.11, 16.15, 15.19, 14.24.
- Required privilege: REPLICATION.
- Required feature: logical decoding/plugin loading path.
- Impact: arbitrary code execution as the PostgreSQL operating-system account.
- Deployment: self-managed PostgreSQL where an attacker can obtain or abuse a role with REPLICATION privilege.
Why this matters
The issue turns a database replication privilege into operating-system code execution and is important where REPLICATION is broadly assigned or database credentials are exposed.
Exploitation Status
No confirmed in-the-wild exploitation was identified in the reviewed vendor material.
What this hunt looks for
Available Microsoft Sentinel telemetry can be reviewed for logical replication activity, REPLICATION privilege changes, PostgreSQL child-process execution, library-file writes, outbound connections, and service-account persistence where PostgreSQL or endpoint logs are ingested.
Required logs
Required logs vary by hunt and are identified by the Sentinel tables referenced in each query below.
Hunt 1 – First-Pass: Logical Replication Slot or Plugin Activity
Syslog
| where TimeGenerated >= ago(30d)
| where SyslogMessage has_any ("logical decoding","replication slot","CREATE_REPLICATION_SLOT","START_REPLICATION","output plugin")
| project TimeGenerated, Computer, HostIP, ProcessName, SeverityLevel, SyslogMessage
| sort by TimeGenerated ascHunt 2 – PostgreSQL Processes Spawning Shells or Utilities
DeviceProcessEvents
| where TimeGenerated >= ago(30d)
| where InitiatingProcessFileName in~ ("postgres","postgres.exe")
| where FileName in~ ("bash","sh","cmd.exe","powershell.exe","pwsh.exe","curl","wget","python","python3","nc","ncat")
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
| sort by TimeGenerated ascHunt 3 – Shared Library Files Written Near PostgreSQL Paths
DeviceFileEvents
| where TimeGenerated >= ago(30d)
| where FolderPath has_any ("/usr/lib/postgresql/","/usr/local/lib/","/var/lib/postgresql/","\PostgreSQL\")
| where FileName endswith ".so" or FileName endswith ".dll"
| project TimeGenerated, DeviceName, ActionType, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by TimeGenerated ascHunt 4 – New Public Destinations From PostgreSQL
let Baseline = DeviceNetworkEvents
| where TimeGenerated between (ago(30d) .. ago(7d))
| where InitiatingProcessFileName in~ ("postgres","postgres.exe")
| where RemoteIPType == "Public"
| summarize by DeviceId, RemoteIP, RemotePort;
DeviceNetworkEvents
| where TimeGenerated >= ago(7d)
| where InitiatingProcessFileName in~ ("postgres","postgres.exe")
| where RemoteIPType == "Public"
| join kind=leftanti Baseline on DeviceId, RemoteIP, RemotePort
| project TimeGenerated, DeviceName, InitiatingProcessCommandLine, RemoteIP, RemoteUrl, RemotePort, ActionType
| sort by TimeGenerated descHunt 5 – PostgreSQL Replication Connections in Syslog
Syslog
| where TimeGenerated >= ago(30d)
| where SyslogMessage has_any ("replication","walsender","logical decoding","START_REPLICATION")
| project TimeGenerated, Computer, HostIP, ProcessName, SeverityLevel, SyslogMessage
| sort by TimeGenerated ascHunt 6 – REPLICATION Role or Privilege Changes
Syslog
| where TimeGenerated >= ago(30d)
| where SyslogMessage has_any ("ALTER ROLE","CREATE ROLE")
| where SyslogMessage has "REPLICATION"
| project TimeGenerated, Computer, HostIP, ProcessName, SeverityLevel, SyslogMessage
| sort by TimeGenerated ascHunt 7 – Unusual PostgreSQL Child Process Followed by Network Activity
let Child = DeviceProcessEvents
| where TimeGenerated >= ago(30d)
| where InitiatingProcessFileName in~ ("postgres","postgres.exe")
| where FileName !in~ ("postgres","postgres.exe")
| project DeviceId, ProcessTime=TimeGenerated, DeviceName, ChildProcess=FileName, ChildCommandLine=ProcessCommandLine;
DeviceNetworkEvents
| where TimeGenerated >= ago(30d)
| join kind=inner Child on DeviceId
| where TimeGenerated between (ProcessTime .. ProcessTime + 10m)
| project ProcessTime, TimeGenerated, DeviceName, ChildProcess, ChildCommandLine, RemoteIP, RemoteUrl, RemotePort
| sort by ProcessTime ascHunt 8 – PostgreSQL Service Account Interactive Shell Activity
Syslog
| where TimeGenerated >= ago(30d)
| where SyslogMessage has_any ("postgres","postgresql")
| where SyslogMessage has_any ("session opened","sudo","su:","bash","sh","authorized_keys","crontab","systemctl")
| project TimeGenerated, Computer, HostIP, ProcessName, SeverityLevel, SyslogMessage
| sort by TimeGenerated ascHunt 9 – SecurityEvent Fallback for postgres.exe Child Processes
SecurityEvent
| where TimeGenerated >= ago(30d)
| where EventID == 4688
| where ParentProcessName endswith @"\postgres.exe"
| where NewProcessName has_any ("cmd.exe","powershell.exe","pwsh.exe","rundll32.exe","regsvr32.exe")
| project TimeGenerated, Computer, Account, ParentProcessName, NewProcessName, CommandLine
| sort by TimeGenerated ascDetection Notes
- Highest signal is unexpected logical-decoding activity combined with unusual output-plugin/library loading or postgres child processes.
- PostgreSQL application logs may not contain every SQL statement unless statement/audit logging is configured.
- Endpoint telemetry on database servers is not guaranteed.
- Logical replication is legitimate in many environments; baseline expected replication users and slots.
- Missing PostgreSQL audit/Syslog data can make privilege abuse difficult to observe.