[心得] 基於記憶體溢位存取進行程式碼注入攻擊
經驗分享──基於記憶體溢位存取進行程式碼注入攻擊
序、
自己於慕尼黑工業大學電機學院(TUM EI)中修習Embedded System and Security課程,其
中作業與組合語言、嵌入式系統及資料安全相關,第一次學習這種系統安全的內容覺得相
當有趣,但搞這個作業時也覺得相當挫敗,正好放假很閒,分享予各位。
平台為Infineon xmc4500 relax kit 開發板,arm cortex-m4
作業題目為在開發板上、於給定的程式中進行code injection attack,點亮開發板上的L
ED作為成功注入程式碼的證據。題目給定之程式為一加密功能的程式,要求便是攻擊此目
標程式。透過於電腦上執行以python傳輸明碼(plaintext)至嵌入式平台,目標程式加密
此plaintext,並將密文回傳至電腦上,流程圖如下,【攻擊方式便是透過特殊的plainte
xt輸入來執行攻擊】。作業內容算是偏簡單,給無經驗的駭客初學者練習個幾天多半就能
完成。
https://i.imgur.com/vZ7Q2kF.jpg
Figure 0. flow chart
基本思路為透過stack overflow存取到return address (link register位置所儲存之資
料),修改return address至injected code的位置,在pop pc指令時使pc (program coun
ter)跳轉到injected code,完成code injection後再跳回正常程式的下個執行點。
自己在家要重現此事較為困難,由於需要有相似/相同之開發板XMC4500作為執行平台,並
且由於原始碼著作權屬於課程助教,在此我不會提供完整題目程式碼,僅提供片段作為學
習用途,但我想可能得有些先備知識才較能看懂我在寫什麼,出於懶貓貓問題我沒辦法把
每件事情寫到很詳細,措辭可能會有不對的地方,例如lr其實不是個記憶體,是個regist
er,但我會混用把儲存lr的記憶體位置稱作lr,不過應該差不多啦,自己搞過一遍應該能
想通我在供三小。
各種內容都會中英混雜,搭配奇怪的縮寫,修embedded system時最恨的就是縮寫,教授
跟助教永遠都用縮寫但我永遠都不知道是什麼意思,簡單列幾個大概會用到的
pc, program counter
sp, stack pointer
lr, link register
fp, frame pointer
KEY WORDS: code injection attack, stack-based buffer overflow, embedded system
security
一、 基礎知識,Instruction memory與data memory
在記憶體儲存中的內容分為幾種,我這次用上的是instruction區和stack,分別對應儲存
CPU指令內容(機器碼)跟local variable,其他還有像是heap儲存dynamic allocated v
ariable、儲存initialized global variable的空間、儲存uninitialized global varia
ble的空間,不過這些不重要。這些空間通常有固定的起始、終點記憶體位置,可以透過a
rm給的datasheet查到。在我這次使用的平台,arm cortex-m4,Instruction memory始於
0x0800 0000,stack始於0x1000 0000,所以像是我的嵌入式系統在運作時,就會執行Ins
truction memory中的Machine Code (又或是可以視為反組譯後的Assembly code),funct
ion中使用的local variable就儲存在stack中。
在執行function call的時候,CPU會做幾件事情,0. Branch到function,pc跳轉到此fun
ction的instruction memory address,lr記錄caller的下個instruction memory addres
s;1. 記錄lr,即下個instruction的address,作為在function return時pc要回到的位
置,此數值push到stack;2. 記錄其他雜七雜八的register,一樣push到stack;3. 最後
會把sp減去一定數值,把這些空出來的位置存local variable。這部分內容看看下方asse
mbly
code應該可以理解。
在執行function return時,CPU會做幾件事情,0. 把sp加回一定數值,因為local varia
ble在function return後就沒用了;1. pop 一些register;2. pop pc,把過去lr記錄的
數值弄回pc,這樣pc就會回到caller的下個instruction memory。一樣看看assembly cod
e應該可以理解。
https://i.imgur.com/BCv8mUJ.jpg
Figure 1. instruction memory
https://i.imgur.com/3kTHXgx.jpg
Figure 2. stack (local variable)
二、 基礎知識,buffer overflow與code injection attack
由於local variable和register、return address (lr)都儲存在stack中,如果在local
variable中有靜態記憶體配置陣列,且user可以隨意賦予值而沒有長度確認,則可以輕鬆
做到stack-based buffer overflow,即為漏洞,vulnerability,常見於memcpy, strcpy
。
code injection attack就是透過把自己想要執行的程式碼,存到stack中,並將PC設定到
這個地址來執行它。
透過寫入超過給定array長度的資料,做到stack-based buffer overflow,修改到return
address的地址數值,就可以做到最簡單的code injection attack。
三、 本次題目的vulnerability
找漏洞算是很麻煩的地方,在這次作業中很貼心的附上了原始碼與debugger,但還是要些
經驗才容易找到,至少一開始我花了整天在另一個function上。本題的加密其實跟要做co
de injection沒什麼關係,畢竟也不是要找ciphertext與plaintext的mapping,如下圖,
加密只是XOR一個固定的數值,透過先加密個一串零就可以知道key是多少。
https://i.imgur.com/oME10vw.jpg
Figure 3. Vulnerability
上圖程式碼中,buf作為local variable且為靜態記憶體配置之陣列,會儲存128 bytes於
stack中,由於沒有對長度作確認,當plaintext長度大於128 bytes時即發生stack overf
low。透過debugger可以知道此frame中的各種情報,包含buf記憶體位置始於多少、lr位
置為何,兩個一減就知道要寫入多長的資料到buf可以overflow到lr的位置。
https://i.imgur.com/e6lEXps.jpg
Figure 4. debugger, info frame
由上圖debugger資訊中,info frame 給了lr資料儲存於記憶體位置0x1000 07c4,print
&buf給了buf資料始於0x1000 0738,寫入0x1000 07c4 - 0x1000 0738 + 4 bytes的資料
至buf即可將最後的4bytes寫入到lr資料位置。
【只要將地址0x1000 07c4中的值,改寫成0x1000 0739】(某些原因要多1),function re
turn後就會執行自buf開始的指令。意即,把自己要執行的機器碼塞到buf中,並且把lr改
寫,讓return時pc跳轉到自buf開始的instruction memory address。
四、 Code injection attack攻擊實作
好,所以現在來回到一開始講的攻擊方式,透過輸入特殊的plaintext來攻擊,已知這段p
laintext有0x1000 07c4 - 0x1000 0738 + 4 bytes,最後4bytes要為0x1000 0739,且要
使buf前面一段作為injected code。
Injected code的生成,首先得搞出機器碼,這裡拿點亮板子上的LED為例,不過基本上就
是你想幹嘛就幹嘛。要搞出機器碼也容易,寫個點亮LED的C code→編譯→反組譯出assem
bly code打開就會看到assembly code和machine code排排站站好了,取自己要的部分。
前述提及XOR加密部分,所以弄出來的machine code要先XOR一遍key,在function執行時
會再XOR key一遍,才會回到正確的machine code。此外,需要逐byte作一些順序上的交
換,畢竟到底記憶體要往上/下讀、讀完後要往上/下跑,這件事情很繁雜,不過拿debugg
er去查memory中存的值很好debug,就嘗試看看慢慢調整就好。
把plaintext寫到這裡應該可以點亮LED了,不過有個問題是程式接下來不會正常運行,畢
竟把pc設到0x10開頭的地方本身就很有問題,正常的machine code存放在0x08開頭的地方
,所以要再做一些加工,不過這裡就可能得寫assembly code了。
要讓程式回歸正常運行的辦法就是得把register中的值(包含pc/lr,其實它們也是個regi
ster)回復到正常的情況,當初為了修改到lr,一併修改了各個register中的值。因此在
點亮LED的machine code後面加入一些對register賦值的machine code,最後把pc設回正
常該回到的地方,就可以使程式繼續正常運行而不會發生錯誤。這邊由於得針對register
作處理,我自己寫了幾行assembly code來編譯出machine code,這也是這次作業中自己
要寫assembly code的地方了,其他就只是讀,然後寫C code。
五、 後話
基本上這份作業已經簡化相當多東西了,首先是提供source code、debugger,這是對駭
客正常來說不會取得的東西(除非哪家廠商把自家產品firmware開源?),此外題目也經
設計,多花點時間觀察、繞過各種陷阱就可以完成。
在軟體上,可以使用canary保護來避免overflow,即透過將locol variable整個用struct
包起來,並在struct中最上方、最下方各擺一個canary變數,在一進function時賦同一值
予此二canary變數,需隨機並且無法預判,如果overflow發生,則由於struct關係在改寫
到register存放位置前一定會先改寫到其中一個canary的值,故在function結束前比較兩
canary值是否仍相等,如不相等則overflow發生,報錯跳exception即可避免受攻擊。
在嵌入式系統中,也存在硬體保護,常見的有MPU (Memory Protect Unit),可以對記憶
體區塊設置rwx權限(read, write, execute),所以例如說把stack設為rw-,把機器碼那
塊設為r-x,當有人把對stack做code injection想要來執行時,pc就會跳到exception,
停止執行,亦或是假如哪個人有超神方法可以改寫原始的machine code也會報錯,不過這
就是作業第二題的內容了。相對應的攻擊方式變為code reuse attack (return-oriented
attack),亦即不注入自己程式碼,而是透過不斷在原始machine code中return來、retu
rn去來達到自己要的目的,這個讓pc跳來跳去的過程奇複雜無比,要找到能用的code片段
(稱為gadget, 小工具)就是難事,還得考慮sp的位置、register的值等等才能讓程式繼續
正常運作而不報錯。
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 101.9.160.202 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/ASM/M.1584196474.A.217.html
※ 編輯: roger5486 (101.9.160.202 臺灣), 03/14/2020 23:11:35
推
03/15 04:04,
4年前
, 1F
03/15 04:04, 1F
→
03/15 04:04,
4年前
, 2F
03/15 04:04, 2F
修改內文加入gadget了,不過覺得近期自己不會再碰相關內容了......,讀assembly cod
e
跟trace stack真的太辛苦了
※ 編輯: roger5486 (101.9.160.202 臺灣), 03/15/2020 08:26:25
推
03/15 14:30,
4年前
, 3F
03/15 14:30, 3F
→
03/15 14:31,
4年前
, 4F
03/15 14:31, 4F
沒錯,已修正~
推
03/15 15:08,
4年前
, 5F
03/15 15:08, 5F
※ 編輯: roger5486 (101.9.160.202 臺灣), 03/15/2020 20:40:19
※ 編輯: roger5486 (101.9.160.202 臺灣), 03/15/2020 20:43:31
推
03/18 18:01,
4年前
, 6F
03/18 18:01, 6F
推
04/28 01:55,
4年前
, 7F
04/28 01:55, 7F
推
05/14 00:07,
4年前
, 8F
05/14 00:07, 8F
ASM 近期熱門文章
PTT數位生活區 即時熱門文章