[DreamHack] Everyday 챌린지 Web1

챠챠비둘기·2023년 10월 26일
0

web 챌린지

목록 보기
1/4

pathtraversal


웹 서버 관련 문제인듯 하다. path traversal 취약점을 이용해 flag를 찾으라고 한다.

pathtraversal 이란?

파일을 옮기는 과정에서 옮기고자 하는 파일 이름에 대한 검증이 없기 때문에 발생하는 취약점
공격자가 웹 서비스의 파일 시스템에 존재하는 임의 파일을 다운로드할 수 있을 때 발생한다.
설정 파일, 암호 파일, DB 백업 파일 등 민감한 정보가 포함된 파일을 탈취하여 2차 공격도 수행 가능하다.

ex>

url 내에서 파일에 대한 경로로 접근 가능한 주소들이다
../ 와 같은 파일 경로를 통해 접근하여 민감한 정보가 담긴 파일 다운로드(passwd와 같은)가 가능해진다.

이렇게 path traversal 취약점을 이용하여 파일 이름을 url로 직접 입력받아, 임의의 디렉토리에 있는 파일을 다운로드 받을 수 있다.

문제

일단 파일을 다운받아보자.

#!/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"]

취약점 사이트로 들어가보자.

get user info로 들어가 봤더니,

userid에 어떤 id를 입력하면, 그 id에 대한 정보가 뜨는 걸 볼 수 있다.
flag의 위치를 보면,

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

/api/flag에 있으므로, userid에 ../flag를 쳐 보자

장렬히 실패
우리는 flag를 찾아야 하는데 여기 코드 부분을 보면

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"]

flag를 찾기 위해서는 엔드포인트 /api/flag에 요청을 보내야 하는데, 문제는 로컬호스트가 아니면 접근이 거부당한다는 것이다. 그러면, http 요청을 어느정도 조작한 후, 서버의 엔드포인트 /api/flag에 요청을 보내서 flag를 받아와야 될 듯 하다.
burp suite를 킨 후, 마찬가지로 userid에 ../flag를 입력하고, 요청을 가로채 보자.

userid에 undefined가 되어 있는걸 볼 수 있다.

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

uid 에 undefined가 들어가는 모양인데, 여기서 ../flag를 넣어주자.
/api/user/..flag = /api/flag

forward를 해주면

flag 발견

app.py 코드를 분석하고, flag를 불러내는 엔드포인트 위치를 찾아 웹 페이지 url에 경로를 삽입해서 flag를 불러오는 문제였다.

profile
개발 + 보안

0개의 댓글