0x00 整數安全
1、整數反轉
由於計算機中各整數類型都有一個寬度,有最大值-最大值範圍。當試圖保存超過該範圍的數據時,無符號數就會發生整數反轉,影響標誌寄存器CF位。
以上試驗,證明GO語言中存在整數反轉的問題,使得程序產生了非法數字。
2、整數溢出
由於計算機中各整數類型都有一個寬度,有最大值-最大值範圍。當試圖保存超過該範圍的數據時,有符號數就會發生整數溢出,影響標誌寄存器OF位。溢出本質上和反轉類似。通過如下代碼進行驗證:
輸入127和1,結果如下:
從結果來看有符號數最大值加1後產生溢出。
3、整數截斷
將一個較大整型轉換爲較小整型,並且該數的原值超出較小整型的表示範圍,就會發生截斷錯誤,原值的低位被保留而高位被丟棄。截斷錯誤會引起數據丟失,甚至可能引發安全問題。
Go語言是一種強類型的靜態語言,強類型意味着一旦定義了,它的類型就不能改變了;靜態意味着類型檢查在運行前就做了。所以如下的整數轉換編譯的時候將不通過:
報錯信息爲:
編譯的時候就會檢查兩邊類型是否一致,強制要求兩邊類型一致,保證寬度一致,避免產生了整數截斷的風險。
然而,Go語言提供了類型強制轉換的功能,來跳過該限制,使用後上述代碼將產生整數截斷的風險:
編譯通過,運行結果爲:
這是由於高位1被截斷,數據位全部爲0。
4、符號問題
有時從帶符號整型向無符號整型轉換時,最高位會喪失作爲符號位的功能,即產生符號丟失但數據不丟失的問題,從而數據失去原來的含義。尤其對於帶符號整型數的值爲負時,它向無符號整型轉換後,結果通常是一個非常大的正數;除非程序意圖如此,否則需要注意值含義轉換的正確性。
上節所述Go語言是一種強類型的靜態語言,編譯時就會檢查類型。所以有符號向無符號轉換時編譯是不通過的。使得Go語言更加安全。
然而,可通過強制類型轉換的功能,來跳過該限制。
除非程序本意就是如此,否則需要注意使用。
5、移位位數足夠
當對整數進行左移時,如果整數的類型位數不夠會丟失高位數據,產生不預期的數據。
Go對無類型的常數依照參與表達式運算數據類型進行推導。進行移位操作時,自動推導出的類型寬度有可能小於實際對移位結果進行(比較)運算所要求的寬度。例如:
運行結果:
本意1左移16位大小爲65536,應該比65535大。但是因爲1經過類型推導爲uint16,左移16位溢出了最高位,值變爲了0。
6、除數爲0
對整數做除法、取模、取餘數操作時,除數或者模爲0(下面統一稱除數爲0),將導致系統拋出異常。除非有默認的恢復機制,否則將導致無法繼續當前的操作或任務。可造成拒絕服務攻擊。
Go語言中檢查不出變量除數被0的情況,編譯能夠通過。然而運行時會使程序退出,可能造成DOS攻擊。
除數爲0的情況造成程序退出。
0x01 案例試驗
1、計數異常
以下爲一個借款的模擬程序:
由於無符號整數反轉,使得金額異常,銀行所示慘重。
修復建議:通過限制值的範圍防止反轉。代碼如下:
2、整數截斷繞過判斷
上面代碼第一處註釋代碼存在整數截斷風險,len函數返回int類型整數,而接收變量爲uint8。如果輸入的密碼參數長度假設爲260,則passwd_len值爲4,使得下面的長度判斷失效,進入非法的條件語句中。下述密碼長度爲260:
輸入字符串長度爲260bytes,程序雲心結果如下所示:
由於繞過判斷,使得進入非法的流程代碼中,造成訪問數組越界,Go運行時程序將異常退出。
如果使用unsafe包對指針進行運算,將導致緩衝區溢出,造成任意代碼執行。
上述代碼稍作修改,tempPwd數組使用tempPwdPTR指針訪問每個元素,將獲取密碼參數寫到本作用域的局部變量中,由於數據校驗的截斷,將發生緩衝區溢出,造成棧數據被非法改寫,甚至進一步會造成惡意代碼執行。代碼如下所示:
運行結果:
isOverWrite代碼中從來沒有對該數組進行寫操作,正是由於緩衝區溢出寫的漏洞,使程序退出前的數據被改變了。從上圖後續也可以看出程序棧數據確實被修改了,理論上改寫RIP可以實施代碼執行。
0x02 整數使用注意事項
- 確保整數運算時不會有溢出或反轉風險。代碼中的所有算數運算(尤其是:+、-、*、++、--、+=、-=、*=),查看是否對參與運算的變量有正確的範圍限定,確保沒有溢出風險;
- 確保整型轉換時不會出現截斷錯誤。避免對變量使用強制類型轉換語法,若必須使用則需要排查是否存在數據截斷隱患;
- 確保整型轉換時不會出現符號錯誤。對於代碼中變量可能爲負數的情況需要排查是否存在違背程序意圖的符號錯誤隱患;
- 確保參與移位的操作數的位數足夠。產生移位的地方,必須小心檢視確保移動位數的對象寬度足夠。建議對移位的常量使用強制類型轉換語法到寬度足夠的類型;
- 確保整數運算不會出現除零。查找程序中所有出現除法的語句,檢查除數是否有爲0的分支判斷或者異常處理,檢查除數來源是否有爲0的風險。
0x03 排查方法
- 在數學運算前是否對參與運算的整數進行範圍判斷。
- 賦值語句處,並且有強制類型轉換需要注意是否從大類型轉換到小類型、或者是否存在符號因素會使值產生壞值。
- 查找移位運算符,並且移位的數據爲常量。則通過表達式分析,檢查常量實際被推導出的類型大小是否滿足移位的要求。
- 查找全文的除法運算髮生的地方,檢查除數爲0的情況是否有判斷,或者除數來源是否有爲0的可能