最近更新: 2015-12-21

連按 28 次退格鍵可跳過 grub 密碼驗證 CVE-2015-8370

今天看到 這篇資訊新聞 報導連按28次退格鍵就能跳過 Grub 的密碼驗證。我這一時間想到的事就是設計者在減一之前忘了檢查輸入字串長度是否已經為零了。

這個漏洞的 CVE 編號是 CVE-2015-8370 。我查看源碼的修正內容 ,還真是因為收到退格鍵時,忘了檢查字串長度是否已經為零。我第一次寫出帶有這種 bug 的程式碼,還是在 DOS 時代。真是令人懷念的老 bug 。

文字輸入行的設計方式,一般就是配置一個儲存已輸入字元的 buf 和一個記錄已輸入字元數量的 length。當讀到一個代表倒退鍵的 ‘\b’ 字元時,它的意思就是減去一個字元,也就是 length 減一。但別忘了當沒有任何字元時就不必減。

char buf[1024];
unsigned cur_len = 0; // it should never be negative number.

key = getch();

// BUG!
if (key == '\b') {
    cur_len--;
}
else if (key >= 0x20 && key <= 0x7f) {
    buf[cur_len++} = key;
}

// FIX!
if (key == '\b' && cur_len > 0) {
    cur_len--;
}
else if (key >= 0x20 && key <= 0x7e) {
    buf[cur_len++} = key;
}

grub2 源碼宣告 cur_len 之型態為無號整數。從純數學語言來看,無號整號為 0 時,就算減一也還是 0。所以一個無號整數本來就不可能為負數值。但這裡用的是十六進位計算機系統的 C 語言,不是純數學語言。 C 語言的運算符操作的是計算機系統的數組,所以 0 還是可以減一,變成 0xFFFF… 。當你拿這個值做為陣列索引值、也就是指標偏移量的時候,那就會碰到你不想碰到的記憶體內容了。

這個 bug 沒有在 cur_len 為 -1 時就立刻爆炸,才會讓設計人員輕忽此事。如果你對於為什麼要 cur_len 為 -28 時才會形成安全漏洞這事有興趣,可以閱讀「Back to 28」之分析。