天天看點

突破核心子產品版本校驗機制作者:王智通為什麼要突破子產品驗證核心是怎麼實作的怎樣去突破總結參考附錄

1、 為什麼要突破子產品驗證

2、 核心是怎麼實作的

3、 怎樣去突破

4、 總結

5、 參考

6、 附錄

linux核心版本很多,更新很快,2個小核心版本中核心函數的定義可能都不一樣,為了確定不一緻的驅動程式導緻kernel oops, 開發者加入了子產品驗證機制。它在加載核心子產品的時候對子產品進行校驗, 如果子產品與主機的一些環境不一緻,就會加載不成功。 看下面一個例子,它簡單的輸出當期系統中的子產品清單:
我們在centos5.3環境中編譯一下:
然後拷貝到另一台主機centos5.1xen上:
用insmod加載:
報錯了,在看下dmesg的資訊:
先不管這是什麼, 總之我們的子產品在另一台2.6.18的主機中加載失敗。 通常的做法是要在主機中對源代碼進行編譯,然後才能加載成功, 但是如果主機中缺少核心編譯環境的話, 我們rootkit就不能編譯, 也不能安裝在主機之中,這是多麼尴尬的事情:)。 沒錯, 這就是linux kernel開發的特點, 你别指望像windows驅動一樣,編譯一個驅動, 然後可以滿世界去裝_. 一些rootkit開發者抛棄了lkm類型rk的開發, 轉而去打kmem, mem的注意,像sk,moodnt這樣的rk大家都喜歡, 可以在使用者層下動态patch核心,不需要編譯環境, wget下來,install即可。 但是它也有很多缺點,比如很不穩定,而且在2.6.x後核心已經取消了kmem這個裝置, mem檔案也做了映射和讀寫的限制。 rk開發者沒法繼續sk的神話了。反過來, 如果我們的lkm後門不需要編譯環境,也可以達到直接insmod的目的,這是件多麼美好的事情,而且lkm後門更加穩定,還不用像sk在核心中添加了很多自己的資料結構。
我們去看看核心在加載子產品的時候都幹了什麼, 或許我們可以發現點bug, 然後做點手腳,欺騙過去:) grep下dmesg裡的關鍵字, 看看它在哪個檔案中:
2.6.18/kernel/module.c: insmod調用了sys_init_module這個系統調用, 然後進入load_module這個主函數,它解析elf格式的ko檔案,然後加載到核心中:
check_modstruct_version就是用來計算子產品符号的一些crc值,不相同就會出現我們在dmesg裡看到的“disagrees about version of symbol”資訊。 get_modinfo取得了核心本身的vermagic值,然後用same_magic函數和核心的vermagic去比較,不同也會使核心加載失敗。 是以在這裡,我們看到核心對子產品驗證的時候采用了2層驗證的方法:子產品crc值和vermagic檢查。
繼續跟蹤check_modstruct_version, 現在的核心預設的都開啟了config_modversions, 如果沒有指定這個選項,函數為空,我們的目的是要在as, centos下安裝子產品,redhat不是吃幹飯的, 當然開了modversions選項。
find_symbol找到了struct_module這個符号的crc值,然後調用check_version去校驗:
它搜尋elf的versions小節, 循環周遊數組中的每個符号表,找到struct_module這個符号,然後去比較crc的值。現在有個疑問, versions小節是怎麼連結到子產品的elf檔案中去的呢? 在看下編譯後的生成檔案, 有一個list.mod.c
這個檔案是子產品在編譯的時候,調用了linux-2.6.18/scripts/modpost這個檔案生成的。裡面增加了2個小節.gnu.linkonce.this_module和versions。 versions小節的内容就是一些字元串和值組成的數組,check_version就是解析這個小節去做驗證。 這裡還有一個module_info宏用來生成子產品的magic字元串,這個在以後的vermagic中要做驗證。
先看下vermagic的格式:
這裡可以看到vermagic跟核心版本,smp,gcc版本,核心堆棧大小都有關。
same_magic忽略了對核心版本的判斷, 直接比較後面的值。
知道了核心是怎麼實作的了, 下面開始想辦法繞過這些驗證:)
在仔細看下代碼:
check_version在循環中隻是在尋找struct_module符号, 如果沒找到呢? 它會直接傳回1! 沒錯, 這是一個邏輯bug,在正常情況下,module必會有一個struct_module的符号, 這是modpost生成的。如果我們修改elf檔案,把struct_module這個符号改名,豈不是就可以繞過crc驗證了嗎? 先做個實驗看下:

.mod.c是由modpost這個工具生成的, 它在linux-2.6.18/scripts/makefile.modpost檔案中被調用, 去看下:

我們用一個很土的方法, 就是在編譯子產品的時候,modpost生成.mod.c檔案後, 暫停下編譯,sleep 30秒吧,我們用

這個時間去改寫下.mod.c, 把struct_module換個名字。

随便将struct_module改個名:

我們是在centos5.3下編譯的, 然後拷貝到centos5.1下, 在執行下insmod看下:
成功了! 這跟我們預期的一樣, 我們用這個邏輯bug繞過了子產品的crc驗證! 這個bug直到2.6.31版本中才得到修正。 我們可以用這種方法在redhat主機中任意安裝子產品了。 那麼怎樣繞過在2.6.31以後的核心呢? 看下它是怎麼修補的:
如果沒找到struct_module也會傳回0, 這樣我們就必須将struct_module的值改為正确後, 才能繼續安裝。如何找到子產品符号的crc值呢? 我們可以去找目标主機中那些已被系統加載的子產品的crc值,如ext3檔案系統的子產品, 自己寫個程式去解析elf檔案, 就可以得到某些符号的crc值了。 還有沒有更簡單的方法呢?去/boot目錄下看看,symvers-2.6.18-128.el5.gz貌似和crc有關,gunzip解壓後看看:

原來核心中所有符号的crc值都儲存在這個檔案中。如何改寫struct_module的值呢,可以用上面那個土方法, 或者自己寫程式去解析elf檔案, 然後改寫其值。本文最後附上一個小程式用來修改elf的符号和crc值。

如果我們用list.mod.c中的做法, 用module_info宏來生成一個與目标主機相同的vermagic呢? 答案是否定的,gcc連結的時候會把modinfo小節連結在最後,加載子產品的時候還是會讀取第一個.modinfo小節。我們可以用上面那種很土的方法, 先用modinfo指令得到目标主機中某個子產品的資訊:
然後在用那個很土的方面, 将.mod.c中vermagic值進行修改。還有一種直接修改elf檔案的方法,附錄在本文後面。
前面有一點沒有提到, 就是某些核心版本的相同接口的函數代碼可能已經變化, 這樣在使用這項技術的時候, 最好在同一個大核心版本使用。你也可能感覺要想跨平台安裝子產品有些麻煩, 這裡還有2個方法, 作為一個專業搞滲透的人來說,他會自己在本地裝很多發行版本的linux,特别是root掉一台主機後,會在本地裝一個一模一樣的發行版本,smp、kernel stack size、gcc version都一樣。在本地機器裝上開發環境,這樣編譯出來的子產品也是可以直接裝到目标主機上的,但這很麻煩,因為linux有太多的發行版本了:), 另一個方法就是自己裝一個linux,編譯下核心,然後将build後的開發包內建到自己的後門裡, 壓縮後大概幾m。 然後傳到主機去解壓,編譯。慶幸的是,現在大多數主機中都有核心開發環境, 直接去主機編譯就ok了。

1、 linux kernel source code

​http://www.kernel.org

2、 module injection in 2.6 kernel – coolq

​http://www.nsfocus.net/index.php?act=magazine&do=view&mid=2533