서버 생성 / 문제 파일 다운
페이지를 살펴보면 404Error, robots.txt 항목은 "Page Not Found"와 각 항목의 이름을 반환하는 걸 알 수 있습니다
#!/usr/bin/python3
from flask import Flask, request, render_template, render_template_string, make_response, redirect, url_for
import socket
app = Flask(__name__)
try:
FLAG = open('./flag.txt', 'r').read()
except:
FLAG = '[**FLAG**]'
app.secret_key = FLAG
@app.route('/')
def index():
return render_template('index.html')
@app.errorhandler(404)
def Error404(e):
template = '''
<div class="center">
<h1>Page Not Found.</h1>
<h3>%s</h3>
</div>
''' % (request.path)
return render_template_string(template), 404
app.run(host='0.0.0.0', port=8000)
그다음 코드를 살펴보겠습니다 @app.errorhandler(404)는 사용자가 요청한 페이지를 찾을 수 없을 때 아래 함수가 작동됩니다 이 함수는 template 변수에 <h1> 태그와 사용자가 입력한 파라미터 경로를 <h3> 태그 안에 할당합니다 이후 render_template_string으로 인해 정의한 템플릿을 렌더링 하여 사용자에게 반환하고 있습니다
앞서 배운 SSTI 대응 방안에서 render_template 함수를 이용해야 사용자가 입력한 템플릿 구문이 문자열로 출력되어 SSTI 취약점을 방지할 수 있었습니다 하지만 해당 페이지는 render_template_string 함수를 이용하여 사용자가 입력한 템플릿 구문이 정상적으로 작동되기 때문에 SSTI 취약점을 악용할 수 있습니다
이런 식으로 {{3*3}}이라는 템플릿 구문을 넣어 올바른 연산 결과를 반환하는 걸 알 수 있습니다
코드 내부는 앞서 설명한 것처럼 <h3>태그 안에 %s 위치에 사용자가 입력한 {{3*3}} 템플릿 구문이 들어가 연산되어 결과를 반환합니다 마치 C언어에 printf와 유사합니다
이제 flag를 찾아야 하는데 단순 config를 입력하여 SECRET_KEY 위치에 flag 값을 확인할 수 있습니다
소스 코드에서 app.secret_key에 flag 값을 할당하는데 config는 이런 중요한 정보들이 담겨 있습니다 추가로 {{config.[항목]}}으로 특정 항목의 결과만 출력할 수 있습니다
하지만 config 말고 지난 시간에 배운 subprocess의 Popen을 이용해서 os 명령어로 flag 값을 찾아봅시다
''.__class__.__mro__[1].__subclasses__() 클래스를 템플릿 구문에 넣어주면 사진과 같이 많은 클래스들을 확인할 수 있습니다 파이썬은 다중 상속 언어로 동시에 여러 개의 클래스를 상속받을 수 있습니다 이 클래스는 상속받은 클래스의 상위 클래스를 순서대로 반환하는 역할을 하고 원하는 클래스를 따로 실행시킬 수 있습니다
여기서 <class 'subprocess.Popen'> 클래스를 찾아 os 명령어를 실행시켜 봅시다
Ctrl+F 검색 기능을 이용하여 subprocess.Popen 클래스를 찾을 수 있지만 이 클래스가 존재하는 인덱스 번호를 알아야 클래스를 실행시킬 수 있습니다 즉 ''.__class__.__mro__[1].__subclasses__()[특정 클래스 인덱스 번호] 명령어를 이용하여 특정 클래스를 실행시킬 수 있습니다
인덱스 번호는 이 클래스에 맨 앞에 존재하는 <class 'type'> 클래스가 0번째 요소가 됩니다
물론 앞에서부터 하나씩 숫자를 세어 subprocess.Popen 위치를 알아낼 수 있지만 너무 비효율적이라 다른 방법을 이용하여 subprocess.Popen 클래스가 존재하는 인덱스 번호를 찾아봅시다
우선 앞에 클래스들을 모두 복사하여 노트패드로 옮겨줍니다 그다음 Ctrl+h를 눌러 특정 단어 기준으로 \r\n으로 모두 바꾸어 다음 줄로 클래스들을 보기 쉽게 나열합니다 그러면 위 사진과 같이 클래스들이 나열된 모습을 볼 수 있습니다
그다음 subprocess.Popen 클래스를 검색하면 409줄 위치에 해당 클래스를 찾아낼 수 있습니다 하지만 인덱스 번호는 0번부터 시작되기 때문에 409-1=408번이 해당 클래스 위치입니다
결과적으로 ''.__class__.__mro__[1].__subclasses__()[408] 구문이 subprocess.Popen 클래스를 작동시키는 명령어가 됩니다
정상적으로 408번 위치에 해당 클래스가 출력됩니다 이제 OS 명령어와 함께 전달해 봅시다
''.__class__.__mro__[1].__subclasses__()[408]('ls',shell=True,stdout=-1).communicate() 이 명령어는 위 사진과 동일하게 ls 명령어를 실행시켜 현재 디렉터리 위치에 flag.txt 파일이 존재하는 걸 알 수 있습니다
'ls' 자리는 실행할 os 명령어
Shell=true : 쉘을 사용하여 명령어를 실행하는 것을 의미함
Stdout=-1 표준 출력으로 설정합니다 (출력 결과 반환)
.communicate()을 이용하여 최종적으로 결과값을 호출합니다
이후 cat 명령어로 flag.txt 파일을 읽어오면 됩니다 config에서 반환한 flag와 동일합니다
[ 선수 학습 내용 ]