본문 바로가기

Web Hacking/Dreamhack

[Dreamhack] BypassIF write up

 

드림핵 웹해킹 워게임 level 1 다 풀어보자!

 

첫 문제 BypassIF

 

서버에 접속하자 입력폼이 하나 존재한다. 코드를 보자.

 

#!/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)

 

admin과 guest는 각각 키값을 가진다. admin의 key는 flag이기 때문에 알 수 없고 guest는 guest를 md5 해시화한 값으로 주어진다.

 

/flag 엔드포인트에 POST 요청을 보낼 수 있다.  조건을 보면 약간 특이하게 비교가 진행되고 있다는 느낌을 받았다. 굳이 cmd가 없거나 key값이 일치할 때를 비교하고 있다. 아무튼 cmd는 쉘 명령어를 실행할 수 있고 이는 필터링을 진행한다. 필터 부분을 보자.

 

 

command_list라고 해서 flag.txt를 읽을만한 명령어들을 필터링하고 있다. filtered_cmd 부분에서 if문들은 참일 경우 모두 True를 리턴한다. flag함수를 보면 filter_cmd가 참일 때 if문이 실행되지 않으므로 두 if을 모두 우회해야한다. 

 

 

if문을 우회하는 조건은 다음과 같다. command_list에 있는 키워드들이 존재하지 않아야하며,  영문자(소문자), 0 ~ 9까지의 숫자, 공백이 존재해야한다.

 

굳이 이렇게 꼬아놓은 이유는 모르겠지만 결론은 if문을 우회하면 된다. 

 

 

ls명령어를 전달해보니 flag.txt가 있는 것이 확인이 됐다. 근데 문제는 읽을 수가 없다..

 

 

코드를 잘 보니 timeout이 발생하면 flag가 출력된다. 이제 timeout을 일으키는 쪽으로 게싱을 해보자.

 

sleep 명령어로 의도적으로 timeout을 일으키니 key값이 출력됐다.

 

 

gg