..

Don't BF Me! (WeCTF 2020)

Solved solo!

The Challenge

CategoryKey Exploit
WebVariable injection

The handout contained server-side PHP code and the HTML for a small website with a password entry form. There was also a Dockerfile in there, so players could spin up local instances for testing.

Original Prompt

Shou uses Recaptcha for his site to make it “safer”.

Hint: The password is so long that makes any bruteforcing method impotent.

Handout: https://github.com/wectf/2020p/blob/master/dont-bf-me/handout.zip

Summary

The files:

  • index.php is processed into the HTML page we interact with

  • login.php registered as the form submit action in index.php, so it handles form submissions from that page

  • constants.php is imported at the tops of the other two files to provide useful constants

The form and backend code use Google’s recaptcha service to verify form submissions. login.php contains checks for a bad or missing g-recaptcha-response parameter. It returns error messages if the checks detect an issue.

After those checks, there’s a simple check for the correct password:

1
2
3
// check password
if($password == $CORRECT_PASSWORD) {
    echo $FLAG;

After reading the prompt, I decided to take them at their word that the password couldn’t, or shouldn’t, be brute-forced. If nothing else, millions of Recaptcha API calls might get expensive. Also, it’s PHP so the odds of finding a vuln in the server-side code seemed pretty good ;)

Finding a vulnerability

Types?

I didn’t know much about PHP going into this, apart from its bad reputation, the fact that recent versions have addressed some issues that contributed to that reputation, and the dreadful === operator.

The use of == in the password check stood out to me. However, careful consideration of documented type coercion rules for ==, along with a few tests, convinced me this wasn’t it. My input would be compared as a string to the string in CORRECT_PASSWORD.

Next, I decided to look at how form fields were being read in, but took a quick detour on the way.

PHP version

PHP has been changing in recent years to address the issues that earned it a bad reputation. While searching through PHP documentation for this challenge, I found a lot of cautions and notes about differences between versions. A quick look at the provided Dockerfile seemed wise:

1
FROM php:7.3-apache

PHP version 7.3. Noted.

parse_str

It just so happens that this function was changed in PHP 8.0 to remove dangerous behavior! In PHP versions before 8.0, if parse_str is given only one its first argument (a string), it parses the input as a query string from a URL and stores the parsed values in variables with the same names as the query parameters. This overrides variables in scope when parse_str is called if there are query parameters with the same names.

In login.php, parse_str is used in the deprecated, unsafe way:

1
parse_str($_SERVER["QUERY_STRING"]);

Since this line comes after include "constant.php";{:.php}, any query parameter that shares a name with a constant can be used to override that constant in login.php!

My solution

Sending a custom request to the challenge server would allow me to set both CORRECT_PASSWORD and the URL used to verify the Recaptcha token. However, I felt like setting up a publicly-hosted JSON file or finding one to point to would be way too much effort.

So I simply pressed F12 to open the developer tools in my browser and added a single line of HTML to the form by right-clicking in the inspector and selecting “Edit as HTML”.

Before:

1
2
3
4
...
<form class="form-group" method="GET" action="login.php">
    <input type="password" class="form-control" name="password" placeholder="Password">
    ...

And after:

1
2
3
4
5
...
<form class="form-group" method="GET" action="login.php">
    <input type="text" class="form-control" name="CORRECT_PASSWORD" placeholder="Password">
    <input type="password" class="form-control" name="password" placeholder="Password">
    ...

All that remained was to type the same thing in both fields and click submit!

Since I hadn’t messed with or omitted the Recaptcha field, the server verified my request’s Recaptcha successfully and gave me the flag.

themed with nostyleplease