lubosdz / yii2-captcha-extended

Extended captcha code generator for Yii2 framework with configurable obfuscation level
Other
9 stars 1 forks source link

Captcha is easily exploitable #2

Closed lrluque closed 9 months ago

lrluque commented 9 months ago

By frequently using the refresh function on a website that uses this module, I've noticed that the hashes have fixed values that repeat consistently along with their solutions. And that this hashes are actually public. For instance, for hash 98, the captchas are 30 - 10, 29 - 9, 43 - 23, etc. All with the same solution.

HASH CAPTCHA SOLUTION
97 10
98 20
99 30
100 40
101 50
102 60
103 70
104 80
105 90
145 100
protected function getCodeMath()
{
    $this->resultMultiplier = intval($this->resultMultiplier);

    if($this->resultMultiplier < 1){
        $this->resultMultiplier = 1;
    }elseif($this->resultMultiplier > 100){
        $this->resultMultiplier = 100;
    }

    $n2 = mt_rand(1, $this->resultMultiplier);

    if(mt_rand(0, 1)){
        // minus formula
        $n1 = mt_rand(1, 9) * $this->resultMultiplier + $n2;
        $code = $n1 .'-'. $n2 .'=';
        $result = $n1 - $n2;
    }else{
        // plus formula
        $n1 = mt_rand(1, 10) * $this->resultMultiplier - $n2;
        $code = $n2 .'+'. $n1 .'=';
        $result = $n2 + $n1;
    }

    return [
        'code' => $code,
        'result' => $result
    ];
}

This is the function where $code is generated. By default, the variable $resultMultiplier is set to 10. This gives us the following range:

  1. $n2 will be between 1 and 10, inclusive.
  2. $n1 will be between 11 (1 10 + 1) and 100 (9 10 + 10), inclusive.
  3. This ensures that the result will always be a multiple of 10. In subtraction, you have values from 10 to 90 (in increments of ten) + $n2, then you subtract $n2, resulting in values of 10, 20, 30, 40...90. With addition, it follows a similar pattern but ranging from 10 to 100 (also in increments of ten), you subtract $n2, then add it back.
  4. The subtraction result, in multiples of 10, ranges between 10 and 90, and the addition result ranges between 10 and 100. So, there are 10 different results: 10, 20, 30, 40, 50, 60, 70, 80, 90, and 100. How does this translate to the hashes we know?
public function generateValidationHash($result)
{
    $result = preg_replace('/\s+/u', '', $result);
    $result = urlencode($result); // convert accented characters to ASCII

    for ($h = 0, $i = strlen($result) - 1; $i >= 0; --$i) {
        $h += ord($result[$i]);
    }

    return $h;
}

This is the function that generates the hash. Essentially, it sums each character of $result in ASCII:

10 = ord(1) + ord(0) = 49 + 48 = 97 20 = ord(2) + ord(0) = 50 + 48 = 98 . . . 100 = ord(1) + 2 * ord(0) = 49 + 2 * 48 = 145

And with this, we resolve the table we had made above.

In summary, the captcha pattern isn't difficult to identify, making it highly vulnerable. You can bypass it with a simple script.

lubosdz commented 9 months ago

You can easily increase entrophy by setting e.g. resultMultiplier = 66 (acceted values are 1-100) - see demo. Please note that resultMultiplier only applies to MODE_MATH and is intentionally set to 10 by default to avoid deteriorated user experience (many users would have problem to calculate formulas like 457 + 8).

Also from my experience almost all crawlers implement quite simple algos to decipher image and submit detected chars rather than evaluating as a formula, which would require extra programming effort. If you feel the need for greater complexity or safety you can change the to MODE_MATHVERBAL, MODE_WORDS, MODE_LOGICAL or framework's MODE_DEFAULT.