SONOTRI
  • [Dreamhack] Dream Beginner_Baby-linux🚩
    2024년 07월 08일 16시 00분 27초에 업로드 된 글입니다.
    작성자: sonootri

    1)

    먼저 문제 설명을 읽어보자.

     

    ->문제 설명을 읽어보니, 목표는 flag.txt 즉 flag 텍스트 파일을 찾아 출력하는것 같다.

     

     

     

     

    2)

      문제 파일을 다운로드 받고, 압축을 풀면 app이라는 PY파일 하나가 있다. 저번에 이 문제를 풀이할 때는, 접속 정보에 있는 웹 페이지에 들어가 명령어를 치고, 실행 안 되고....이런 과정을 반복했는데 별 소득이 없었다. 그래서 이번에는 문제 파일 먼저 살펴보고 가려고 한다.

    <<app 파일 내용>>
    #!/usr/bin/env python3
    import subprocess
    from flask import Flask, request, render_template
    
    APP = Flask(__name__)
    
    @APP.route('/', methods=['GET', 'POST'])
    def index():
        if request.method == 'POST':
            user_input = request.form.get('user_input')
            cmd = f'echo $({user_input})'
            if 'flag' in cmd:
                return render_template('index.html', result='No!')
    
            try:
                output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)
                return render_template('index.html', result=output.decode('utf-8'))
            except subprocess.TimeoutExpired:
                return render_template('index.html', result='Timeout')
            except subprocess.CalledProcessError:
                return render_template('index.html', result='Error')
    
        return render_template('index.html')
    
    if __name__ == '__main__':
        APP.run(host='0.0.0.0', port=8000)

      

      #!가 보인다. 이 형식은 칼리 리눅스에서 vi편집기를 사용했을 때 봤었다. 옆에 보면 python이라고도 적혀있으니 파이썬+리눅스를 염두에 두고 코드를 살펴보자.

     

     

     

     

    3)

      일단 저 코드를 Python으로 실행시켜봤다. 근데 오류가 뜬다. 파이썬 코드 형식이 아닌가보다. 

     

      근데 import, flask..이런건 어디서 쓰는걸까. 먼저 import subprocess에 대해 검색해봤다. 근데 뭔가 나온다. 'subprocess' 모듈을 사용하면, 파이썬 코드에서 셸 명령을 실행할 수 있고 실행 결과의 출력을 가져올 수도 있습니다.'라는 정보를 얻었다.

      

      이 과정에서 Flask라는 것에 대해 알게 되었다. Flask는 Python의 마이클 웹 프레임워크이다. 

     

    .......결국은 드림핵 공식 라이트업을 구매했다. 라이트업을 따라가며 이런 종류의 워게임을 어떻게 풀어야하는지 배우자!!

     

     

    ---------------------------------------------------------------------------------------------------------------------------------------------------------

    <배경>

    본 문제는 Flask 프레임워크로 작성된 웹 페이지이다. -> 여기까지는 접근 했다

     

    문제를 해결하기 위해서는 기초적인 리눅스 명령을 알고, Python으로 작성된 웹앱 코드를 이해할 수 있어야한다. 본 문제를 통해 커맨드 인젝션(Command Injection)취약점을 이해하기 위한 기초 지식을 쌓을 수 있다.

     

     

     

    <분석>

    1)

    웹 페이지에 접속하면(by 접속 정보), 사용자의 입력을 받는 입력 칸, 제출 버튼, 그리고 결과를 출력하는 Result 부분이 있다. ->나는 이 페이지를 구성하는 html문서를 볼 생각을 했는데, 풀이는 이 과정이 없다. 즉 필요 없는 행동이라는 뜻이다

     

     

    2) 

    문제를 풀기 위해서는 app.py의 소스코드를 확인하여 앱이 어떻게 동작하는지 파악해야한다. index 페이지의 코드는 다음과 같다 -> app.py파일의 존재 이유를 알았다. 그리고 왜 index 페이지 코드만 보는걸까? 다른 부분은 불필요한 부분인가?

    @APP.route('/', methods=['GET', 'POST']) #Flask에서 사용하는 데코레이터 형식
    def index():
    	#POST. 해당 메소드를 사용한다고 명시하고있다.
        if request.method == 'POST':
            user_input = request.form.get('user_input')
            cmd = f'echo $({user_input})'
            if 'flag' in cmd:
                return render_template('index.html', result='No!')
    
            try:
                output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)
                return render_template('index.html', result=output.decode('utf-8'))
            except subprocess.TimeoutExpired:
                return render_template('index.html', result='Timeout')
            except subprocess.CalledProcessError:
                return render_template('index.html', result='Error')
    
    	#GET. POST처럼 명시되지는 않지만, 조건문 밖에 있는 것으로 구별할 수 있다.
        return render_template('index.html')

     

    -def를 사용하여 index함수를 정의하고있다.

    -if문을 사용해, POST 메소드로 요청할 때, 따라야하는 방식을 지정하고있다.

     

    -user_input = request.form.get('user_input')

    : HTML폼에서 user_input이라는 이름의 입력값을 가져온다.

     

    -user_input(사용자 입력값)을 포함한 echo $({user_input}) 명령어를 cmd 변수에 저장한다.

    -if문을 사용하여, 사용자 입력값에 flag문자열이 있으면 NO!를 출력한다.

     

    -output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)
                return render_template('index.html', result=output.decode('utf-8'))

    : 조건에 걸리지 않으면 subprocess.check_output()함수를 호출하여 cmd 명령 실행 결과를 output 변수에 저장하고, return에서 알 수 있듯, 이를 출력한다.

    +subprocess는 파이썬에서 다른 프로세스(서브 프로세스)를 생성할 수 있게 해주는 라이브러리이다. subprocess를 사용하면 파이썬에서 시스템 명령을 실행할 수 있다.

    +subprocess.check_output() 함수는 인자로 받은 시스템 명령어를 실행하여(위의 설명 따라가기), 출력되는 문자열을 반환한다.

     

    -except subprocess.TimeoutExpired:
                return render_template('index.html', result='Timeout')
            except subprocess.CalledProcessError:
                return render_template('index.html', result='Error')

    : 여기는 정상적인 작동 환경이 안될 때, 각각 Timeout과 Error 문자열을 반환한다는 부분이다.

     

    -return render_template('index.html')

    : render_template는 Flask 웹 프레임워크에서 템플릿을 렌더링하여 HTML을 반환하는 함수입니다. 이는 주로 HTML 파일을 동적으로 생성하여 웹 브라우저에 전송할 때 사용된다. -> GET 메소드로 요청하면, index.html을 출력한다.

     

     

     

     

     

    3)

    여기 코드를 자세히 보자. 

    user_input = request.form.get('user_input')
            cmd = f'echo $({user_input})'

     

    -echo는 알다시피, 인자로 입력한 텍스트를 출력하는 리눅스 명령어이다. echo 'dreamhack'의 실행 결과는 dreamhack이다.

    -셸에서 $()는 명령어는 치환하는 메타 문자로, 괄호 안의 명령어를 실행한 결과로 치환된다. 예를 들어, echo $(echo dreamhack)의 실행 결과는 echo dreamhack이다.

    -cmd괄호 안의 명령을 실행한 결과를 출력하는 명령어이다. 괄호 안에 들어가는 값은 user_input에 저장된 사용자의 입력 값이다. -> 이를 통해, 사용자가 입력한 명령어를 실행하고 결과를 출력하는 프로그램이라는 것을 알 수 있습니다. 이 시스템을 이해하고 워게임을 풀면 된다.

     

     

    근데 궁금한게 있다. 왜 cmd = f 'echo $({user_input})'에서 f는 왜 붙는걸까?

     

     

     

     

     

    <풀이>

    1)

    문제 설명을 보면, 플래그는 flag.txt파일에 있다. 현재 flag파일의 위치를 모르므로, 먼저 #ls 명령을 입력하여 파일 및 디렉토리 목록을 출력한다.

     

     

    2)

    dream hint.txt라는 파일이 보인다. 해당 파일을 #cat dream hint.txt명령어를 통해 읽어보자

     

     

     

    3)

    flag.txt가 있는 위치를 알려주는것 같은 문장이 보인다. 형식을 보니 디렉토리를 알려주는 것 같다. #cat ./dream/hack/hello/flag.txt 명령어를 사용해서 flag.txt의 내용을 확인해보자.

     

     

     

    4)

    엇 근데 NO!가 보인다. 아까 살펴본 코드가 제대로 작동한다!

     

    flag.txt를 출력해야 하는데, flag문자열을 필터링하므로 0개 이상의 임의의 문자를 의미하는 특수문자 #*를 사용하여 필터링을 우회해야겠다. # cat ./dream/hack/hello/fl*를 입력해서 이제 플래그를 얻자! 🚩

    댓글