
Bloomberg Memray의 이스케이프되지 않은 명령줄 메타데이터를 통한 저장형 XSS
블룸버그 Memray의 이스케이프되지 않은 명령줄 메타데이터를 통한 저장형 XSS
저는 Memray(블룸버그의 Python 메모리 프로파일러)를 검토하던 중 다음과 같은 간단한 질문을 염두에 두고 이 문제를 발견했습니다:
공격자가 제어하는 런타임 메타데이터가 안전하지 않은 방식으로 브라우저에 렌더링되는 보고서 출력에 유입될 수 있을까?
이 경우, 답은 '그렇다'였습니다.
이 버그는 Memray의 HTML 보고서 생성 경로에 있었으며, 명령줄 메타데이터가 이스케이프 없이 브라우저에서 열리는 보고서에 렌더링되었습니다. 이로 인해 운영 필드가 실행 가능한 HTML 싱크(sink)로 변했고, 결국 CVE-2026-32722이 되었습니다.
프로젝트: GitHub의 Memray
권고: GHSA-r5pr-887v-m2w9 / CVE-2026-32722
attacker-controlled argv → metadata.command_line → HTML template sink without escaping → raw markup in generated report → browser-side JavaScript execution
Memray는 Python 메모리 프로파일러입니다.
Python 프로세스를 계측하고, 할당 동작을 기록하며, 개발자가 다음 사항을 이해하는 데 도움이 되는 보고서를 생성합니다:
이러한 보고서 중 일부는 HTML로 생성되어 브라우저에서 열립니다.
이는 보고서 생성을 실제 보안 경계로 만듭니다.
핵심 질문은 Memray가 "로컬 도구"인지 여부가 아닙니다.
핵심 질문은 공격자가 제어하는 데이터가 안전하지 않은 방식으로 브라우저 렌더링 출력에 유입될 수 있는지 여부입니다.
이 경우, 그럴 수 있었습니다.
많은 사람들이 개발자 도구를 과소평가합니다.
그것은 실수입니다.
도구가 다음을 수행하게 되면:
웹 애플리케이션과 동일한 출력 인코딩 위험을 물려받게 됩니다.
이것이 이번 사례의 핵심 문제였습니다.
이 버그는 프로파일링 로직에 있지 않았습니다. 할당 추적에도 있지 않았습니다. 네이티브 메모리 처리에도 있지 않았습니다.
이는 전형적인 신뢰 경계(trust-boundary) 실패였습니다:
그 정도면 실제 취약점을 만들기에 충분합니다.
저는 무작위 CLI 옵션을 퍼징하거나 크래시를 쫓는 방식으로 Memray에 접근하지 않았습니다.
더 강력한 접근 방식은 가장 높은 확률의 보안 표면을 먼저 식별하는 것이었습니다.
Memray의 경우 그것은 HTML 보고서 생성이었습니다.
왜일까요?
HTML 출력은 브라우저 싱크를 도입하고, 브라우저 싱크는 다음 조건이 충족되면 일반적인 메타데이터 버그를 보안 문제로 바꾸기 때문입니다:
이것이 바로 이번 사례에서 일어난 일입니다.
이 버그는 두 줄로 요약됩니다.
다음 코드에서:
def get_render_environment() -> jinja2.Environment:
loader = jinja2.PackageLoader("memray.reporters")
env = jinja2.Environment(loader=loader)
Jinja 환경이 autoescape 없이 생성됩니다.
그런 다음 다음 코드에서:
Command line: <code>{{ metadata.command_line }}</code><br>
metadata.command_line이 HTML에 직접 렌더링됩니다.
이것이 취약점의 전부입니다.
metadata.command_line이 공격자의 영향을 받기 때문입니다.
Memray는 프로파일링되는 프로그램을 실행하는 데 사용된 명령줄을 기록합니다.
즉, argv의 사용자 제어 값이 메타데이터로 보존된 후 나중에 보고서에 삽입됩니다.
따라서 악용 체인은 간단합니다:
metadata.command_line에 저장합니다이로 인해 메타데이터가 실행 가능한 브라우저 콘텐츠로 변합니다.
중요한 차이는 실행(execution)입니다.
많은 버그가 잘못된 형식의 HTML을 생성합니다. 그것만으로는 충분하지 않습니다.
이 경우, 공격자가 제어하는 콘텐츠는 단순히 페이지 소스에 보이는 것에 그치지 않았습니다. 브라우저에 의해 활성 HTML로 해석되어 JavaScript로 실행되었습니다.
이것이 다음 사이의 차이입니다:
따라서 질문은 다음과 같지 않았습니다:
"보고서에 HTML이 나타날 수 있는가?"
실제 질문은 다음과 같았습니다:
"공격자가 제어하는 HTML이 보고서가 열릴 때 실행 가능해질 수 있는가?"
답은 '그렇다'였습니다.
제 초기 재현 코드는 명시적 스크립트와 공격자가 제어하는 인수를 사용했습니다:
cat > victim.py <<'PY'
x = [b"A" * 1024 for _ in range(1000)]
print("done")
PY
python -m memray run -o poc.bin victim.py ''
python -m memray flamegraph -o poc.html poc.bin
생성된 HTML에는 원시 공격자 제어 마크업이 포함되어 있었습니다:
Command line: <code>~/memray/src/memray/__main__.py run -o poc.bin victim.py </code><br>
생성된 보고서를 열거나 새로고침하면 JavaScript 실행이 트리거되었습니다.
이로써 핵심 주장이 입증되었습니다:
이후, 조정된 공개(coordinated disclosure) 과정에서 유지보수자가 재현 코드를 더 단순화했습니다:
python -m memray run -o poc.bin -c '# '
이 버전은 취약한 경계를 더 직접적으로 격리하기 때문에 더 좋습니다:
페이로드는 의도적으로 단순하게 선택되었습니다:
화려한 페이로드에 관한 것이 아닙니다.
다음 이유로 깔끔한 실행 프로브입니다:
이런 유형의 버그에는 그것으로 충분합니다.
보고서 유형 하나만으로도 이슈를 입증하기에 충분했을 것입니다.
하지만 저는 이것이 단발성인지 구조적인지 알고 싶었습니다.
다음에서 동일한 동작을 확인했습니다:
--no-web으로 생성된 flamegraph 보고서이는 두 가지 이유로 중요했습니다.
취약한 싱크가 여러 HTML 출력에서 재사용된다는 것을 보여주었습니다.
버그가 외부 CDN 호스팅 자산에 의존하지 않는다는 것을 증명했습니다.
--no-web으로도 여전히 이슈가 재현되었는데, 이는 문제가 원격 JS 동작이 아니라 Memray 자체의 생성 HTML 및 템플릿 처리에 있음을 의미합니다.
이로 인해 사례가 훨씬 강력해졌습니다.
유지보수자의 주요 우려는 실질적인 공격자 통제였습니다.
타당한 지적입니다.
인증되지 않은 원격 공격자가 노출된 HTTP 엔드포인트를 공격하여 즉각적인 영향을 얻는 종류의 이슈는 아닙니다.
악용 조건은 더 좁습니다:
따라서 현실적인 분류는 낮은 심각도였습니다.
그렇다고 약한 버그라는 뜻은 아닙니다.
심각도는 악용 조건과 예상 영향에 관한 것입니다. 유효성은 이슈가 실제인지 여부에 관한 것입니다.
이 이슈는 분명히 실제였습니다:
그렇기 때문에 여전히 CVE가 되었습니다.
수정은 최소한이면서도 정확했습니다.
유지보수자는 다음을 변경했습니다:
{{ metadata.command_line }}
다음으로:
{{ metadata.command_line|e }}
취약한 싱크를 직접 해결하기 때문에 올바른 수정입니다.
다음과 같은 원시 마크업을 내보내는 대신:
템플릿은 이제 이스케이프된 텍스트를 내보냅니다:
<img src=x onerror=alert(1)>
이는 브라우저 실행 경로를 제거하면서 명령줄 필드의 정보적 가치를 보존합니다.
유지보수자는 또한 나머지 템플릿 컨텍스트를 검토하고 다음과 같이 결론을 내렸습니다:
|tojson으로 렌더링되었습니다따라서 이슈는 metadata.command_line으로 정확히 좁혀졌습니다.
이것이 바로 실제 공개에서 기대하는 수정 검토의 유형입니다.
이 문제는 GitHub Security Advisories를 통해 비공개로 보고되었습니다.
유지보수자들은:
이슈에는 다음이 할당되었습니다: CVE-2026-32722
여기서 핵심 교훈은 간단합니다:
메타데이터는 운영적으로 보인다고 해서 자동으로 신뢰되지 않습니다.
공격자가 제어하는 콘텐츠가 이스케이프 없이 브라우저 렌더링 출력에 유입된다면 그 어떤 것도 중요하지 않습니다.
도구가 HTML을 생성하는 순간, HTML을 생성하는 애플리케이션처럼 취급되어야 합니다.
그것이 진짜 핵심 교훈입니다.
이 취약점은 교묘한 페이로드에 관한 것이 아니었습니다.
올바른 경계를 식별하는 것에 관한 것이었습니다.
Memray는 공격자의 영향을 받은 명령줄 메타데이터를 가져와 이스케이프 없이 생성된 HTML로 렌더링했습니다. 나머지는 브라우저가 해냈습니다.
그렇기 때문에 이것이 CVE-2026-32722이 되었습니다.
Memray 1.19.2에서 수정되었습니다.
