Introduction

Context Explanation

Craphp is a web-based CTF challenge where the user is presented with an interface asking for a password to retrieve a flag. The challenge also provides access to the server’s source code (index.php), which we leveraged to analyze the vulnerabilities and design a strategy for exploitation. The critical insight in solving this challenge lies in understanding and exploiting weaknesses in the use of CRC (Cyclic Redundancy Check) hash algorithms within the authentication logic.

Craphp Homepage

Directive

Our objective is to bypass the authentication mechanism and eventually retrieve the flag from the server. The crux of the solution is identifying a vulnerability in the CRC hash algorithm used for password comparison, which allows us to craft a collision: a password that produces the same two hash outputs (CRC16 and CRC8) as the hardcoded password.


Solution

Analyzing Source Code

By examining the source code provided (index.php), we identified the fundamental authentication mechanism:

  1. The hardcoded password is stored in $MYPASSWORD = "AdM1nP@assW0rd!";.
  2. The authentication logic proceeds as follows:
    • User-submitted input ($_POST['password']) is rejected if its length does not match that of $MYPASSWORD.
    • Two hash functions crc16 and crc8 calculate hash values for the hardcoded $MYPASSWORD and the submitted password.
    • The calculated hashes ($pwhash3 and $pwhash4) from the user’s password are compared against the stored hashes ($pwhash1 and $pwhash2) from $MYPASSWORD.
    • If both hashes match but the submitted password does not equal $MYPASSWORD, the server results in a successful bypass and sends back the hidden $FLAG.

Here’s the relevant snippet of the code where the authentication logic is implemented:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
if(isset($_POST['password']) && strlen($MYPASSWORD) == strlen($_POST['password'])) {
    $pwhash1 = crc16($MYPASSWORD);
    $pwhash2 = crc8($MYPASSWORD);

    $password = $_POST['password'];
    $pwhash3 = crc16($password);
    $pwhash4 = crc8($password);

    if($MYPASSWORD == $password) {
        die("oops. Try harder!");
    }
    if($pwhash1 != $pwhash3) {
        die("Oops. Nope. Try harder!");
    }
    if($pwhash2 != $pwhash4) {
        die("OoOps. Not quite. Try harder!");
    }
    $access = true;

    if($access) {
        echo "You win a flag: $FLAG";
    } else {
        echo "Denied! :-(";
    }
} else {
    echo "Try harder!";
}

Observations and Vulnerability Insight

At first glance, this design might seem secure. However, a significant cryptographic vulnerability exists due to the use of non-cryptographically secure hash functions (crc16 and crc8):

  • CRC Collisions: Both CRC16 and CRC8 are designed for error detection in transmission systems and not for cryptographic purposes. They are not resistant to collisions—different inputs can produce identical CRC values. This is especially problematic in situations like this, where multiple CRC hashes are used for validating passwords.
  • An adversary can exploit these weak hash functions to generate a password that produces matching CRC16 and CRC8 hash values without knowledge of the real password.

Given this, the attack boils down to creating a password collision such that:

1
2
3
4
"AdM1nP@assW0rd!" != $password
strlen("AdM1nP@assW0rd!") == strlen($password)
crc16("AdM1nP@assW0rd!") == crc16($password)
crc8("AdM1nP@assW0rd!") == crc8($password)

Exploitation

Crafting the Collision

Our strategy involves brute-forcing to find a password of the same length as $MYPASSWORD (15 characters) that satisfies the CRC collision condition.

We wrote a PHP script to automate this process. The script:

  1. Computes the CRC16 and CRC8 of the hardcoded $MYPASSWORD.
  2. Randomly generates candidate passwords of the same length.
  3. Computes CRC16 and CRC8 for each candidate password.
  4. Compares the hash values of each candidate with the corresponding hashes of $MYPASSWORD.
  5. Stops when a collision is found and outputs the colliding password.

Here is the script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<?php

$MYPASSWORD = "AdM1nP@assW0rd!";
$pwhash1 = crc16($MYPASSWORD);
$pwhash2 = crc8($MYPASSWORD);

for ($i = 0; $i < 100000000; $i++) {
    $password = str_random(strlen($MYPASSWORD));
    $pwhash3 = crc16($password);
    $pwhash4 = crc8($password);

    if ($pwhash1 == $pwhash3 && $pwhash2 == $pwhash4) {
        echo "Collision found ! : $password\n";
        break;
    }
}

function str_random($length) {
    $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+';
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, strlen($characters) - 1)];
    }
    return $randomString;
}

function crc16($string) {
  $crc = 0xFFFF;
  for ($x = 0; $x < strlen ($string); $x++) {
    $crc = $crc ^ ord($string[$x]);
    for ($y = 0; $y < 8; $y++) {
      if (($crc & 0x0001) == 0x0001) {
        $crc = (($crc >> 1) ^ 0xA001);
      } else { $crc = $crc >> 1; }
    }
  }
  return $crc;
}


// https://stackoverflow.com/questions/507041/crc8-check-in-php/73305496#73305496
function crc8($input)
{
$crc8Table = [
    0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
    0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
    0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
    0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
    0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
    0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
    0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
    0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
    0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
    0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
    0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
    0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
    0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
    0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
    0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
    0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
    0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
    0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
    0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
    0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
    0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
    0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
    0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
    0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
    0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
    0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
    0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
    0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
    0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
    0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
    0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
];

    $byteArray = unpack('C*', $input);
    $len = count($byteArray);
    $crc = 0;
    for ($i = 1; $i <= $len; $i++) {
        $crc = $crc8Table[($crc ^ $byteArray[$i]) & 0xff];
    }
    return $crc & 0xff;
}

?>

Accessing the Flag

Running the script eventually yields a matching random password that satisfies the CRC collision condition:

1
2
3
┌──(kali㉿kali)-[~/Desktop]
└─$ php crc_collision.php  
Collision found ! : Tp#UTARjQMz+6X8

Submitting this password to the web application succeeds in bypassing the CRC checks, and the server returns the flag:

Craphp flag

1
You win a flag: ENO{Cr4hP_CRC_Collison_1N_P@ssw0rds!}

Tips & Tricks

  1. Understand Cryptographic vs Non-Cryptographic Hashing: Always remember that CRC-based functions are purpose-built for error detection in data transmission, but they’re unsuitable for critical operations like password validation.

  2. Look for Hash Collisions in Challenges: When encountering hash functions in CTF challenges, check if they are prone to collisions. This often allows for bypass attacks.

  3. Automating Brute Force: Writing scripts to brute-force collisions using weak hash functions can save plenty of time.

  4. Optimize with Known Characteristics: CRC functions have predictable patterns and might be optimized further using input-specific behaviors, potentially reducing brute-force runtime.