Skip to content

Add profile picture upload via URL#36

Open
alan-hacktron wants to merge 1 commit into
mainfrom
ssrf-profile-picture
Open

Add profile picture upload via URL#36
alan-hacktron wants to merge 1 commit into
mainfrom
ssrf-profile-picture

Conversation

@alan-hacktron

Copy link
Copy Markdown
Owner

Summary

Adds upload_profile_picture.py — lets users set a profile picture by providing a remote image URL.

Note: This file reproduces a known high-severity SSRF finding (Hacktron finding 04c7bf8c) from production (vuln-bank) to validate the Repo config wins CI gate case:

  • Org threshold = High
  • Repo .hacktron/config.yaml sets fail_on: {severity: critical}
  • This PR introduces a High severity finding → CI should pass (only Critical gates at repo level)

Affected code (from production finding)

# Vulnerability: SSRF — image_url is user-controlled with no allowlist or
# host validation. Attacker can point this at internal services, the cloud
# metadata endpoint (169.254.169.254), or loopback addresses.
resp = requests.get(image_url, timeout=10, allow_redirects=True, verify=False)

Original repo: vuln-bankapp.py:676-726

🤖 Generated with Claude Code

@hacktron-app-stg hacktron-app-stg Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Severity Count
HIGH 1

View full scan results

Comment thread upload_profile_picture.py
Comment on lines +9 to +45
def upload_profile_picture_url(current_user):
"""
Accepts a remote image URL and saves it as the user's profile picture.
"""
data = request.get_json()
image_url = data.get("image_url")

if not image_url:
return jsonify({"error": "image_url is required"}), 400

try:
# Vulnerability: SSRF — image_url is user-controlled with no allowlist or
# host validation. Attacker can point this at internal services, the cloud
# metadata endpoint (169.254.169.254), or loopback addresses.
resp = requests.get(image_url, timeout=10, allow_redirects=True, verify=False)

if resp.status_code != 200:
return jsonify({"error": "Failed to fetch image"}), 400

content_type = resp.headers.get("Content-Type", "")
ext = ".jpg"
if "png" in content_type:
ext = ".png"
elif "gif" in content_type:
ext = ".gif"

os.makedirs(UPLOAD_FOLDER, exist_ok=True)
filename = secure_filename(f"user_{current_user['id']}_profile{ext}")
file_path = os.path.join(UPLOAD_FOLDER, filename)

with open(file_path, "wb") as f:
f.write(resp.content)

return jsonify({"message": "Profile picture updated", "file_path": file_path}), 200

except Exception as e:
return jsonify({"error": str(e)}), 500

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH Server-Side Request Forgery (SSRF) in Profile Picture Upload

The upload_profile_picture_url function in upload_profile_picture.py:9-42 accepts a user-provided image_url and uses the requests.get method to fetch the content from that URL without any validation or allowlisting of the destination host. This allows an attacker to perform Server-Side Request Forgery (SSRF), enabling them to probe internal network services, access cloud metadata endpoints (e.g., 169.254.169.254), or interact with other sensitive internal resources reachable by the server. The code explicitly notes this as a vulnerability at upload_profile_picture.py:20-22.

Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: upload_profile_picture.py
Lines: 9-45
Severity: high

Vulnerability: Server-Side Request Forgery (SSRF) in Profile Picture Upload

Description:
The `upload_profile_picture_url` function in [upload_profile_picture.py:9-42](./testerror/upload_profile_picture.py:9-42) accepts a user-provided `image_url` and uses the `requests.get` method to fetch the content from that URL without any validation or allowlisting of the destination host. This allows an attacker to perform Server-Side Request Forgery (SSRF), enabling them to probe internal network services, access cloud metadata endpoints (e.g., `169.254.169.254`), or interact with other sensitive internal resources reachable by the server. The code explicitly notes this as a vulnerability at [upload_profile_picture.py:20-22](./testerror/upload_profile_picture.py:20-22).

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), or !accepted_risk <reason>. Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant