https://dreamhack.io/wargame/challenges/28/
XSS(Cross Side Scripting)은 클라이언트 사이드 취약점 중 하나로, 공격자가 웹 리소스에 악성 스크립트를 삽입해 이용자의 웹 브라우저에서 해당 스크립트를 실행할 수 있다.
자바스크립트는 웹 문서의 동작을 정의하는데, 이는 이용자가 버튼 클릭 시에 어떤 이벤트를 발생시킬지와 데이터 입력 시 해당 데이터를 전송하는 이벤트를 구현할 수 있다. 이러한 기능 외에도 이용자와의 상호 작용 없이 이용자의 권한으로 정보를 조회하거나 변경하는 등의 행위가 가능하다.
이러한 행위가 가능한 이유는 이용자를 식별하기 위한 세션 및 쿠키가 웹 브라우저에 저장되어 있기 때문이며, 따라서 공격자는 자바스크립트를 통해 이용자에게 보여지는 웹 페이지를 조작하거나, 웹 브라우저의 위치를 임의의 주소로 변경할 수 있다.
XSS로는 서버의 데이터베이스 또는 파일 등의 형태로 저장된 악성 스크립트를 조회할 때 발생하는 stored XSS, 서버가 악성 스크립트가 담긴 요청을 출력할 때 발생하는 reflected XSS가 있다.
reflected XSS는 stored XSS와 달리 URL과 같은 이용자의 요청에 의해 발생하며, 사용자의 URL 접속 유도를 통해 공격한다. 이때, 이용자에게 링크를 직접 전달하는 방법은 악성 스크립트 포함 여부를 이용자가 눈치챌 수 있기 때문에 주로 Click Jacking 또는 Open Redirect 등 다른 취약점과 연계하여 사용한다.
시작 웹 모습은 다음과 같으며, 각 카테고리를 살펴보자
vuln(xss) page에 들어가면 URL의 param= 뒤에 스크립트 코드가 웹에 직접적인 동작을 취한다
그러나 memo page에 들어가면 memo= 뒤의 변수가 스크립트 건 무엇이건 문자열 그대로 출력을 한다
그러면 python코드를 보자
param= 뒤의 값은 param변수로 들어오게 되며, 그 내용이 그대로 반환되어 우리에게 주어진 그대로 결과를 가지고 오게 된다.
즉, 스크립트 코드를 넣으면 웹 동작에 대한 결과가 그대로 나타나는 XSS가 발생한다. 이것이 문제 웹 사이트의 취약점이다.
따라서 우리는 /vuln
에서 플래그를 불러오도록 유도하는 스크립트를 실행하도록 하면 된다!
반면 memo 카테고리에서 memo= 의 매개변수로 준 입력은 문자열 연산에 의해 text화 되어 하나의 memo_text 문자열로 변한채 웹 렌더링 된다.
이때는 memo= 뒤에 스크립트 코드를 넣어도, 결국 문자열로 나오기때문에 XSS가 발생하지 않는다.
마저 코드를 살펴보면, check_xss()
에 param= 뒤의 값으로 줬던 변수를 받아 url
을 완성하고, 쿠키와 같이 해당 url을 전달하는 read_url()
이 실행된다
driver.get("http://127.0.0.1:8000/")
을 통해 해당 url에 이동하여 받아왔던 쿠키를 저장한다.
param= 뒤에 칸이 있는 곳은 초기 화면에서 flag 카테고리로 가면 나오는 곳이다.
/flag
에서 param= 뒤에 어떤 값을 넣으면 쿠키로 플래그 값이 check_xss()
에 전달된다. 결국 이 쿠키는 read_url()
에서 http://127.0.0.1:8000
에 플래그가 저장되는 것이다.
driver.get(url)
은 코드가 실행된 환경에서 내부적으로 해당 url에 http/s 요청을 하여 접근하는 방식이며, 우리가 직접 해당 url에 접속해 개발자 툴을 열고 쿠키를 확인하는 것은 불가능하다.
read_url()
메소드는 웹 드라이버를 통해 브라우저를 제어하는 Selenium 모듈로 작성되었으며, Selenium 웹 드라이버가 사용하는 브라우저 세션은 코드가 실행되는 동안만 유효하며, 코드 실행이 끝나면 driver.quit() 또는 driver.close()를 호출하여 브라우저 세션을 종료한다. 이 과정에서 생성된 쿠키도 함께 소멸하게 되기 때문이다.
따라서 read_url()
메소드를 타고 진행될 때, 동시에 저장된 쿠키의 값을 얻어내야 한다!
이때 read_url()
에 접근한 url에 XSS를 발생시키면 어떨까?
스크립트 코드를 param 값으로 제공해주는 것이다.
코드의 서순이 XSS를 시도하라는 신호를 보내고 있던 것이다.
엔드포인트가 /flag
인 URL에서 param값을 입력 후 제출한 뒤, http://127.0.0.1:8000/
에 플래그가 담긴 쿠키를 저장하고, param값을 통해 만들어진 url에 접근할 때, 스크립트가 실행되어 쿠키를 가져오게 하면 될 것이다.
이 목적에 맞는 코드를 작성하기 위해 속성을 몇 개 알아야한다.
location.href - 전체 URL을 반환하거나, URL을 업데이트할 수 있는 속성값이다.
document.cookie - 해당 페이지에서 사용하는 쿠키를 읽고, 쓰는 속성값이다.
이때, 주목할 점이 하나 있다.
이 코드는 read_url()
맨 처음 실행되는 코드인데 cookie.update()
는 쿠키 정보를 갱신한다. 만약 키 값이 이미 존재하면 value값을 덮어씌우고, 키 값이 없는 것은 새로 추가한다.
쿠키에 해당 domain에 대한 접속도 허가해준다. 도메인에 쓰일 IP 127.0.0.1 (localhost)로 기존 VM 웹 주소의 도메인 대신 사용하는 것이 가능하다. 따라서 127.0.0.1/vuln
과 같이 기존 엔드포인트에 접속이 가능해지는 것을 유의하자!
그런데 driver.get(url)
을 통해 이미 이동해있는 위치는 /vuln이다.
그곳에서 param에 존재하는 스크립트를 실행시키기만하면 된다!
이미 접속한 /vuln에서는 XSS가 가능하므로 어딘가에 쿠키 정보를 기록해주기만 하면 될 것이다.
마침 입력변수를 문자열로 저장해주는 카테고리가 하나 있었다.
바로 /memo 이다! 이곳에 쿠키 정보를 변수로 기록하게 해주자!
이 작동을 가능케하는 스크립트는 다음과 같다
<script>location.href = "/memo?memo" + document.cookie</script>
이후 memo 카테고리를 확인해보면 flag가 기록되어 있는 것을 볼 수 있다!