https://dreamhack.io/wargame/challenges/26
[1] ๋ฌธ์ ๋ถ์
# ๋ฌธ์ ํ์ด์ง ๋ถ์
(1) ์ด๊ธฐ ํ๋ฉด (Home)
(1) /vuln(csrf) page
script ๊ฐ *๋ก ์นํ๋์ด์๋ค.
/script๋ ์ ๊ฑฐ๋์๋ค.
์์ค ์ฝ๋์์๋ ์์๋ผ ์ ์๋ ๊ฒ์ด ์์๋ค.
(2) /memo
URL์ ๋ณด๋ฉด /memo ํ์ด์ง์์ memo ํ๋ผ๋ฏธํฐ๋ก hello๋ฅผ ๋ฐ์๋ค์ฌ์ ํ์ด์ง ํ๋ฉด์ ์ถ๋ ฅํ๋ ๊ฒ์ ์ ์ ์๋ค.
์ฆ ?memo= ๋ค์์ hi๋ฅผ ์ ๋ ฅํ๋ฉด,
์ด๋ ๊ฒ ๋ฐ์์ด ๋๋ค.
๊ทธ๋ฆฌ๊ณ ํ์ด์ง๋ฅผ ๋ค์ ๋ค์ด๊ฐ ๋๋ง๋ค ์ด์ ์ ์ถ๋ ฅ๋์๋ ๋ฌธ์์ด ์ "hello"๊ฐ ์ถ๊ฐ๋์ด์ ์ถ๋ ฅ๋๋ค.
(3) /notice flag ํ์ด์ง
์ฃผ์๋ /admin/notice_flag ์ด๊ณ "Access Denied"๋ผ๋ ๋ฌธ๊ตฌ๊ฐ ์ถ๋ ฅ๋์๋ค.
์์ค์ฝ๋๋ฅผ ํ์ธํด๋ ์ ์ ์๋ ๊ฒ์ด ์์๋ค.
(4) /flag ํ์ด์ง
์ด์ ์ ํ์๋ ๋๋ฆผํต์ xss ๋ฌธ์ ๋ค์์ ๋ดค๋ ๊ฒ์ฒ๋ผ
/vulnํ์ด์ง์ "param" ๋งค๊ฐ๋ณ์์ ๊ฐ์ ๋ฃ์ด์ ์ ์ถํ ์ ์๋๋ก ํ๋ ํ์์ผ๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
์๋์์ ์์ค์ฝ๋๋ฅผ ์ดํด๋ณด๋๋ก ํ๊ฒ ๋ค.
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="/static/css/non-responsive.css">
<title>Index CSRF-1</title>
<style type="text/css">
.important { color: #336699; }
</style>
</head>
<body>
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">CSRF-1</a>
</div>
<div id="navbar">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<form method="POST">
http://127.0.0.1:8000/vuln?param=<input type="text" name="param"/><br/>
<input type="submit"/><br/>
</form>
</div> <!-- /container -->
<!-- Bootstrap core JavaScript -->
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
</body>
</html>
๋ค๋ฅธ ๋์ ๋๋ ๋ถ๋ถ์ ๋ฑํ ์๊ณ ...
POST ๋ฐฉ์์ผ๋ก ๋น์นธ์ ์ ๋ ฅํ ๊ฐ์ param์ผ๋ก ๋ฐ์๋ค์ฌ ์ ์ถํ๊ฒ ๋๋ ํผ์ ์ฌ์ฉํ๋ ๊ฒ์ ์ ์ ์๋ค.
ํฌ๊ฒ ์์๋ธ ๊ฒ์ด ์์๋ค.
.
.
.
๊ทธ๋ ๋ค๋ฉด ์ด์ ๋ค์ด๋ก๋ํ ๋ฌธ์ ํ์ผ์ ์ดํด๋ณด๊ฒ ๋ค.
# ๋ค์ด๋ก๋ํ ๋ฌธ์ ํ์ผ ๋ถ์
โ๏ธ app.py
#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
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:
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("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)
def read_url(url, cookie={"name": "name", "value": "value"}):
def read_url(url, cookie={"name": "name", "value": "value"}):
# ๊ธฐ๋ณธ ์ฟ ํค์ ๋๋ฉ์ธ ์ ๋ณด ์ถ๊ฐ
cookie.update({"domain": "127.0.0.1"})
try:
# Chrome ์น ๋๋ผ์ด๋ฒ ์ค์
service = Service(executable_path="/chromedriver")
options = webdriver.ChromeOptions()
# Chrome ์ต์
์ค์
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("http://127.0.0.1:8000/")
# ์ฟ ํค ์ถ๊ฐ
driver.add_cookie(cookie)
# ์ฃผ์ด์ง URL๋ก ์ด๋
driver.get(url)
except Exception as e:
# ์ค๋ฅ ๋ฐ์ ์ ๋๋ผ์ด๋ฒ ์ข
๋ฃ ๋ฐ ์ค๋ฅ ์ถ๋ ฅ
driver.quit()
print(str(e))
return False
# ์ ์์ ์ผ๋ก ์คํ๋๋ฉด ๋๋ผ์ด๋ฒ ์ข
๋ฃ ๋ฐ ์ฑ๊ณต ๋ฐํ
driver.quit()
return True
์... ์์ง์ ์ด ๋ถ๋ถ์์ ๋ฌด์ธ๊ฐ๋ฅผ ์์์ฐจ๋ฆด ์๋ ์์๋ค.
def check_csrf(param, cookie={"name": "name", "value": "value"}):
def check_csrf(param, cookie={"name": "name", "value": "value"}):
# CSRF ๊ณต๊ฒฉ์ ์๋ฎฌ๋ ์ด์
ํ๊ธฐ ์ํด URL์ ์์ฑ
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
# ์์ฑ๋ URL์ ๋ํ ์์ฒญ์ ๋ณด๋ด๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ
return read_url(url, cookie)
์ด ์ฝ๋์ ๋ชฉ์ ์ CSRF ๊ณต๊ฒฉ์ ๊ฒ์ฌํ๋ค.
1. 'urllib.parse.quote(param)' ์ ํตํด param ๊ฐ์ URL์ ์์ ํ๊ฒ ํฌํจ์ํจ๋ค. ์ด ํจ์๋ URL์ ์ฌ์ฉ๋ ์ ์๋ ๋ฌธ์๋ฅผ ์ ์ ํ ํํ๋ก ์ธ์ฝ๋ฉํ๋ค.
(๊ตฌ์ฒด์ ์ผ๋ก ๊ณต๋ฐฑ์ '%20', URL์์ ์ฌ์ฉ๋๋ฉด์ ํน๋ณํ ์๋ฏธ๋ฅผ ๊ฐ๋ ๋ฌธ์๋ค์ %๋ค์์ ๋ ์๋ฆฌ 16์ง์๋ก ๋ณํํ๋ค๊ณ ํ๋ค. )
2. url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}" ๋ถ๋ถ์์, ์๋ฎฌ๋ ์ด์ ๋ CSRF ๊ณต๊ฒฉ์ ์ํํ๊ธฐ ์ํด, "http://127.0.0.1:8000/vuln" ๊ฒฝ๋ก๋ก ์์ฒญ์ ๋ณด๋ด๋ URL์ ์์ฑํ๋ค. param์ urllib.parse.quote๋ฅผ ์ฌ์ฉํ์ฌ ์์ ํ๊ฒ ์ธ์ฝ๋ฉ๋์ด ํฌํจ๋๋ค.
(์ด ๋ถ๋ถ์ด ์ดํด๊ฐ ์ ๊ฐ์ง ์์๋ค...)
3. return read_url(url, cookie): ์์ฑ๋ URL์ ๋ํ ์์ฒญ์ read_url ํจ์์๊ฒ ์์ํ๊ณ , ํด๋น ํจ์์์์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
@app.route("/vuln")
์ด ๋ถ๋ถ์ /vuln ํ์ด์ง์ ๋ํ ์์ฒญ์ ์ฒ๋ฆฌํ๋ค.
๊ธฐ๋ฅ ์ค๋ช :
1. "param" ํ๋ผ๋ฏธํฐ ๊ฐ์ ๊ฐ์ ธ์์ ์๋ฌธ์๋ก ๋ณํํ ํ "param"์ ์ ์ฅํ๊ณ , ๋ง์ฝ "param"์ด๋ผ๋ ์ด๋ฆ์ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๊ฐ ์์ผ๋ฉด ๋น ๋ฌธ์์ด์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ฌ์ฉํ๋ค.
2. xss_filter ๋ผ๋ ๋ฆฌ์คํธ์ "frame", "script", "on"์ด๋ผ๋ ๋ฌธ์์ด์ ํฌํจ์ํจ๋ค.
3. xss_filter์ ์๋ ๊ฐ ๋ฌธ์์ด์ param์์ "*"๋ก ๋์ฒดํ๋ค.
4. 'return param'์ ํตํด ์ฒ๋ฆฌ๋ param ๊ฐ์ ๋ฐํํ๋ค.
>> xss ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๋ ๋ถ๋ถ์ผ๋ก "frame", "script", "on"์ "*"๋ก ๋์ฒดํ๋ฉฐ ํํฐ๋งํด์ค๋ค.
.
.
.
@app.route("/flag", methods=["GET", "POST"])
"/flag" ํ์ด์ง์ ๋ํ GET ๋ฐ POST ์์ฒญ์ ์ฒ๋ฆฌํ๋ ํจ์๋ฅผ ์ ์ํ๋ ๋ถ๋ถ์ด๋ค.
์ฃผ๋ก GET ์์ฒญ ์์๋ "flag.html"์ ๋ ๋๋งํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ๋ณด์ฌ์ฃผ๊ณ ,
POST ์์ฒญ ์์๋ chech_csrf ํจ์๋ฅผ ์ฌ์ฉํ์ฌ CSRF ๊ฒ์ฆ์ ์ํํ๊ณ ๊ทธ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ๋ค๋ฅธ ์๋ต์ ๋ฐํํ๋ ์ฉ๋๋ก ์ฌ์ฉ๋๋ค.
.
.
.
@app.route("/memo")
์ด ์ฝ๋๋ ์ฌ์ฉ์๊ฐ "/memo" ํ์ด์ง๋ก ์์ฒญ์ ๋ณด๋ด๋ฉด,
์์ฒญ์ ํฌํจ๋ "memo"๋ผ๋ ํ๋ผ๋ฏธํฐ ๊ฐ์ memo_text์ ๋ง๋ถ์ด๊ณ ,
๊ทธ ๊ฒฐ๊ณผ๋ฅผ "memo.html" ํ ํ๋ฆฟ์ ์ฌ์ฉํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ๋ค.
>> ์ฌ์ฉ์๊ฐ ๋ฉ๋ชจ๋ฅผ ์ถ๊ฐํ๊ณ ๊ทธ ๋ด์ฉ์ ์๊ฐ์ ์ผ๋ก ํ์ธํ ์ ์๊ฒ ํ๋ค.
.
.
.
@app.route("/admin/notice_flag")
1. ํด๋ผ์ด์ธํธ์ IP ์ฃผ์๊ฐ "127.0.0.1"(๋ก์ปฌํธ์คํธ)์ด ์๋ ๊ฒฝ์ฐ, "Access Denied"์ ๋ฐํํ๋ค.
2. ์์ฒญ์ ํ๋ผ๋ฏธํฐ ์ค "userid"๊ฐ "admin"์ด ์๋ ๊ฒฝ์ฐ, "Access Denied 2"๋ฅผ ๋ฐํํ๋ค.
์ด๋ ํธ์ถ์๊ฐ ํน์ ์ฌ์ฉ์ ID("admin")๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ๋ง ํจ์๋ฅผ ํธ์ถํ ์ ์๋๋ก ํ๋ค.
3. ํจ์๊ฐ ํธ์ถ๋๋ฉด, memo_text์ ๋ฌธ์์ด "[Notice] flag is {FLAG}"๋ฅผ ์ถ๊ฐํ๋ค.
[2] ๊ณต๊ฒฉ ์คํ
"on", "frame", "script"๋ฅผ ์ฌ์ฉํด์ ๊ณต๊ฒฉํ ์ ์์ผ๋ฏ๋ก,
๋ฌธ์์ด ์นํ ๋ฐฉ์์ด๋, ์ค๊ฐ์ %00์ ์ฝ์ ํ๋ ๋ฐฉ์์ ์ฌ์ฉํด๋ณด์๋๋ฐ ๊ณต๊ฒฉ์ด ๋์ง ์์์
(xss ๊ณต๊ฒฉ ๋ฌธ์ ๊ฐ ์๋๋ผ์ ์ด ๋ฐฉํฅ์ ์๋ ๊ฒ ๊ฐ๊ธฐ๋ ํ๋ค.)
๊ทธ๋ฅ '<img src=~' ๋ฅผ ์ฌ์ฉํด์ ๋ถ๋ฌ์ค๋ ๊ฒ์ผ๋ก ํ๋ ค๊ณ ํ๋ค.
๊ทธ๋ฐ๋ฐ ๋ฌธ์ ํ์ผ์ ๋ฐ์์ ๋ถ์ํด๋ณด์๋ ์ด๋ป๊ฒ ๊ณต๊ฒฉํด์ผ ํ๋์ง ๊ฐ์ด ์ค์ง ์์๋ค.
IP์ฃผ์๊ฐ "127.0.0.1"(๋ก์ปฌํธ์คํธ)๊ฐ ์๋๋ฉด /notice_flag ํ์ด์ง์์ Access Denied๋ฅผ ๋ฐํํ๋ ๊ฒ์ ์ด๋ป๊ฒ ํด๊ฒฐํด์ผ ํ ์ง ๋ชจ๋ฅด๊ฒ ์ด์ ๋ค๋ฅธ ์ฌ๋์ ํ์ด๋ฅผ ์ฐพ์๋ณด์๋ค.
์ด ๋ฌธ์ ์ ์ด๋ฆ์ด "csrf-1"์ธ ๋งํผ, xss ๊ณต๊ฒฉ๊ณผ crsf ๊ณต๊ฒฉ์ ์ฐจ์ด์ ์ ๋จผ์ ์ง๊ณ ๊ฐ์๋ฉด
xss ๊ณต๊ฒฉ์ ๊ณต๊ฒฉ์๊ฐ ์ ์์ ์ธ ์คํฌ๋ฆฝํธ๋ฅผ ์น ํ์ด์ง์ ์ฝ์ ํ๋ ๊ณต๊ฒฉ์ด๋ฉฐ ํด๋ผ์ด์ธํธ์์ ์คํ๋๊ณ
csrf ๊ณต๊ฒฉ์ ๊ณต๊ฒฉ์๊ฐ ํผํด์์ ์ธ์ฆ๋ ์ธ์ ์ ์ด์ฉํ์ฌ ์น ์๋ฒ์ ์ ์์ ์ธ ์์ฒญ์ ๋ณด๋ด๋ ๊ณต๊ฒฉ์ด๋ฉฐ ์๋ฒ์์ ์คํ๋๋ค.
์์์ ๋ฌธ์ ํ์ผ ๋ถ์์ ํ ๋,
/notice_flag ๊ฒฝ๋ก์์
1. ๋ก์ปฌํธ์คํธ๋ก ์ ๊ทผํ๋ ๊ฒฝ์ฐ
2. ?userid=admin์ธ ๊ฒฝ์ฐ flag ๊ฐ์ ์ป๊ธฐ ์ํ ๊ฒฝ๋ก๋ก ์ ๊ทผ์ด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์
1. /flag ํ์ด์ง๋ฅผ ํตํด /vuln์ ์์ฒญ์ ๋ณด๋ด๋ ํผ์ ์ ์ถํ๋ฉฐ
2. <img src="/admin/notice_flag?userid=admin" /> ๋ฅผ ์ ๋ ฅํด์ฃผ๋ฉด ๋๋ค๊ณ ํ๋ค.
๋ฌธ์ ํ์ด ์๋ฃ!
[3] ์ฐธ๊ณ
https://goldsony.tistory.com/261
[4] ๋๋ ์
1. ์ ์ด๊ฒ crsf ๊ณต๊ฒฉ์ธ์ง ์ ์ดํด๋์ง ์๋๋ค.
2. ํนํ ๋ก์ปฌํธ์คํธ๋ก ์ ๊ทผํ๋ ๊ฒ์ด ์๋๋ฉด ์ ๋๋ค๊ณ ํ๋๋ฐ, ๋ด๊ฐ ์ดํดํ ๋ฐ๋ก๋ ?userid=admin ์กฐ๊ฑด๋ง ์ถฉ์กฑ์ํจ ๊ฒ ๊ฐ์ด ๋๊ปด์ก๋ค.
๋ฌธ์ ๊ฐ ๊ฐ์ ๋์ง ์ผ๋ง๋์ง ์์์ ๊ตฌ๊ธ๋ง์ ํด๋ ์ ๋ณด๊ฐ ๋ง์ด ๋จ์์์ง ์์ ๊ถ๊ธ์ฆ์ ํด๊ฒฐํ์ง ๋ชปํด ์์ฌ์ ๋ค...
ํํ ๋ฌธ์ ํ์ด์๊ฐ์ ์ง๋ฌธํด์ผ ๊ฒ ๋ค!
'SWLUG > ์น ํดํน' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Dreamhack/๋๋ฆผํต] baby-linux (0) | 2023.11.16 |
---|---|
[Dreamhack/๋๋ฆผํต] csrf-2 (1) | 2023.11.11 |
[Dreamhack/๋๋ฆผํต] XSS Filtering Bypass (0) | 2023.11.10 |
[webhacking.kr] Challenge old-23 (0) | 2023.11.09 |
[Dreamhack/๋๋ฆผํต] xss-2 (0) | 2023.11.06 |