程式漏洞利用(program exploitation)是駭客的基本功。

讓程式做它「被寫成要做」、而非「被設計要做」的事#

如同上一章所示,一支程式由一組複雜的規則構成,依循某種執行流程,最終告訴電腦該做什麼。利用一支程式,不過就是用巧妙的方式讓電腦做你想要它做的事——即使當前執行的程式原本就是為了阻止那個行為而設計的。

既然程式真的只能做它被設計要做的事,那麼安全漏洞其實就是程式設計、或程式執行環境中的瑕疵與疏忽。找出這些漏洞、並寫出能利用它們的程式,需要創造性的頭腦。

程式只會照字面執行它被寫成的樣子。不幸的是,寫出來的東西不總是與程式設計師的本意一致。一則笑話說明了這個原則:

一個男人在森林中散步,撿到一盞神燈。他一擦,精靈冒了出來,感謝他的釋放並答應實現三個願望。

「首先,我要十億美元。」——精靈一彈指,一只裝滿鈔票的公事包憑空出現。

「接著,我要一台法拉利。」——精靈一彈指,法拉利在一團煙霧中現身。

「最後,我要對女人有無法抗拒的吸引力(irresistible to women)。」——精靈一彈指,男人變成了一盒巧克力。

正如男人的最後一個願望是依他所說、而非他所想而實現的,程式也會精確依循它的指令,結果未必是程式設計師所期望的——有時後果甚至是災難性的。

兩類典型的疏忽#

Off-by-one/fencepost 錯誤,與 OpenSSH 的真實案例

最常見的錯誤之一是 off-by-one 錯誤:程式設計師少數或多數了一個。用一個問題說明:要建一道 100 英尺的圍籬、柱子間隔 10 英尺,需要幾根柱子?直覺答案是 10,但正確答案是 11。這類錯誤常被稱為 fencepost 錯誤(籬笆柱錯誤),發生在程式設計師誤把「物件」當成「物件之間的間隔」來數(反之亦然)。同理,處理第 N 到第 M 項時,項數是 M - N + 1 而非 M - N

一個經典案例是 OpenSSH。它的通道配置程式碼有個 off-by-one 錯誤,被大量利用。程式碼寫的是:

if (id < 0 || id > channels_alloc) {

而應該是:

if (id < 0 || id >= channels_alloc) {

這個簡單的錯誤讓一個正常認證登入的使用者能取得系統的完整管理權限——這當然不是設計者對 OpenSSH 這種安全程式的本意,但電腦只會照被告知的去做。

IIS 的 Unicode 反斜線疏忽,與 LaMacchia 漏洞

當程式為擴充功能而被快速修改時,也容易滋生可利用的錯誤。微軟的 IIS 網頁伺服器有路徑檢查程式碼,防止使用者用反斜線往上穿越目錄樹。但加入 Unicode 字元集支援後,複雜度增加:%5c 在 Unicode 中會被轉譯成反斜線,而這個轉譯發生在路徑檢查程式碼執行「之後」。於是用 %5c 取代 \ 就能穿越目錄。Sadmind 蠕蟲與 CodeRed 蠕蟲都利用了這種 Unicode 轉換疏忽來竄改網頁。

同樣的「照字面」原則也出現在電腦程式之外——LaMacchia 漏洞。1993 年底,21 歲的 MIT 學生兼駭客 David LaMacchia 架設了一個名為 Cynosure 的布告欄系統供人盜版軟體。聯邦大陪審團起訴他串謀違反電信詐欺法,但控罪被駁回——因為依當時的著作權法,他的行為不構成犯罪,因為侵權並非出於商業利益或個人金錢利益。立法者從未預料到有人會出於「非金錢動機」從事這類活動。(國會於 1997 年以《No Electronic Theft Act》堵上了這個漏洞。)

法官與法院可以被想成是「執行法律系統這支程式」的電腦。駭客的抽象概念超越了運算本身,可套用到生活中許多涉及複雜系統的面向。