SWLUG/์›น ํ•ดํ‚น

[Dreamhack/๋“œ๋ฆผํ•ต] csrf-2

waterproof 2023. 11. 11. 22:57

 

https://dreamhack.io/wargame/challenges/269

 

 


[1] ๋ฌธ์ œ ๋ถ„์„

 

# ๋ฌธ์ œ ์›น ํŽ˜์ด์ง€ ๋ถ„์„

 

(1) /Home

 

์ดˆ๊ธฐ ํ™”๋ฉด(Home ํ™”๋ฉด)์ด๋‹ค.

 

(2) /vuln(csrf) page

 

script๋Š” *๋กœ ๋Œ€์ฒด๋˜์—ˆ๊ณ 

/script๋Š” ๊ณต๋ฐฑ ์ฒ˜๋ฆฌ๋œ ๊ฒƒ ๊ฐ™๋‹ค.

 

 

 

์†Œ์Šค ์ฝ”๋“œ๋„ ์ด๊ฒŒ ์ „๋ถ€๋‹ค.

 

 

 

(3) /flag ํŽ˜์ด์ง€

 

 

/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-2</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-2</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>

 

 

/flag ํŽ˜์ด์ง€์˜ ์†Œ์Šค ์ฝ”๋“œ์ด๋‹ค.

๋ฐฉ๊ธˆ ๋ถ„์„ํ•œ ๊ทธ๋Œ€๋กœ์ด๊ณ , ์ด์™ธ์— ๋”ฑํžˆ ํžŒํŠธ๊ฐ€ ๋˜๋Š” ๋‚ด์šฉ์€ ์—†์—ˆ๋‹ค.

 

 

(4) /login ํŽ˜์ด์ง€

 

 

๋กœ๊ทธ์ธ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚œ๋‹ค.

 

<!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>Login CSRF-2</title>
    
  

  </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-2</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">
      
<h1>Login</h1><br/>
<form method="POST">
  <div class="form-group">
    <label for="InputName">username</label>
    <input type="text" class="form-control" id="InputName" placeholder="username" name="username" required>
  </div>
  <div class="form-group">
    <label for="InputPassword">password</label>
    <input type="password" class="form-control" id="InputPassword" placeholder="password" name="password" required>
  </div>
  <button type="submit" class="btn btn-default">Login</button>
</form>

    </div> <!-- /container -->

    <!-- Bootstrap core JavaScript -->
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script> 
</body>
</html>

 

/login ํŽ˜์ด์ง€์˜ ์†Œ์Šค ์ฝ”๋“œ์ด๋‹ค.

username๊ณผ password๋ฅผ ๋ฐ›์•„๋“ค์ด๊ณ  ์žˆ๋‹ค.

ํŠน๋ณ„ํ•œ ์ ์€ ์—†์–ด๋ณด์ธ๋‹ค.

 

 

 

# ๋‹ค์šด๋กœ๋“œํ•œ ๋ฌธ์ œ ํŒŒ์ผ ๋ถ„์„

 

โš™๏ธ app.py

#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for
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**]"

users = {
    'guest': 'guest',
    'admin': FLAG
}

session_storage = {}

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():
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not an admin"}')


@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", "")
        session_id = os.urandom(16).hex()
        session_storage[session_id] = 'admin'
        if not check_csrf(param, {"name":"sessionid", "value": session_id}):
            return '<script>alert("wrong??");history.go(-1);</script>'

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


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    elif request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        try:
            pw = users[username]
        except:
            return '<script>alert("not found user");history.go(-1);</script>'
        if pw == password:
            resp = make_response(redirect(url_for('index')) )
            session_id = os.urandom(8).hex()
            session_storage[session_id] = username
            resp.set_cookie('sessionid', session_id)
            return resp 
        return '<script>alert("wrong password");history.go(-1);</script>'


@app.route("/change_password")
def change_password():
    pw = request.args.get("pw", "")
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    users[username] = pw
    return 'Done'

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

 

 

users = { ... }

 

users = {
    'guest': 'guest',
    'admin': FLAG
}

 


์ด ๋ถ€๋ถ„์€ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋‹ด์€ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์ •์˜ํ•˜๊ณ  ์žˆ๋‹ค. (ํŒŒ์ด์ฌ์—์„œ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฌธ๋ฒ•์„ ์˜ค๋žœ๋งŒ์— ๋– ์˜ฌ๋ ธ๋‹ค.)

 

์Œ... id์— 'guest'๋ฅผ ๋„ฃ๊ณ  password์— 'guest' ๋„ฃ์—ˆ์„ ๋•Œ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋ฉด 

๋”•์…”๋„ˆ๋ฆฌ์˜ key : value๊ฐ€ username : password ๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

ํ•œ ๋ฒˆ ์‹œ๋„๋ฅผ ํ•ด๋ณด์•˜๋‹ค.

 

 

์˜ค!

 

/login ํŽ˜์ด์ง€์—์„œ username์— 'gues', password์— 'guest'๋ฅผ ๋„ฃ์—ˆ๋”๋‹ˆ 

guest๋กœ ๋กœ๊ทธ์ธ๋˜์—ˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ๊ตฌ๊ฐ€ ์ถœ๋ ฅ๋˜์—ˆ๋‹ค.

.

.

.

 

๊ทธ ๋ง์€ ์ฆ‰,

admin ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๋ ค๋ฉด, username์— 'admin'์„ ๋„ฃ์œผ๋ฉด ๋˜๋Š” ๊ฒƒ์ด๊ณ ,

password๋Š” FLAG๋ผ๊ณ  ์ ํ˜€์žˆ๋Š” ๊ฒƒ์„ ๋ณด๋‹ˆ password ๊ฐ’์„ ๊ตฌํ•˜๋Š” ๊ฒƒ์ด ๊ด€๊ฑด์ธ ๊ฒƒ ๊ฐ™๋‹ค.

 

๊ทธ๋Ÿฌ๋‹ˆ๊นŒ ์ด์ œ๋ถ€ํ„ฐ password ๊ฐ’์„ ๊ตฌํ•˜๊ธฐ ์œ„ํ•ด app.py์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์„ ๋ถ„์„ํ•ด๋ณด๊ฒ ๋‹ค.

 

 

@app.route("/")

@app.route("/")
def index():
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not an admin"}')

 

 

์ด ๋ถ€๋ถ„์€ ์ดˆ๊ธฐ ํ™”๋ฉด์„ ์ •์˜ํ•˜๋Š”๋ฐ,

์ง€๊ธˆ ์ƒํ™ฉ(guest๋กœ ๋กœ๊ทธ์ธ์„ ์„ฑ๊ณตํ–ˆ๊ณ , username์ด admin์ธ ๊ฒƒ์„ ์•„๋Š” ์ƒํ™ฉ)์—์„œ ์ฃผ๋ชฉํ•  ๋งŒํ•œ ๋ถ€๋ถ„์€


return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not an admin"}') ๋ถ€๋ถ„์ด๋‹ค.

 

 

๋กœ๊ทธ์ธ์„ ํ–ˆ์„ ๋•Œ, admin์ด ์•„๋‹ˆ๋ฉด, "Hello {username}, you are not an admin" ์„ ์ถœ๋ ฅํ•˜๊ณ ,

admin์œผ๋กœ ๋กœ๊ทธ์ธ์„ ์„ฑ๊ณตํ–ˆ์„ ๋•Œ, "Hello {username}, flag is " + FLAG" ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ์ •์˜๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๊ฐ€ ๋ฐฉ๊ธˆ ์ „์— 'guest'๋กœ ๋กœ๊ทธ์ธํ–ˆ์„ ๋•Œ,

"Hello guest, you are not an admin" ๋ฌธ๊ตฌ๊ฐ€ ์ถœ๋ ฅ๋œ ๊ฒƒ์ด๋‹ค.

 

 

@app.route("/vuln")

@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

 

๋ฐ›์•„์˜จ param ๊ฐ’์„ ๋ชจ๋‘ ์†Œ๋ฌธ์ž๋กœ ๋ฐ”๊พผ ๋‹ค์Œ, 

"frame", "script", "on" ์ผ ๊ฒฝ์šฐ ํ•„ํ„ฐ๋งํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๋ฌธ์ž์—ด์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๋ ค์ค€๋‹ค.

 

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— img ํƒœ๊ทธ๋ฅผ ํ™œ์šฉํ•  ๊ฒƒ์ด๋‹ค.

 

 

@app.route("/change_password")

@app.route("/change_password")
def change_password():
    pw = request.args.get("pw", "")
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    users[username] = pw
    return 'Done'

 

๋ฌธ์ œ ํŽ˜์ด์ง€์—์„œ๋Š” ๋ณผ ์ˆ˜ ์—†์—ˆ๋˜ "/change_password" ๊ฒฝ๋กœ๋ฅผ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.

 

์ด ๋ถ€๋ถ„์„ ๋ณด๋ฉด /change_password ํŽ˜์ด์ง€์—์„œ GET ๋ฐฉ์‹์œผ๋กœ  pw ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ๊ฐ€์ ธ์™€ pw์— ๊ทธ ๊ฐ’์„ ํ• ๋‹นํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

(์ฃผ์†Œ์ฐฝ์— /change_password?pw=1234 ๋ผ๊ณ  ์ž…๋ ฅํ•˜๊ณ  ์ „์†กํ•˜๋ฉด pw์— 1234๋ฅผ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์ด๋‹ค.)

 

๊ทธ ๋’ค์— users์— pw ๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ €์žฅํ•œ๋‹ค.

 

 

@app.route('/login', methods=['GET', 'POST'])

 

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    elif request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        try:
            pw = users[username]
        except:
            return '<script>alert("not found user");history.go(-1);</script>'
        if pw == password:
            resp = make_response(redirect(url_for('index')) )
            session_id = os.urandom(8).hex()
            session_storage[session_id] = username
            resp.set_cookie('sessionid', session_id)
            return resp 
        return '<script>alert("wrong password");history.go(-1);</script>'

 

๊ทธ๋ฆฌ๊ณ  /login ํŽ˜์ด์ง€๋ฅผ ๋™์ž‘์‹œํ‚ค๋Š” ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด

/login ํŽ˜์ด์ง€์—์„œ ์ž…๋ ฅ๋ฐ›์•„์˜จ password ๊ฐ’์ด pw ๊ฐ’์ด๋ž‘ ๊ฐ™์„ ๋•Œ,

์‚ฌ์šฉ์ž๋ฅผ 'index'๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ํ•˜๋Š” ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

.

.

.

์•„๊นŒ index ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ๋•Œ, admin์œผ๋กœ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋ฉด, "Hello {username}, flag is " + FLAG" ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค๊ณ  ํ–ˆ๋‹ค.

 

 

 

๊ทธ๋ ‡๋‹ค๋ฉด!

 

1. /vuln(csrf) page์— img ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ทจ์•ฝ์ ์ด ์žˆ์Œ.

2. pw ๊ฐ’์€ /change_password ํŽ˜์ด์ง€์—์„œ GET ๋ฐฉ์‹์œผ๋กœ ๊ฐ’์„ ๋ฐ›์•„๋“ค์—ฌ admin์˜ password๊ฐ€ ๋จ.

.

.

.

 

์œ„์˜ ์‚ฌ์‹ค์„ ์•Œ์•„๋ƒˆ์œผ๋‹ˆ,

/flag ํŽ˜์ด์ง€์—  <img src="/change_password?pw=1" /> ๋ฅผ ์ž…๋ ฅํ•ด์„œ password ๊ฐ’์„ 1๋กœ ๋ฐ”๊ฟ”์ฃผ๊ณ  ๋กœ๊ทธ์ธ์„ ์‹œ๋„ํ•ด๋ณด์ž.

 

4

 

 

์ดˆ๊ธฐ ํ™”๋ฉด ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ๋˜๋ฉด์„œ ์˜ˆ์ƒ๋Œ€๋กœ flag ๊ฐ’์ด ๋‚˜ํƒ€๋‚ฌ๋‹ค.

 

 

 

์„ฑ๊ณต!!


[3] ๋Š๋‚€ ์ 

 

ํŒŒ์ด์ฌ ๋ฌธ๋ฒ•๋„ ์ž˜ ์•Œ์•„์•ผ ์›น์ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์„ ์ž˜ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค!

(์ฒ˜์Œ์— user๋ฅผ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์„ ํ•ด์„ํ•˜์ง€ ๋ชปํ•ด์„œ ๋ฌธ์ œ ํ’€์ด์— ์–ด๋ ค์›€์„ ๊ฒช์—ˆ์—ˆ๋‹ค.)