Finding a Basic RCE Vulnerability on a Prominent News Channel

Inspiring new bug bounty hunters with a simple RCE vulnerability discovery.

Usually, when newcomers approach me in the bug bounty field, they often ask about the tools, methods, and any other "secret sauce" I use when searching for vulnerabilities in bug bounty programs. I'm sure many of them might feel I sound arrogant or condescending when I reply, "Concentrate on recon, and search on Google for things you don't get." This discovery truly confirms this idea.

I decided to take a look at a private program late last night, but wanted to just casually look. I set my scope in BurpSuite to the primary FQDN that was in-scope at the time, and decided to manually "walk the app", and passively collect requests in BurpSuite. Going back to BurpSuite, I noticed that one of the requests hit on a BCheck Template that I had (more on this in another article) which showed that the application had Symfony Debug Mode enabled, and the /_profiler endpoint was accessible publicly. I decided to navigate to the URL in browser to see what information was available to help me finding something impactful to report.

I found this interesting and decided to search on Google to learn more about Symfony and any known vulnerabilities. I wasn't looking for information disclosures. I discovered that Symfony is a free and open-source PHP framework that makes web development faster and easier. There is a known Remote Code Execution vulnerability on the /_fragment endpoint. In my research, I found out that Symfony has a built-in feature called /_fragment which allows clients to input custom PHP commands and receive HTML output. It secures requests with a secret key to prevent misuse. However, if the secret key is weak or leaked, it could lead to Remote Code Execution. This means that anyone with the /_fragment secret key can run PHP code on the server, potentially causing Remote Code Execution.

Armed with this information, I proceeded to navigate to the URL in my browser to see what happens.

However, when I attempted to access this endpoint, I encountered an Access Denied error. This prompted me to investigate further to determine my next steps. I discovered that the /_fragment endpoint responds with a 403 Forbidden status code when an invalid request is made to it. However, if a valid signature is provided, the endpoint is expected to return a 404 Not Found status code. The signature is created using the HMAC algorithm and plain text, which includes the URL and a secret key. This secret key is only known to the server. If the signature doesn't match the expected value, the request is denied.

Well, that's great, but how can I obtain the secret key to sign requests for accessing this endpoint and proceed with my investigation? Many individuals tend to give up too easily and are "inches from gold." This means that if they exerted a little more effort, they would discover the success they seek in this field.

After further research (Googling), I discovered that Symfony utilizes a default secret key. If this key hasn't been altered, it should provide the desired outcomes. This secret key might be exposed in different places, such as phpinfo(). Considering time constraints, I assumed it was still in use. The default secret key for Symfony is: "ThisTokenIsNotSoSecretChangeIt".

Armed with this information, I used PHP to generate a valid signature with the default secret key to test my luck for the night. The code creates an HMAC signature for the provided URL/Endpoint, secret key, and algorithm. It then encodes the signature using base64 and URL encoding. The resulting output is utilized to send requests to the /_fragment endpoint.

php -r "echo(urlencode(base64_encode(hash_hmac('sha256', '', 'ThisTokenIsNotSoSecretChangeIt', 1))) . PHP_EOL);"

As seen in the screenshot below, we have generated a hash that we can use to check its validity.

In order to check the validity of the hash that I just generated, I needed to send a request to the /_fragment endpoint, and append my outputted hash to the _hash parameter, and issue the GET request.<generated-hash-here>

To my surprise, I no longer received the Access Denied error. Instead, I got a 404 status code, confirming that the hash I generated is valid. Now, it's time to dig deeper and see if I can achieve RCE now that I have access to the /_fragment endpoint.

I could smell the money raining from the sky, even though the RCE wasn't confirmed yet, my excitement almost got the best of me.

After regaining my composure, I had to continue progressing towards my main objective. Since we've verified the hash's validity, to exploit this, we must utilize the path parameter in conjunction with the controller, system function, and the desired command. Then, we follow the same steps as before to obtain a valid signature for the Exploit URL.

It basically sets the _controller value as the system function, and then executes the id command.


I then used PHP once more to generate a valid hash using the default secret for this URL.

php -r "echo(urlencode(base64_encode(hash_hmac('sha256', '', 'ThisTokenIsNotSoSecretChangeIt', 1))) . PHP_EOL);"

I then opened the URL in my browser and, as the skilled hackers would say, BOOM! The RCE attack was successful. We can observe that we have effectively executed the id system command, and its output is displayed on the page.

This concludes your regularly scheduled programming. I hope this demonstrates that you don't need to use lots of tools, scanners, or random payloads to discover critical P1 vulnerabilities. Take the time to understand what you're dealing with, and search online for anything you need to know or don't understand. Remember, tools and scripts don't define the hacker; it's the hacker who creates the tools and scripts. Slow down, think like a hacker, tackle each challenge step by step, and utilize Google to learn about unfamiliar concepts. Hacking isn't about spraying and praying. It's a mindset that involves being curious and willing to learn something new, enabling you to manipulate systems to your advantage.

Until next time... Sergio Medeiros