我们破解了 Gemini 的 Python 沙箱并泄露了其源代码 (至少一部分)
我们破解了 Google 的 A.I. Gemini,并泄露了其源代码 (至少一部分)
2025 年 3 月 27 日 RONI CARTA | LUPIN gemini, llm, google, source code, leak, bug bounty, hack
重返 Vegas,这次我们带回了 MVH 奖!
在 2024 年,我们发布了一篇博文 We Hacked Google A.I. for $50,000,讲述了 2023 年我们与 Joseph "rez0" Thacker、Justin "Rhynorater" Gardner 以及我自己 Roni "Lupin" Carta 一起前往拉斯维加斯的黑客之旅,这场旅程从拉斯维加斯、东京一直延伸到法国,全部为了在 Google 的 LLM bugSWAT 活动中寻找 Gemini 的漏洞。 嗯,我们又做到了……
生成式人工智能 (GenAI) 和大型语言模型 (LLM) 的世界仍然是科技界的狂野西部。 自 GPT 爆红以来,争夺 LLM 领域霸主地位的竞赛愈演愈烈,Meta、Microsoft 和 Google 等科技巨头竞相推出最好的模型。 但现在还有 Anthropic、Mistral、Deepseek 等公司也加入了竞争,并在大规模地影响着这个行业。
在各公司竞相部署 AI 助手、分类器和无数其他 LLM 驱动的工具时,一个关键问题仍然存在:我们的构建是否安全? 正如我们去年强调的那样,快速采用有时会让我们忘记基本的安全原则,从而为新的和熟悉的漏洞打开了大门。
AI 代理正在迅速成为人工智能领域的下一个颠覆者。 这些智能实体利用先进的思维链推理,即模型生成连贯的内部推理步骤序列来解决复杂任务的过程。 通过记录他们的思维过程,这些代理不仅增强了他们的决策能力,还提供了透明度,使开发人员和研究人员能够理解和改进他们的性能。 这种自主行动和可见推理的动态结合,正在为更具适应性、可解释性和可靠性的 AI 系统铺平道路。 随着我们看到越来越多的应用,从交互式助手到复杂的决策支持系统。 在 AI 代理中集成思维链推理正在为这些模型在实际场景中能够实现的目标设定新的标准。
值得称赞的是,Google 积极地认识到 AI 安全这个新兴领域,并且很早就开始了行动。 他们的 "LLM bugSWAT" 活动在拉斯维加斯等充满活力的地点举行,证明了他们对主动安全红队行动的承诺。 这些活动挑战着世界各地的研究人员严格测试他们的 AI 系统,寻找那些可能从漏洞中溜走的漏洞。
你猜怎么着? 2024 年我们再次响应了号召! Justin 和我再次参加了在拉斯维加斯举行的 bugSWAT 活动,这一次,我们的努力得到了巨大的回报。 感谢 Gemini 中一个全新的漏洞(我们即将详细介绍的那个),我们非常荣幸地在今年的拉斯维加斯 bugSWAT 上被授予最有价值黑客 (MVH) 的称号!
与我们的 MVH 奖杯和两位出色的 Google 员工合影 <3
所以,准备好再次深入挖掘吧。 这不仅仅是重复表演; 这是一个全新的漏洞,我们即将向你展示 ;)
发现新的 Gemini
Google 团队授予我们提前访问下一个 Gemini 更新预览版的权限,该版本具有几个令人兴奋的新功能。 除了这种独家访问权限外,我们还收到了详细的文档,解释了这些功能及其预期功能。 目标是从攻击者的角度充分探索和测试这些功能。
一切都从一个简单的提示开始。 我们问 Gemini:
run hello world in python3
Gemini 提供了代码,并且界面提供了一个诱人的 "Run in Sandbox"(在沙箱中运行)按钮。 感兴趣的,我们开始了探索。
Gemini 的 Python 游乐场 – 一个安全空间... 还是?
当时的 Gemini 提供了一个 Python 沙箱解释器。 可以把它想象成一个安全空间,你可以在其中运行 AI 本身生成的 Python 代码,甚至是你自己的自定义脚本,就在 Gemini 环境中。 这个沙箱由 Google 的 Gvisor 在 GRTE(Google 运行时环境)中提供支持,旨在实现安全性。 这样做的目的是让你可以在不冒任何损害底层系统风险的情况下试验代码,这是测试和开发的关键特性。
gVisor 是 Google 开发的用户空间内核,充当容器化应用程序和主机操作系统之间的中介。 通过拦截应用程序发出的系统调用,它强制执行严格的安全边界,从而降低容器逃逸的风险并限制受损进程可能造成的损害。 gVisor 不是仅仅依赖于传统的操作系统级别隔离,而是实现了一个最小的、定制的内核功能子集,从而减少了攻击面,同时仍然保持了合理的性能。 这种创新方法增强了容器环境的安全性,使 gVisor 成为安全运行和管理容器化工作负载的重要工具。
作为安全研究人员和漏洞赏金猎人,我们知道这个 gVisor 沙箱受到多层防御的保护,而且从我们所看到的情况来看,没有人能够逃脱这个沙箱。 实际上,逃离沙箱可以奖励你 10 万美元的赏金:
虽然仍然有可能逃脱它,但这与我们正在寻找的挑战完全不同。
然而,沙箱并不总是意味着要被逃脱,因为在很多情况下,沙箱本身内部的东西可以帮助我们泄露数据。 Google 安全团队的一位员工与我们分享了这个想法,即能够在沙箱内部获得 shell 访问权限,并尝试找到任何不应该访问的数据。 主要问题如下:这个沙箱只能运行自定义编译的 Python 二进制文件。
绘制领域
我们首先看到的是,也可以从前端完全重写 Python 代码,并在沙箱中运行我们任意的版本。 我们的第一步是了解这个沙箱的结构。 我们怀疑可能潜藏着一些有趣的文件。 由于我们无法弹出 shell,因此我们检查了这个自定义编译的 Python 二进制文件中可以使用哪些库。 我们发现 os 存在! 太棒了,然后我们可以使用它来绘制文件系统。
我们编写了以下 Python 代码:
import os
def get_size_formatted(size_in_bytes):
if size_in_bytes >= 1024 ** 3:
size = size_in_bytes / (1024 ** 3)
unit = "Go"
elif size_in_bytes >= 1024 ** 2:
size = size_in_bytes / (1024 ** 2)
unit = "Mb"
else:
size = size_in_bytes / 1024
unit = "Ko"
return f"{size:.2f} {unit}"
def lslR(path):
try:
# Determine if the path is a directory or a file
if os.path.isdir(path):
type_flag = 'd'
total_size = sum(os.path.getsize(os.path.join(path, f)) for f in os.listdir(path))
else:
type_flag = 'f'
total_size = os.path.getsize(path)
size_formatted = get_size_formatted(total_size)
# Check read and write permissions
read_flag = 'r' if os.access(path, os.R_OK) else '-'
write_flag = 'w' if os.access(path, os.W_OK) else '-'
# Print the type, permissions, size, and path
print(f"{type_flag}{read_flag}{write_flag} - {size_formatted} - {path}")
# If it's a directory, recursively print the contents
if type_flag == 'd':
for entry in os.listdir(path):
entry_path = os.path.join(path, entry)
lslR(entry_path)
except PermissionError:
print(f"d-- - 0Ko - {path} (PermissionError: cannot access)")
except Exception as e:
print(f"--- - 0Ko - {path} (Error: {e})")
此代码的目标是具有某种文件和目录的递归列表函数,以便能够查看存在哪些文件、它们的大小以及它们的权限。
我们使用该函数列出了 lslR("/usr")
目录。
这个调用识别了一个位于 /usr/bin/entry/entry_point
的二进制文件。 这听起来很诱人!
泄露 entry_point 文件
我们的下一步是提取这个文件,但由于它的大小为 579Mb,直接进行 base64 编码并在前端打印它是不可能的,它导致整个沙箱挂起,直到最终超时。
我们尝试看看是否可以运行 TCP、HTTP 和 DNS 调用来泄露信息。 有趣的是,我们所有的出站连接尝试都失败了,沙箱似乎与外部网络完全隔离。 这导致了一个有趣的难题:如果沙箱如此紧密地隔离以至于无法进行外部调用,那么它如何与 Google Flights 等 Google 服务交互? 嗯……我们稍后可能会回答这个问题;D
所以我们需要通过在控制台中分块打印来泄露这个二进制文件,为此,我们使用了 seek() 函数来遍历二进制文件,并以 10 MB 的块检索整个二进制文件。
import os
import base64
def read_and_encode(file_path, kilobytes):
try:
# Calculate the number of bytes to read
num_bytes = kilobytes * 1024
# Open the file and read the specified number of bytes
with open(file_path, 'rb') as file:
file_content = file.read(num_bytes)
# Base64 encode the bytes
encoded_content = base64.b64encode(file_content)
# Print the encoded string
print(encoded_content.decode('utf-8'))
except FileNotFoundError:
print(f"FileNotFoundError: {file_path} does not exist")
except PermissionError:
print(f"PermissionError: Cannot access {file_path}")
except Exception as e:
print(f"Error: {e}")
read_and_encode("/usr/bin/entry/entry_point", 10000)
然后我们使用 Caido 在我们的代理中捕获将运行沙箱调用并获取结果的请求,然后将其发送到 Automate 功能。 Automate 功能允许你批量发送请求。 此功能提供了一种灵活的方式来启动暴力破解/模糊测试,以使用单词列表快速修改请求的某些参数。
Lupin 的提示:在文章中,这似乎是一条直接的路径,但实际上我们花了几个小时才达到这一点。 当时是凌晨 3 点,我们和 Justin 一起进行黑客攻击,当我睡在键盘上时,Justin 正在使用 Caido 泄露二进制文件。
一旦我们有了所有的 base64 块,我们在本地重建了整个文件,我们准备好查看它的内容了。
如何读取这个文件?
file 命令?
在二进制文件上运行 file 命令显示其身份为 binary: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /usr/grte/v5/lib64/ld-linux-x86-64.so.2
这证实了该文件是一个二进制文件。 嗯,我们可以用这个做什么?
strings 命令?
当我们执行 strings 命令时,由于多次引用 google3
,Google 的内部存储库,输出特别有趣。 这表明存在从未打算公开的内部数据路径和代码片段,清楚地表明该二进制文件包含 Google 专有软件的痕迹。 但这实际上有任何安全影响吗?
Binwalk FTW!
真正的突破来自于使用 Binwalk。 这个工具设法从二进制文件中提取了整个文件结构,揭示了一个全面的沙箱布局。 提取过程发现了多个目录和文件,描绘了内部架构的详细图景,并暴露了组件,我们对所发现内容的反应就像……OMG。
等等……那是内部源代码吗?
当深入研究我们 Binwalk 分析生成的提取时,我们意外地发现了内部源代码。 提取过程揭示了专有的 Google 源代码的整个目录。 但它是否敏感?
带有 Python 代码的 Google3 目录
在 binwalk 提取的目录中,我们可以找到一个 google3
目录,其中包含以下文件:
total 2160
drwxr-xr-x 14 lupin staff 448B Aug 7 06:17 .
drwxr-xr-x 231 lupin staff 7.2K Aug 7 18:31 ..
-r-xr-xr-x 1 lupin staff 1.1M Jan 1 1980 __init__.py
drwxr-xr-x 5 lupin staff 160B Aug 7 06:17 _solib__third_Uparty_Scrosstool_Sv18_Sstable_Ccc-compiler-k8-llvm
drwxr-xr-x 4 lupin staff 128B Aug 7 06:17 assistant
drwxr-xr-x 4 lupin staff 128B Aug 7 06:17 base
drwxr-xr-x 5 lupin staff 160B Aug 7 06:17 devtools
drwxr-xr-x 4 lupin staff 128B Aug 7 06:17 file
drwxr-xr-x 4 lupin staff 128B Aug 7 06:17 google
drwxr-xr-x 4 lupin staff 128B Aug 7 06:17 net
drwxr-xr-x 9 lupin staff 288B Aug 7 06:17 pyglib
drwxr-xr-x 4 lupin staff 128B Aug 7 06:17 testing
drwxr-xr-x 9 lupin staff 288B Aug 7 06:17 third_party
drwxr-xr-x 4 lupin staff 128B Aug 7 06:17 util
在 assistant
目录中,还发现了与 RPC 调用(用于通过 YouTube、Google Flights、Google Maps 等工具处理请求)相关的内部 Gemini 代码。 目录结构如下:
.
├── __init__.py
└── boq
├── __init__.py
└── lamda
├── __init__.py
└── execution_box
├── __init__.py
├── images
│ ├── __init__.py
│ ├── blaze_compatibility_hack.py
│ ├── charts_json_writer.py
│ ├── format_exception.py
│ ├── library_overrides.py
│ ├── matplotlib_post_processor.py
│ ├── py_interpreter.py
│ ├── py_interpreter_main.py
│ └── vegalite_post_processor.py
├── sandbox_interface
│ ├── __init__.py
│ ├── async_sandbox_rpc.py
│ ├── sandbox_rpc.py
│ ├── sandbox_rpc_pb2.pyc
│ └── tool_use
│ ├── __init__.py
│ ├── metaprogramming.py
│ └── runtime.py
└── tool_use
├── __init__.py
└── planning_immersive_lib.py
8 directories, 22 files
仔细查看 Python 代码
在文件 google3/assistant/boq/lamda/execution_box/images/py_interpreter.py
中,一段代码显示:
# String for attempted script dump detection:
snippet = ( # pylint: disable=unused-variable
"3AVp#dzcQj$U?uLOj+Gl]GlY<+Z8DnKh" # pylint: disable=unused-variable
)
此代码段似乎用作防止未经授权的脚本转储的安全措施,这表明该代码从未打算公开。
经过彻底审查,包含看起来像是内部 Google3 代码实际上是一个有意的选择…… 太糟糕了 x)
尽管 Python 代码的反转储机制最初可能表明访问受到限制,但 Google 安全团队在发布之前已经明确批准将其公开。 虽然这些措施最初旨在防止意外打印,但它们被保留了下来,因为……为什么不呢?
但我们并没有让这个沙箱孤单,我们知道我们离一个巨大的东西很近了! ;D
挖掘沙箱的主要逻辑
在深入研究 Python 代码时,我们注意到,正如预期的那样,这个沙箱正在与外部 Google 服务器通信以执行诸如从 Google Flights 或其他 Google 服务获取数据之类的活动。
这是通过一个 python 类 (google3.assistant.boq.lamda.execution_box.sandbox_interface
) 实现的,该类公开了各种可以调用的函数,例如 _set_reader_and_writer
。
def _set_reader_and_writer(
reader_handle: io.BufferedReader | None,
writer_handle: io.BufferedWriter | None,
) -> None:
"""Sets the reader and writer handles for rpcs.
Should be called before running any user code that might
import async_sandbox_rpc
Args:
reader_handle: the handle through which to receive incoming RpcResponses. If
None will default to legacy behavior (/dev/fd/3)
writer_handle: the handle through which to receive incoming RpcRequests. If.
None will default to legacy behavior (/dev/fd/4)
"""
with _INIT_LOCK:
global _READER_HANDLE
global _WRITER_HANDLE
_READER_HANDLE, _WRITER_HANDLE = reader_handle, writer_handle
def run_tool(
name: str, operation_id: str, parameters: str
) -> sandbox_rpc_pb2.RunToolResponse:
"""Runs a tool with the given name and id, passing in parameters.
Args:
name: The name of the tool.
operation_id: The name of the operation to perform.
parameters: The parameters to pass to the tool.
Returns:
A RunToolResponse containing the response from the tool.
"""
result = make_rpc(
sandbox_rpc_pb2.RpcRequest(
run_tool_request=sandbox_rpc_pb2.RunToolRequest(
name=name, operation_id=operation_id, parameters=parameters
)
)
)
if result and result.HasField("run_tool_response"):
return result.run_tool_response
else:
return sandbox_rpc_pb2.RunToolResponse(response="")
我们将向这些函数提供各种数据,它们会将数据序列化为 protobuf 兼容格式,然后通过写入本地文件描述符 5
通过 RPC 进行调用。 然后可以通过从本地文件描述符 7
读取来读取响应。 通过利用在大型二进制文件中找到的 protos,我们能够制作与此 RPC 服务器之间的消息,并直接调用这些 Google 工具。
但是,我们注意到一些有趣的事情,并非每个沙箱都具有相同的一组可用的 Google 服务。 这取决于沙箱是由前端生成的以能够运行 Python 源代码,还是由 Google Agent 生成的。 我们是什么意思?
ReAct 研究论文!
在解释下一部分之前,我们需要解释一下 Google 的团队向我们展示了以下 Gemini 所依据的研究论文:
这篇论文介绍了一种新颖的方法(当时),即语言模型在生成推理轨迹和执行特定动作之间交替,从而有效地以交错的方式合并思维和行为。 在实践中,这意味着当模型通过问题进行推理时,创建一个透明的思维轨迹,帮助它计划、跟踪和调整其行动,它同时与外部来源交互以在需要时收集更多数据。 这种动态的相互作用不仅通过减轻常见的诸如幻觉和错误传播等问题来提高模型的性能,而且还使它的决策过程对于人类操作员来说更易于解释和控制。
通过整合内部推理和外部知识,ReAct 提供了一个灵活且通用的框架,该框架在各种任务中表现出色,从问题解答和事实验证到基于文本的游戏和 Web 导航。 本质上,ReAct 利用推理和行动的组合优势来创建更健壮、与人类对齐和通用的语言模型。
好的,但是我们为什么需要理解这个? 好吧,如果我们遵循这篇论文的逻辑,基本上 Gemini 可以多次提示自己,以便改进命令并拥有正确的思维链。
如果用户问“多伦多和纽约之间最早的航班是什么?”,Gemini 大致会有一个如下的思维链:
- 用户要求我搜索多伦多和纽约之间最早的航班
- 创建一个计划,例如: 1. 我需要运行一个 Python 代码,该代码将连接到 Google Flights 工具 2. 代码需要查找从今天到下周从 YYZ 机场到 JFK 机场的所有航班 3. 代码需要按日期对航班进行排序并选择第一个项目
- 完成计划后,它将使用 Agent 拥有的可用工具生成代码
- 代码生成后,它将生成一个具有正确权限的沙箱(例如:可以通过文件描述符访问 Google Flights 工具)
- 运行代码,处理输出
- 向用户做出响应
因此,我们的理论如下:
- 如果我们可以在 Gemini 规划部分进行二次提示注入,以便访问权限更高的沙箱,那会怎么样?
在 Google 安全团队的帮助下,我们测试了这个想法并观察到,根据生成种子和温度等因素(所有常见的概率 LLM 细微差别),我们偶尔可以访问看起来权限更高的沙箱。
我们所说的“权限更高的沙箱”是指可以通过两个新的文件描述符访问扩展的沙箱。 这些文件描述符始终存在,但并不总是主动侦听,当代理调用沙箱时,它们会监视对扩展(Google 服务)的任何调用,以便我们可以与 API 交互,而如果通过 Python 解释器访问,则这些扩展仍然无法访问。
这让我们相信,存在一个真正的 P0 漏洞的机会:存在一个特定的消息处理程序,它可能允许在 Google 的内部基础设施上读取文件,并且我们希望具有工具扩展的沙箱可以启动对此特定工具的 RPC 调用。 鉴于攻击的概率性质,这使得难以一致地重现,因此我们让 Google 安全团队评估这种情况。 最终,他们的审查表明,可疑的消息处理程序无法通过 RPC 获得,只能从外部调用。
即使我们的测试受到限制,但如果我们将核心思想进一步推进,它仍然具有一些真正的潜力。 在沙箱上下文中运行代码并不意味着要赋予额外的权力,它被视为不受信任的,并且安全检查位于沙箱之外,并且每个工具调用都经过过滤。 但是能够运行代码确实提供了一些好处:
- 可靠性:一旦你可以运行代码,你就可以更一致地触发操作。
- 链接/复杂性:通过纯文本控制多个工具或微调参数很困难; 代码执行可以让你构建更复杂的链,即使安全措施仍然存在。
- 工具输出中毒:你或许可以更有效地操纵工具的输出。
- 泄露:环境中可能存在其他隐藏的部分,如果暴露出来,可能会提供额外的优势。
这表明我们的想法仍然具有进一步升级的希望。 并且那种“泄露”的潜力,我们想看看我们是否至少可以确认这个理论……
我们找到了我们的泄露;D
在深入挖掘时,我们发现了泄露 proto 文件的几种方法。 如果你不熟悉,proto 文件(Protocol Buffer 文件的缩写)就像数据的蓝图,定义了消息的结构以及系统不同部分之间如何交换信息。 乍一看,它们可能看起来无害,但泄露这些文件可以让你非常详细地了解 Google 的内部架构。
暴露 classification.proto
事实证明,通过运行如下命令:
strings entry_point > stringsoutput.txt
然后在结果文件中搜索“Dogfood”,我们设法检索了内部 protos 的片段。 提取内容的部分内容包括极其敏感的 protos 的元数据描述。 它本身不包含用户数据,但这些文件是 Google 用于分类用户数据的内部类别。
由于法律原因,我们无法显示此命令的结果 x)
为什么要专门搜索字符串“Dogfood”? 在 Google,“dogfood”指的是在公开推出之前,在公司内部使用公司自己的产品和原型的预发布版本来测试和改进它们的做法。 它允许开发人员在上线之前测试这些产品中的部署和潜在问题。
此外,还暴露了以下文件 privacy/data_governance/attributes/proto/classification.proto
,其中详细说明了数据如何在 Google 内部进行分类。 尽管该文件包含对相关文档的引用,但这些文档仍然高度机密,不应公开访问。
Lupin 的再次提示:这是在我们泄露二进制文件的整个通宵之后第二天发现的。 我们在 Google 预订的酒店房间的一个套房里,我们与安全团队合作以了解我们前一天晚上发现的东西。 这次是 Justin 睡在沙发上,哈哈! 这个 bug 真的很耗时,但也很有趣! 😀
暴露内部安全 Proto 定义
相同的输出还显示了许多本应保持隐藏的内部 proto 文件。 运行:
cat stringsoutput.txt| grep '\.proto' | grep 'security'
列出了几个敏感文件,包括:
security/thinmint/proto/core/thinmint_core.proto
security/thinmint/proto/thinmint.proto
security/credentials/proto/authenticator.proto
security/data_access/proto/standard_dat_scope.proto
security/loas/l2/proto/credstype.proto
security/credentials/proto/end_user_credentials.proto
security/loas/l2/proto/usertype.proto
security/credentials/proto/iam_request_attributes.proto
security/util/proto/permission.proto
security/loas/l2/proto/common.proto
ops/security/sst/signalserver/proto/ss_data.proto
security/credentials/proto/data_access_token_scope.proto
security/loas/l2/proto/identity_types.proto
security/credentials/proto/principal.proto
security/loas/l2/proto/instance.proto
security/credentials/proto/justification.proto
在二进制字符串中查找 security/credentials/proto/authenticator.proto
确认其数据确实已暴露。
为什么这些 protos 在那里?
正如我们之前所说,Google 安全团队彻底审查了沙箱中的所有内容,并允许公开披露。 但是,用于编译沙箱二进制文件的构建管道包含一个自动化步骤,每当它检测到二进制文件可能需要它们来强制执行内部规则时,它会将安全 proto 文件添加到二进制文件中。
在这种特殊情况下,该步骤不是必需的,从而导致意外地将高度机密的内部 protos 包含在野外!
作为漏洞赏金猎人,深入了解管理公司运营的业务规则至关重要。 我们报告了这些 proto 泄露,因为我们知道 Google 将它们视为永远不应公开的高度机密信息。 我们越了解目标的内部运作和优先级,就越能识别并标记那些可能被忽略的细微 bug。 这种深入的知识不仅可以帮助我们查明漏洞,还可以确保我们的报告与组织的关键安全问题保持一致。
结论
在我们结束之前,值得一提的是在这些尖端 A.I. 系统上线之前对其进行测试至关重要。 由于有如此多的互连和很棒的功能,即使是访问不同扩展的简单沙箱,也始终存在意外惊喜的可能性。 我们已经亲眼看到,当所有这些部分协同工作时,即使是一个小小的疏忽也可能为问题开辟新的途径。 因此,彻底的测试不仅仅是最佳实践; 这是确保一切保持安全并按预期运行的唯一方法。
归根结底,使整个体验如此令人难忘的是旅程的纯粹乐趣。 破解漏洞、探索隐藏代码以及突破 Gemini 沙箱的极限与挑战一样,也与狩猎的兴奋有关。 我们在拉斯维加斯的 bugSWAT 活动中遇到的人都很棒。 对意外曲折的共同欢笑,以及智胜复杂系统的刺激,使这次技术之旅变成了一次我们永远不会忘记的冒险。 正是像这样的时刻,即严肃的黑客行为与美好时光相遇,提醒我们为什么要做我们所做的事情。
最后,要向所有其他获奖者和参与者大声疾呼,他们让 bugSWAT 2024 如此精彩。 我们要祝贺 Sreeram 和 Sivanesh 的出色团队合作,Alessandro 离榜首如此之近,以及 En 登上领奖台。 很高兴见到如此多的出色黑客和安全专业人士,你们的精力和热情使这次活动令人难忘。 我们迫不及待地想在下一次 bugSWAT 上再次见到大家,在那之前,继续进行黑客攻击并玩得开心!
当然,感谢 Google 安全团队! 一如既往,你们太棒了 ❤️