Linuxでgdbでカーネルrootkitを検出


攻撃ベクトルの理解
カーネルrookitは通常、システム呼び出しを攻撃ターゲットとし、主に2つの理由で使用されます.
a.カーネル状態のハイジャックシステムの呼び出しは小さい代価でシステム全体を制御することができ、あまり多くのものを必修しない.
b.アプリケーション層の多くの関数は1つ以上のシステム呼び出しの異なる形式のパッケージであり、システム呼び出しを変更することは、その上位のすべての関数がだまされることを意味する.
kernel−2.4.27には約230以上のシステム呼び出しがあり、kernel−2.6.9には約290以上のシステム呼び出しがあり、システム呼び出しの個数はカーネルバージョンに依存する.完全なシステム呼び出しリストは/usr/include/asm/unistd.hヘッダファイルで取得します.
また、侵入者は、すべてのシステム呼び出しを変更するのではなく、いくつかを置き換えるのに役立つことに注意してください.これらのシステム呼び出しは、表1に示すように、システム管理者および侵入検出システム(OS kernelレベルIDS)によって監視され、manコマンドで各システム呼び出しの完全な説明を得ることができる.
 

       System call name Short description ID 
--------------------------------------------------------------------------------------- 
sys_read used for reading from files 3 
sys_write used for writing to files 4 
sys_open used to create or open files 5 
sys_getdents/sys_getdents64 used to list a content of directories(also /proc) 141/220 
sys_socketcall used for managing sockets 102 
sys_query_module used for querying loaded modules 167 
sys_setuid/sys_getuid used for managing UIDs 23/24 
sys_execve used for executing binary files 11 
sys_chdir used to change the directory 12 
sys_fork/sys_clone used to create a child process 2/120 
sys_ioctl used to control devices 54 
sys_kill used to send signal to processes 37

上記の表のシステム呼び出し番号に注意します.これらのIDはkernel-2.4.18-3向けです.
本明細書のすべての例はRedhat 7にある.3 kernel-2.4.18-3でテストに合格し、他のバージョンで最新の2.6を含めることもできます.xでは、2.6のいくつかの内部構造、例えば、システム呼び出しテーブルのアドレスがシステム呼び出し処理ルーチンsystem_に含まれていたことなど、類似のステップで検討される.callでsyscallに変更しましたコール関数です.
システム呼び出しテーブルの変更
現在のシステム呼び出しアドレスはシステム呼び出しテーブルに保存され、オペレーティングシステムがカーネルのために保持するメモリ空間(仮想アドレスが最大1 GB)に位置し、システム呼び出しエントリアドレスの格納順序は/usr/include/asm/unistdと同じである.hにおける配列順序は、システム呼び出し番号でインクリメントされる.
0 x 80ソフトブレークが発生する前に、対応するシステム呼び出し番号がeaxレジスタ、例えばsys_writeが呼び出されると、対応するシステム呼び出しID:4がeaxに押し込まれます.
侵入者が使用する最初の方法は、システム呼び出しテーブルのシステム呼び出しアドレスを変更することです.これにより、システム呼び出しが発生すると、攻撃者が自分で作成した関数にジャンプして実行されます.システム呼び出しテーブルのシステム呼び出しエントリアドレスを観察することにより,gdbを用いてこの攻撃挙動を比較的容易に検出できた.
元のシステム呼び出しアドレスはカーネルコンパイルフェーズで指定され、変更されません.元のシステム呼び出しアドレスと現在のカーネル状態のシステム呼び出しアドレスを比較することで、システム呼び出しが変更されたかどうかを確認できます.元のシステム呼び出しアドレスは、コンパイル時に2つのファイルに書き込まれます.
a.System.mapこのファイルにはすべてのシンボルアドレスが含まれており、システム呼び出しも含まれています.
b.システム初期化時にまずメモリに読み込むカーネルイメージファイルvmlinux-2.4.x;
vmlinux-2.4.xファイルは通常圧縮形式で/bootディレクトリの下に格納されるので、比較する前にこのファイルを解凍しなければならない.もう一つの問題は、systemを仮定することである.mapおよびvmlinuzイメージは侵入者によって変更されていないため、より安全な方法は、システムがきれいなときにこの2つのファイルの信頼できるコピーを作成し、ファイルのmd 5 hashを作成することです.
原文にはカーネルモジュール[gcc-c scprint.c-I/usr/src/`uname-r`/include/]も列挙されており、このモジュールを使用してシステム呼び出しアドレスを印刷し、syslogに自動的に書き込むことで、リアルタイムの比較が可能である.
ほとんどのカーネルバックドアがマウントされている場合、カーネルはシステムの初期化後に変更され、rootkitがロードされたmoduleまたは直接読み書き/dev/kmemにインプラントされたon-the-fly kernel patchの後に変更されます.通常rootkitはvmlinuzとsystemを変更しない.mapの2つのファイルなので、この2つのファイルのシンボルアドレスを印刷すると、システムの元のシステム呼び出しアドレスがわかります.システムの現在の実行中のシステム呼び出しアドレス(変更される可能性があります)は/procの下のkcoreファイルと比較して得ることができ、両者を比較すると結果がわかります.
1.まず、システム呼び出しテーブルのアドレスを見つけます.
 

       [root@rh8 boot]# cat System.map-2.4.18-13 | grep sys_call_table c0302c30 D sys_call_table

2.nmコマンドを使用して、stripされていないimageファイルのすべてのシンボルアドレスを印刷できます.
 

       [root@rh8 boot]# nm vmlinux-2.4.18-13 | grep sys_call_table 
c0302c30 D sys_call_table

gdbを用いる、カーネルソースコードのentryに対応するすべてのシステム呼び出しエントリアドレスを印刷することができる.Sファイルで定義されます.たとえば、次のようになります.
 

       entry 0 (0xc01261a0) sys_ni_syscall     
entry 1 (0xc011e1d0) sys_exit     
entry 2 (0xc01078a0) sys_fork     

#gdb /boot/vmlinux-2.4.* 
(gdb) x/255 0xc0302c30 
0xc0302c30 <sys_call_table>: 0xc01261a0 0xc011e1d0 0xc01078a0 0xc013fb70 
0xc0302c40 <sys_call_table+16>: 0xc013fcb0 0xc013f0e0 0xc013f230 0xc011e5b0 
0xc0302c50 <sys_call_table+32>: 0xc013f180 0xc014cb10 0xc014c670 0xc0107940 
0xc0302c60 <sys_call_table+48>: 0xc013e620 0xc011f020 0xc014bcd0 0xc013e9a0 
...

システム呼び出し名を使用して、システム呼び出しのアドレスを印刷することもできます.
 

       (gdb) x/x sys_ni_syscall 
0xc01261a0 <sys_ni_syscall>: 0xffffdab8 
((gdb) x/x sys_fork 
0xc01078a0 <sys_fork>: 0x8b10ec83

現在の実行システムのシステム呼び出しアドレスを印刷するには、gdbに2つのパラメータを追加する必要があります.
a.最初のパラメータはカーネルイメージファイルvmliux-2.4である.x
b.2番目のパラメータは/proc/kcoreバイナリファイル
 

       #gdb /boot/vmlinux-2.4.* /proc/kcore 
(gdb) x/255x 0xc0302c30 
0xc0302c30 <sys_call_table>: 0xc01261a0 0xc011e1d0 0xc01078a0 0xc88ab11a <<-- 
0xc0302c40 <sys_call_table+16>: 0xc013fcb0 0xc013f0e0 0xc013f230 0xc011e5b0 
0xc0302c50 <sys_call_table+32>: 0xc013f180 0xc014cb10 0xc014c670 0xc0107940 
0xc0302c60 <sys_call_table+48>: 0xc013e620 0xc011f020 0xc014bcd0 0xc013e9a0 
...

最初の行の最後の0 xc 88 ab 11 aというアドレスは明らかに正常ではないことに気づいた.これはシステム呼び出し番号3のシステム呼び出し、すなわちsys_である.read(システム呼び出しは0から始まる).
正常でないという顕著な標識は、0 xc 8 xxxxxより高いアドレスであり、Linuxのデフォルトの4 GBの線形アドレスであり、そのうち最高1 GBの0 x 00000000-0 xffffffffはカーネル保持であり、モジュールがカーネルに挿入されると、vmalloc関数はアドレス空間を割り当て、このアドレスは通常0 xc 8800000から始まる...ここまで来たのはもう明らかでしょう?
システム呼び出しハイジャック
ハイジャック・システム・コールは、前の方法とは異なり、システム・コール・テーブルのエントリ・アドレス、すなわち、各システム・コールへのジャンプ・ポインタを直接変更するのではなく、hookを希望するシステム・コールの前にジャンプ・コードを追加し、実行フローを侵入者自身のカーネル状態関数にリダイレクトさせることであり、これらのhookのシステム・コールの前部には通常call、jmpなどのアセンブリ命令がある.
この攻撃を検出するには、gdbとvmlinux-2.4.*を使用します.および/proc/kcoreの2つのパラメータを逆アセンブリシステムで呼び出します.
 

       #gdb /boot/vmlinux-2.4.* /proc/kcore 
(gdb) disass sys_read 
Dump of assembler code for function sys_read: 
0xc013fb70 <sys_read>: mov $0xc88ab0a6,%ecx 
0xc013fb73 <sys_read+3>: jmp *%ecx <<-- 
0xc013fb77 <sys_read+7>: mov %esi,0x1c(%esp,1) 
0xc013fb7b <sys_read+11>: mov %edi,0x20(%esp,1) 
0xc013fb7f <sys_read+15>: mov $0xfffffff7,%edi 
...

「mov$0 xc 88 ab 0 a 6,%ecx--jmp*%ecx」という2つの命令に注意して、彼は他の場所にジャンプして実行しました.
次に、hookによって呼び出される前のシステム呼び出しコマンドを見てみましょう.
 

       #gdb /boot/vmlinx-2.4.* 
(gdb) disass sys_read 
Dump of assembler code for function sys_read: 
0xc013fb70 <sys_read>: sub $0x28,%esp 
0xc013fb73 <sys_read+3>: mov 0x2c(%esp,1),%eax 
0xc013fb77 <sys_read+7>: mov %esi,0x1c(%esp,1) 
0xc013fb7b <sys_read+11>: mov %edi,0x20(%esp,1) 
0xc013fb7f <sys_read+15>: mov $0xfffffff7,%edi 
...

見たでしょう、違います.
システム呼び出し処理ルーチンの変更
侵入者は、システム呼び出し処理ルーチンsystem_などの重要なカーネル関数を変更する可能性があります.call関数は、名前の通り、ユーザが要求したシステム呼び出しに応答し、システム呼び出しテーブルに対応するエントリアドレスを探し、そこにジャンプして実行され、システム呼び出しテーブルのアドレスが保存されます.攻撃者は何ができるのだろうか.別のメモリ領域を開き、そこで攻撃者が自分のシステム呼び出しテーブルを偽造し、systemを変更します.call関数のシステム呼び出しテーブルアドレスはそこを指すとよい.
逆アセンブリシステムでcall関数は、システム呼び出しテーブルのアドレスを特定します.
 

       (gdb) disass system_call 
Dump of assembler code for function system_call: 
0xc01090dc <system_call>: push %eax 
0xc01090dd <system_call+1>: cld 
0xc01090de <system_call+2>: push %es 
0xc01090df <system_call+3>: push %ds 
0xc01090e0 <system_call+4>: push %eax 
0xc01090e1 <system_call+5>: push %ebp 
0xc01090e2 <system_call+6>: push %edi 
0xc01090e3 <system_call+7>: push %esi 
0xc01090e4 <system_call+8>: push %edx 
0xc01090e5 <system_call+9>: push %ecx 
0xc01090e6 <system_call+10>: push %ebx 
0xc01090e7 <system_call+11>: mov $0x18,%edx 
0xc01090ec <system_call+16>: mov %edx,%ds 
0xc01090ee <system_call+18>: mov %edx,%es 
0xc01090f0 <system_call+20>: mov $0xffffe000,%ebx 
0xc01090f5 <system_call+25>: and %esp,%ebx 
0xc01090f7 <system_call+27>: testb $0x2,0x18(%ebx) 
0xc01090fb <system_call+31>: jne 0xc010915c <tracesys> 
0xc01090fd <system_call+33>: cmp $0x100,%eax 
0xc0109102 <system_call+38>: jae 0xc0109189 <badsys> 
0xc0109108 <system_call+44>: call *0xc0302c30 (,%eax,4) <<--        
0xc010910f <system_call+51>: mov %eax,0x18(%esp,1) 
0xc0109113 <system_call+55>: nop 
End of assembler dump.

注:上記の出力には、通常のシステム呼び出しテーブルアドレスが表示されます.
ユーティリティ
1つの方法は、ホストベースの侵入検出システムHIDSを使用して重要なカーネル構造をリアルタイムで監視することであり、例えばSamhainツールを使用して、システム呼び出しテーブル、IDTなどを監視することができ、「Host Integraty Monitoring:Best Practices for Deployment」に関連する説明がある.
訳者注
本明細書で述べる方法はkstat 2にある.4版にはコードの実装があり、kstat/2.4/src/syscalを参照することができる.c、gdbを使用するのは手作業で検出する方法で、それが解決できる問題はシステムが変更されたかどうかを検出することです.カーネルrootkitを見つけるにはmadsysのphrack 60上のmodule_などのツールが必要です.hunter.c、2.4と2.6のバージョンがあり、grip 2、coolqはそれをいくつか修正し、このコードは絶えず完備しています.
 
責任編集趙毅zhaoyi#51 cto.com TEL:(010)68476636-8001