今回Linuxのハッキング事件のレポートを書かせて頂きます。
内容的には「Linux OS x86」、「ELFバイナリリバーシング」と「シェルコード」の絡みとなります。
この記事を読むだけでもOKですし、もし再現したい場合ASM、gccとLinuxリバーシングのノウハウが必要だと思います。環境的にLinuxのシェルですので解析が全てradare2でやりました。
取り合えず簡単に書きますので、リラックスしながら楽しみに読んで下さい。
■ハッキングされた情報
とあるLinuxマシンに怪しいプロセスが発見されました↓
28641 ? S 0:00 [kworker/2:0] 30514 ? S 0:00 [kworker/1:0] 30518 pts/1 S+ 0:00 [sh] 31544 ? S 0:00 [kworker/3:2] 31670 pts/1 S 0:00 netstat -tc <======ココプロセスツリーで確認したら↓
systemd-+-acpid |-agetty |-cron |-dbus-daemon |-irqbalance |-rsyslogd-+-{in:imklog} | |-{in:imuxsock} | `-{rs:main Q:Reg} |-sshd-+-sshd---bash ←私はここに居ますよw | `-sshd---sshd---bash---sh---netstat <===========ココ |-systemd---(sd-pam)「netstat -tc」のコマンドを使ったこと無いって聞いたいたので。
「netstat」の親プロセスを探すと「sh」コマンド、それと「sshd」がありますね。
と言う事で親プロセスはここで↓
28641 ? S 0:00 [kworker/2:0] 30514 ? S 0:00 [kworker/1:0] 30518 pts/1 S+ 0:00 [sh] <==========親プロセス 31544 ? S 0:00 [kworker/3:2] 31670 pts/1 S 0:00 netstat -tc <========子プロセス不思議な「sshd」プロセスが見つかりませんでした。
探すと、historyで下記のコマンドが実行されたそうで分かりました↓
2097 2017-01-XX 14:30:54 rm -rf sshdhddはext4のフォーマットなので、extundelete /○○ --restore-allで偽sshdを取り戻しました↓
-rwxr-xr-x 1 xxx xxx 3336 Jan XX 13:48 sshd完全に怪しいと思って、「sshd」ファイルの形をチェックしました↓
sshd: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, xxx, strippedダイナミックタイプですね、次、リンクされたライブラリをチェック↓
$ ldd sshd linux-gate.so.1 (0xb7712000) libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb7556000) /lib/ld-linux.so.2 (0xb7715000)やはり偽者です。
もっと見たら(readelfで)ハッキングされたマシンでコンパイルさてら物と分かりました↓
ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x8048357 Start of program headers: 52 (bytes into file) Start of section headers: 2216 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 8 Size of section headers: 40 (bytes) Number of section headers: 28 Section header string table index: 27 : Symbol table '.dynsym' contains 6 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ 2: 00000000 0 FUNC GLOBAL DEFAULT UND mmap@GLIBC_2.0 (2) 3: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (2) 4: 00000000 0 FUNC GLOBAL DEFAULT UND munmap@GLIBC_2.0 (2) 5: 08048518 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_usedここ迄見たら、どうやてコンパイルしたのか分からないので、ここからsshdのバイナリを調査しす。
因みに残したネットワークソケットの情報があるので、ハッカーの元IPが分かりました↓
180.97.220.28:8080 BGP情報↓ 23650 | 180.97.220.0/24 | CHINANET-JS-AS | CN | chinatelecom.com.cn | ChinaNet Jiangsu Province Networkプロキシっぽいですね。。
■バイナリーのリバーシング
最初に偽sshdのストリングでチェックし、面白い物を見つけた↓
[^_] [^_] ;*2$"( RRRT[S_ /bin <==== (ノ゚ο゚)ノ オオオオォォォォォォ... //sh@u <====== ..ノオオオォ…(ノ゚ο゚)ノミ(ノ _ _)ノコケッ!! .shstrtab .interp「bin」と「sh」の単語を見たら気分が悪くなりますね..
sshdバイナリのDATAセクションを見たら「bin」と「sh」の単語が近い所にあります....
さくっとradare2をインストールし、asmにリバースしました↓
0x0804974c 31f6 xor esi, esi <==== DATA XREF 0x08048343 0x0804974d f6f7 div bh 0x0804974f ~ e652 out 0x52, al 0x08049750 .string "RRRT[S_" ; len=8 0x08049758 .string "\\a/bin" ; len=6 0x0804975e 47 inc edi 0x0804975f ~ 042f add al, 0x2f 0x08049760 .string "//sh@u" ; len=7 0x08049767 b03b mov al, 0x3b 0x08049769 0f05 syscall <===== (ノ゚ω゚)ノ*...ウオオォォォォォォォー!!こんど「syscall」の単語が出て来ました(--;;)
0x0804974cが0x08048343からxrefされたので、0x08048343を見ようと↓
0x08048330 8d4c2404 lea ecx, [esp + local_4h_2] 0x08048332 2404 and al, 4 0x08048334 83e4f0 and esp, 0xfffffff0 0x08048337 ff71fc push dword [ecx - 4] 0x0804833a 55 push ebp 0x0804833b 89e5 mov ebp, esp 0x0804833d 51 push ecx 0x0804833e 83ec0c sub esp, 0xc 0x08048341 6a25 push 0x25 0x08048343 684c970408 push 0x804974c <====(1) => "1...RRRT[S_../bin.G.//sh@u..;..1....." @ 0x804974c 0x08048348 e8fe000000 call fcn.0804844b <======(2) 0x0804834d 8b4dfc mov ecx, dword [ebp - local_4h] 0x08048350 31c0 xor eax, eax 0x08048352 c9 leave 0x08048353 8d61fc lea esp, [ecx - 4] 0x08048356 c3 ret恐らく0x08048330はmain()の関数ですね。コードに書いた(1)と(2)の説明は下記となります↓
(1)はxrefの元のアドレスで、そこに0x0804974cからのデータをスタック(stack)にプッシュされた。
(2)その後fcn.0804844bの関数をコールされます。
fcn.0804844bはこの辺にありますね↓
↑関数の元名前がstripされたそうですね。
fcn.0804844bをradareで分析をすると↓
0x0804844b 55 push ebp 0x0804844c 89e5 mov ebp, esp 0x0804844e 57 push edi 0x0804844f 56 push esi 0x08048450 53 push ebx 0x08048451 83ec24 sub esp, 0x24 0x08048454 8b5d0c mov ebx, dword [ebp + arg_ch] 0x08048457 8b7508 mov esi, dword [ebp + arg_8h] 0x0804845a 6a00 push 0 0x0804845c 6aff push -1 0x0804845e 6a22 push 0x22 0x08048460 6a07 push 7 0x08048462 53 push ebx 0x08048463 6a00 push 0 0x08048465 e896feffff call sym.imp.mmap <=====mmap() 0x0804846a 89d9 mov ecx, ebx 0x0804846c 89c7 mov edi, eax 0x0804846e 8945e4 mov dword [ebp - local_1ch], eax 0x08048471 f3a4 rep movsb byte es:[edi], byte ptr [esi] 0x08048473 83c420 add esp, 0x20 0x08048476 ffd0 call eax 0x08048478 8b45e4 mov eax, dword [ebp - local_1ch] //memcpyなはずですが.... 0x0804847b 895d0c mov dword [ebp + arg_ch], ebx 0x0804847e 894508 mov dword [ebp + arg_8h], eax 0x08048481 8d65f4 lea esp, [ebp - local_ch] 0x08048484 5b pop ebx 0x08048485 5e pop esi 0x08048486 5f pop edi 0x08048487 5d pop ebp 0x08048488 e993feffff jmp sym.imp.munmap <===munmap()↑このASMの意味はこんな感じです↓
サイズ0x24とPROT_EXEC+PROT_WRITE+PROT_READフラグのmmap(バーチャルメモリのマッングlinux syscall機能)がコールされ、そしてメモリ(stack)上でにデータを書き込んで(およそmemcpyの動きで)、PROT_EXECのフラグなので書き込み終わったらそのままで実行されるように見えます。
この偽sshdがどうやってコンパイルしたか分からないけど、上記のASMが複雑し過ぎて、恐らくコンパイルの時にstripだけじゃなくて、関数とデータが別れるように設定されたそうなので「memcpy」があるはずだが見えない状況です。
その分析ASMデータをもっとシンプルで書くと(少しパッチして、radare2のESILでアナライズをすると)memcpyが見えるようにしました↓
分かり易く上記のASMをCコードに書きましょう!これはデコードに基本ですね、結果は大体こんな感じです↓
void fnc.0804844b(char *_BLOB_DATA, int __size) { void *JunkToExec; // argument passed -> int __size=0x24; JunkToExec=mmap(0,__size,PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); memcpy (JunkToExec, *_BLOB_DATA, __size); munmap (JunkToExec, __size); }よーし!ここ迄0x0804844b関数の意味が分かったでしょ。実行のローダー機能ですね、英語だと「executable loader」かな? こんな感じのC言語で書いたシェルコードに多いですね。
所で、どうやってfnc.0804844bが起動されでしょ?
ここで上記に書いたmain関数(0x08048330)の流れに繋がりますが、
C言語でmain()関数を書けば大体はこんな感じですね↓
#define __ExecData_SIZE 0x24 // global variable is needed for the "size".. char __ExecData[] = {"fugahoge.."}; // 実行の為のデータはこんな感じ.. int main(int argc, char *argv[]) { fnc.0804844b (__ExecData, __ExecData_SIZE); //ここで↑2件のバリューを0x0804844bに送ります return 0; }
という意味では"fugahoge"って→「bin」や「sh」単語があるデータ(0x0804974c!0x24)ですね。シェルコードですか?もしシェルコードなら、どんな動きでしょうか?
詳しくは確認しましょう。
■シェルコードのリバーシング
一応、偽sshdのリバーシングの時に一応最初からシェルコードの所に行ってしまいましたが、当時未だ色々分かりませんでした。 今、シェルコードがある事、そしてサイズとローダーがある事も分かりますので、明確にシェルコーの部分を見えるようになりました。
この辺ですね↓
それで、radareで最初に偽sshdの「0x0804974c」を開いたけど、 その時にr2は0x0804974cからバイナリがのデータとして分析されてしまいましたので、ちゃんとx86実行バイナリのopcodeに分析されななかった。
それをやり直しましょう。
Linux x86のopcode読む方法やツールがあるけど、このシェルコードのサイズが小さいので遣りやすい方法でやりましょう。
私が秀丸で調べ/計算しながら解析したので、その分析した結果はこんな感じです↓
↑まるでシェールコードの形ですね。ASMで結構です。C言語に書く必要がありません。
『重要な調査のヒント』面白いなのは「jnz 0x1f」のコードですね。
意味合い的にこのシェルコードで2回syscall execve("/bin//sh",0,0)を実行されます。自分はこれを初めて見ました。
■再現
解析の結果が正しいかどうかの確認が必要です。自分ですと必ず再現をします。
偽sshdバイナリを再現したらシェールコードが起動された時にトレースで確認が出来ます。
ちょっとだけミスがありますが結果が大体合っています。
ダブル「/bin//sh」についての再現は↓
※)もし色々試したいならバイナリーがここに保存しまました、どうぞ試してください。
■結論
ここ迄の分かった事まとめたら↓
1. 中国IPアドレス(180.97.220.28)からのプロキシでハッカーがこのボックスに入りました。
2. ハッキングの方法は恐らくssh経由の攻撃です。
3. ラッピングされたシェルコードのCファイルを偽sshdバイナリとしてコンパイルしたようです。
4. 実行された後にその偽sshdを削除し、開いたshell(sh)を残し、ハッカーが最後に実行したコマンドもそのshell(sh)で発見されました。
5. ハッカーが使ったシェルコードの特徴があり、2回「sh」を実行します。
※)その他の情報が申し訳御座いませんがここで公開が出来ません。
Linuxのリバーシングは楽しいですね!(^-^v #MalwareMustDie!
@unixfreaxjp/0day.jp Tue Jan 31 01:45:34 JST 2017 - AVTokyo ELF Workshop
0 件のコメント:
コメントを投稿