2025. 3. 31. 20:53ㆍHacking/WebHacking Wargame Writeup
문제
문제 링크
https://dreamhack.io/wargame/challenges/12
pathtraversal
사용자의 정보를 조회하는 API 서버입니다. Path Traversal 취약점을 이용해 /api/flag에 있는 플래그를 획득하세요! Reference Server-side Basic
dreamhack.io
프론트엔드 개발을 했던게 그나마(?) 여기서 쓸모가 있는건가.. /api/flag면 flag를 얻는 화면이나 기능이 사이트에 숨겨지거나 만들어져 있을거고, 아마 그 화면에 대한 접근 권한은 admin만 가질 수 있고 그 안에 flag를 얻거나,,, 아니면 session 문제처럼 세션 값을 이용한 브루트포스 탈취가 필요할 수도 있다. 가능성은 여러가지~
일단 화면의 기능을 탐색해보는 것도 추론에 꽤나 도움이 된다고 생각해서 이것저것 만져보았다.
session 만들고 스니펫 그대로 옮겨쓰셨나 싶을정도로 화면 디자인이 거기서 거기다.. 근데 나같아도 그럴거같다..(디자인 구상하는거 여간 귀찮은게 아님 ㅇㅈㅇㅈ)
get user info라는 버튼이 눈에 띈다.
냅다 guest부터 입력하고 view를 누른다.
guest에 대한 정보가 뜨고 level에 따라 접근 권한이 다르다.
admin이 존재할 것이고, 그렇다면 admin은 level이 더 높고 접근 권한이 다르겠지~예측해주면서 admin도 입력해주고 view를 누른다.
역시나... admin에 대한 정보가 뜨면서 level:9999로 뜬다.
이건 그냥 한 번 해본 삽질... 아무리 대놓고 이런 해답은 주진 않겠지만 혹시나..
/api/flag라길래 나는 도메인 창에 get_info#flag라고 쳐본다. (가끔 api를 직접 도메인에 넣기도 한다.)
{}라고 뜨는데 아마 뭔가를 하면(여기선 admin권한을 유지하면서 get info/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"]
전체 코드를 보면 users에 대한 정보가 있다. 접근은 잘했구나~확인 해준다.
@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)
userid와 함께 보내는 값을 조작해 admin 권한을 얻으면 /api/flag에 닿는 거 같고
@app.route('/api/flag')
@internal_api
def flag():
return FLAG
-> /api/flag에 들어가면 flag() 함수가 작동되면 FLAG값이 출력된다고 나온다.
-> 단순 도메인 조작은 아까 실패했으니까 이번에도 브루트포스를 사용한다.
-> 여기 소스코드에는 admin 권한에 대한 단서가 보이지 않아 userid 자체에 /api/flag로 변환을 주면 되는 건가... 하는 방법도 생각해본다(타 writeup을 참고했다.)
다들 여기서 userid를 직접 수정하던데 난 Request에서 바로 수정 안 되던데?? 했는데 repeater로 send to (역시나 이번에도 request창에 마우스 우클릭하면 send to repeater가 뜬다.)
상대경로 원리를 이용해 userid를 조작해준다. 아, 여기서 userid를 조작하는게 무슨 상관이냐,, 한다면,
@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)
여기 라우팅 requests.get(...) 부분 보면 userid값이 도메인에 박제된(?) 채로 날라가는 것을 볼 수 있다. 직접 도메인을 조작하는 것도, 입력창을 통해 조작하는 건 안 되지만 버프 스위트 안에 chromium 인터셉트로 경로 조작을 할 수 있다~고 이해하면 좋다.
블록 처리한 곳이 DH{...} flag 값이고 이게 곧 정답이다~
'Hacking > WebHacking Wargame Writeup' 카테고리의 다른 글
[Writeup] / [DreamHack] Movie time table (0) | 2025.04.11 |
---|---|
[WriteUp] / [Dreamhack] session (0) | 2025.03.31 |
[WriteUp]/[Lord Of SQLInjection] dragon (0) | 2025.03.07 |
[WriteUp]/[Lord of SQLInjection] xavis (0) | 2025.03.07 |
[Writeup]/[Lord of SQLInjection] nightmare (0) | 2025.03.05 |