hh54188

阿里云效泄露了我的 GitHub OAuth Access Token,以下是排查过程和证据

  •  
  •   hh54188 · 2h 10m ago · 481 views

    我知道这是很严肃的指责,我也怕我的推理过程有什么错,于是发出来让大家帮忙 review 一下,如果真的其中有误我道歉,丢人现眼也值了。文中涉及到的所有描述,包括攻击,提交,日志等等都有截图。嫌麻烦暂时就先不放了,感觉最后的日志是实打实的证据。

    背景

    从十月 20 号开始,我发现的我的私人 repo 频繁被“盗”(注意,是几乎所有的私人 repo ,这个信息非常重要,后面会用到),具体来说行为包括但不限于

    • 被提交恶意的 woff2 文件脚本,即虽然文件格式是字体文件 woff2 ,但是里面是恶意的盗取各种 secrets 的脚本,并间接通过自动化任务(.vscode/tasks.json 文件)触发执行
    • 恶意的 shell 被提交到代码库中,并试图通过 postinstall 脚本来执行
    • 莫名其妙的分支被创建

    可以猜测这些行为都是通过 AI agent 批量完成,因为 1 )从提交时间看它们操作的行为极快; 2 ) postinstall 的脚本很明显是根据 package.json 的上下文生成的,它让你感觉是你自己编写的代码的一部分; 3 )通过恶意脚本里涉及到 IP 地址,我在 twiiter 上有搜索到有人遭受到了相似的攻击,但是它所生成的脚本是一个 yaml 文件,很明显这也是根据上下文生成的

    排查

    事实上我的账号在半年前被盗过一次了,并且采取了一些列补救措施(见我的这篇博客《在 GitHub 账号在被盗四次之后,这是我的补救和反思》)。上次的经验让我这次排查过程变得得心应手。

    首先说说问题不可能出现在哪里:

    PAT (Personal Access Token)

    在经历过上次被盗事件之后,我已经放弃了 PAT 。不能说没有 PAT ,但是我目前生成的 PAT 权限已经小到可怜,不会有任何的写权限,所以这次入侵不可能是它引起的。

    SSH Key

    我也怀疑过是不是因为我本地中毒导致我的 SSH Key 泄漏,好在 GitHub 会实时显示你的 SSH Key 最近使用时间是什么时候。于是我做了一个实验:生成全新的 SSH Key 并保证一段时间不使用它,看恶意提交还会不会发生,结果恶意提交还是发生了,证明不是 SSH Key 导致的

    登陆我的 GitHub 账号

    不可能,我的 GitHub 账号开启了 2FA ,并且从自带的 security log 上看,我账号的每日登陆地区都是预期之内的。

    其他间接渠道,例如 VPS

    我的确有一些 repo 被部署在我的私人 VPS 上,假设我管理不善不小心打通了 VPS 部署到代码提交的渠道,那理论上也只应该涉及到这一个 repo 而已。正如我前文所示,我几乎所有的私人 repo 都产生了恶意修改,在确认 SSH Key 和 PAT 已经处于是安全的情况下,黑客不太可能能够实现这一点。

    Authorized GitHub Apps

    注意上面所说的是 Authorized GitHub Apps 而不是 Authorized OAuth Apps ,阿里云效属于后者,这个我们稍后再。这里我们先排除 Authorized GitHub Apps 的可能性。

    我的 Authorized GitHub Apps 只安装了两个:Cursor 和 Render ,它们都是来自于正规渠道,并且两者授权的 repo 都极其有限,Cursor 我只授权了 4 个私人 repo 的访问权限,Render 我只授权了 1 个 repo 的访问权限,这也与我看到的大量 repo 被改动的现状不符。

    下套抓捕

    于是嫌疑犯只剩下一个了,那就是阿里云效,之所以它的嫌疑最大,因为与 GitHub Apps 不同的是,它算是一个 (已经过时了的) OAuth App 。你无法在安装它时选择默认可以访问的 repo ,这其实是一种极大的安全隐患。我使用它只是为了能够将我的 jekyll 博客 repo 部署到阿里云服务器上,但这也意味着它拿到了我所有 repo 的访问权限。一旦它访问我账号的 token 被泄露,我的所有 repo 也就被泄露了。

    要知道这个 token 对我来说都是不可见的,在阿里云效中我使用的是一台位于香港的服务器负责拉去我博客的最新 artifact ,授权以及 token 的保存应该发生在那台服务器上面

    如何证明是它导致的呢?分为两个阶段

    首先我在 11 月 1 日停止了对它的授权,看看攻击行为还会不会发生。因为就我观测到的结果看,攻击行为发生的非常频繁,平均一周几次。结果就是从 11 月 1 日至 11 月 5 日,没有任何攻击行为发生。

    然后在 11 月 5 日,我重新开始了对它的授权,不过这次不一样的地方在于,我把我的账号升级到了 GitHub Enterprise 版本,并且建了一个 organization 以及塞了一些临时生成的又或者是不痛不痒的 repo 进去,并且把这个 organization 授权给了它。之所以要这么做,是因为只有 GitHub Enterprise 版本支持更详细的 audit log ,如果恶意攻击再次发生,我就可以拿到更详细的提交者的信息。

    然后攻击真的发生了, 不过攻击变成了它直接下载我的 repo 的 zip 包。

    不过不重要,这次我拿到了完整的 log ,我把涉及到的 log 放出来,为了便于说明,这里我只列举了授权那天的 log ,以及最新的下载 log ,并且把日志中的 actor 、repo 、org 等关键信息给 mask 了

    [
      {
        "@timestamp": 1794189036015,
        "_document_id": "eRtUkmVro5ipin8ld2jpqg",
        "action": "repo.download_zip",
        "actor": "ACTOR_1",
        "actor_id": "ACTOR_ID_1",
        "actor_is_bot": false,
        "actor_location": {
          "country_code": "BG"
        },
        "business": "ORG_1",
        "business_id": "BUSINESS_ID_1",
        "created_at": 1794189036015,
        "hashed_token": "[REDACTED]",
        "oauth_application_id": 1242664,
        "operation_type": "access",
        "org": "ORG_1",
        "org_id": "ORG_ID_1",
        "programmatic_access_type": "OAuth access token",
        "public_repo": false,
        "repo": "REPO_2",
        "repo_id": "REPO_ID_2",
        "request_access_security_header": null,
        "request_id": "9A02:16F6:BEDE03:12177E3:6AF126EB",
        "token_id": "TOKEN_1",
        "token_scopes": "admin:repo_hook,repo,user",
        "user_agent": "bifrost-scanner",
        "visibility": "internal"
      },
      {
        "@timestamp": 1793872603949,
        "_document_id": "ph7RU2FqntEvRbVgBsdkfQ",
        "action": "org.oauth_app_access_approved",
        "actor": "ACTOR_1",
        "actor_id": "ACTOR_ID_1",
        "actor_is_bot": false,
        "actor_location": {
          "country_code": "SG"
        },
        "business": "ORG_1",
        "business_id": "BUSINESS_ID_1",
        "created_at": 1793872603949,
        "oauth_application_name": "Flow",
        "operation_type": "modify",
        "org": "ORG_1",
        "org_id": "ORG_ID_1",
        "request_access_security_header": null,
        "request_id": "6984:14BCCF:132EBE4:1C0EAA9:6AEC52DB",
        "url": "https://github.com/orgs/xxx/policies/applications/1242664/set_state?state=%5BFILTERED%5D",
        "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36"
      }
    ]
    
    

    可以看出我在最早的 11 月 5 日授权的 oauth_application_name 就叫做 Flow ,并且 url 里的 application ID 是 1242664 ,然后在最新的日志里涉及到的 oauth_application_id 刚好就是 1242664 ,很明显发出下载行为的 application 就是 Flow 。

    也就是说,有人拿到了 Flow 生成的 OAuth Token ,以我的身份下载了我的 repo ,那个人是谁呢?不知道,反正不是我

    5 replies  •  2026-11-09 20:26:04 +08:00
    catazshadow
    catazshadow
    1h 43m ago via Android
    1
    可能又是降本增笑
    itnoob
    itnoob
    1h 41m ago
    2
    华为云都在找不到我 ak/sk 被泄露的情况下,通过接口给我创建了按需应用,没有任何通知,然后产生了几十刀的费用;那个账号好久不用,偶尔登上去才发现的,各种推辞,无语了。
    biu7
    biu7
    1h 29m ago
    3
    暂时没看出问题,但是我先去把云效撤销授权了。。
    baleeny
    baleeny
    41 mins ago
    4
    有没有大佬指导下我这个: https://www.v2ex.com/t/1255670#reply0
    milkleeeeee
    milkleeeeee
    40 mins ago
    5
    火钳刘明
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2321 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 41ms · UTC 13:06 · PVG 21:06 · LAX 05:06 · JFK 08:06
    ♥ Do have faith in what you're doing.