๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
[Dreamhack]WebHacking/Wargame&CTF

[Dreamhack] CTF Season 5 Round #4 - BypassIF

by Yun2๐Ÿ‘ 2024. 2. 25.
๋ฐ˜์‘ํ˜•

๐Ÿ›Ž๏ธ Access

Admin์˜ KEY๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค! ์•Œ๋งž์€ KEY๊ฐ’์„ ์ž…๋ ฅํ•˜์—ฌ ํ”Œ๋ž˜๊ทธ๋ฅผ ํš๋“ํ•˜์„ธ์š”.
ํ”Œ๋ž˜๊ทธ ํ˜•์‹์€ DH{...} ์ž…๋‹ˆ๋‹ค.

 

 

๐Ÿ‘พ Exploit Algorithm & Payload

> ./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)

> ./templates/base.html

๋”๋ณด๊ธฐ
<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-theme.min.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/non-responsive.css') }}">
    <title>{% block title %}{% endblock %} | Dreamhack </title>
    {% block head %}{% endblock %}
  </head>
<body>
      <!-- Fixed navbar -->
      <nav class="navbar navbar-default navbar-fixed-top">
        <div class="container">
          <div class="navbar-header">
            <a class="navbar-brand" href="/">BypassIF</a>
          </div>
          <div id="navbar">
            <ul class="nav navbar-nav">
              <li><a href="/">index page</a></li>
            </ul>
          </div><!--/.nav-collapse -->
        </div>
      </nav><br/><br/><br/>
    <div class="container">
      {% block content %}{% endblock %}
    </div> <!-- /container -->

    <!-- Bootstrap core JavaScript -->
    <script src="{{ url_for('static', filename='js/jquery.min.js')}}"></script>
    <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script> 
</body>
</html>

> ./templates/index.html

๋”๋ณด๊ธฐ
{% extends "base.html" %}
{% block title %}Index{% endblock %}

{% block head %}
  {{ super() }}
{% endblock %}

{% block content %}
<h1>hello dream</h1>
<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>
<br/><br/>
{% if txt %}
  <pre>{{ txt }}</pre>
{% endif %}
{% endblock %}

> ./templates/flag.html

๋”๋ณด๊ธฐ
{% extends "base.html" %}
{% block title %}Flag{% endblock %}

{% block head %}
  {{ super() }}
{% endblock %}

{% block content %}
<h1>hello admin</h1>
<form action="/flag" method="POST">
  <div class="row">
    <div class="col-md-6 form-group">
      <br/><input type="text" class="form-control" placeholder="ls" name="cmd_input" required>
    </div>
  </div>
  <button type="submit" class="btn btn-default">Submit</button>
</form>
<br/><br/>
{% if txt %}
  <pre>{{ txt }}</pre>
{% endif %}
{% endblock %}

> ./templates/guest.html

๋”๋ณด๊ธฐ
{% extends "base.html" %}
{% block title %}Index{% endblock %}

{% block head %}
  {{ super() }}
{% endblock %}

{% block content %}
<h1>hello guest</h1>
<br/><br/>
{% if txt %}
  <pre>{{ txt }}</pre>
{% endif %}
{% endblock %}

...

 

 

#1


: '/' ํŽ˜์ด์ง€์—์„œ key๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์ „์†กํ•˜๋ฉด ๋ฐ˜์‘ํ•  ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.

 

 

#2


https://tophix.com/ko/development-tools/encrypt-text

 

์˜จ๋ผ์ธ ํ…์ŠคํŠธ ์•”ํ˜ธํ™” ๋„๊ตฌ | MD5, SHA, AES ์•”ํ˜ธํ™”, AES ๋ณตํ˜ธํ™”

MD5, SHA, AES ๋ณตํ˜ธํ™” ๋ฐ AES ์•”ํ˜ธํ™” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์˜จ๋ผ์ธ ํ…์ŠคํŠธ ์•”ํ˜ธํ™” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

tophix.com

: app.py์—์„œ guest_key๊ฐ€ 'hashlib.md5(b"guest").hexdigest()'๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋™์ž‘ํ•จ์„ ํ™•์ธํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— md5 ๋ณตํ˜ธํ™” ํ•ด์„œ guest์˜ key ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

: ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€๋กœ ํ•ด์„ํ•˜๋‹ค๋ณด๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋‘๊ฐœ์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. (key, cmd_input)

 

 

#3


: /index ํŽ˜์ด์ง€์—์„œ /flag ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐˆ ๋•Œ, ์ „๋‹ฌํ•˜๋Š” ๊ฐ’์ด key ๋ฟ์ด์—ˆ์ง€ BurpSuite ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํŒจํ‚ท์„ ๊ฐ€๋กœ์ฑ„์„œ cmd_input๋„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ž‘์„ฑํ•˜์˜€๋‹ค.

: ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ช…๋ น์–ด๊ฐ€ ์ž˜ ๋™์ž‘ํ•จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

: ๊ทธ๋Ÿฌ๋‚˜ ํŒŒ์ผ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์šฐํšŒ ๋ฐฉ๋ฒ•์€ ์ฐพ๊ธฐ ์‰ฝ์ง€ ์•Š์•˜๋‹ค.(์žˆ์„์ง€ ์—†์„์ง€๋Š” ํ™•์‹คํ•˜์ง€ ์•Š๋‹ค.)

- ํ•„ํ„ฐ๋ง: 'flag','cat','chmod','head','tail','less','awk','more','grep' 

- ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ŒํŒŒ๋ฒณ ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑ ๋˜๋Š” ์ˆซ์ž๋งŒ ๊ฐ€๋Šฅ. ์ฆ‰, ๋ฌธ์ž๋„ ์‚ฌ์šฉ ๋ถˆ๊ฐ€.

- ๋‹ค๋ฅธ ์šฐํšŒ ๋ฐฉ๋ฒ•์„ ํ™œ์šฉํ•ด๋ดค์œผ๋‚˜ ๋จนํžˆ์ง€ ์•Š์•˜์Œ.

ex) fold, tac, nl, rev, sort, diff, /b?n/c?t, ...

 

 

#4


...
        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')
...

: ์ฝ”๋“œ๋ฅผ ์ž์„ธํžˆ ํ™•์ธํ•˜๋‹ˆ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

: cmd๊ฐ€ ํ•„ํ„ฐ๋ง ํ•จ์ˆ˜์— ํ•ด๋‹นํ•˜๋Š” ๊ฒƒ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ฒŒ ๋œ๋‹ค. ํ†ต๊ณผ๋œ๋‹ค๋ฉด try๋ฌธ์œผ๋กœ ์ด๋™ํ•œ๋‹ค.

: try๋ฌธ์—์„œ cmd์—์„œ ์ž‘์„ฑํ–ˆ๋˜ ์‰˜ ๋ช…๋ น์–ด๊ฐ€ ์‹คํ–‰๋˜๊ณ  ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋œ๋‹ค. ์ด๋•Œ, ๋ช…๋ น์–ด ์‹คํ–‰์— 5์ดˆ ์ด์ƒ ์ง€์—ฐ์ด ๋ฐœ์ƒํ•˜๋ฉด ์—๋Ÿฌ๋ฌธ์„ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

: except subprocess.TimeoutExpired๋Š” timeout ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด flag.html ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  KEY(Admin์˜ KEY)๋ฅผ ์ „๋‹ฌํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

: ๋”ฐ๋ผ์„œ ์ง€์—ฐ์‹œํ‚ค๋ฉด Admin์˜ KEY๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

 

 

๐Ÿ”‘Analysis and results for obtaining the Flag DH{…}


๋ฐ˜์‘ํ˜•

'[Dreamhack]WebHacking > Wargame&CTF' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Dreamhack] Level1: [wargame.kr] strcmp  (1) 2024.02.25
[Dreamhack] Level2: login-1  (2) 2024.02.25
[Dreamhack] Level2: baby-sqlite  (0) 2024.02.23
[Dreamhack] Level4: KeyCat  (0) 2024.02.23
[Dreamhack] Level1:Beginner blue-whale  (1) 2024.02.07