Captcha

A CAPTCHA is a type of challenge-response test used in computing to determine whether the user is human in order to deter bot attacks and spam.

Captcha Workflow

For Google Recaptcha v3:

Site Registration

The site key is used on the client side (in the web pages), while the secret key is kept confidential and used on the server side to communicate with Google’s reCAPTCHA service.

The website owner needs to register their site with Google reCAPTCHA and obtain API keys. These keys are used to authenticate communication between the website and Google’s reCAPTCHA service. After registering the site, the website owner obtains two keys from Google: the site key and the secret key.

Adding reCAPTCHA to web pages

The website owner integrates the reCAPTCHA v3 API into their website by adding a script tag with the necessary JavaScript code provided by Google. This code includes embedding the reCAPTCHA widget into specific web pages or forms where user interaction and bot detection are required. The website owner sets a threshold score (e.g., 0.5) to decide whether to consider a user as human or possibly a bot. Based on the assigned score, the website can take different actions, such as allowing the user to proceed or triggering additional verification steps.

User Interaction

When a user interacts with the website, reCAPTCHA v3 runs in the background, analyzing user behavior to determine whether the user is likely a human or a bot. Instead of challenging users with tests, reCAPTCHA v3 assigns a score to each user based on their behavior. The score ranges from 0.0 to 1.0, with 1.0 indicating a high confidence that the user is legitimate, and 0.0 indicating a high likelihood of being a bot.

Server-Side Validation

The website’s server, using the secret key, sends a request to Google’s reCAPTCHA API to verify the user’s score. This helps prevent malicious actors from faking or manipulating the scores on the client side. Depending on the score received from Google’s reCAPTCHA service, the website owner can take appropriate actions, such as allowing the user to submit a form, showing a CAPTCHA challenge, or blocking access. To validate the response, send a GET request:

https://www.google.com/recaptcha/api/siteverify?secret=your_secret&response=response_string&remoteip=user_ip_address

Parameters & Cookies

  • Parameters:
    • k: site key (client-side)
  • Cookies:
    • _GRECAPTCHA: reCAPTCHA sets a necessary cookie (_GRECAPTCHA) when executed for the purpose of providing its risk analysis. See FAQ (Google).

Captcha Bypass

Intercept requests using Burp Suite. Send the request to the Repeater module.

POST /api/myprecious HTTP/2
Host: example.com
[...]

{"someparam":"whatever","captcha-response":"thisisthecaptcharesponse"}

Reuse the captcha response

Resend the original request without modification. Also try with a different sessionID.

POST /api/myprecious HTTP/2
Host: example.com
[...]

{"someparam":"whatever","captcha-response":"thisisthecaptcharesponse"}

Empty captcha parameter

Send an empty captcha parameter.

POST /api/myprecious HTTP/2
Host: example.com
[...]

{"someparam":"whatever","captcha-response":""}

Remove the captcha parameter

Remove the captcha parameter (or HTTP header).

POST /api/myprecious HTTP/2
Host: example.com
[...]

{"someparam":"whatever"}

Find the captcha value

Check if the value of the captcha is in the source code of the page or inside a cookie. Check if the solution to the CAPTCHA might be the alt-text of the image(s), filename(s), or a value in an associated hidden field.

Fuzz parameters

Change parameter values to see if the captcha is enabled for certain values only and disabled for others. Remove the captcha parameter.

POST /api/myprecious HTTP/2
Host: example.com
[...]

{"someparam":"NO CAPTCHA FOR THIS PARAM VALUE"}

Also fuzz with the captcha data entry points (if present) with common injection payloads or special characters sequences.

Bad workflow

When the captcha is part of a workflow with multiple steps, try skipping the step with the captcha. For example, if the captcha is on a login page asking for the username, then a second step asks for the password, try submitting the second step only (with username & password).

Check for alternative methods that might not have CAPTCHA enforced, such as an API endpoint meant to facilitate mobile app access.

See if you can find the captcha verification sent from the client-side instead of the server-side. Look for a call to “/recaptcha/api/siteverify” and see if you can drop the request to bypass the captcha verification. If you find the secret key (parameter “secret”), see if you can send requests to the server without going through the client-side captcha challenge.

Clear cookies

Check if clearing cookies causes the captcha to be bypassed (for example if the captcha is only shown after a number of failures).

Automate the captcha solving

Try challenge Captcha me if you can (Root-Me). See solution (protected post).

  • Assess CAPTCHA challenges and attempt automating solutions depending on difficulty – like automating the calculation of mathematical operations.
  • Attempt to re-submit previously identified known good responses.
  • For text within images, check manually or with code how many images are being used and if only a few images are being used, detect them by MD5, or use an OCR (https://github.com/tesseract-ocr/tesseract).

Other ideas

From Testing for Weak Lock Out Mechanism (OWASP, WSTG-ATHN-03).

  • Attempt to submit request without solving CAPTCHA via the normal UI mechanism(s).
  • Attempt to submit request with intentional CAPTCHA challenge failure.
  • Attempt to submit request without solving CAPTCHA (assuming some default values may be passed by client-side code, etc) while using a testing proxy (request submitted directly server-side).

Automate the actions with JS:

<script>
    // Click button by ID
    document.getElementById('buttonId').click();

    // Click button by class
    document.querySelector('.buttonClass').click();

    // Click button by type
    document.querySelector('button[type="button"]').click();

    // Click button by text content
    Array.from(document.querySelectorAll('button')).find(el => el.textContent === 'Button Text')?.click();
</script>