[Dreamhack] curling
2025. 2. 23.

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

 

curling

Description simple api server

dreamhack.io

이번 드림핵 ctf에 출제되었던 웹 해킹 문제

타깃 사이트로 이동해도 api server만 구현되어 있기 때문에 404 not found가 뜬다. 바로 코드를 살펴보자.

code

더보기
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:   # 호스트가 allowed_hosts인 경우
        if url.startswith('http://' + host):
            break # for 문 탈출

        return {'result': False, 'msg': 'Not Allowed host'}
    
    if url.endswith('/test/internal'): # /api/v1/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"]) # get 메소드
def test(): # ip는 원격 주소 정보
    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 요청을 검증하는 2개의 로직이 있다.

1. ALLOWED_HOST 목록 확인

ALLOWED_HOSTS  = ['dreamhack.io', 'tools.dreamhack.io'] 에 있는 도메인만 허용한다는 의미

 

2. url이 /test/internal로 끝날 경우 필터링

post 요청으로 들어오는 변수값 url이 /test/internal로 끝날 경우 필터링

 

만약 위의 검증 로직을 우회해서 /api/v1/test/internal 엔드포인트로 get 메소드로 요청을 보낼 수 있다면, 그리고 해당 요청을 보낼 때 로컬 주소로 요청을 보낼 수 있다면 flag를 알아낼 수 있을 것으로 보인다.

 

Exploit

※ user info(사용자 정보) url에 포함시키기

만약 @ 문자를 넣고 그 뒤에 다른 호스트를 넣으면 실제 요청은 @ 뒤의 호스트가 받게된다.
문자열의 시작 : 허용 도메인 / 실제 요청 : 다른 호스트
http://dreamhack.io@127.0.0.1:8000
이 경우, 실제 요청은 127.0.0.1:8000 에서 받게된다. 

 

url에 더미값을 담은 쿼리변수 넣기

url이 정확히 /test/internal로 끝나면 필터링이 되는 로직

-> 이경우, url 끝부분에 쿼리변수를 넣고, 해당 변수에 더미값을 넣으면 /test/internal로 끝나지 않게 되면서 우회할 수 있다

ex> ?foo=bar 를 넣으면 사이트 요청에는 영향이 가지 않으면서 동시에 /test/internal로 끝나지 않게 되면서 우회할 수 있게 된다.

payload

curl -X POST -d "url=http://dreamhack.io@127.0.0.1:8000/api/v1/test/internal?foo=bar" http://host1.dreamhack.games:12024/api/v1/test/curl

post 요청을 보낼때 url 변수에 우선 ALLOWED_HOSTS를 우회하기 위한 http://dreamhack.io를를 넣고, 그 후 @를 넣어 실제 요청은 127.0.0.1:8000/api/v1/test/internal?foo=bar 가 받게 되고, /api/v1/test/internal은 foo를 쿼리 변수로 받지 않기 때문에 ?foo=bar는 아무 효력이 없어지게 되면서 실질적으로는 127.0.0.1:8000/api/v1/test/internal 에 대해 요청이 가게 된다.

위의 payload를 실행하면

플래그 출력

'보안 > Web hacking' 카테고리의 다른 글

[Dreamhack] Simple Note Manager  (0) 2025.02.11
[Dreamhack] Base64 based  (0) 2025.02.10
[Dreamhack] Pearfect Markdown  (0) 2025.02.05
[Dreamhack] baby-jwt  (0) 2025.01.31
[Dreamhack] 삐리릭... 삐리리릭...  (0) 2025.01.30