Line 1:
Line 1:
−
Here is a pseudocode implementation of roughly the same type of bug that existed until [[IOS37]]:
+
Here is an extremely concise example of the same type of bug that existed until [[IOS37]]:
−
struct rsa_cert {
+
char *hash1=hash(realstuff);
−
u32 key_id;
−
char rsa_signature[1024];
−
char metadata[32];
−
char content_hash[20];
−
};
−
int verify_cert (struct rsa_cert cert) {
+
invalid=strncmp(hash1,signaturehash,HASHLENGTH);
−
char *cert_hash=SHA1(cert.metadata + cert.content_hash);
−
char *sig_hash=rsa_decrypt(cert.rsa_signature, cert.key_id);
−
if (strncmp(cert_hash, sig_hash, SHA1_LENGTH) == 0) {
+
if(invalid!=0) {
−
return CERT_OK;
+
pretendItsNotAWiiDisc();
−
} else {
−
return CERT_BAD;
−
}
−
}
−
−
int is_a_valid_disc(struct rsa_cert cert, char *disc_hash) {
−
if(memcmp(disc_hash, cert.content_hash, SHA1_LENGTH) != 0) {
−
return DISC_BAD;
−
}
−
−
if(verify_cert (cert) == CERT_BAD) {
−
return DISC_BAD;
−
} else {
−
return DISC_OK;
−
}
}
}
−
The bug here is that the hash can (and very likely does) contain a NULL byte, (chr)0 that is.
+
The bug here is that the hash can (and likely does) contain a NULL byte, (chr)0 that is.
To quote from the first google hit for strncmp:
To quote from the first google hit for strncmp:
Line 41:
Line 19:
This reduces the effective length of the hash to the number of bytes before the NULL byte. This means that the difficulty of finding a hash collision is reduced from 2^(HASHLENGTH*8) to 2^(bytes before the null * 8). That is a big change if the NULL is early in the hash. Assuming the NULL is at the 5th byte, that means that there is a one in 2^(4*8) chance that the hash matches, or one in 4 294 967 296, fairly computable within a reasonable time frame on a current computer that can try a few million hash inputs each sec.
This reduces the effective length of the hash to the number of bytes before the NULL byte. This means that the difficulty of finding a hash collision is reduced from 2^(HASHLENGTH*8) to 2^(bytes before the null * 8). That is a big change if the NULL is early in the hash. Assuming the NULL is at the 5th byte, that means that there is a one in 2^(4*8) chance that the hash matches, or one in 4 294 967 296, fairly computable within a reasonable time frame on a current computer that can try a few million hash inputs each sec.
+
+
Now, it would require a signed contents that begins with a null to completely bypass the checking. Normally, that would mean a need to find an existing signed content that can be exploited. But, the hashing function has an important behavior, with only null bytes as the input, the hash is only null bytes. Given that we control all the hashed data and can safely set it all to null, we don't even need to piggyback on something that Nintendo have already signed.
tmbinc has a more thorough explanation [http://debugmo.de/?p=61 here].
tmbinc has a more thorough explanation [http://debugmo.de/?p=61 here].