Parse Me, Baby, One More Time: Bypassing HTML Sanitizer via Parsing Differentials (1)

2025. 5. 12. 22:01Project/mutation xss

mutation xss는 브라우저 파싱과 일반적인 보안 프레임워크의 파싱차이로 인해 발생되는 취약점이다. 브라우저가 파싱을 다양한 알고리즘으로 하는 반면 보안 프레임워크는 그러지 못하여 발생되는 취약점이다.

 

 

XSS를 필터링하는 방법은 크게  2가지이다. client 측 필터링 serverside 측 필터링.

그런데 serverside가 제대로 된 필터링를 진행하기 위해서는 browser의 html 파싱 알고리즘을 정확히 알아야 한다. 하지만 html 명세서만 봐도 엄청 복잡하며, 또한 명세서에 따라서 파싱이 꼭 이루어지는 것은 아니다.(이거는 브라우저 잘못임)

 

이에 재미있는 연구를 진행하셨다. 일단 먼저 HTML 명세를 분석하고, 파싱 동작에 영향을 있는 html 조각을 모은 뒤 mutation을 진행한다. 이후 제대로 세니타이즈 시키는 세니타이저를 통해서 통과 시킨다. 만약 bypass되면 세니타이즈의 취약점이 존재한다는 뜻이다.

 

 

일단 html과 xml은 SGML 이라는 구에서 파생되었다. 

 

1. 현대의 XML 파서가지 프로파일을 제공한다: SAX 기반 파싱DOM 기반 파싱이다.
 

DOM 기반 방식에서는 전체 문서를 파싱하여 트리 구조로 번에 반환한다. SAX 파싱스트림 기반의 파싱 방식이기 때문에 메모리 사용량이 적다. (문자열처럼 쭉읽으면서 이벤트를 반환함)

 

2. HTML 파싱은 단계로 나뉜다:

  1. 토크나이징(tokenization)들어오는 바이트 데이터를 토큰으로 변환
  2. 트리 구성(tree construction)위에서 생성된 토큰을 바탕으로 DOM(Document Object Model) 트리를 구성

 

이런 파싱 차이 때문에 XML 파서를 HTML에 적용하기 힘들다.

 

일단 당연히, XML 파서중에서  SAX 파싱 방식은 HTML를 제대로 파싱할 수 없다. 그 이유는 HTML을 랜더링 하는 브라우저의 프로세스가 이미 처리한 HTML tag를 재배치하는 경우가 많기 때문이다.

 

브라우저는 두가지의 파싱 방법을 지원한다. 문서 전체 파싱과 DOM 조각 파싱방법이다.

조각 파싱의 경우 element.innerHTML 와 같은 경우를 대비하기 위하여 있는 방식이다.

그런데 웃긴 것은 문서 전체 파싱과 DOM조각 파싱에 차이가 존재한다. 또한 DOM 조각 파싱에는 여러개의 알고리즘을 지원한다.

 

여기서 끝이 아니다 브라우저는 외부 문서등을 지원하기 위해서 SVG, MathML 등과 같은 태그를 지원하는데 이 또한 복잡한 파싱 알고리즘이 지원된다.

 

참고로 이러한 복잡한 파싱 알고리즘이 브라우저 마다 다르다.

 

그러면 XML 파서중 DOM 기반으로 파싱을 한 뒤, 위험한 태그를 제외하기 위해서는 이러한 복잡한 알고리즘을 브라우저별로 모두 고려하여 파싱해야 된다.

 

그렇다면 현재의 세니타이저들은 어떤 식으로 방어할까?

 

일단 무작정 정규식으로 a태그 및 기타 코드 등을 삭제시킬 수 없다. 그렇기에 아래와 같은 방법을 사용한다.

 

정제기는 특정 텍스트 조각이 코드를 실행할 있는 마크업을 포함하는지 판단있어야 한다.

일반적인 정제 방식다음과 같다:

  1. 입력을 HTML 명세에 따라 파싱하여
  2. DOM 트리(DOM tree)생성하고,
  3. DOM순회하면서(traverse),
    • 허용된 태그 목록(allowlist)없는 태그를 제거하거나,
    • 혹은 차단할 태그 목록(blocklist)있는 태그를 제거/변형한다.
  4. 이후, 정제된 DOM다시 텍스트 형태로 직렬화(serialize)하여 호출자에게 반환한다.

 

물론 인코딩 방식,  즉 <, > 이러한 특수문자를 모두 html 인코딩으로 인코딩 하는 방식은 모든 html다 막아 xss 효과적으로 방어한다. 그렇지만 html을 일부분 허용해야되는 경우 해당 방식을 사용할 수 없다.

 

 

Mutation Cross Site Scripting (mXSS)

 

위의 취약점은 파싱, 직렬화 다시 파싱하는 경우에 발생되는 취약점이며 해당 취약점은 두 가지 부분에서 발생된다.

 

1. 브라우저가 DOM을 JS를 통해서 업데이트할 때 DOM의 요소가 달라지는 경우

2. 세니타이저가 파싱한 뒤 직렬화하는데 DOM의 파싱결과가 달라지는 경우

 

1번의 경우 는 브라우저의 잘못이다. 그리고 브라우저 측 해결하고 끝이 난다. 

2번의 경우 파싱과 직렬화는 서버사이드에서 발생되며 파싱은 한번만 브라우저에서 발생한다.

또한 브라우저의 잘못이 아니며, 파싱과 직렬화의 경우 브라우저에서 한번만 일어난다.

 

위의 논문의 경우 2번의 경우를 찾는 논문이다.

 

https://portswigger.net/web-security/cross-site-scripting/cheat-sheetConsuming tags 가 mxss 페이로드이다.

'Project > mutation xss' 카테고리의 다른 글

CSS injection  (0) 2025.05.17
Muation XSS  (0) 2025.05.04