roaris / ctf-log

0 stars 0 forks source link

HackTheBox: baby CachedView (Web) #31

Open roaris opened 4 months ago

roaris commented 4 months ago

https://app.hackthebox.com/challenges/baby%2520CachedView

roaris commented 4 months ago

build-docker.shを実行すると、AttributeError: 'Flask' object has no attribute 'before_first_request'というエラーが発生する https://community.render.com/t/flask-object-has-no-attribute-before-first-request/15671 によると、Flaskのバージョンを下げれば良いとのこと requirements.txtでFlaskのバージョンに2.2未満を指定する

今度はImportError: cannot import name 'url_quote' from 'werkzeug.urls' (/app/venv/lib/python3.8/site-packages/werkzeug/urls.py)というエラーが発生する https://qiita.com/croquette0212/items/02d50cd77932f5a0aae1 によると、Flaskが依存するWerkzeugのバージョンを下げれば良いとのこと requirements.txtでWerkzeugのバージョンに3.0未満を指定する

最終的なrequirements.txt

Flask < 2.2
selenium
Werkzeug < 3.0
roaris commented 4 months ago

これでアプリケーションは立ち上がるが、URLを送信すると、__init__() got an unexpected keyword argument 'executable_path'というエラーが発生する https://gammasoft.jp/blog/python-selenium-webdriver-deprecation-warning-by-executable_path/ によると、Seleniumのバージョンを下げれば良いとのこと requirements.txtでSeleniumのバージョンに4.0未満を指定する

Flask < 2.2
selenium < 4.0
Werkzeug < 3.0

今度はTimeout value connect was , but it must be an int, float or None.というエラーが発生する 折れたので、ローカルで動かすのは諦める

roaris commented 4 months ago

URLを送信すると、そのURLのスクリーンショットが保存されるというアプリケーション /flagにアクセスすると、フラグ画像が手に入るが、送信元IPが127.0.0.1じゃないと403が返るので、外部から/flagにアクセスすることは出来ない

from flask import Blueprint, request, render_template, abort, send_file
from application.util import cache_web, is_from_localhost

web = Blueprint('web', __name__)
api = Blueprint('api', __name__)

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

@api.route('/cache', methods=['POST'])
def cache():
    if not request.is_json or 'url' not in request.json:
        return abort(400)

    return cache_web(request.json['url'])

@web.route('/flag')
@is_from_localhost
def flag():
    return send_file('flag.png')
def is_from_localhost(func):
    @functools.wraps(func)
    def check_ip(*args, **kwargs):
        if request.remote_addr != '127.0.0.1' or request.referrer:
            return abort(403)
        return func(*args, **kwargs)
    return check_ip

送信するURLをhttp:\//127.0.0.1/flagにしても、送信先IPアドレスが127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 0.0.0.0/8の範囲にある場合は弾かれる

def cache_web(url):
    scheme = urlparse(url).scheme
    domain = urlparse(url).hostname

    if not domain or not scheme:
        return flash(f'Malformed url {url}', 'danger')

    if scheme not in ['http', 'https']:
        return flash('Invalid scheme', 'danger')

    def ip2long(ip_addr):
        return struct.unpack('!L', socket.inet_aton(ip_addr))[0]

    def is_inner_ipaddress(ip):
        ip = ip2long(ip)
        return ip2long('127.0.0.0') >> 24 == ip >> 24 or \
                ip2long('10.0.0.0') >> 24 == ip >> 24 or \
                ip2long('172.16.0.0') >> 20 == ip >> 20 or \
                ip2long('192.168.0.0') >> 16 == ip >> 16 or \
                ip2long('0.0.0.0') >> 24 == ip >> 24

    if is_inner_ipaddress(socket.gethostbyname(domain)):
        return flash('IP not allowed', 'danger')

    return serve_screenshot_from(url, domain)
roaris commented 4 months ago

ローカルのflag.pngを見ると、TOCTOUやRebindingと書いてある https://blog.tokumaru.org/2022/05/dns-rebinding-protection.html を読む

わざわざAレコードを変更しなくても、初めからhttp:\//trap.example.org/にイントラネットにアクセスするようなJavaScriptを仕込んで入れば良さそうな気がするけど違う?

それはともかく、SSRF攻撃と組み合わせることが出来て、今回やりたいのはこれ

roaris commented 4 months ago

https://lock.cmpxchg8b.com/rebinder.html を使う 127.0.0.1と142.251.222.14(google.comのIP)を設定する

$ nslookup 7f000001.8efbde0e.rbndr.us                                                                                                                                        
Server:         172.23.64.1
Address:        172.23.64.1#53

Non-authoritative answer:
Name:   7f000001.8efbde0e.rbndr.us
Address: 142.251.222.14

$ nslookup 7f000001.8efbde0e.rbndr.us                                                                                                                                        
Server:         172.23.64.1
Address:        172.23.64.1#53

Non-authoritative answer:
Name:   7f000001.8efbde0e.rbndr.us
Address: 127.0.0.1

名前解決の結果が、IPアドレスのバリデーション時は142.251.222.14で、スクリーンショットを取るためのリクエスト時は127.0.0.1だと上手くいく

roaris commented 4 months ago

http:\//7f000001.8efbde0e.rbndr.us/flagを入力する 何度か試しているとフラグ画像が取れた

image

roaris commented 4 months ago

meta refreshを使った別解もある https://skelter.hashnode.dev/htb-baby-cachedview-writeup-alternative

index.html

<meta http-equiv="refresh" content="0; URL=http://127.0.0.1/flag" />

python -m http.serverngrok http http://localhost:8000 として、ngrokによって生成されるURLを送信する 確認画面で止まってしまい、駄目だった

image