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

일단 문제 설명에서 flag가 있는 경로를 알려주었다.
여기에서 path traversal를 알아보면
path traversal란?
서비스에서 사용자로부터 받은 입력이 path 형태의 백엔드에서 처리 로직을 가지는 경우, 이를 조작하여 공격자가 원하는 경로로 접근하여 동작을 수행하는 공격기법을 의미한다.
File을 처리하는 과정에서 많이 발생하며, 파일 이름 등을 사용자로 부터 받는 경우 사용자가 ../ 같은 구문을 통해 상위 디렉토리로 접근하거나 허용된 디렉토리의 범위를 벗어나 시스템 파일을 읽을 수 있다.

버튼을 눌러버리면 아래 사진처럼 뜬다.

한번 guest로 들어가보겠다.

admin으로 해보면 이렇게 된다.

그래서 문제 소스 코드를 확인해보았다.
#!/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)
@app.route('/api')
@internal_api
def api():
return '/user/<uid>, /flag'
입력된 userid값은 /api/user/{userid}에 저장되며, FLAG 는 /api/flag에 저장 된다.
이것을 이용해 /api/user/{userid}에서 /api/flag로 이동해보도록 하겠다.
../flag 를 입력해보면


실패했다.
코드를 더 살펴보면
users = {
'0': {
'userid': 'guest',
'level': 1,
'password': 'guest'
},
'1': {
'userid': 'admin',
'level': 9999,
'password': 'admin'
}
}
guest 와 admin의 키 밸류 쌍을 볼 수 있는데, 여기서 admin을 입력하면 1로 바뀌어서 전달된다.
그럼 이 값을 ../flag로 바꿔주면 어떨까?
console창을 이용해서 바꾸어보았다.


이처럼 guest를 입력하면 flag가 뜨는 것을 알 수 있다.