본문 바로가기

SWLUG/CTF

[CTF/ Dreamhack] pathtraversal

 

pathtraversal

사용자의 정보를 조회하는 API 서버입니다. Path Traversal 취약점을 이용해 /api/flag에 있는 플래그를 획득하세요! Reference Server-side Basic

dreamhack.io

 

 

 

문제 확인

 

 

 

 

 

path traversal 공격

: 파일의 경로를 조작하여, 응용 프로그램의 서버 내의 임의의 파일을 읽고, 쓸 수 있는 arbitary file read, arbitary file write가 발생할 수 있다. 

(경로 문자열에 대한 검사가 미흡하여 허용되지 않는 경로에 접근할 수 있는 취약점)

 

 

 

path traversal 우회 방법 

1. 절대 경로 사용
filename=/etc/passwd와 같은 절대 경로 사용하여 직접 파일에 접근


2. 중첩 traversal sequences 사용
내부 구문이 제거되면 기본 traversal sequence로 변환됨


3. url encoding 또는 double url encoding

정규화를 우회하기 위해 .../를 url encoding 또는 double url encoding 할 수 있음. 각각 그 값은 %2e%2e%2f와 %252ㄷ%252e%252e%252f임

4. 기본 폴더 포함

요구하는 기본 폴더를 포함하여 뒤에 적절한 traversal sequences를 사용

5. 특정한 파일 확장자를 요구

null byte를 포함하여 요구하는 확장자가 나오기 전에 경로를 효과적으로 종료시킴

 

 

 

 

 

문제 풀이

 

서버를 생성한다.

 

 

 

view를 누른 화면

userid에 guest를 입력해도 아무런 변화가 생기지 않는다.

 

 

 

다운로드한 파일의 코드를 확인해주었다.

#!/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_info 함수에서 POST 요청으로 userid 값을 받아오면 페이지는 /api/user/{userid} 값을 info 변수에 저장한다.

get_flag 함수에서는 /api/user/<uid> 페이지를 요청하면 info 변수에 저장된 uid가 실제 존재하는 user인지 확인해서 json으로 값을 반환하는데

flag 함수에서는 /api/flag 페이지를 요청하면 flag를 반환한다.

 

일단 대충 슥 보니, /api/flag 위치에 가면 FLAG를 리턴해주는 것 같다.
조작가능한 변수는 userid 변수이고 이 변수를 통해  /api/flag를 요청할 것이다.

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

 

기존: /api/user/{userid}

공격: userid=../flag

합체: /api/user/../flag => /api/flag

 

 

그래서 아래와 같이 입력해보았다.

 

 

 

 

 

 

특별히 얻어가는 게 없다. 그래서 개발자도구에서 콘솔 창을 이용하여 guest 값을 통해 ../flag로 강제로 접근한다.

 

그럼 이제 guest를 입력하면 아래와 같이 flag 값을 확인할 수 있다.

 

 

'SWLUG > CTF' 카테고리의 다른 글

[CTF/ RootMe] Directory traversal  (1) 2024.11.04
[CTF/ Dreamhack] file-download-1  (3) 2024.11.04
[CTF/WebGoat] SQL Injection (advanced)  (0) 2024.10.30
[CTF/ Dreamhack] sql injection bypass WAF  (0) 2024.10.30
[CTF/ Dreamhack] weblog-1  (0) 2024.10.30