[Dreamhack] crawling


문제 설명

  • 드림이는 웹 크롤링 사이트를 구축했습니다.
  • 크롤링 사이트에서 취약점을 찾고 flag를 획득하세요!


풀이

문제 사이트에 접속하면 url을 입력하는 폼이 존재한다.

{A9C0CDA0-BCDD-45D0-A432-FCCA0E05E40D}


/admin 경로와 /validation 경로가 존재하는데 index.html에서 제출 버튼을 누르면 해당 값이 validation의 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)


http://dreamhack.io 를 입력하니 아래와 같이 IP와 아래 응답 텍스트가 출력되고 있다.

{E869A7A4-BBB2-45DC-851E-0492EE652B2E}


먼저 check_get 함수를 분석해보면 ip가 False 이거나 0.0.0.0이 아니고, 공인 IP 주소여야 한다. 또한 referer 헤더에서 localhost나 127.0.0.1이 존재하면 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


이 문제를 풀기 위해서 먼저 아래와 같은 URI 구조를 알면 쉽게 풀 수 있다. 따라서 위를 우회하기 위해서는 http://google.com:80@127.0.0.1:3333/admin 과 같은 URL을 요청하면 admin 경로의 응답 텍스트를 받을 수 있을 것이다. 포트는 맨 아래에서 3333번 포트로 서버를 열고 있기 때문에 포트도 3333번으로 해주어야 한다.

scheme://[userinfo@]host[:port][/path][?query][#fragment]


URL 입력 폼에 입력하면 아래와 같이 flag 값이 잘 출력되는 것을 확인할 수 있다.

{4CE8B5C6-3B0C-4D21-90AE-73C1D0242324}

댓글남기기