FlawOpen

flawopen.com/디렉터리/226

● CWE-367 / CWE-829 · 높음
보안 연구 · FlawOpen

Marimo GHSL-2026-226: 테스트 릴리스 워크플로의 TOCTOU

같은 초에 발생하는 타임스탬프 경쟁으로 Pull Request 변경이 maintainer의 테스트 릴리스 승인을 통과해 TestPyPI와 npm 게시 작업까지 도달할 수 있었습니다. 취약한 워크플로는 삭제되었고 CVE는 할당되지 않았습니다.

💡 알기 쉬운 설명 (ELI5)

실험실 책임자는 특정 샘플을 승인하지만 실험실은 시계의 초만 확인합니다. 같은 초에 샘플을 바꾸면 교체된 샘플도 승인된 것처럼 보입니다. Marimo 워크플로는 Pull Request 커밋에서 같은 실수를 했고 빌드한 패키지를 게시 작업으로 전달했습니다.

핵심 개념 및 용어

TOCTOU
Time-of-check to time-of-use: 보안 검사 후 실제 사용 전에 리소스가 바뀌는 문제입니다.
OIDC 게시
CI 작업이 GitHub Actions ID를 패키지 레지스트리의 단기 자격 증명과 교환하므로 장기 비밀번호를 저장하지 않습니다.
워크플로 아티팩트
GitHub Actions 작업 간 전달되는 파일 묶음입니다. 후속 작업은 신뢰할 수 없는 워크플로에서 나온 아티팩트를 신뢰할 수 없는 입력으로 취급해야 합니다.
GHSL-2026-226
Marimo 워크플로 결함에 대한 GitHub Security Lab 권고 식별자입니다. 권고에서 CVE는 할당되지 않았습니다.
CWE-367 / CWE-829
권고는 검사-사용 시간 경쟁과 신뢰할 수 없는 제어 영역의 기능 실행을 지적합니다.

프레임워크 매핑

  • 이 가이드에서 설명하는 약점의 CWE 참조: CWE-367 CWE-829

단계별 공격 실행 흐름

단계 1

Maintainer가 테스트 릴리스를 승인

Maintainer가 Pull Request에 댓글을 달아 Marimo 테스트 릴리스 워크플로를 시작합니다. 승인은 검토한 Pull Request 상태에 묶여야 합니다.

단계 2

같은 초에 Pull Request head가 변경

워크플로는 pushed_at과 댓글의 created_at을 정수 초로 변환한 뒤 비교합니다. 같은 초의 push는 같은 값을 만듭니다.

단계 3

취약한 검사가 같은 값을 허용

삭제된 워크플로는 댓글 시간보다 엄격히 늦은 pushed_at만 거부했습니다. 같은 값은 통과했고 업데이트된 커밋이 기록되었습니다.

단계 4

신뢰할 수 없는 코드가 패키지 아티팩트를 빌드

작업은 해당 커밋을 checkout하고 저장소가 제어하는 action과 빌드 스크립트를 실행했습니다. 결과는 아티팩트로 업로드된 뒤 신뢰할 수 있는 커밋에서 재빌드되지 않은 채 게시 작업에서 사용되었습니다.

단계 5

게시 권한이 신뢰할 수 없는 출력에 도달

TestPyPI와 npm 작업은 OIDC 게시 권한을 사용했습니다. 권고는 아티팩트가 게시 경로에 도달할 수 있음을 설명하지만 실제 패키지 침해를 보고하지는 않습니다.

소스 코드 비교: 취약한 구현 vs 보안 패치

GHSL-2026-226은 취약한 workflow가 PR #10765에서 삭제됐다고 설명합니다. 이는 프로젝트가 여기에 표시된 비교 연산자 변경을 적용했다는 뜻이 아닙니다. 이전 검사는 -gt를 사용해 초 단위로 같은 값이 통과했습니다. 예시의 -ge는 같은 값을 거부하지만 승인을 정확히 검토한 커밋에 묶는 편이 더 강합니다. 신뢰할 수 없는 빌드와 OIDC 작업을 분리하고 승인된 SHA에서 재빌드한 뒤 게시 전에 출처를 확인하세요. exploit payload는 포함하지 않습니다.

취약한 구현
# 삭제된 marimo-bot workflow: 정수 초 비교
if [[ $(date -d "$pushed_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
    exit 1
fi
# 기록한 head_sha를 checkout하고 아티팩트를 별도
# 게시 작업으로 전달했습니다.
보안 강화 패치
# 설명용 통제입니다. GHSL은 취약한 workflow가
# 제자리에서 수정된 것이 아니라 삭제됐다고 설명합니다.
if [[ $(date -d "$pushed_at" +%s) -ge $(date -d "$COMMENT_AT" +%s) ]]; then
    exit 1
fi
# 더 강한 설계: 검토된 불변 SHA를 승인하고 신뢰된 작업에서
# 해당 SHA를 다시 빌드한 뒤 검증된 출력만 게시합니다.

엔지니어링 및 시스템 보안 강화 체크리스트

출처