[Write-Up] PortSwigger - DOM XSS in Document.write Sink Using Source Location.search Inside a Select Element
목차
문제 분석
난이도:
PRACTITIONER
Lab: DOM XSS in document.write sink using source location.search inside a select element
상품 재고 확인 기능은 location.search에서 storeId를 읽어 document.write()로 <select> 요소 내부에 새로운 <option>을 만든다. 현재 option과 select 컨텍스트를 탈출해 alert() 함수를 호출하면 문제가 해결된다.
DOM XSS 진단
상품 상세 페이지에서 재고 확인 기능의 JavaScript를 확인한다.

코드는 URL에서 storeId를 가져와 다음과 같은 HTML 문자열을 생성한다.
<select name="storeId">
<option selected value="USER_INPUT">USER_INPUT</option>
</select>
/product?productId=1&storeId=xss로 요청하면 드롭다운에 xss가 새로운 option으로 표시된다.

입력값이 큰따옴표로 감싼 value 속성과 option 본문에 들어가므로, 속성과 요소를 순서대로 닫아 select 바깥의 HTML 컨텍스트로 이동할 수 있다.
익스플로잇
storeId 파라미터에 다음 페이로드를 전달한다.
"></select><img src=1 onerror=alert(1)>
URL에 적용할 때는 다음과 같이 인코딩한다.
/product?productId=1&storeId=%22%3E%3C%2Fselect%3E%3Cimg%20src%3D1%20onerror%3Dalert%281%29%3E
첫 ">가 option의 value 속성과 시작 태그를 닫고, </select>가 현재 select 컨텍스트를 끝낸다. 뒤의 img는 일반 HTML 컨텍스트에서 생성되며 잘못된 src로 인해 onerror를 실행한다.

alert()가 호출되면서 문제가 해결된다.

정리
document.write()에 입력이 도달한다는 사실만으로 페이로드가 결정되지는 않는다. 이 랩에서는 입력이 <select>와 <option> 안에 있기 때문에 기존 속성과 요소를 모두 닫아야 안정적으로 실행 가능한 요소를 삽입할 수 있다. DOM 트리에서 현재 파싱 컨텍스트를 확인하는 과정이 중요한 이유다.
