Kestrel
대시보드로 돌아가기
CVE-2026-53530HIGHGHSA대응게시일: 2026. 07. 07.수정일: 2026. 07. 07.

ratex-parser panics on `\verb` with a multibyte delimiter (UTF-8 byte-boundary slice)

위협 신호 · CVSS · EPSS · KEV

정기 패치· 높은 악용 신호 없음
CVSS
high

이론적 심각도 점수

EPSS

예측 데이터 없음

KEV
미등재

실측 악용 기록 없음

권장 대응 기한60일 이내CISA SSVC 기준

계획된 패치 주기 내 조치(60일 이내)

외부 노출· KEV 미등재 · 자동화 어려움 · 부분 영향 · 외부 노출

CVSS 벡터 · 메트릭

CVSS 벡터 정보 없음

상세 설명

Summary

The public parser entrypoint ratex_parser::parse(&str) panics on the 9-byte input \verbéxé (i.e. \verb followed by the non-ASCII delimiter é). When handling a \verb command, the parser slices the verbatim argument with byte indices (arg[1..arg.len() - 1]); if the delimiter character is multibyte UTF-8, index 1 lands inside that character and Rust panics with “byte index 1 is not a char boundary”. Because RaTeX’s release profile sets panic = "abort" (Cargo.toml:48), the panic aborts the entire process — not just the current request/thread — making this a hard denial of service for any service that renders untrusted LaTeX.

Details

Affected code

crates/ratex-parser/src/parser.rs, parse_symbol_inner:

text
1if let Some(stripped) = text.strip_prefix("\\verb") { // parser.rs:901
2 self.consume();
3 let arg = stripped.to_string(); // e.g. "éxé"
4 let star = arg.starts_with('*');
5 let arg = if star { &arg[1..] } else { &arg }; // parser.rs:905 (also byte-sliced)
6 if arg.len() < 2 { // byte length
7 return Err(ParseError::new("\\verb assertion failed", Some(&nucleus)));
8 }
9 let body = arg[1..arg.len() - 1].to_string(); // parser.rs:910 <-- PANIC on multibyte delimiter
10 ...
11}

For input \verbéxé: arg = "éxé", where é = U+00E9 (bytes C3 A9). arg.len() is the byte length (5), the < 2 guard passes, and arg[1..4] starts at byte index 1 — inside the first é (bytes 0..2) — so the slice panics. The lexer groups \verb<delim>…<delim> correctly with char semantics (lexer.rs lex_verb); only the parser mishandles it.

PoC

<img width="1109" height="205" alt="image" src="https://github.com/user-attachments/assets/cd4bc6ae-23dd-458f-826c-6ce4e85c7005" />
bash
1$ printf '\\verb\xc3\xa9x\xc3\xa9\n' | ./target/release/parse
2thread 'main' panicked at crates/ratex-parser/src/parser.rs:910:27:
3start byte index 1 is not a char boundary; it is inside 'é' (bytes 0..2 of string)
4Aborted (core dumped) # exit 134 — panic=abort kills the whole process

Impact

Any application that renders untrusted LaTeX through RaTeX (web “render this math” endpoint, WASM in-browser use, the FFI embedded in another app) can be crashed by a tiny string. With panic = "abort" in release builds, the crash takes down the whole process / server, so a single malicious formula causes a full-service DoS (and, in batch pipelines, drops all queued work).

Remediation

Slice by character boundaries instead of byte indices, mirroring the UTF-8-correct logic the lexer already uses. For example:

text
1let chars: Vec<char> = arg.chars().collect();
2if chars.len() < 2 { return Err(ParseError::new("\\verb assertion failed", Some(&nucleus))); }
3let body: String = chars[1..chars.len() - 1].iter().collect();

(Apply the same char-aware handling to the * strip at parser.rs:905.) More broadly, consider not using panic = "abort" for builds embedded in long-running services, and/or wrapping parsing in catch_unwind at the FFI/WASM boundary — but the byte-slice fix is the direct correction.

AI 심층 분석

공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.