# Hell’s Keychain: Supply-chain vulnerability in IBM Cloud Databases for PostgreSQL allows potential unauthorized database access

SQL injection in IBM Cloud PostgreSQL's create_subscription SECURITY DEFINER function led to superuser RCE, then chained K8s token, registry creds, and CI/CD secrets to reach internal build servers.

- Payout: Research
- Severity: critical
- Program: IBM Cloud Databases for PostgreSQL
- Credited: Wiz Research
- Disclosed: 2022-12-01
- Write-up quality: 9/10
- Original: https://www.wiz.io/blog/hells-keychain-supply-chain-attack-in-ibm-cloud-databases-for-postgresql
- Library page: https://bugbountylibrary.com/r/blog%3Awiz-io-blog-hells-keychain-supply-chain-attack-in-ibm-cloud-databases-for-postgresql
- Tags: domain:cloud impact:internal-access impact:rce kind:research platform:threat-intel sev:critical tech:ci-cd tech:docker tech:kubernetes technique:chained technique:recon technique:source-review vuln:misconfiguration vuln:secrets-leak vuln:sqli

**Target:** Managed PostgreSQL service, Kubernetes pod, internal container registry and CI/CD build servers
**Root cause:** create_subscription SECURITY DEFINER function passed unsanitized args into dblink_exec, allowing SQL injection as superuser; plus plaintext secrets scattered in K8s secrets and image manifest history, and permissive network access from production pods to internal build servers.
**Steps:**
1. Connect to managed PostgreSQL as low-priv user
2. Call `create_subscription` with injected SQL in a parameter, e.g. `... PUBLICATION test2_publication WITH (create_slot = false); INSERT INTO public.test3(data) VALUES(current_user);`
3. Confirm execution as `ibm` superuser via inserted row
4. Use PostgreSQL `COPY ... TO PROGRAM` to run `id` on underlying VM
5. Read `/var/run/secrets/kubernetes.io/serviceaccount/token`, upload kubectl, run `kubectl auth can-i --list`
6. `kubectl get pods -o json` to find `imagePullSecrets: pipeline`
7. `kubectl get secrets -o json pipeline`, base64-decode `.dockerconfigjson` for registry creds
8. Pull images (postgresql-db-12.7, etcd-mgmt-3.2.7, etcd-portal-3.2.7, etcd-portalmgmt-3.2.7, postgresql-mgmt-12.7), unpack layers, run detect-secrets on manifest history JSON
9. Extract FTP and internal artifact repo credentials from build command history
10. From the PostgreSQL VM, reach internal build servers and authenticate with extracted creds
11. Write test files into artifact repositories used in PostgreSQL image build to prove supply-chain write access
**How found:** Routine PostgreSQL-as-a-Service audit: read the create_subscription function source, noticed SECURITY DEFINER plus unsanitized format() into dblink_exec, hypothesized SQLi. After RCE, recon of env vars revealed POD_NAME/KUBERNETES_PORT indicating a K8s pod; found service account token file; enumerated RBAC; noticed imagePullSecrets in pod spec; applied prior container-registry-scraping technique; scanned each image layer's manifest history for build-time secrets.
**Exploitation:** Attacker with a managed PostgreSQL instance escalates to superuser via SQLi, achieves RCE on the pod VM, steals the K8s service account token, uses get-permission on secrets to retrieve the pipeline imagePullSecret, pulls internal images, extracts CI/CD credentials from image manifest history, and—because the production pod has network access to internal build servers—authenticates and writes to artifact repositories, enabling tampering with packages installed on every PostgreSQL instance.
**Escalation:** Chained SQLi → superuser → RCE → K8s token → registry secret → image scraping → CI/CD creds → internal build server write access, escalating from single-tenant DB access to cross-customer supply-chain compromise.
**Insight:** When auditing managed DBaaS, grep SECURITY DEFINER functions for format()/EXECUTE/dblink sinks—vendor-added helper functions are prime injection points. After RCE, check env vars for KUBERNETES_PORT/POD_NAME, then read the service account token and enumerate RBAC; even get-only secrets access is enough if pod specs leak secret names via imagePullSecrets. Scan every image layer's manifest history JSON, not just the filesystem, for build-time credentials.
