๋ฌธ์ ๋ ์ด๋ ๋ค.
csrf ์ทจ์ฝ์ ์ ์ด์ฉํ์ฌ flag๋ฅผ ์ป์ด๋ด๋ ๊ฒ์ด ๋ฌธ์ ์ด๋ค.
์ฃผ์ด์ง ๋งํฌ์ ์ ์ํ๋ฉด
์ ๊ทธ๋ฆผ์ฒ๋ผ 4๊ฐ์ ํ์ด์ง์ ์ ์ํ ์ ์๋
๋ฉ์ธ ํ์ด์ง๊ฐ ๋ฌ๋ค.
:: vuln(csrf) page ::
์ ์ฌ์ง์์ ๋ณด๋ฉด, url์ get์ผ๋ก ๋ณด๋ธ <script>alert(1)</script> ๊ฐ
์ถ๋ ฅ๋์์์ ์ ์ ์๋๋ฐ,
script ๋ผ๋ ๋ฌธ์์ด์ด ํํฐ๋ง๋๊ณ ์์์ ์ ์ ์๋ค.
:: memo ::
์ด๋, hello๋ผ๋ ๋ฌธ์๊ฐ ์ถ๋ ฅ๋๋๋ฐ,
ํ๋ฒ ๋ ๋ค์ ์ ์ํ๋ฉด,
์ด๋ ๊ฒ hello๊ฐ ๋๋ฒ ์ถ๋ ฅ๋๋ ๊ฒ์ ์ ์ ์๋ค.
:: notice_flag ::
์ ์ ๊ถํ์ด ์์ด์ Acees Denied ๊ฐ ๋ฐ์์ค์์ ํ์ธ๊ฐ๋ฅํ๋ค.
notice_flag์ธ ๊ฑฐ๋ก ๋ด์๋ ์ด ํ์ด์ง์ flag๊ฐ ์์ง ์์๊น๋ผ๊ณ ์๊ฐ๋๋ค.
:: flag ::
์ด ํ์ด์ง์ ๋ณด์ด๋ ์ ๋ ฅ์ฐฝ์ XSS๋ฅผ ์ผ์ผํฌ ์ฝ๋๋ฅผ ์ ๋ ฅํด์ผ ํ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
๊ทธ๋ผ, notice_flag ํ์ด์ง์์ ๋งํ์์๋ ํด๋น ํ์ด์ง์ ์ ์์ ์ฑ๊ณตํ๋ค๋ฉด
flag๋ฅผ ์ป์ ์ ์๋ค๊ณ ๊ฐ์ ํ๋ค๋ฉด,
/admin/notice_flag ์์น์ ์ ์์ ์์ฒญํด์ผ ํ๋๋ฐ,
flagํ์ด์ง๋ฅผ ๋ณด๋ฉด,
http://127.0.0.1:8000/vuln?param=[์ ๋ ฅ์นธ]
์ด๋ผ๊ณ ๋์ด์์ด์ vulnํ์ด์ง๋ก ์ ๋ ฅ์ฝ๋๋ฅผ ๋ณด๋ด๋ ๊ฒ์์ ์ ์ ์๋ค.
์ด๋, ์๊น scriptํ๊ทธ๋ ํํฐ๋ง ๋์์ผ๋ ๋ค๋ฅธ ํ๊ทธ๋ฅผ ์ฌ์ฉํ์.
img ํ๊ทธ๋ฅผ ์ฌ์ฉํด๋ณด์!
<img src="/admin/notice_flag">
์ด๋ ๊ฒ ์ฝ๋๋ฅผ ์ ๋ ฅํด๋ณด๊ฒ ๋ค.
์ด๋ ๊ฒ good ์ด๋ผ๋ ๋ง๊ณผ ํจ๊ป ์๋ฆผ์ฐฝ์ด ๋ฌ๋ค.
memo๋ก ๋ค์ด๊ฐ์ ํน์ flag๊ฐ ์ถ๋ ฅ๋์๋์ง ํ์ธํด ๋ณด์์ผ๋,
hello๋ง ์ถ๋ ฅ๋์์ ๋ฟ, ์๋ฌด์ผ ์๋ค.
์ด์ ํท๊ฐ๋ฆฌ๋
๋๋ฆผํต์์ ์ค ์์ค์ฝ๋๋ฅผ ๋ด๋ณด์.
#!/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)
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()
print(str(e))
# return str(e)
return False
driver.quit()
return True
def check_csrf(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("/")
def index():
return render_template("index.html")
@app.route("/vuln")
def vuln():
param = request.args.get("param", "").lower()
xss_filter = ["frame", "script", "on"]
for _ in xss_filter:
param = param.replace(_, "*")
return param
@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_csrf(param):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
memo_text = ""
@app.route("/memo")
def memo():
global memo_text
text = request.args.get("memo", None)
if text:
memo_text += text
return render_template("memo.html", memo=memo_text)
@app.route("/admin/notice_flag")
def admin_notice_flag():
global memo_text
if request.remote_addr != "127.0.0.1":
return "Access Denied"
if request.args.get("userid", "") != "admin":
return "Access Denied 2"
memo_text += f"[Notice] flag is {FLAG}\n"
return "Ok"
app.run(host="0.0.0.0", port=8000)
์ด๋ ๊ฒ ๊ธธ๋ค...
๋ถ์ํ๋๋ฐ ํ๋ฃจ ๋ค ๋ณด๋ธ ๊ฒ ๊ฐ๋ค. ใ ใ ...
๋จผ์ , /admin/notice_flag
๋ถ๋ถ๋ถํฐ ๋ถ์ํด๋ณด์.
/admin/notice_flag
@app.route("/admin/notice_flag")
def admin_notice_flag():
global memo_text
if request.remote_addr != "127.0.0.1": # 127.0.0.1์ด์ด์ผ ํจ.
return "Access Denied"
if request.args.get("userid", "") != "admin": # userid ๋ admin์ด์ด์ผ ํจ.
return "Access Denied 2"
memo_text += f"[Notice] flag is {FLAG}\n"
return "Ok"
Flag๋ฅผ ์ป์ผ๋ ค๋ฉด 2๊ฐ์ if๋ฌธ์ ๊ฑฐ์น์ง ์๊ณ ํต๊ณผํด์ผ ํ๋๋ฐ,
์ค์, ์์ธํ ๋ณด๋ฉด, ์ด /admin/notice_flag ํ์ด์ง์์ flag๋ฅผ ์ง์ ์ฃผ๋ ๊ฒ์ ์๋๊ณ ,
memo_text ๋ผ๋ ๋ณ์์ flag๊ฐ์ ์ ์ฅํด์ฃผ๋ ์ญํ ์ ํ๋ค.
์ฆ, 2๊ฐ์ if๋ฌธ์ ์กฐ๊ฑด๋ฌธ์ ์ถฉ์กฑํ์ง ์๋๋ก
127.0.0.1์ ์ฌ์ฉํ๋ฉฐ , userid=admin ์ด์ด์ผ ํ๋ ๊ฒ์ด๋ค.
๋ํ, memo_text ๋ผ๋ ๋ณ์์ flag๋ฅผ ์ ์ฅํ๋ ๊ณผ์ ๊น์ง ํด๋๋ค๋ฉด,
memo_text ๋ ์ด๋ ์ฝ๋์์ ์ถ๋ ฅํด์ฃผ๋์ง๋ ํ์ธ์ ํด์ผ ๋๋ค.
์ด์ , ๊ทธ๋ผ memo_text ๋ ์ด๋์์ ์ถ๋ ฅ์์ผ์ฃผ๋์ง ํ์ธํด๋ณด์.
/memo
@app.route("/memo")
def memo():
global memo_text
text = request.args.get("memo", None)
if text:
memo_text += text
return render_template("memo.html", memo=memo_text)
# ์ถ๋ ฅ๋๋ ํ์ผ์ memo ์ด๊ณ , memo๋ memo_text์ ๋ค์ด์๋ ๋ด์ฉ์ด ์ ์ฅ๋ ํ ์ถ๋ ฅ๋จ.
memo ํ์ด์ง์ ์ฝ๋์ด๋ค.
memo_text ๋ ์ ์ญ๋ณ์์์ ์ ์ ์๊ณ ,
์์งํ flask ์ ํ์ด์ฌ ์ฝ๋๋ฅผ ์์ธํ ์์ง๋ ๋ชปํด์
์ด๋ค ์ฝ๋์ธ์ง ์๋ฒฝํ ์ค๋ช ํ๊ธด ํ๋ค์ง๋ง,
๋ง์ง๋ง ์ค์์ memo_text๋ฅผ memo์ ์ ์ฅํ๊ณ return ํ๋ ๊ฒ์ผ๋ก ๋ณด์,
memo ํ์ด์ง์์ flag๊ฐ์ด ์ถ๋ ฅ๋๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
/flag
@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_csrf(param): # check_csrf ํจ์ ํธ์ถ.
return '<script>alert("wrong??");history.go(-1);</script>'
# check_csrf๊ฐ False ์ธ ๊ฒฝ์ฐ.
return '<script>alert("good");history.go(-1);</script>'
# check_csrf๊ฐ True ์ธ ๊ฒฝ์ฐ.
์ด๋ฒ์ flag ํ์ด์ง์ด๋ค.
request ๋ฐฉ์์ด GET ์ธ ๊ฒฝ์ฐ์ POST ์ธ ๊ฒฝ์ฐ๋ฅผ ๋๋์ด ์ฝ๋๋ฅผ ์คํํ๋๋ฐ,
GET ์ธ ๊ฒฝ์ฐ๋ ๊ทธ๋ฅ ๋ค์ flagํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ์ ๋๋๋ก๋์ด ์์ผ๋ ์๋ฏธ๊ฐ ์๋ค.
POST ์ธ ๊ฒฝ์ฐ๊ฐ ์ฐ๋ฆฌ๊ฐ ํ์ธํ ์ฝ๋์ธ๋ฐ,
(์ฌ์ค ์ฐ๋ฆฌ๋ URL ๋ง๊ณ ๋น ์ ๋ ฅ์ฐฝ์ ์ ๋ ฅํ์ผ๋ ๋น์ฐํ POST์ธ ๊ฒฝ์ฐ์ ์ฝ๋๋ฅผ ํ์ธํด์ผ ํ๋ค.)
๋ด๊ฐ ์ ๋ ฅํ ๊ฐ์ param ์ด๋ผ๋ ๋ณ์๋ก ๋ฐ๊ณ ์๊ณ ,
๋๊ฐ๊ฐ ๋ค์ด๊ฐ๊ฒ ๋ง๋ค์๋๋ฐ, ํ๋๋ ์ฐ๋ฆฌ๊ฐ ์ ๋ ฅํ ๊ฐ, ํ๋๋ ๋น๋ฌธ์์ด์ด๋ค.
๊ทธ ํ , check_csrf()ํจ์์ ์ธ์๋ก param ์ ๋ฃ์ด์ ํธ์ถํ๊ณ ์๋ค.
๊ทธ๋ฆฌ๊ณ , ๋ง์ฝ check_csrf()ํจ์๊ฐ ๊ฑฐ์ง์ด๋ฉด wrong ์, ์ฐธ์ด๋ฉด good์ ์ถ๋ ฅํ๋๋ก๋์ด์์์ ํ์ธํ๋ค.
๊ทธ๋ผ ๋จผ์ , check_csrf() ํจ์๊ฐ ๋ฌด์์ธ์ง ํ์ธํด๋ณด์!
check_csrf() ํจ์
def check_csrf(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) # read_url ํจ์ ํธ์ถ.
check_csrf() ํจ์๊ฐ ์ด๋ค ํจ์์ธ์ง ํ์ธํด๋ณด์.
๋จผ์ , param์ ์ ์ฅ๋์๋ ์ฐ๋ฆฌ๊ฐ ์ ๋ ฅํ ๊ฐ์
url ์ด๋ผ๋ ๋ณ์์ ์์ฒ๋ผ urlํ์์ผ๋ก ๋ณํ๋์ด ์ ์ฅ๋๊ณ ,
param์ ์ ์ฅ๋์๋ ๋น๋ฌธ์์ด์ name ๊ณผ value ๋ผ๋ ํน์ฑ์ด ์ ์ฅ๋์๋ค.
๊ทธ ํ read_url() ์ด๋ผ๋ ํจ์์ ๊ฐ๊ฐ ์ธ์๋ก ๋์ด๊ฐ๋๋ฐ,
์ด๋, ๋ read_url() ์ด๋ผ๋ ํจ์๋ฅผ ํธ์ถํ๊ธฐ ๋๋ฌธ์
์ด๋ฒ์ read_url() ์ด๋ผ๋ ํจ์๋ ํ์ธ์ ํด๋ด์ผ๊ฒ ๋ค.
read_url() ํจ์
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()
print(str(e))
# return str(e)
return False
driver.quit()
return True
read_url() ํจ์๊ฐ ๋ฌด์์ธ์ง ํ์ธํด๋ณด์.
๋จผ์ , ์๊น check_csrf()ํจ์์์ ์ธ์๋ก ๋ณด๋ธ ๊ฐ ์ค ํ๋์ธ cookie์ ์์ฑ์
domain:127.0.0.1 ์ด๋ผ๋ ๊ฒ์ ์ถ๊ฐํด์ฃผ๋ ์ฝ๋๊ฐ ๋ณด์ธ๋ค.
๊ทธ๋ฆฌ๊ณ ์ด ์์ฑ์ ์๊น /admin/notice_flag์์ ํ์ธํ ์กฐ๊ฑด์ค ํ๋์ด๋ค.
๊ทธ ํ, ๋ญ๊ฐ ๋ณต์กํ ์ฝ๋๊ฐ ๋ณด์ด๋๋ฐ, ์ ๋ง ๋จธ๋ฆฌ๊ฐ ํ์์ง๋ ์ฝ๋์ด๋ค.ใ ..
try ๋ฌธ์ด ์๊ณ , ๊ทธ ์์ for ๋ฌธ์ด ์๋๋ฐ,
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage"
์ด 5๊ฐ์ ๊ฐ์ ๋ํด์ for๋ฌธ์ ๋ชจ๋ ์ํํ๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
๊ทผ๋ฐ, ์๊น flag ํ์ด์ง ์์
<img src="/admin/notice_flag">
๋ฅผ ์ ๋ ฅํ์ ๋, good ์ด ๋ด์์ผ๋๊น,
์์ read_url() ํจ์์์๋ try๋ฌธ, for๋ฌธ์ด ์ฐจ๋ก๋ก ์คํ๋๊ณ , except๋ฌธ์ ๋์ด๊ฐ๊ณ
True ๊ฐ return ๋์์์ ์ ์ ์๋ค.
์ด์ ์ฐ๋ฆฌ๊ฐ ํ์ธํ ๊ฒ์ ๋น ์ง ๊ฒ์ด ๋ฌด์์ธ์ง ์ด๋ค.
์๊น /admin/notice_flag ํ์ด์ง์์
์ป์ ์กฐ๊ฑด์ 127.0.0.1 ๊ณผ userid=admin ์ด์๋๋ฐ,
์์ ์ฝ๋๋ค์ ํ์ธํ๋ฉด์ 127.0.0.1 ์ ๋ํ ์ฝ๋๋ ์กด์ฌํ์์ผ๋,
userid=admin ์ผ๋ก ์ค์ ํ ์ฝ๋๋ ์์๋ค.
์ฆ, userid=admin ์ด๋ผ๋ ๊ฐ๋ ์ ๋ ฅํด์ฃผ์ด์ผ ํ๋ค๋ ๊ฒ์ผ๋ก ์๊ฐ๋์ด์ง๋ค.
๋ฐ๋ผ์
<img src="/admin/notice_flag?userid=admin">
์ด๋ผ๊ณ flagํ์ด์ง์ ์ ๋ ฅํ์ฌ ๋ณด๊ฒ ๋ค.
memo ํ์ด์ง์ FLAG ๊ฐ์ด ์ถ๋ ฅ๋์์์ ์ ์ ์๋ค.
์ฑ๊ณต!!!
':: DreamHack ๐ฉ > wargame - web' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[ dreamhack ] - [ web | login-1 ] (0) | 2022.04.25 |
---|---|
[ dreamhack ] - [ web | csrf-2 ] (0) | 2022.04.05 |
[ dreamhack ] - [ web | image-storage ] (0) | 2022.03.17 |
[ dreamhack ] - [ web | proxy-1 ] (0) | 2022.03.17 |
[ dreamhack ] - [ web | command-injection-1 ] (0) | 2022.03.16 |
๋๊ธ