flawopen.com/Teardowns/cve-2026-102422-shell-quote-comment-line-terminator-injection
CVE-2026-102422: shell-quote 주석 토큰 인젝션
shell-quote 1.11.0 이전에는 주석 토큰 뒤에 줄 종료 문자가 든 문자열이 오면, 인용된 출력을 POSIX 셸에 전달할 때 의미가 바뀔 수 있었습니다. 공격자가 토큰에 영향을 주고, quote()를 호출하며, 출력을 셸에서 실행해야 합니다. 1.11.0 이상으로 업데이트하세요.
라벨의 주석은 그 줄의 나머지 내용을 숨길 수 있습니다. 다음 부분에 줄바꿈이 있으면 주석이 일찍 끝나고 뒤의 내용이 다시 드러납니다. 이 결함은 후속 토큰이 셸용 인용 출력에서 이런 상황을 만들 수 있게 했습니다.
핵심 개념 및 용어
shell-quote- 셸 형태 토큰을 파싱하고 인용하는 JavaScript 패키지입니다. 출력은 셸 문법용이며 범용 안전 문자열 형식이 아닙니다.
주석 토큰- 셸 주석 문법을 나타내며 줄 종료 문자가 나올 때까지 뒤의 텍스트에 영향을 줄 수 있는 토큰입니다.
줄 종료 문자- CR, LF, U+2028, U+2029 같은 줄 경계이며 셸은 이를 주석의 끝으로 처리할 수 있습니다.
CWE-78- 운영체제 명령의 특수 요소를 적절히 무력화하지 못한 취약점입니다.
프레임워크 매핑
- 이 가이드에서 설명하는 약점의 CWE 참조: CWE-78
근본 원인 분석 (Root Cause)
영향받는 quote() 경로는 주석 토큰 안의 줄 종료 문자는 거부했지만, 주석을 이미 출력했는지 기록하지 않았습니다. 따라서 뒤따르는 문자열 토큰에 줄 종료 문자가 들어갈 수 있었습니다. 공격자가 영향을 준 토큰이 quote()를 거치고 반환된 텍스트가 POSIX 셸에서 실행되면, 줄 경계가 주석을 끝내고 셸은 뒤의 텍스트를 문법으로 해석합니다. 업스트림 수정은 주석 상태를 기록하고 이후 모든 문자열 토큰의 줄 종료 문자를 거부합니다. 이는 주석 토큰 자체의 줄 종료 문자를 거부한 CVE-2026-9277 수정과 다른 문제입니다. 관리자는 High, CVSS 3.1 8.1로 평가하고 Ubuntu는 별도로 CVSS 4.0 9.2 Critical을 게시했습니다. 서로 다른 CVSS 버전이므로 점수를 같은 기준으로 취급하면 안 됩니다.
단계별 공격 실행 흐름
토큰이 인용 함수에 전달됨
앱이 주석과 뒤따르는 문자열이 있는 토큰을 구성하며 공격자가 그 순서에 영향을 줄 수 있어야 합니다.
함수가 주석 문법을 출력함
1.11.0 이전 quote()는 주석 텍스트를 검사했지만 그 뒤에 문자열이 오는지는 기억하지 않았습니다.
후속 토큰에 줄 경계가 있음
CR, LF, U+2028, U+2029가 셸 주석을 끝낼 수 있습니다.
앱이 셸에서 출력을 실행함
문서화된 셸 해석은 출력을 POSIX 셸에 전달하는 호출자에게 해당합니다.
업데이트가 위험한 순서를 거부함
1.11.0은 주석 상태를 기록하고 후속 문자열에 줄 종료 문자가 있으면 오류를 발생시킵니다.
소스 코드 비교: 취약한 구현 vs 보안 패치
취약한 코드 발췌는 주석 토큰의 줄 종료 문자를 검사하지만 후속 문자열의 상태를 기록하지 않습니다. 업스트림 변경은 주석이 출력된 상태를 저장하고 줄 종료 문자가 든 후속 문자열을 거부합니다. 이 발췌는 파싱과 인용의 경계를 설명하며 익스플로잇이나 셸 명령이 아닙니다.
소스 코드 발췌: quote.js — focused upstream diff hunks
// 커밋 6002b2e 이전 quote.js — 주석 토큰 관련 부분
if ('comment' in s && typeof s.comment === 'string') {
if (LINE_TERMINATORS.test(s.comment)) {
throw new TypeError('`comment` must not contain line terminators');
}
return '#' + s.comment;
}
// 커밋 6002b2e 이후 quote.js — 추가된 가드와 주석 상태의 정확한 부분
var sawComment = false;
return xs.map(function (s) {
if (sawComment && typeof s === 'string' && LINE_TERMINATORS.test(s)) {
throw new TypeError('a token after a `comment` must not contain line terminators');
}
if ('comment' in s && typeof s.comment === 'string') {
if (LINE_TERMINATORS.test(s.comment)) {
throw new TypeError('`comment` must not contain line terminators');
}
sawComment = true;
return '#' + s.comment;
}
});
엔지니어링 및 시스템 보안 강화 체크리스트
- ✓직접 및 전이
shell-quote의존성을 1.11.0 이상으로 업데이트하고 잠금 파일과 배포 산출물의 실제 버전을 확인하세요. - ✓
quote()가 모든 문맥에서 안전하다고 여기거나 셸 호출을 피하는 방법의 대체재로 사용하지 마세요. - ✓가능하면
shell: false와 인자 배열로 고정 실행 파일을 호출하고 대상 프로그램에 맞게 각 인자를 검증하세요. - ✓셸이 불가피하면 명령 구조를 고정하고 실행 전에 주석 토큰 뒤에 줄 종료 문자가 오는 순서를 거부하세요.
- ✓후속 문자열의 CR, LF, U+2028, U+2029 회귀 테스트를 추가하고
parse()에서 생성된 토큰도 검사하세요. - ✓공격자가 영향을 준 토큰을
quote()에 넣고 결과를 실행하는 호출 지점을 검토하세요. 패키지 사용만으로 악용 가능성이 입증되지는 않습니다. - ✓npm 버전으로 Ubuntu 패키지 상태를 추정하지 말고 해당 배포판의 공지를 확인하세요.