This "exploit" is pretty neat because it's extremely small. 3-liner. All it does is making hash easier to find by making part of it constant.
So here we go. First of all show sits in PBKDF2_SHA256().
Tricked call is:
memcpy(&buf[i * 32], T + j , clen - j)
while original call is:
memcpy(&buf[i * 32], T, clen)
j is just a counter a bit below, so attacker set it to own value when it's no longer used:
j = ((i + 1) * 32 >= dkLen) && (dkLen == 32);
As you can see it can take one of only two values: 0 or 1. When it's 0, it changes nothing. So when it's value is 1?
PBKDF2_SHA256 is called in scrypt() and scrypt_nosalt (scrypt.cpp). Always this way:
PBKDF2_SHA256((const uint8_t*)input, inputlen, (const uint8_t*)input, inputlen, 1, (uint8_t *)X, 128);
scrypt_core(X, V);
PBKDF2_SHA256((const uint8_t*)input, inputlen, (uint8_t *)X, 128, 1, (uint8_t*)&result, 32);
return result;
Let's go back to setting j. As you can see it's matter only on second PBKDF2_SHA256 call, since it's called with dkLen=32. This call is affected. So now j becomes an small offset which will make a its job in memcpy call:
memcpy(&buf[0], T + 1, clen - 1)
clen is calculated this way:
clen = dkLen - i * 32;
if (clen > 32)
clen = 32;
Because dkLen=32 this loop executes only once (only 0 * 32 < dkLen). So clen = dkLen. clen - 1 => 31. So we copy last 31 bytes. What about first byte?
Now let's back to start.
Between computing HMAC state and block iteration buf[0] is zeroed:
memset(&buf[0], 0, dkLen);
So that memcpy ABOVE does not touch it neither it gets garbage from memory.
This is how first byte of scrypt()'s result is always zero!