Dreamhack CTF-BypassIF

리안·2024년 2월 24일
0

CTF

목록 보기
5/5

이 문제 풀었다. 나름 간단한 문제였다!

문제 페이지이다. 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}이다.

0개의 댓글