文章总结: 本文研究VSCodeDevTunnels的底层协议实现,通过LLM辅助分析其分层架构:REST管理接口获取隧道信息与令牌,WebSocket中继连接机制,以及潜在的红队利用价值。关键发现包括完整的连接流程、认证令牌生成方式和隧道交互方法,为安全研究提供技术参考。 综合评分: 78 文章分类: 红队,内网渗透,安全工具,技术标准
意外的 C2:借道 VS Code Dev Tunnels 实现远程访问
Adam Chester Adam Chester
securitainment
2026年5月23日 10:24 中国澳门
在小说阅读器读本章
去阅读
| 原文链接 | 作者 | | — | — | | https://blog.xpnsec.com/accidental-c2/ | XPN / Adam Chester |
我是在从曼彻斯特飞往 JFK 的航班上开始写这篇博客文章的。每当我出行时,我通常会从研究待办清单中挑出一个小项目,戴上耳机,把世界隔绝在外,直到飞机降落。这趟旅程也不例外,给了我大约 7 小时的时间专注于一个困扰我已久的话题 —— Visual Studio Code Dev Tunnels。
其他人已经尝试过用 dev tunnels 来代理 C2 流量,但 VS Code 本身可以执行远程 shell 命令并搬运文件。所以底层一定有某些东西在红队评估中是有用的。
通常我不会尝试在跨大西洋航班上启动这样的项目,主要是因为 dev tunnels 严重依赖稳定的网络连接 (而大多数时候,£25 的机上 Wi-Fi 连接感觉就像 IPoAC)。但这次,我有一个新的依靠来应对这种不稳定性。
放飞 Bishop
“Bishop”(以《异形》中的机器人科学官命名) 是我新搭建的 LLM 工作机。它是一台基础款的 M4 Mac mini,唯一的任务就是远程处理长时间运行的 Claude Code、Codex 和 OpenCode 会话。
我的想法是,通过远程启动新任务来缓解不稳定的 Wi-Fi 问题,让 LLM 在后台埋头工作,等网络恢复时再拉回一份精炼的报告。
于是我从一个简单的 prompt 开始:
I am creating a research-project into VS Code Dev Tunnels. Primarily the goals of this research will be:
1. Create a standalone tool which will allow me to list/add/interact with existing dev-tunnels
2. Interact with existing authentication tokens (Azure/GitHub) to view existing tunnels and interact with them
First I need to understand how they work under the hood. This will be imperative to understanding how my research will go.
The repo is at: https://github.com/microsoft/vscode.git
First take a clone of this and start exploring, looking for answers to the above.
Other resources which may be useful:
* https://github.com/microsoft/dev-tunnels.git - Dev Tunnels source
令人惊讶的是,在调用 GPT-5.4-Cyber模型后,几分钟内就返回了一份初始报告,让我对 Dev Tunnels 协议的工作原理以及相关代码段的位置有了一个良好的概览。
如果你感兴趣,可以在这里查看初始报告。
随后又生成了进一步的迭代和代码示例,这同样意味着在我专注于研究的同时,其他 agent 正在后台对任务进行迭代。
VS Code Dev Tunnels 的分层结构
Dev Tunnels 在 VS Code 中已经存在一段时间了。你通常会在侧边栏的 Remote Explorer 中看到它们:
你或许会以为这项功能的底层实现相当简单。毕竟,你本质上是在两台主机之间建立一条 HTTP 隧道,而我们知道 Microsoft 有好几款用于隧道传输的产品,这能有多难呢?
遗憾的是,Microsoft 的 dev tunnels 实际上是多层次、而且相当不标准的,这意味着为了理解这一切是如何运作的,我不得不与 Bishop 一起逐层剖析。
我发现最简单的处理方式是从客户端到服务器的连接流程开始,逐层重建。所以让我们从 VS Code 如何搜索现有隧道开始,逐层深入,直到我们抵达代码执行环节。
Layer 0 – REST 管理
当连接到现有的 dev-tunnel 时,VS Code 需要了解有哪些现有的服务器可用。这是通过向一个标准的 JSON REST 端点发送 GET 请求来完成的:
GET /tunnels?includePorts=true&labels=vscode-server-launcher&allLabels=true&global=true&api-version=2023-09-27-preview HTTP/1.1
Host: global.rel.tunnels.api.visualstudio.com
Authorization: github gho_GITHUB_TOKEN_HERE
User-Agent: vscode.dev.remote-server Dev-Tunnels-Service-TypeScript-SDK/1.2.1
响应随后提供了我们可用的隧道范围的信息:
HTTP/1.1 200 OK
...
{
"value": [{
"regionName": "UkSouth",
"value": [{
"clusterId": "uks1",
"tunnelId": "wild-fog-s1alk0t",
"name": "",
"description": "",
"labels": ["prometheus", "protocolv4", "vscode-server-launcher", "_flag3"],
"options": {
"isGloballyAvailable": true
},
"status": {
"hostConnectionCount": 0,
"lastHostConnectionTime": "2026-04-08T16:16:57Z",
"clientConnectionCount": {
"current": 0,
"limit": 20
},
"lastClientConnectionTime": "2026-03-31T23:11:04Z",
"clientConnectionRate": {
"current": 0
},
"uploadRate": {
"periodSeconds": 1,
"resetTime": 0,
"current": 0,
"limit": 20971520
},
"downloadRate": {
"periodSeconds": 1,
"resetTime": 0,
"current": 0,
"limit": 20971520
},
"uploadTotal": 45013860,
"downloadTotal": 19000550,
"apiReadRate": {
"current": 0
},
"apiUpdateRate": {
"current": 0
}
},
"endpoints": [{
"hostRelayUri": "wss://uks1-data.rel.tunnels.api.visualstudio.com/api/v1/Host/Connect/wild-fog-s1alk0t",
"clientRelayUri": "wss://uks1-data.rel.tunnels.api.visualstudio.com/api/v1/Client/Connect/wild-fog-s1alk0t",
"id": "45e5e54c-1acf-41f3-96d4-c2085c0dfe35-relay",
"connectionMode": "TunnelRelay",
"hostId": "45e5e54c-1acf-41f3-96d4-c2085c0dfe35",
"portUriFormat": "https://a5n51h3l-{port}.uks1.devtunnels.ms/",
"tunnelUri": "https://a5n51h3l.uks1.devtunnels.ms/",
"portSshCommandFormat": "ssh a5n51h3l-{port}@ssh.uks1.devtunnels.ms",
"tunnelSshCommand": "ssh [email protected]"
}],
"ports": [{
"clusterId": "uks1",
"tunnelId": "wild-fog-s1alk0t",
"portNumber": 31545,
"protocol": "auto",
"options": {
"isGloballyAvailable": true
},
"status": {},
"portForwardingUris": ["https://a5n51h3l-31545.uks1.devtunnels.ms/"],
"inspectionUri": "https://a5n51h3l-31545-inspect.uks1.devtunnels.ms/"
}],
"created": "2025-11-03T11:20:51.376614Z",
"expiration": "2026-05-08T16:17:03Z"
},
...
为了连接到一个正在运行的隧道服务器,我们接下来需要生成一个访问令牌。但在此之前,我们需要从上面的响应中获取几项信息:
-
clusterId -
目标隧道所分配到的中继集群。
-
tunnelId -
这是隧道创建时随机生成的名称,看起来类似于
cheeky-sausage-a123456b7
有了这些信息后,我们就可以通过以下方式请求一个新的访问令牌:
GET /tunnels/wild-fog-s1alk0t?includePorts=true&tokenScopes=connect&api-version=2023-09-27-preview HTTP/1.1
Host: CLUSTERID.rel.tunnels.api.visualstudio.com
Authorization: github gho_GITHUB_TOKEN_HERE
User-Agent: vscode.dev.remote-server Dev-Tunnels-Service-TypeScript-SDK/1.2.1
从 URL 参数中我们可以看到,我们正在请求一个 connect 范围的令牌,并在响应中收到它:
HTTP/1.1 200 OK
...
{
"clusterId": "uks1",
"tunnelId": "wild-fog-s1alk0t",
"name": "",
"description": "",
"labels": ["prometheus", "protocolv4", "vscode-server-launcher", "_flag8"],
"accessTokens": {
"connect": "eyJhbGci[REDACTED]FxfRpNXiNGjcw"
},
"accessControl": {
"entries": []
},
"options": {
"isGloballyAvailable": true
},
"status": {
"hostConnectionCount": 0,
"lastHostConnectionTime": "2026-04-13T16:05:29Z",
"clientConnectionCount": {
"current": 0,
"limit": 20
},
"lastClientConnectionTime": "2026-04-13T15:46:16Z",
"clientConnectionRate": {
"current": 0
},
"uploadRate": {
"periodSeconds": 1,
"resetTime": 0,
"current": 0,
"limit": 20971520
},
"downloadRate": {
"periodSeconds": 1,
"resetTime": 0,
"current": 0,
"limit": 20971520
},
"uploadTotal": 6366,
"downloadTotal": 15840,
"apiReadRate": {
"current": 0
},
"apiUpdateRate": {
"current": 0
}
},
"endpoints": [{
"hostRelayUri": "wss://uks1-data.rel.tunnels.api.visualstudio.com/api/v1/Host/Connect/wild-fog-s1alk0t",
"clientRelayUri": "wss://uks1-data.rel.tunnels.api.visualstudio.com/api/v1/Client/Connect/wild-fog-s1alk0t",
"id": "45e5e54c-1acf-41f3-96d4-c2085c0dfe35-relay",
"connectionMode": "TunnelRelay",
"hostId": "45e5e54c-1acf-41f3-96d4-c2085c0dfe35",
"portUriFormat": "https://a5n51h3l-{port}.uks1.devtunnels.ms/",
"tunnelUri": "https://a5n51h3l.uks1.devtunnels.ms/",
"portSshCommandFormat": "ssh a5n51h3l-{port}@ssh.uks1.devtunnels.ms",
"tunnelSshCommand": "ssh [email protected]"
}],
...
同样,从这个响应中我们需要在继续之前提取几项内容:
-
connectToken -
连接到中继服务器所需的 JWT 访问令牌
-
clientRelayUri -
我们需要连接的客户端 WebSocket URI
生成访问令牌后,我们可以进入下一层。但在此之前,让我们稍微绕个弯,讨论一下初始身份验证。
使用 GitHub 进行身份验证
你可能已经从上面的 Authorization标头中注意到,在向 REST 服务器发出请求时使用了 GitHub token。
该 token 是当你首次使用 GitHub 账户设置 dev tunnels 时,由 VS Code 发起的 OAuth2 流程生成的 (使用 GitHub OAuth2 Client ID 01ab8ac9400c4e429b23以及 read:org和 user:email这两个 scope)。
该 Client ID 也已获准用于 Device Code 流程,这意味着 Device Code Phishing 是获取 dev tunnels 身份验证所需 GitHub token 的一种可行方法:
POST /login/device/code HTTP/1.1
Host: github.com
client_id=01ab8ac9400c4e429b23&scope=read:org,user:email
OAuth2 流程完成后,GitHub 会返回一个 token:
HTTP/1.1 200 OK
...
{
"access_token": "gho_[REDACTED]",
"token_type": "bearer",
"scope": "read:org,user:email"
}
正是这个 access_token值会被用于最初的 API REST 调用中,通过 Authorization: github TOKENHERE标头来生成 connect token。
使用 Azure 进行身份验证
如果你选择使用 Entra ID 作为 SSO 提供商,那么身份验证 token 同样遵循标准的 Microsoft OAuth2 流程,其 Client ID 为 aebc6443-996d-45c2-90f0-388ff96faa56,scope 为 46da2f7e-b5ef-422a-88d4-2a7f9de6a0b2/all。
我们得到的响应是一个典型的 access token 和 refresh token:
HTTP/1.1 200 OK
...
{
"token_type": "Bearer",
"scope": "46da2f7e-b5ef-422a-88d4-2a7f9de6a0b2/all 46da2f7e-b5ef-422a-88d4-2a7f9de6a0b2/.default",
"expires_in": 4397,
"ext_expires_in": 4397,
"access_token": "[REDACTED]",
"refresh_token": "[REDACTED]",
"id_token": "[REDACTED]",
}
随后,该 Access Token 会作为 Bearertoken 用于 Authorization标头中。
就隧道功能而言,GitHub 和 Azure 各自是相互隔离的隧道会话容器。这意味着使用 GitHub 进行身份验证的 VS Code 隧道服务端无法通过 Entra ID 账户访问,反之亦然。
我们暂且讨论到这里,但稍后会在文章中再回到这个身份验证流程。
Layer 1 – WebSocket 隧道
REST 层完成之后,接下来是 WebSocket 连接。
该 WebSocket 连接是建立到先前在 endpointsJSON 数据块中观察到的 clientRelayUri上的。
最初的 HTTP 请求如下:
GET /api/v1/Client/Connect/new-cat-zpzt4s0 HTTP/1.1
Authorization: tunnel [CONNECT-TOKEN-HERE]
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: w17AVnNaZMHAkcZ6BrYzyg==
Host: uks1-data.rel.tunnels.api.visualstudio.com
Sec-WebSocket-Protocol: tunnel-relay-client-v2-dev, tunnel-relay-client
在 Sec-WebSocket-Protocol标头中我们有两个子协议,然而,对于 VS Code 而言,我们将聚焦于 tunnel-relay-client,它标识了在已建立的 WebSocket 隧道之上建立 SSH 连接时所使用的协议类型和认证方法。
正如上述请求中所看到的,先前生成的 connectJWT 令牌现在被用于 Authorization 标头中。
一旦此握手完成且 WebSocket 隧道建立成功,Microsoft Relay Server 就会在客户端 WebSocket 与服务端 WebSocket 之间充当中转,在两者之间转发消息,以避免任何防火墙或伪装方面的问题。
Layer 2 – SSH 连接
第一层建立之后,VS Code 接着会通过 WebSocket 隧道发起一个 SSH client 连接。
可惜从这里开始事情就有点棘手了。当通过 VS Code(或在 CLI 中执行 code --tunnel) 启动一个新的 dev-tunnel 服务端时,会使用 Rust crate russh。如果我们尝试使用诸如 golang 的 crypto/ssh或 Python 的 Paramiko这类标准库去连接此 SSH 服务端,事情只能走到某一步就会卡住。
当 SSH 连接建立时,会使用用户名 tunnel以及 None认证方法。之所以允许这样,是因为外层的 WebSocket 已经通过 connectJWT 完成了身份验证。
在 VS Code 这里,隧道建立过程会通过 KEX 协商 HMAC_SHA256_ETMMAC 算法。如果未能协商成功,则 SSH 连接将在认证握手期间失败。
认证完成后,会建立一个从服务端到客户端的 port forward。然而,要发起这一过程,是由客户端 (而非服务端) 使用 forwarded-tcpip通道来与服务端端口 31545建立连接。这一点偏离了常规做法,因为通常情况下,forwarded-tcpip通道是在有连接进入远程端口转发 (ssh -R 8080:localhost:8080) 时由服务端发起的,这当然意味着标准库在不经修改的情况下无法工作。
如果我们使用 russhcrate 作为我们的 SSH client,所需的补丁可以在这里找到,并可通过以下命令应用:
git apply ../patch/russh.patch
到这一阶段,我们的 SSH 隧道连接已在 WebSocket 隧道内建立完成。
Layer 3 – MsgPack RPC
随着 SSH 隧道已经建立并通过认证,我们现在具备了交换消息的能力。客户端与服务端之间的消息通过 MsgPack 进行序列化。RPC 消息格式如下所示:
pubstructRequest<T> {
pub id: u32,
pub method: String,
pub params: T
}
Bishop 在审阅源代码时,梳理出了以下有用的 RPC 方法:
-
spawn_cli -
通过 VSCode server 二进制文件中内置的 shell 执行命令
-
spawn -
直接执行命令
-
fs_read -
读取文件内容
-
fs_write -
写入文件内容
-
fs_connect -
将 named pipe 或 unix socket 通过该隧道转发
-
fs_stat -
获取文件信息
-
fs_rm -
删除文件
-
fs_mkdirp -
创建新目录
-
fs_readdir -
列出目录内容
-
fs_rename -
重命名文件
-
sys_kill -
结束进程
我想要进行 POC 验证的一个 RPC 方法是 spawn命令,它会在隧道服务端上执行远程命令。请求中传递的参数为:
pubstructSpawnParams {
pub command: String,
pub args: Vec<String>,
pub cwd: Option<String>,
pub env: HashMap<String, String>,
}
服务端返回的响应同样会被编码为:
pubstructSuccessResponse<T> {
id: u32,
result: T,
}
而在 “Spawn” 的场景下,result 的内容将是:
pubstructSpawnResult {
pub message: String,
pub exit_code: i32,
}
为了能够单独调用每个 RPC 方法,Bishop 和我开发了一款名为 Ouroboros的工具,它将各 RPC 命令封装在一个 Rust 工具中,项目地址如下:
https://github.com/xpn/Ouroboros
该工具的使用方式十分直观。你可以通过 management命令列出与当前账户 token 关联的现有隧道:
[*] Starting Ouroboros Management Client
Tunnel List:
Name: sneaky-fog-s6llk1t
Labels: ["prometheus", "protocolv4", "vscode-server-launcher", "_flag3"]
Created: "2025-11-03T11:20:51.376614Z"
Name: interesting-pony-lztwqbj
Labels: ["serenity", "protocolv4", "vscode-server-launcher", "_flag8"]
Created: "2026-04-13T11:54:57.1330336Z"
Name: new-cat-zpzt4s1
Labels: ["bishop", "protocolv4", "vscode-server-launcher", "_flag8"]
Created: "2026-03-20T12:59:47.5209985Z"
Name: swift-dog-jzb5q13
Labels: ["romulus", "protocolv4", "vscode-server-launcher", "_flag8"]
Created: "2026-03-31T13:06:23.5272748Z"
选定目标之后:
使用 Ouroboros 实现我们的 D33dz
那么这个工具在我们不断扩展的武器库里能扮演什么角色呢?这种攻击技术适用于多个场景,其中最明显的就是持久化,通过从一个已被攻陷的主机上设置新的隧道来实现。
Ouroboros 也允许我们很好地横向移动到其他运行 dev tunnels 的系统。API 凭据存储在 state.vscdb数据库中,并使用 Electron 的 safeStorage进行加密,这意味着可以通过 inspect-brk技巧,由任何在与 VS Code 相同用户账户下运行的进程提取出 token:
但实际上还有另一种场景,dev tunnels 也可以派上用场,那就是初始访问。
让我们重新审视访问 dev tunnels 所需的条件。当身份验证首次启动时,会通过 OAuth2 请求访问 Entra token:
POST /organizations/oauth2/v2.0/token?client-request-id=ab6dc52d-ddf8-40bc-8300-f1a4bf27a4d2 HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
Host: login.microsoftonline.com
...
client_id=aebc6443-996d-45c2-90f0-388ff96faa56&
redirect_uri=http%3A%2F%2Flocalhost%3A50906&
scope=46da2f7e-b5ef-422a-88d4-2a7f9de6a0b2%2F.default%20email%20openid%20profile%20offline_access&
code=CODE-HERE&
x-client-SKU=msal.js.node&
x-client-VER=3.8.3&
x-client-OS=darwin&
x-client-CPU=arm64&
x-ms-lib-capability=retry-after%2C%20h429&
x-client-current-telemetry=5%7C871%2C0%2C%2C%2C%7C%2C&
x-client-last-telemetry=5%7C0%7C%7C%7C0%2C0&
code_verifier=zkg1aTdQZ1k2RnUablgxQXljBWhMU0taUGl1dnVoTVpz&
grant_type=authorization_code&client_info=1
通过 entrascopes.com,我们可以快速看到这个 Client ID 与 Visual Studio Code 相关联。
资源标识为 46da2f7e-b5ef-422a-88d4-2a7f9de6a0b2,对应的是 Dev Tunnels Service:
如果我们将此资源访问情况与其他 Entra 客户端进行交叉对比,就会发现还有其他几个客户端也拥有对 Dev Tunnels Service的访问权限。
这意味着,如果你能够获取此列表中的任意客户端的用户 access token,你就拥有了访问该用户和/或组织的 dev tunnels 所需的全部条件:
但我们还可以更进一步。如果仔细观察,可以看到其中一个客户端是 Family of Client IDs (FOCI) 的成员:
https://entrascopes.com/?appId=872cd9fa-d31f-45e0-9eab-6e460a02d1f1
这意味着什么?这意味着如果我们能够攻陷任何一个 FOCI 成员,我们就可以获得 dev tunnels 的 access token,例如:
此图仅展示了 3 个潜在的客户端,但实际上在发布这项研究时已有 49 个 FOCI 客户端,可在此处找到:https://entrascopes.com/?foci=true
那么假设我们使用 Device Code Phishing 来为 Teams 请求一个 access token:
完成后,我们获取得到的 Refresh Token,并用它来换取访问 Visual Studio - Classic的 access token:
在测试时,这种方法在 Microsoft(也就是 Live) 账户上效果良好,但遗憾的是 Entra ID 租户似乎会拒绝将 Visual Studio - Classic作为 FOCI 客户端。这可能意味着它现已从 FOCI 列表中被移除。
考虑到这种情况,我也转而审查 BroCI (NAA) 客户端,这些客户端由同事 Specter Hope Walker 在此处进行了记录。同样,BroCI 扩展了我们可利用的攻击面。
在审查 BroCI 选项时,我们发现了 AzurePortal Console App。
AzurePortal Console App 是一个 Nested Application。正如 Redirect URIs 所显示的那样,它依赖于 Azure Portal Application 作为其 broker 来铸造新的 access token。这意味着如果我们拥有用户对 Azure Portal 的会话,我们也就有了为该用户/组织访问 Dev Tunnels 的路径。
二元性
我必须承认,当我最初完成这个项目的初步研究时,我能预见到这会变成一篇宣称”看 LLM 能做这一切有多酷”的博客文章。直到我开始复盘攻击路径时,我才开始质疑你现在所读到的内容究竟有多少要归功于 Bishop。所以我想稍微反思一下这个问题,因为我知道许多人也在质疑同样的事情。
首先,当我开始使用 LLM 时,我就清楚自己绝对不会把 LLM 生成的工作据为己有,并且会确保在任何使用了 LLM 协助的地方都加以注明。所以这是本项研究的分工:Bishop 梳理了 dev tunnels 协议并创建了 russhcrate 的补丁。我亲手构建了 Ouroboros 的初稿 (如此处所示),并探索了攻击路径以构建 Red Team 的叙事框架。而最有可能的是,某个模型正在协助审阅这篇博客,同时还有我们出色的技术编辑 Sarah,是她确保我们产出的杂乱文字最终能转化为各位读到的这篇连贯博客。
我认为我们已经建立了良好的惯例,即在直接使用他人工作或以其作为研究灵感时给予作者署名。鉴于我们能看到 LLM 在多大程度上依赖于他人的成果 (试试就你正在研究的某个新颖课题进行提示,你就会看到训练数据匮乏所带来的差异),将这些过去 40 年间黑客们在互联网上无偿贡献的智力结晶累加起来据为己有,感觉是不对的。但正如我们站在巨人的肩膀上,在 LLM 生成的研究之上加入你自己有意义的见解应当被视为一种值得庆祝的成就,与此同时模型所提供的协助也应当持续得到承认。
话虽如此,这篇博客确实给我留下了一些疑问。第一个是:”这项研究真的是我完成的吗?”
把一个项目交给 LLM 并看到如此惊艳的成果,而无需经历多个夜晚的痛苦,这种感觉依然很奇怪。但对我来说,现实情况是这样的:
从职业层面上讲,这并不重要!我的意思是,价值并非由敲下代码的那个人交付的。我希望存在、可供其他 Specters 在评估中使用的工具已被创造出来。它能运作、它履行了我被付费履行的职责,并且是在以往所需时间的一小部分内完成的,使其能够比以往更早地投入使用。
从个人层面上讲,我是否感到被”剥夺”了发现的过程或揭示新技术的机会?没有。再说一次,在这个领域驱动我前进的,是我通过拆解一项技术、彻底理解它,并加上我自己恶意手笔时所获得的知识。我可以坦白地说,这个项目结束时的感觉,和我此前完成的任何项目完全一样。
那么,我本可以把 Claude 或 GPT 的输出直接复制到这篇博客里就收工。但由于我的目标始终如一,我反而坐下来,根据研究的产出,亲手打造了 Ouroboros 工具的初稿,这样我才能确切地理解所有部件是如何组合在一起的。
成果是一个非常早期的 POC,我在内部发布了它:
一旦我确信自己已经掌握了想要获得的知识水平,并且把新的技战术更新到了我的 Notion playbook 中,剩下的就只是把其他 RPC 命令接入到工具里。这正是 LLM 高效擅长的事情,所以我让 Bishop 接手,去完成最终的精雕细琢版本。这使得它能够以远比以往可能的速度更快地在内部发布。
还有另一个时刻让我对 Bishop 在这个项目中的贡献产生了思考,那就是 Entra ID FOCI/BroCI 那一节。
我的研究提示词清晰地将模型框定在为 Red Team 目的构建 C2 工具上。GPT-5.4-Cyber 自始至终都没有将 Device Code Phishing 与 GitHub OAuth 认证的应用方式联系起来。同样,也没有任何迹象表明 FOCI 可以被用来将一个 refresh token 转换为 access token,用于跳板进入现有的 dev tunnel。相反,这个联系来自于 2024 年我和一屋子 Specters 与 Dirk-jan 共度一周、参加他 Entra 培训的记忆。同样地,与模型交互的过程中也从未出现过 BroCI;这个联系来自于去年早些时候在芝加哥的一次内部 hack-a-thon 上,Chris、Hope、Darrius和 Costa 展示 BroCI 时留下的记忆。
那么如果直接问,模型能否建立起这种联系?当然可以。如果通过提示词引导它走向这个主题,它能到达那里吗?当然!但这正是问题所在 —— 我没有问,是因为当时我并不知道研究的方向走向何处。这项技术是否还存在多种其他可被模型识别的利用方式?我相信它们存在,但要让它们浮现出来需要恰当的提示词,而构思恰当的提示词所需要的领域上下文,比我目前所积累的还要多。
或者换种说法…… LLM 不会在某个周一早晨醒来后选择施暴。那全是我!
先前研究
在撰写这篇博文的过程中,我开始搜寻其他可能记录过该协议的研究或文章。
Karpagarajan Vikkii 和 Amanda Rousseau 在 4 月初于 straiker.ai (https://www.straiker.ai/blog/nomshub-cursor-remote-tunneling-sandbox-breakout) 发布了一篇关于 Cursor 的精彩文章。该文章清晰地讨论了隧道规范,并重点介绍了 RPC 命令,我希望对他们的研究表示认可。最初当我看到有人已经发布了一篇文章时,我本打算放弃这篇博文,但鉴于我们得出的结论有所不同,我认为一篇专注于红队视角的工具与文章仍然具有价值。
有一点值得关注:尽管该研究基于 Cursor,但 Cursor 是 VS Code 的一个分支,并使用相同的 dev tunnels 协议。因此,这篇博文的适用范围远不止于 VS Code。
祝各位 h4xx0rz 狩猎愉快!
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:securitainment Adam Chester Adam Chester《意外的 C2:借道 VS Code Dev Tunnels 实现远程访问》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论