오늘 풀 문제는 curling이다.
from flask import Flask, request
from os import urandom
from subprocess import run, TimeoutExpired
app = Flask(__name__)
app.secret_key = urandom(32)
try:
FLAG = open("/flag", "r").read().strip()
except:
FLAG = "[**FLAG**]"
ALLOWED_HOSTS = ['dreamhack.io', 'tools.dreamhack.io']
@app.route("/api/v1/test/curl", methods=["POST"])
def admin():
url = request.form["url"].strip()
for host in ALLOWED_HOSTS:
if url.startswith('http://' + host):
break
return {'result': False, 'msg': 'Not Allowed host'}
if url.endswith('/test/internal'):
return {'result': False, 'msg': 'Not Allowed endpoint'}
try:
response = run(
["curl", f"{url}"], capture_output=True, text=True, timeout=1
)
return {'result': True, 'msg': response.stdout}
except TimeoutExpired:
return {'result': False, 'msg': 'Timeout'}
@app.route('/api/v1/test/internal', methods=["GET"])
def test():
ip = request.remote_addr
if not ip == '127.0.0.1':
return {'result': False, 'msg': 'Only local access is allowed'}
return {'result': True, 'msg': FLAG}
if __name__ == '__main__':
app.run(host='0.0.0.0', port='8000')
코드는 위와 같이 주어진다.
먼저 /api/v1/test/curl은 POST요청만을 받고,
url 파라미터의 시작이 http://dreamhack.io 또는 http://tools.dreamhack.io 이어야 한다.
또한 url 파라미터의 끝이 /test/internal이면 안된다.
이 2개를 우회하게 되면 curl로 url에 요청을 보내게 된다.
이 요청은 아마도 /test/internal로 보내져서 로컬 서버에 접근하게 되어야 한다.
우리가 로컬 권한으로 접근을 하여야 FLAG를 얻을 수 있기 때문이다.
2개의 조건을 우회해서 보내면 될 것이다.
어떻게 우회할 수 있을까? URL 구조를 잘 알고 있다면 간단하게 해결된다.
먼저 첫번째 조건
http://dreamhack.io까지 맞춘다. 이 때 이부분을 @을 이용하여 실제 URl은 뒤가 되게 할 수 있다.
옛날 URL 구조는 앞에는 유저네임이 뒤에는 실제 주소가 들어갔다.
현재 SSH 요청과 같은 구조이다.
즉 http://dreamhack.io@127.0.0.1:8000 이 되면 요청하는 곳은 @ 뒤가 된다.
http://dreamhack.io@127.0.0.1:8000/api/v1/test/internal
의 뒷부분은 어떻게 우회를 할까?
바로 의미없는 파라미터값을 붙히는 것이다.
http://dreamhack.io@127.0.0.1:8000/api/v1/test/internal?hi=hello
로 POST 요청을 보내보도록 하겠다.

위와 같이 FLAG를 반환하는 모습을 볼 수 있다.
FLAG : DH{S5Rf_1s_funNy:hxfj2m4mRBxYdSCknWkCsw==}