이 문제 풀었다. 나름 간단한 문제였다!
문제 페이지이다. app.py 코드 확인해보면
#!/usr/bin/env python3
import subprocess
from flask import Flask, request, render_template, redirect, url_for
import string
import os
import hashlib
app = Flask(__name__)
try:
FLAG = open("./flag.txt", "r").read()
except:
FLAG = "[**FLAG**]"
KEY = hashlib.md5(FLAG.encode()).hexdigest()
guest_key = hashlib.md5(b"guest").hexdigest()
# filtering
def filter_cmd(cmd):
alphabet = list(string.ascii_lowercase)
alphabet.extend([' '])
num = '0123456789'
alphabet.extend(num)
command_list = ['flag','cat','chmod','head','tail','less','awk','more','grep']
for c in command_list:
if c in cmd:
return True
for c in cmd:
if c not in alphabet:
return True
@app.route('/', methods=['GET', 'POST'])
def index():
# GET request
return render_template('index.html')
@app.route('/flag', methods=['POST'])
def flag():
# POST request
if request.method == 'POST':
key = request.form.get('key', '')
cmd = request.form.get('cmd_input', '')
if cmd == '' and key == KEY:
return render_template('flag.html', txt=FLAG)
elif cmd == '' and key == guest_key:
return render_template('guest.html', txt=f"guest key: {guest_key}")
if cmd != '' or key == KEY:
if not filter_cmd(cmd):
try:
output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)
return render_template('flag.html', txt=output.decode('utf-8'))
except subprocess.TimeoutExpired:
return render_template('flag.html', txt=f'Timeout! Your key: {KEY}')
except subprocess.CalledProcessError:
return render_template('flag.html', txt="Error!")
return render_template('flag.html')
else:
return redirect('/')
else:
return render_template('flag.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
필터링하는 함수를 좀 더 자세히 살펴보자.
# filtering
def filter_cmd(cmd):
alphabet = list(string.ascii_lowercase)
alphabet.extend([' '])
num = '0123456789'
alphabet.extend(num)
command_list = ['flag','cat','chmod','head','tail','less','awk','more','grep']
for c in command_list:
if c in cmd:
return True
for c in cmd:
if c not in alphabet:
return True
흠. alphabet에는 소문자, 공백, 숫자만 포함된다. 'flag','cat','chmod','head','tail','less','awk','more','grep'을 필터링 하는데, cmd에 필터링하는 단어들이 있으면 True, 그리고 cmd에 소문자, 공백, 숫자가 아닌 문자가 들어가면 True를 반환한다.
@app.route('/flag', methods=['POST'])
def flag():
# POST request
if request.method == 'POST':
key = request.form.get('key', '')
cmd = request.form.get('cmd_input', '')
if cmd == '' and key == KEY:
return render_template('flag.html', txt=FLAG)
elif cmd == '' and key == guest_key:
return render_template('guest.html', txt=f"guest key: {guest_key}")
if cmd != '' or key == KEY:
if not filter_cmd(cmd):
try:
output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)
return render_template('flag.html', txt=output.decode('utf-8'))
except subprocess.TimeoutExpired:
return render_template('flag.html', txt=f'Timeout! Your key: {KEY}')
except subprocess.CalledProcessError:
return render_template('flag.html', txt="Error!")
return render_template('flag.html')
else:
return redirect('/')
else:
return render_template('flag.html')
이제 이 부분을 살펴보자. 어떻게 해야 flag를 얻을 수 있을지 고민해야한다. 우선 guest_key를 입력해보자.
guest_key = hashlib.md5(b"guest").hexdigest()
...음. 별 소득은 없었다.
다시 코드를 살펴보면
if cmd == '' and key == KEY:
return render_template('flag.html', txt=FLAG)
key가 KEY, 즉 hashlib.md5(FLAG.encode()).hexdigest()과 일치하면 FLAG를 반환하고
if cmd != '' or key == KEY:
if not filter_cmd(cmd):
try:
output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)
return render_template('flag.html', txt=output.decode('utf-8'))
except subprocess.TimeoutExpired:
return render_template('flag.html', txt=f'Timeout! Your key: {KEY}')
except subprocess.CalledProcessError:
return render_template('flag.html', txt="Error!")
return render_template('flag.html')
flag를 우회해서 획득하거나, Timeout 에러를 발생시켜 KEY를 얻는다.
일단 버프스위트로 이것저것 시도해본다.
기본 페이지에서 cmd_input을 파라미터로 전송했더니 /flag로 접속하여 hello admin이라는 문구와 함께 cmd 명령어를 입력하는 페이지에 접속되었다. ls 명령어를 전송하니
app.py
flag.txt
requirements.txt
static
templates
이 다섯가지 파일이 있다고 나온다.
cat flag.txt를 사용하여 바로 FLAG를 얻으면 좋겠지만... cat과 flag는 필터링되어있고, .은 alphabet에 포함되지 않는다. 특수문자를 사용할 수 없으니 우회도 쉽지 않을 것 같다.
alphabet에 숫자가 포함이니, 아마 Timeout 에러를 발생시키는 것이 맞을 것 같다.
5초 내에 수행되지 않을 시 Timeout이 발생한다. 그래서 인터셉트 끄고 sleep 10을 입력해보겠다.
10초 기다리니 KEY, 즉 flag를 md5로 해싱한 값을 얻었다. 해시는 일방향이니까... 이 값으로 flag를 다시 찾는 것은 거의 불가능하고,
if cmd == '' and key == KEY:
return render_template('flag.html', txt=FLAG)
이 코드를 사용해야겠다. 다시 기본 페이지로 돌아가 얻은 KEY를 입력해주자.
오. flag를 얻었다.
Admin의 KEY는 409ac0d96943d3da52f176ae9ff2b974이고,
flag는 DH{9c73a5122e06f1f7a245dbe628ba96ba68a8317de36dba45f1373a3b9a631b92}이다.