
CVE-2026-100671에 대한 로컬 전용 개념 증명 검증기로, 루프백 대상에 대해 Grav Twig 페이지 캐시 세션 쿠키 노출 및 재생을 재현합니다.
Grav 2.0.25에서 수정된 Grav 콘텐츠-Twig/페이지 캐시 세션 탈취에 대한 최소한의, 대상 범위로 한정된 공격자 측 검증기입니다.
권고문:
공격자는 관리자 사용자 이름이나 비밀번호를 가지고 있지 않습니다.
실제 공격 체인은 다음과 같습니다:
관리자 동작은 이 스크립트 외부에서 발생합니다. exploit.py는
GRAV_ADMIN_USERNAME 또는 GRAV_ADMIN_PASSWORD 설정을 받지 않습니다.
루프백이 기본 안전 경계입니다. 비루프백 대상은 명시적인
--allow-remote 플래그가 필요하며, 이는 승인된 HTB, TryHackMe 또는 소유한
랩 머신을 위한 것입니다.
클라이언트는 리디렉션이 선택된 대상 오리진에 머무를 때만 이를 따르며,
Python 표준 라이브러리 HTTP 클라이언트를 사용하고, 요청 타임아웃을 적용하며,
--show-cookie가 명시적으로 제공된 경우에만 캡처된 쿠키를 출력합니다.
소유하거나 테스트가 명시적으로 승인된 Grav 인스턴스에만 사용하십시오. 이 스크립트는 공개 증명 페이지를 생성합니다. 대상이 실행 간 콘텐츠를 유지하는 경우 테스트 후 해당 페이지를 제거하십시오.
get_cookie()가 방문 중인 관리자의 세션 쿠키를 서버 측에서 읽습니다.페이지 작성 계정에는 낮은 권한의 쓰기 경로만 필요합니다. api.pages.read,
admin.*, 슈퍼 관리자 접근 또는 관리자 자격 증명은 필요하지 않습니다.
2.0.19부터 2.0.24까지: 기본 제공 콘텐츠-Twig 구성에서 취약합니다.2.0.0부터 2.0.18까지, 그리고 1.7.x: 콘텐츠 Twig가 명시적으로
활성화된 경우에만 영향을 받습니다.2.0.25: 수정됨.다음 조건으로 Grav 대상을 실행하십시오:
이 두 파일 저장소는 Grav, PHP, 플러그인, 계정 또는 관리자 봇을 프로비저닝하지 않습니다. 피해자 방문은 외부 동작으로 남습니다.
낮은 권한의 작성자 자격 증명 또는 토큰만 설정하십시오:
export GRAV_TARGET=http://127.0.0.1:8080
export GRAV_WRITER_USERNAME=low
export GRAV_WRITER_PASSWORD='local-writer-password'
python3 exploit.py
스크립트는 임의의 공개 경로를 생성하고, 해당 URL을 출력한 뒤 대기합니다. 관리자가 해당 URL을 한 번 방문하게 한 다음 Enter를 누르십시오. 스크립트가 익명 수집과 로컬 API 재생을 수행합니다.
기존 writer API 토큰을 대신 사용하십시오:
export GRAV_WRITER_TOKEN='local-low-privilege-token'
python3 exploit.py
비대화형 터미널이나 별도의 피해자 브라우저에 유용합니다.
페이지를 준비하세요:
python3 exploit.py --prepare-only
관리자가 인쇄된 URL을 방문하도록 하십시오. 그런 다음 인쇄된 경로를 사용하여 수집하고 재생하십시오. 이 두 번째 단계에서는 자격 증명이 필요하지 않습니다:
python3 exploit.py --collect-only --route /cookie-proof-XXXXXXXX --show-cookie
--show-cookie는 로컬/승인된 랩 디버깅을 위해 캡처된 세션 쿠키를 출력합니다:
captured_cookie=grav-site-<hash>=<session-value>
이 값은 실제 자격 증명입니다. GitHub, 이슈, 채팅, 스크린샷 또는 로그에 붙여넣지 마십시오. --show-cookie 없이도 스크립트는 재생 상태를 통해 동일한 영향을 입증하면서 값을 출력에서 제외합니다.
관리자 방문 전에 수집이 실행되면 공격자 요청이 빈 값으로 캐시를 채울 수 있습니다. 새로운 무작위 경로를 준비하고 반복하십시오.
저장소에 머신 IP를 하드코딩하지 마십시오. 런타임에 현재 대상을 제공하십시오:
export GRAV_WRITER_USERNAME=low
export GRAV_WRITER_PASSWORD='writer-password'
python3 exploit.py \
--target http://10.10.10.25:8080 \
--allow-remote \
--prepare-only
관리자/봇이 출력된 URL을 방문한 후, 다음을 수집하고 재생합니다:
python3 exploit.py \
--target http://10.10.10.25:8080 \
--allow-remote \
--collect-only \
--route /cookie-proof-XXXXXXXX \
--show-cookie
--base-url는 --target에 대한 호환성 별칭으로 유지됩니다. 원격 HTTP는
평문 경고를 출력합니다. 머신이 지원하는 경우 HTTPS를 사용하세요.
page_create_status=201
victim_action_url=http://127.0.0.1:8080/cookie-proof-XXXXXXXX
anonymous_page_status=200
cookie_name_disclosed=yes
cached_cookie_visible_to_attacker=yes
captured_cookie=grav-site-<hash>=<session-value>
replayed_cookie_api_status=200
result=VULNERABLE
captured_cookie 줄은 --show-cookie를 사용할 때만 나타납니다.
Grav 2.0.25 이상을 대상으로 동일한 2단계 흐름을 실행한 다음, 다음과 같이 수집합니다:
python3 exploit.py --collect-only --route /cookie-proof-XXXXXXXX --expect fixed
예상되는 수정 후 동작: 공격자가 볼 수 있는 세션 값이 없고 세션 재생도 성공하지 않아야 합니다. 정상적으로 수정된 결과는 --expect fixed와 함께 0으로 종료됩니다.
Grav 2.0.25 이상으로 업그레이드하십시오. 이전 배포 환경의 경우, 권고문에서 다음과 같은 임시 통제 조치를 제시합니다:
security.twig_content.process_enabled: false로 콘텐츠 Twig를 비활성화하거나; 또는system.pages.never_cache_twig: true를 설정하십시오;HttpOnly, Secure, SameSite를 수정 조치로 간주하지 마십시오: 취약한 쿠키 읽기는 Twig 렌더링 중 서버 측에서 발생합니다.
canhieu가 보고했습니다. 이 저장소는 이미 공개된 권고문을 위한 재현 지원 자료이며, 원격 스캐너나 자격 증명 수집 도구가 아닙니다.