Web Hacking 과제_4주차( 수업 내용 정리 및 path Traversal 개념 및 문제풀이)

2024. 11. 6. 13:28hacking : p_study/webhack

절대 경로와 상대경로

 

절대경로

루트 디렉토리(/)로 부터 접속하고자 하는 희망 파일에 이를 때까지 거쳐야하는 디렉토리 명을 모두 연결하여 구성한다.

각 디렉토리는 /로 구분된다.

현재 사용자가 어떤 디렉토리에 있다해도, 절대경로를 사용하면 대상 파일을 가리킬 수 있다.

 

 

상대경로

 

 

현재 디렉토리를 기준으로 다른 파일에 이르는 경로를 상대적으로 표현한 것

리눅스에서 ..는 이전 디렉토리를 .는 현재 디렉토리를 의미하는데, 이를 이용하여 상대 경로를 구성할 수 있음

절대 경로와 달리 어떤 파일을 가리키는 상대 경로의 수는 무한하다.

 

 

Path Traversal 공격

Path Traversal 취약점은 파일의 경로를 조작하여, 응용 프로그램의 서버 내의 임의의 파일을 읽고, 쓸 수 있는 arbitary file read, arbitary file write가 발생할 수 있음

데이터 또는 동작을 수정하고 서버를 완전히 제어할 수 있다.

 

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

 

공격자가 실행 중인 서버의 임의의 파일을 읽을 수 있게 해주며, 파일의 종류는 아래와 같다.

 

  • Application 코드와 데이터
  • back-end 의 증명서
  • 운영체제의 중요 파일들(e.g.*.env)

 

Path Traversal 우회 방법

 

1. 절대경로 사용

2. 중첩 Traversal sequences 사용

3. URL encoding 또는 double URL encoding

4. 기본 폴더 포함

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

 

 

 

Path Traversal 문제풀이

 

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

 

pathtraversal

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

dreamhack.io

 

문제 설명

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

 

 

 

문제설명에 대해 해당 취약점을 이용해 /api/flag를 주의깊게 봐보자.!

 

 

 

 

해당 서버를 생성해서 내용을 살펴보니 이렇게 생겼는데 어떤식으로 동작하는 지 궁금해서 아무 글자를 입력해보았다.

우선 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"]

 

 

역시나 나와있었다.

 

코드를 살펴보며 /api/flag 를 들여다보니, flag함수가 FLAG를 반환한다.

그리고 GET과 POST방식을 사용하고 있는데, 흠 어떤식으로 풀어볼까 찾아보니까, 여러가지 방법으로 푸는데, burp suite로 푸는 경우도 있었다. 과거 풀이를 보니 나도 이런식으로 푼 경험이 이미 있는 것같아서, 다른 방법으로 풀어보기로 했다.

 

 

 

FLAG 는 /api/flag 에 저장이 되므로, 저장된 값은 /api/user/{userid} 에 FLAG 값은 /api/flag 에 저장이 되기 때문에,

../flag 를 입력해보았다.

 

 

여기다 입력했더니 이렇게 뜨는 걸 보니 다른 방법으로 시도해야할 것 같다.

 

burp suite를 통해 userid를 바꾸는 것 대신에 콘솔창을 이용해 풀이를 해보았다.