CTF, War game

[Dreamhack] Level 2: crawling

mnzy๐ŸŒฑ 2024. 4. 19. 14:04

1. ๋ฌธ์ œ 

https://dreamhack.io/wargame/challenges/274

 

crawling

๋“œ๋ฆผ์ด๋Š” ์›น ํฌ๋กค๋ง ์‚ฌ์ดํŠธ๋ฅผ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค. ํฌ๋กค๋ง ์‚ฌ์ดํŠธ์—์„œ ์ทจ์•ฝ์ ์„ ์ฐพ๊ณ  flag๋ฅผ ํš๋“ํ•˜์„ธ์š”! ๋ฌธ์ œ ์ˆ˜์ • ๋‚ด์—ญ 2024.02.13 ํ’€์ด์ž์—๊ฒŒ ์ œ๊ณต๋˜๋Š” ํŒŒ์ผ ์ผ๋ถ€๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

dreamhack.io

2. ํ•ด๊ฒฐ ๊ณผ์ •

ํฌ๋กค๋งํ•  URL์„ ์ž…๋ ฅํ•˜๋Š” ํ™”๋ฉด์ด ๋ณด์ธ๋‹ค.

#app.py
from re import split
import socket
import requests
import ipaddress
from urllib.parse import urlparse
from flask import Flask, request, render_template

app = Flask(__name__)
app.flag = '__FLAG__'

def lookup(url):
    try:
        return socket.gethostbyname(url)
    except:
        return False

def check_global(ip):
    try:
        return (ipaddress.ip_address(ip)).is_global
    except:
        return False

def check_get(url):
    ip = lookup(urlparse(url).netloc.split(':')[0])
    if ip == False or ip =='0.0.0.0':
        return "Not a valid URL."
    res=requests.get(url)
    if check_global(ip) == False:
        return "Can you access my admin page~?"
    for i in res.text.split('>'):
        if 'referer' in i:
            ref_host = urlparse(res.headers.get('refer')).netloc.split(':')[0]
            if ref_host == 'localhost':
                return False
            if ref_host == '127.0.0.1':
                return False 
    res=requests.get(url)
    return res.text

@app.route('/admin')
def admin_page():
    if request.remote_addr != '127.0.0.1':
    		return "This is local page!"
    return app.flag

@app.route('/validation')
def validation():
    url = request.args.get('url', '')
    ip = lookup(urlparse(url).netloc.split(':')[0])
    res = check_get(url)
    return render_template('validation.html', url=url, ip=ip, res=res)

@app.route('/')
def index():
    return render_template('index.html')

if __name__=='__main__':
    app.run(host='0.0.0.0', port=3333)

 

admin์— ์ ‘๊ทผํ•˜๋ฉด ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฆฌํ„ดํ•ด์ค€๋‹ค. 

@app.route('/admin')
def admin_page():
    if request.remote_addr != '127.0.0.1':
    		return "This is local page!"
    return app.flag

 

ํฌํŠธ๋Š” ์ฝ”๋“œ์˜ ๊ฐ€์žฅ ๋ฐ‘์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

3333์ด๋‹ค.

if __name__=='__main__':
    app.run(host='0.0.0.0', port=3333)

 

์ž…๋ ฅ๊ฐ’(url)์— ๋Œ€ํ•œ ํ•„ํ„ฐ๋ง์ด ์กด์žฌํ•œ๋‹ค.  

  • ip๊ฐ€ false๊ฐ’์ด ๋‚˜์˜ค๊ฑฐ๋‚˜ 0.0.0.0(์˜๋ฏธ์—†๋Š” ๊ฐ’)์ด๋ฉด ํ•„ํ„ฐ๋ง์— ๊ฑธ๋ฆผ 
  • false
    • check_global() -> Can you access my admin page~? ๋ฆฌํ„ด 
    • referer ๊ฐ’์ด localhost / 127.0.0.1 ์ผ ๋•Œ false ๋ฆฌํ„ด 
def check_get(url):
    ip = lookup(urlparse(url).netloc.split(':')[0])
    if ip == False or ip =='0.0.0.0':
        return "Not a valid URL."
    res=requests.get(url)
    if check_global(ip) == False:
        return "Can you access my admin page~?"
    for i in res.text.split('>'):
        if 'referer' in i:
            ref_host = urlparse(res.headers.get('refer')).netloc.split(':')[0]
            if ref_host == 'localhost':
                return False
            if ref_host == '127.0.0.1':
                return False 
    res=requests.get(url)
    return res.text
  1. IP ์ฃผ์†Œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ:
    • ์กฐํšŒ๋œ IP ์ฃผ์†Œ๊ฐ€ False์ด๊ฑฐ๋‚˜ 0.0.0.0์ผ ๊ฒฝ์šฐ, ์œ ํšจํ•˜์ง€ ์•Š์€ URL๋กœ ๊ฐ„์ฃผํ•˜๊ณ  "Not a valid URL." ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ˜ํ™˜
  2. ์™ธ๋ถ€ IP ํ™•์ธ:
    • ๊ณต์ธ ip๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ (์‚ฌ์„ค IP์ธ ๊ฒฝ์šฐ) - "Can you access my admin page?" ์ถœ๋ ฅ
    • ๊ณต์ธ IP์ผ ์‹œ referer ip๊ฐ€ ๋กœ์ปฌ ์ฃผ์†Œ์ธ์ง€ ํ™•์ธํ•œ๋‹ค.
  3. HTTP ํ—ค๋” ๋ฐ Referer ๊ฒ€์‚ฌ:
    • ์‘๋‹ต ํ—ค๋”์—์„œ 'refer' ๊ฐ’์„ ์ถ”์ถœํ•˜๊ณ  ์ด๋ฅผ ๋‹ค์‹œ ํŒŒ์‹ฑํ•˜์—ฌ ํ˜ธ์ŠคํŠธ ์ด๋ฆ„์„ ์–ป๋Š”๋‹ค.
    • ํ˜ธ์ŠคํŠธ ์ด๋ฆ„์ด 'localhost' ๋˜๋Š” '127.0.0.1'์ธ ๊ฒฝ์šฐ, False๋ฅผ ๋ฐ˜ํ™˜

์ฆ‰, ์ผ๋ฐ˜์ ์ธ SSRF ์ทจ์•ฝ์ ์„ ๊ณต๊ฒฉํ•˜๋Š” ๊ฒƒ์œผ๋กœ๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์—†๋‹ค. (๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ ํ•„ํ„ฐ๋ง)

 

ํ…Œ์ŠคํŠธ ์ฐจ์›์—์„œ http://dreamhack.io๋ฅผ ๋„ฃ์–ด๋ณด๋ฉด, ํฌ๋กค๋ง์„ ํ•ด์ค€๋‹ค.

 

 

http://127.0.0.1:3333/admin ๋ฅผ ์ž…๋ ฅํ•ด๋ณด๋ฉด "Can you access my admin page~?"๋ผ๋Š” ๋ฌธ์ž์—ด์ด ์ถœ๋ ฅ๋œ๋‹ค.

์ฆ‰, ํ”Œ๋ž˜๊ทธ๊ฐ’์€ ํ™•์ธํ•  ์ˆ˜ ์—†์—ˆ๋‹ค.

 

URL ๋‹จ์ถ• (https://tinyurl.com/app ์‚ฌ์šฉ):  ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ™œ์šฉ 

+)

ToCToU

์ด ์ฝ”๋“œ๋Š” lookup๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ํ›„, ๋งŽ์€ ์—ฐ์‚ฐ์„ ํ•œ ๋’ค์— admin ํŽ˜์ด์ง€๋กœ get ์š”์ฒญ์„ ๋‚ ๋ฆฌ๊ณ  ์žˆ๋‹ค.

๋”ฐ๋ผ์„œ ์‹ค์ œ๋กœ ip๋ฅผ ํ™•์ธํ•˜๋Š” ๋กœ์ง์ธ lookup๋ฉ”์†Œ๋“œ์™€ admin ํŽ˜์ด์ง€์˜ request.remote_addr ์‚ฌ์ด์— ์—ฐ์‚ฐ์œผ๋กœ ์ธํ•œ race condition์ด ๋ฐœ์ƒํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ์ทจ์•ฝ์ ์„ Time of check to time of use(ToCToU) ์ทจ์•ฝ์ ์ด๋ผ๊ณ  ํ•œ๋‹ค. 

def lookup(url):
    try:
        return socket.gethostbyname(url)
    except:
        return False

 

์ด ์ทจ์•ฝ์ ์„ ํŠธ๋ฆฌ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” DNS rebinding ๊ณต๊ฒฉ์„ ํ•ด์•ผํ•œ๋‹ค. 

DNS rebinding ๊ณต๊ฒฉ์ด๋ž€, ๋„๋ฉ”์ธ ์ด๋ฆ„์„ ํ™•์ธํ•˜๋Š” ๋กœ์ง์„ ์šฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

 

lookup์„ ํ†ตํ•ด ๋„๋ฉ”์ธ์— ๋Œ€ํ•œ ip๋ฅผ ํ™•์ธํ•  ๋•Œ์—๋Š” check_global(ip)๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„๋ฉ”์ธ์— ๋งค์นญ๋˜๋Š” ip๊ฐ€ global ip ์—ฌ์•ผํ•˜๊ณ , ์ดํ›„ admin ํŽ˜์ด์ง€์—์„œ request.remote_addr์„ ํ†ตํ•ด ip๋ฅผ ๊ฒ€์‚ฌํ•  ๋•Œ์—๋Š” ๋„๋ฉ”์ธ์— ๋งค์นญ๋˜๋Š” ip๊ฐ€ local ip์ธ 127.0.0.1์ด์—ฌ์•ผ ํ•œ๋‹ค.

  • lookup - global ip
  • request.remote_addr - local ip (127.0.0.1)

 

๊ทธ๋ ‡๋‹ค๋ฉด ๊ฐ™์€ ๋„๋ฉ”์ธ์— 2๊ฐœ์˜ ip๊ฐ€ ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉฐ ๋งค์นญ๋˜๋„๋ก ํ•œ๋‹ค๋ฉด, flag๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

 

๊ณต๊ฒฉ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•˜๋‚˜์˜ ๋„๋ฉ”์ธ์— 2๊ฐœ์˜ IP๋ฅผ ๋งคํ•‘ํ•ด๋‘๊ณ  TTL๊ฐ’์„ ๋น ๋ฅด๊ฒŒํ•˜์—ฌ request๊ฐ€ ์ „์†ก๋˜๊ธฐ ์ „์— global IP์ธ ์ƒํƒœ๋กœ ๊ฒ€์ฆ๋กœ์ง์„ ์šฐํšŒํ•˜๊ณ , ๋‹ค์‹œ local IP๋กœ ๋ณ€ํ™˜๋˜์–ด์•ผ ํ•œ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด ํ•˜๋‚˜์˜ ๋„๋ฉ”์ธ์— 2๊ฐœ์˜ IP๋ฅผ ๊ฑธ์–ด๋‘๊ณ  TTL์€ 30์ •๋„๋กœ ๋‘๋ฉด 30์ดˆ์— ํ•œ๋ฒˆ์”ฉ ๋žœ๋ค์œผ๋กœ 2๊ฐœ์˜ IP์ค‘ ํ•˜๋‚˜๋ฅผ ๋„๋ฉ”์ธ์— ๋ฐ”์ธ๋”ฉํ•ฉ๋‹ˆ๋‹ค.
๋งŒ์•ฝ ๋„๋ฉ”์ธ์ด ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” DNS rebinding tool์„ ์‚ฌ์šฉํ•˜์—ฌ 2๊ฐ€์ง€ ip๋ฅผ ํ•˜๋‚˜์˜ ๋„๋ฉ”์ธ์— ๋ฐ”์ธ๋”ฉํ•˜๊ณ , ํ•ด๋‹น ๋„๋ฉ”์ธ์œผ๋กœ admin ํŽ˜์ด์ง€์— ์ ‘์†ํ•˜์—ฌ ํƒ€์ด๋ฐ์„ ๋งž์ถฐ์ฃผ๋ฉด flag๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

http://7f000001.6565a4b0.rbndr.us:3333/admin