在 CTF 算是常見的 crypto 題型,但是現實生活中卻幾乎沒看過實際應用例子 . . .
主要受影響的 hash 演算法為 MD5 及 SHA1, 本文以 MD5 為例
弱點成因
- MD5, SHA1 使用同一種壓縮演算法(Merkle–Damgård construction)
利用情境
- 已知 md5(secret || message) 結果
- 未知 secret 的前提下,可以計算出 md5(secret || message || append)
原理解析
- MD5 會將訊息以 64 bytes 為一個 block 做分區,每個 block 做完 某種複雜的計算 後得到該回合(round) 的狀態值
- 使用 A, B, C, D 四個 register 儲存䅮一回合的狀態
- 以 0x80 表示 message 結尾,利用 0x00 padding 直到滿足 len(message) % 64 bytes = 56 bytes
- 第 57 個 byte 用來表示 message 長度,hex(len(message) * 8) # 以 bit 為單位
- 用 0x00 補滿直到 64 bytes
攻擊原理
- 其實所謂的狀態值就是該 block 的 hash 結果,最終的 md5 輸出亦同
- 每八個 digits 一組以 little endian 方式排列分別賦值給 A, B, C, D
- padding 到下一個 block
- 加上額外訊息
PoC
#include <stdio.h>
#include <openssl/md5.h>
#include <arpa/inet.h>
int main(void) {
MD5_CTX c;
unsigned char buffer[MD5_DIGEST_LENGTH];
int i;
MD5_Init(&c);
MD5_Update(&c, "secret", 6); // secret # 6
MD5_Update(&c, "haha", // message # 4
"\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // 14
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // 14
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // 14
"\x00\x00\x00\x00" // 4
"\x50\x00\x00\x00\x00\x00\x00\x00" // 8
"append", 64);
MD5_Final(buffer, &c);
for(i=0; i < 16; i++) printf("%02x", buffer[i]);
return 0;
}
#include <stdio.h>
#include <openssl/md5.h>
#include <arpa/inet.h>
int main(void) {
MD5_CTX c;
unsigned char buffer[MD5_DIGEST_LENGTH];
int i;
MD5_Init(&c);
MD5_Update(&c, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 64); // 64 個 A 之後會被覆蓋不重要
//e80f10c8 273e04a5 9b5edda6 cd3722da # md5(secrethaha)
c.A = htonl(0xe80f10c8);
c.B = htonl(0x273e04a5);
c.C = htonl(0x9b5edda6);
c.D = htonl(0xcd3722da);
MD5_Update(&c, "append", 6);
MD5_Final(buffer, &c);
for(i=0; i < 16; i++) printf("%02x", buffer[i]);
return 0;
}
使用 gcc -lssl -lcrypto
編譯以後,兩個程式的輸出都是 fae9e4890b4ba03881c2a009bccc8bc8
修補方式
- 將 secret 後置
- hash(message || hash)
- 使用 HMAC
- hash(secret || hash(secret || message))
現實中利用的例子