Element.innerHTML 자바스크립트 실행 취약점

HTML 에서 Element.innerHTML
태그는 요소(Element) 내 포함된 HTML, XML 마크업 정보를 읽거나 쓸 때 사용된다.
일반적인 사용법은 다음과 같다.
const content = element.innerHTML; // element 의 HTML 값을 읽어 content 에 저장.
element.innerHTML = htmlString; // htmlString 의 값을 읽어 element 의 HTML 값으로 씀.
HTML5 에서는 설령 innerHTML
에 <script>
태그를 사용하는 자바스크립트가 포함되어도 <script>
태그를 자동으로 제거하기 때문에 실행되지 않는다.
출처 : https://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml0
Note : script Elements inserted using innerHtml do not execute when they are inserted.
하지만 <script>
태그를 이용하지 않고 자바스크립트를 실행하는 방식은 작동하기 때문에 취약점으로 사용될 수 있다.
const name = "<img src='x' onerror='alert(1)'>"; // 이미지 로딩 실패 시 alert() 호출.
el.innerHTML = name;
모종의 방법으로 공격자가 웹사이트의 innerHTML
요소에 임의의 값을 쓸 수 있다면, 해당 페이지에서 자바스크립트를 실행하도록 유도할 수 있다.
Example
Github 에 해당 취약점을 테스트해볼 수 있는 Docker 파일을 업로드했다. 이를 이용하여 실제로 테스트 해보자. 아래 예제 테스트는 Docker, git 이 설치된 Linux OS 환경에서 진행했다. Docker 를 이용하기 싫다면 Flask 를 실행할 수 있는 환경을 구성한 후 deploy
폴더 내 app.py
를 실행해도 된다.
git 을 이용해 docker 파일 다운로드, 폴더로 이동한다.
$ sudo git clone https://github.com/Moonding/innerHTML-Vulnerability
$ cd innerHTML-Vulnerability
docker 명령어로 innerhtml_vul
이라는 이름의 이미지를 빌드한다.
$ sudo docker build -t innerhtml_vul .
해당 도커 컨테이너는 내부적으로 8000번 포트를 오픈하고 있으므로, 호스트 PC 의 적절한 포트에 바인딩해야 한다. 아래 명령어는 앞서 빌드한 innerhtml_vul
이미지를 이용하여 innerhtml_container
라는 이름의 컨테이너를 생성, 8000번 포트에 바인딩했다.
$ sudo docker run -d -p 8000:8000 --name innerhtml_container innerhtml_vul
정상적으로 컨테이너가 실행되었다면 브라우저를 이용하여 127.0.0.1:8000 포트로 접속할 수 있다.

해당 예제 서버는 index.html
에서 GET 방식으로 param
이라는 이름의 인자 값을 받아 script
id 값을 가지는 <div>
태그에 값을 쓴다. index.html
내 Form 에 문자열을 입력하고 Submit
버튼을 클릭하면 param
에 입력 값이 전달된다.
# index.html
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<div id='script'>This is script div</div>
<script>var x=new URLSearchParams(location.search); document.getElementById('script').innerHTML = x.get('param');</script>
<form action="/" method="get">
<label for="param"></label>
<input type="text" id="param" name="param" placeholder="Type Javascript Here"><br><br>
<input type="submit" value="Submit">
</form>
{% endblock %}
Form 에 <script>alert(1);</script>
이라는 경고문 출력 스크립트를 입력하면 자바스크립트가 실행되지 않은 채 <div>
에 값만 입력된 것을 확인할 수 있다.

하지만 Form 에 <img src='x' onerror='alert(1)'>
을 입력하면, 이미지 로딩에 실패하면서 경고문이 실행되는 것을 확인할 수 있다.
