[CTF] Dreamhack 문제 풀이: BypassIF

[Dreamhack] BypassIF

1. Intro

Dreamhack 링크: https://dreamhack.io/wargame/challenges/1151

2. Code

2.1. /index

코드 접기/펼치기
<form action="/flag" method="POST">
  <div class="row">
    <div class="col-md-6 form-group">
      <br/><input type="text" class="form-control" placeholder="your key" name="key" pattern="[A-Za-z0-9\s]{2,35}" required>
    </div>
  </div>
  <button type="submit" class="btn btn-default">Submit</button>
</form>
  • /flag 페이지로 key 값을 POST 형식으로 요청한다.

2.2. /flag

코드 접기/펼치기
@app.route('/flag', methods=['POST'])
def flag():
     # POST request
    if request.method == 'POST':
	    # 파라미터 key, cmd_input를 받음
        key = request.form.get('key', '')
        cmd = request.form.get('cmd_input', '')
        
        # cmd 값이 없고 key가 admin이면 flag 보여줌
        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}")
        
        # cmd 값이 있거나 key가 admin이면
        if cmd != '' or key == KEY:
	        # filter에 안 걸렸으면
            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'))
                # timeout 에러 발생 시 admin 키를 보여줌
                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')
  1. key, cmd_input을 받아온다.
  2. cmd 입력이 없을 때 key를 비교해 admin, guest를 구분한다. admin이면 flag를 보여준다.
  3. cmd 입력이 있거나 key가 admin일 때 filter_cmd(cmd)에 걸리지 않았다면
    1. 쉘 스크립트를 실행한다.
    2. timeout(5초)일 경우 admin 키를 보여준다.
    3. 쉘 스크립트 에러 발생했을 경우 에러 메시지를 보여준다.

2.3. filter_cmd(cmd)

코드 접기/펼치기
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
  1. cmd에 flag, cat, chmod, head, tail, less, awk, more, grep 가 있을 경우 → True
  2. 알파벳 소문자, 공백, 숫자가 아닐 경우 → True

3. Vuln

  • 불충분한 인증
  • 조건문 로직 결함 - 타임아웃을 이용한 키 유출

4. Payload

  1. guest의 md5 해시값을 넣어본다. Captured Image Captured Image
  2. request 요청에 cmd_input을 추가한다.
    • 기본 폼에는 key만 있지만, 백엔드 코드에서 request.form.get('cmd_input', '')로 인자를 받기 때문에 POST 요청할 때 cmd_input 파라미터를 임의로 추가해준다. Captured Image Captured Image
  3. ls를 입력한다. Captured Image Captured Image
  4. sleep 6을 입력한다.
    • subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)로 인해 명령어가 5초 이상 실행되면 TimeoutExpired 예외가 발생한다.
    • 예외 처리 구문에서 f'Timeout! Your key: {KEY}'를 반환한다. 즉, sleep 6를 통해 타임아웃을 유발해 admin key를 획득하는 것이다. Captured Image Captured Image
  5. admin의 키를 입력한다. Captured Image
  6. flag를 획득했다. Captured Image

Comments

Newest Posts