[Playbook] XSS 진단 Cheat Sheet
목차
이 문서는 XSS 진단 중 입력이 어느 파서 컨텍스트에 도달했는지 확인하고 다음 페이로드를 고르기 위한 빠른 참고서다. 복잡한 필터 우회와 프레임워크 내부 동작은 짧은 예외 상황으로만 정리하고 관련 Write-up과 Analysis로 연결한다.
승인된 테스트 환경과 실습에서만 사용한다. 처음부터 세션·사용자 데이터를 외부로 전송하지 않고
alert(1),print(), DOM marker처럼 영향이 없는 방식으로 JavaScript 실행 여부만 확인한다.
30초 진단 흐름
- 고유 marker가 Raw Response 또는 DOM에 나타나는지 확인한다.
< > " ' ` \\ & / $ { }문자가 어떻게 변환되는지 비교한다.- HTML, Attribute, URL, JavaScript, JSON, DOM 중 컨텍스트를 결정한다.
BREAKOUT → EXECUTION → TRIGGER → REPAIR순서로 페이로드를 조립한다.- 실행되지 않으면 필터, 이벤트 조건, CSP, 프레임워크 순서로 확인한다.
| marker 위치 | 우선 판단 | 다음 단계 |
|---|---|---|
| HTTP 응답 HTML에 그대로 존재 | Reflected·Stored 가능 | HTML parser 컨텍스트 확인 |
| Raw Response에서는 인코딩되지만 DOM 값은 복원됨 | 다단계 파싱 가능 | DOM property와 다음 parser 확인 |
| Raw Response에는 없고 JavaScript 실행 후 DOM에 생성 | DOM-Based 가능 | Source → Transform → Sink 추적 |
| JSON 응답에 존재 | 즉시 XSS로 확정 불가 | JSON.parse, eval, HTML sink 사용 여부 확인 |
| URL·href·src에 존재 | URL 컨텍스트 | 허용 scheme과 클릭·탐색 조건 확인 |
1. 반사 위치와 문자 변환 확인
실행 페이로드보다 먼저 고유 marker와 경계 문자를 넣는다.
KMINGON<>'"`\\&/${}
다음 세 값을 비교한다.
- HTTP Raw Response
- 개발자 도구 Elements의 DOM
- JavaScript가 읽은 실제 property 값
| 확인 결과 | 의미 | 다음 단계 |
|---|---|---|
<·>가 그대로 태그로 파싱 | HTML 요소 삽입 가능 | 이벤트가 있는 안전한 PoC 요소 사용 |
<·>는 인코딩되지만 따옴표가 남음 | 기존 속성 탈출 가능성 | 새 속성·이벤트 추가 |
| 따옴표가 escape되지만 역슬래시는 남음 | escape 상쇄 가능성 | 최종 JavaScript 문자열 확인 |
| Entity가 DOM에서 문자로 복원 | 다음 parser가 복원값을 해석할 수 있음 | 이벤트 핸들러·srcdoc 등 확인 |
| 입력이 텍스트로만 표시 | 실행 컨텍스트가 아닐 수 있음 | 다른 반사 위치와 client-side sink 확인 |
2. 출력 컨텍스트별 빠른 선택
페이로드는 다음 네 부분으로 나누어 생각한다.
BREAKOUT → EXECUTION PRIMITIVE → TRIGGER → SYNTAX REPAIR
| 컨텍스트 | 1차 확인 예시 | 핵심 조건 | 관련 실습 |
|---|---|---|---|
| HTML 태그 사이 | <img src=x onerror=alert(1)> | 새 요소가 태그로 파싱되는가 | HTML Context |
| 큰따옴표 속성 | " autofocus onfocus=alert(1) x=" | "로 기존 값을 닫을 수 있는가 | Attribute Context |
| 작은따옴표 속성 | ' autofocus onfocus=alert(1) x=' | 작은따옴표가 인코딩되는가 | Canonical Link |
| URL 속성 | javascript:alert(1) | scheme 검증과 사용자 클릭 조건 | Stored href |
| 작은따옴표 JS 문자열 | '-alert(1)-' | HTML보다 JavaScript 문자열 경계가 핵심 | JavaScript String |
| Template Literal | ${alert(1)} | backtick 탈출 없이 표현식 삽입 가능 여부 | Template Literal |
| 이벤트 핸들러 내부 문자열 | Entity로 구분자 복원 여부 확인 | HTML parse 후 JavaScript compile | onclick Context |
| JSON 응답 | "·\\ 처리 비교 | JSON이 이후 eval 또는 HTML sink로 이동하는가 | Reflected DOM XSS |
PoC가 실행되면 남은 HTML·JavaScript가 문법 오류를 만들지 않도록 빈 속성, 문자열, 연산자, 주석 등으로 뒤쪽 구문을 복구한다.
3. DOM-Based XSS 빠른 확인
Raw Response에서 취약한 반사 지점을 찾지 못했다면 client-side 데이터 흐름을 확인한다.
Source → decode·replace·concatenation → Sink → Trigger
자주 확인할 Source
| Source | 확인할 값 |
|---|---|
location.search | URL decoding 전후 query string |
location.hash | 서버로 전송되지 않는 fragment |
location.href | 전체 URL과 재직렬화 결과 |
document.referrer | 외부에서 유입된 이전 URL |
postMessage | origin 검증과 message 데이터 |
localStorage·sessionStorage | 이전 요청에서 저장된 사용자 제어 값 |
자주 확인할 Sink
| Sink | 입력이 해석되는 컨텍스트 | 빠른 판단 | 실습 |
|---|---|---|---|
innerHTML | HTML | <script>보다 이벤트 요소로 확인 | innerHTML Sink |
document.write | 현재 HTML parser 상태 | 주변 태그·속성·select 컨텍스트 확인 | document.write, select 내부 |
eval·Function | JavaScript | 서버·클라이언트 escape를 모두 추적 | Reflected DOM XSS |
jQuery $() | Selector 또는 HTML | 버전과 입력 첫 문자 확인 | jQuery Selector |
jQuery .attr('href', value) | URL | javascript:와 클릭 조건 확인 | jQuery href |
innerHTML에 <script>가 보이지만 실행되지 않는 것과, 기존의 비어 있는 <script> 요소 내용이 변경되며 실행되는 것은 서로 다른 동작이다.
4. Trigger 확인
DOM에 페이로드가 생성됐다는 사실과 이벤트가 발생해 실행됐다는 사실을 분리한다.
| Trigger | 주로 사용하는 상황 | 확인할 조건 | 실습 |
|---|---|---|---|
error | 잘못된 이미지·미디어 URL | 오류가 실제로 발생하는가 | innerHTML Sink |
load | SVG·iframe·body | 요소의 load 이벤트 지원 여부 | 관련: SVG 이벤트 실습 |
focus | input·포커스 가능한 요소 | autofocus, fragment focus | Attribute Context |
hashchange | fragment를 읽는 handler | 페이지 로드 후 fragment가 변경되는가 | jQuery hashchange |
resize | resize handler | iframe 크기 변화로 이벤트 발생 가능 여부 | body onresize |
| click·accesskey | URL·보이지 않는 요소 | 사용자 상호작용이 필요한가 | Canonical Link |
자동 실행이 필요하면 먼저 기존 페이지에서 자연스럽게 발생하는 이벤트를 찾는다. 이벤트가 없으면 fragment 변경, focus, iframe load 같은 전달 조건을 별도로 구성한다.
5. 필터에 막혔을 때
차단 응답과 정상 응답을 비교해 태그 → 속성 → 값 → Trigger 순서로 범위를 좁힌다.
| 증상 | 우선 확인 | 다음 선택 | 실습 |
|---|---|---|---|
<·>만 인코딩 | 기존 요소의 따옴표가 남는지 | 속성 탈출·이벤트 추가 | Attribute Context |
| 일반 태그 대부분 차단 | 허용 태그와 이벤트를 따로 열거 | body·SVG 등 허용 조합 | 태그·속성 차단 |
| 표준 태그 전부 차단 | Custom element 허용 여부 | focus 가능한 사용자 정의 요소 | Custom Tag |
| 이벤트 속성 차단 | SVG animation·URL 속성 등 다른 실행면 | 허용된 동적 속성 확인 | SVG href 변경 |
| 작은따옴표가 escape | 공격자 역슬래시도 escape되는지 | escape 상쇄 또는 HTML 종료 규칙 | Escape 상쇄, script 종료 |
| 공백·괄호 등 일부 문자 차단 | 대체 문법이 필요한지 | 특수 상황 표 확인 | JavaScript URL |
6. CSP 빠른 확인
PoC 요소가 DOM에 존재하지만 실행되지 않는다면 Console의 CSP 오류와 응답 헤더를 확인한다.
| 확인 항목 | 의미 |
|---|---|
script-src | 기본 script 로드·실행 정책 |
script-src-elem | <script> 요소에 더 구체적으로 적용 |
script-src-attr | 이벤트 핸들러 속성에 적용 |
| nonce·hash | 허용된 inline script 식별 |
strict-dynamic | nonce·hash 기반 신뢰 전파 여부 |
base-uri, form-action | script 외 탐색·폼 제출 제한 |
CSP는 XSS 존재 여부와 별개인 추가 방어선이다. HTML Injection이 가능하지만 script 실행만 차단된 상황인지, 입력 자체가 안전하게 인코딩된 상황인지 구분한다.
7. 특수·예외 상황
아래 항목은 일반 컨텍스트 페이로드가 맞지 않을 때만 확인한다. 레거시 프레임워크나 복잡한 우회 문법은 여기서 확장하지 않고 관련 실습으로 연결한다.
| 상황 | 짧은 판단 기준 | 관련 글 |
|---|---|---|
| 일부 SVG만 허용 | 애니메이션 요소와 onbegin 같은 SVG 이벤트 확인 | SVG Markup Write-up |
href와 이벤트가 모두 차단 | SVG가 실행 시점에 링크 속성을 변경할 수 있는지 확인 | SVG animate href |
| AngularJS 표현식이 평가됨 | {{7*7}} 결과와 버전을 먼저 확인 | AngularJS 기본 표현식, Sandbox Escape |
| AngularJS와 CSP가 함께 적용 | 브라우저 이벤트 속성이 아닌 framework directive 경로 확인 | AngularJS + CSP |
| CSP 헤더 일부에 입력이 반사 | 세미콜론으로 새 지시어가 주입되는지 확인 | CSP Policy Injection |
| script 실행 없이 정보 유출 필요 | form-action 등 빠진 navigation 제한 확인 | Strict CSP + Form Hijacking |
| 괄호·공백이 차단된 JavaScript URL | 암시적 형 변환·전역 오류 처리 같은 대체 실행 경로 | Blocked Characters |
| Stored DOM에서 일부 문자만 치환 | 전체가 아닌 첫 번째 일치만 replace되는지 확인 | Stored DOM XSS |
Quick Checklist
- 고유 marker가 Raw Response와 DOM 중 어디에 나타나는지 확인했는가?
- 경계 문자의 인코딩·escape·복원 결과를 비교했는가?
- HTML·Attribute·URL·JavaScript·JSON·DOM 컨텍스트를 구분했는가?
- BREAKOUT·EXECUTION·TRIGGER·REPAIR를 각각 확인했는가?
- DOM XSS라면 Source → Transform → Sink 흐름을 추적했는가?
- 페이로드가 생성된 것과 실제 Trigger가 발생한 것을 구분했는가?
- CSP가 존재한다면 차단된 실행 종류와 적용 지시어를 확인했는가?
- 일반 흐름에 맞지 않을 때만 특수·예외 상황을 확인했는가?