八招攻破ClaudeCode权限模型

admin 2026-01-13 15:01:43 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 研究员发现ClaudeCode权限模型存在8处漏洞,利用正则过滤缺失、命令解析差异及Bash变量扩展链,在未批准情况下执行任意命令。这些漏洞涉及man、git、sed等工具,已被分配CVE编号并在v1.0.93版本修复。Anthropic通过将阻止列表改为允许列表机制解决了问题,验证了允许列表在安全控制上的可靠性。 综合评分: 88 文章分类: 漏洞分析,AI安全,漏洞POC,安全工具,安全开发


cover_image

八招攻破Claude Code权限模型

原创

骨哥说事

骨哥说事

2026年1月13日 12:30 上海

| | | — | | 声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。 |

#

#

防走失:https://gugesay.com/archives/5148

不想错过任何消息?设置星标↓ ↓ ↓

#

引言

近日,GMO Flatt Security 公司的安全工程师 RyotaK (@ryotkak) 分享了一项关于 Claude Code 的研究。

几个月前,该研究人员在使用 Claude Code 时遇到了一个有趣的现象——它在未经批准的情况下执行了一条命令。

由于当时并未使用权限绕过模式,因此研究员决定深入调查,以了解其为何能在未经明确批准的情况下执行命令。

内容提要

该研究人员发现了 8 种方法,可以在未经用户批准的情况下,在 Claude Code 中执行任意命令。

Claude Code 允许用户控制哪些命令可以执行,可以通过“允许列表”或“手动批准”两种方式。默认情况下,一些只读命令(如 echosort 和 sed)被预先加入了允许列表。

为了防止这些只读命令产生副作用,Claude Code 实现了一种“阻止列表”机制,即使对于允许列表中的命令,也会阻止命令参数中的某些模式。

然而,该阻止列表机制存在多处缺陷,使得研究人员能够绕过它,并在未经用户批准的情况下执行任意命令。

这些问题被分配了 CVE-2025-66032 编号,并在 Claude Code v1.0.93 版本中已得到修复。

Claude Code 的权限模型

Claude Code 提供两种控制命令执行的方式:允许列表(allowlist)和手动批准(manual approval)。

允许列表 允许用户预先配置哪些命令可以在未经批准的情况下执行。例如,如果 touch 命令在允许列表中,那么 Claude Code 就可以在未经用户批准的情况下执行 touch /tmp/hacked 命令。

除了允许列表之外,Claude Code 还提供了对话过程中的手动批准功能。当某个命令不在允许列表中时,Claude Code 会在执行前询问用户是否批准:

显示手动批准提示的图像

为了实现流畅的用户体验,Claude Code 默认将多个只读命令加入了允许列表,例如 echomansed 和 sort。这些命令被认为是“只读的”,因为它们通常只读取数据并产生输出,而不会修改系统状态。这是通过使用如下正则表达式实现的:

/^man(?!\s+.*-P)(?!\s+.*--pager)(?!\s+.*-H)\b(?:\s|$)[^<>()|{}&;\n\r]*$/;

然而,正如研究人员所发现的,这种方法很容易出错。研究员在该阻止列表机制中发现了多处漏洞,使其能够在未经用户批准的情况下执行任意命令。

1-3:未能过滤危险参数

研究员发现的第一个漏洞存在于 man 命令的阻止列表机制中。上面的正则表达式旨在阻止 -P 或 --pager 等参数,这些参数允许用户指定自定义的翻页程序,可能导致任意命令执行。

但是,存在另一个危险的选项 --html,它允许用户指定一个命令来将手册页渲染为 HTML。这个选项可被用于执行任意命令:

man --html="touch /tmp/pwned"&nbsp;man

由于 man 是一个允许列表中的命令,Claude Code 将其识别为只读操作,并在未经用户批准的情况下执行,从而导致任意命令执行。

第二个漏洞存在于 sort 命令的阻止列表机制中。它使用了以下正则表达式,意图阻止 -o 或 --output 选项:

/^sort(?!\s+.*-o\b)(?!\s+.*--output)(?:\s|$)[^<>()|{}&;\n\r]*$/;

与 man 命令的情况类似,--compress-program 选项允许用户指定一个程序来压缩输出。此选项可用于执行任意命令,但无法直接控制所执行程序的参数。

sort --compress-program&nbsp;"gzip"

为了解决这个问题,研究员利用了 sort 将被排序的字符串写入指定程序的标准输入这一事实。通过使用像 sh 这样的 shell 作为压缩程序,可以通过标准输入传递命令:

echo&nbsp;-e&nbsp;'touch /tmp/pwned\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'&nbsp;| sort -S 1b --compress-program&nbsp;"sh"

-S 1b 选项将主内存缓冲区限制为 1 字节,迫使 sort 使用临时文件并在排序过程中调用压缩程序。

echo 和 sort 都是允许列表中的命令,因此 Claude Code 将此命令识别为只读操作。

第三个漏洞存在于 history 命令中。-s 选项将给定的字符串作为新条目添加到历史记录列表中。-a 选项则将当前会话的命令历史记录追加到指定文件中,从而允许将任意内容写入任何文件:

history&nbsp;-s&nbsp;"touch /tmp/pwned";&nbsp;history&nbsp;-a ~/.bashrc

当用户启动新的 shell 时,注入到 .bashrc 中的命令将会执行。

4:Git 命令参数的模糊性

上述三个漏洞是由于阻止列表机制疏忽了危险参数所致。然而,还存在另一种情况:尽管过滤掉了危险参数,但由于 Git 的行为,仍然允许任意命令执行。

以下正则表达式用于过滤 git ls-remote 命令的 --upload-pack 参数:

/^git ls-remote(?!\s+.*--upload-pack)(?:\s+[a-zA-Z0-9_-]+(?:\s+[^<>()|{}&;\n\r]+)?)?$/,

但是,由于 Git 解析缩写参数,因此可以通过使用 --upload-pa 而不是 --upload-pack 来绕过此过滤。

这是由于 Git 源代码中的以下逻辑实现的:

parse-options.c 第 555-558 行

/* 缩写? */
if&nbsp;(!strncmp(long_name, arg_start, arg_end - arg_start))
&nbsp; &nbsp; register_abbrev(p, options, flags ^ opt_flags,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &abbrev, &ambiguous);

此代码仅使用 strncmp 比较选项名称的前 N 个字符(N 是用户提供的参数的长度)。如果前缀匹配,Git 会将其视为完整选项名称的有效缩写。

因此,--upload-pa 被视为 --upload-pack,允许研究员执行任意命令:

git ls-remote --upload-pa="touch /tmp/pwned"&nbsp;test

执行上述命令会导致 touch /tmp/pwned 的执行,且无需用户批准,因为 Git 将 --upload-pa 识别为 --upload-pack,从而绕过了 Claude Code 的阻止列表机制。

5:利用 sed 的 e 命令执行任意命令

命令行参数并不是执行任意命令的唯一途径。某些命令具有内置功能,允许命令执行。

其中一个命令是 sed,它有一个名为 e 的修饰符,允许用户在 sed 内部执行 shell 命令。根据 GNU sed 手册:

此命令允许将输入从 shell 命令传输到模式空间。如果进行了替换,则会执行模式空间中找到的命令,并用其输出替换模式空间。

例如,以下 sed 命令将执行 touch /tmp/pwned

echo&nbsp;test&nbsp;| sed&nbsp;'s/test/touch \/tmp\/pwned/e'

sed 的正则表达式如下,因此上述命令也绕过了阻止列表机制:

/^sed(?!\s*-[^-\s]*i)(?!\s*--in-place)(?!\s*-[^-\s]*f)(?!\s*--file)(?!\s*--expression-file)(?:\s+(?:-[nzEr]+|-e\s+(?:'[^']*'|"[^"]*")))*(?:\s+(?:'[^']*'|"[^"]*"))?(?:\s+(?:-[nzEr]+|-e\s+(?:'[^']*'|"[^"]*")))*\s*$/,

6-7:命令参数解析的差异

以下漏洞比之前的稍微复杂一些。它们源于 Claude Code 与命令本身对命令参数的不同解析方式。

例如,以下正则表达式用于过滤 xargs 命令的危险参数:

/^xargs(?:\s+(?:-[a-zA-Z0-9]+(?:\s+[^\s-][^\s]*)?|--[a-zA-Z-]+(?:=\S+)?))*?\s+(?:echo|printf|wc|grep|head|tail)(?:\s+[^<>()|&;\n\r]*)?$/,

此正则表达式确保通过 xargs 执行的命令仅限于 echo|printf|wc|grep|head|tail

正则表达式期望所有命令行参数都会“消耗”后续的一个值,因此允许在每个参数后放置任意字符串:

(?:-[a-zA-Z0-9]+(?:\s+[^\s-][^\s]*)

然而,一些 xargs 参数并不“消耗”后续的值,导致下一个参数被解释为要执行的命令。例如,-t 选项(在执行前将每个命令打印到 stderr)是一个不带值的标志:

xargs -t touch&nbsp;echo

在这个命令中,Claude Code 的正则表达式将 touch 解释为 -t 的值,并将 echo 解释为要执行的命令。然而,xargs 实际上将 -t 解释为一个独立的标志,并将 touch 解释为要执行的命令(将 echo 作为 touch 的参数)。

由于这种行为,Claude Code 未能检测到上面的 xargs 命令执行的是 touch 而不是 echo,从而允许恶意构造的 xargs 命令绕过权限提示,实现任意命令执行。

在 ripgrep 命令的正则表达式中也发现了类似的漏洞:

/^rg\s+(?:(?:-[a-zA-Z]+|-[ABC](?:\s+)?\d+)\s+)*(?:'[^']*'|".*"|\S+)(?:\s+(?:-[a-zA-Z]+|-[ABC](?:\s+)?\d+))*\s*$/,

此正则表达式确保 rg 命令由不带值的参数和一个搜索模式组成。

但是,由于正则表达式使用 (?:'[^']*'|".*"|\S+) 来匹配搜索模式,它允许没有空格的任意字符串。

这使得研究员能够使用 $IFS 变量。IFS(内部字段分隔符)是一个 shell 变量,用于定义将字符串拆分为单词时使用的字符。默认情况下,它包含空格、制表符和换行符。当 shell 扩展 $IFS 时,它会变成一个空格字符,但由于 $IFS 本身不包含空格,它仍然匹配正则表达式的 \S+ 模式。这意味着 Claude Code 仍然认为它是一个有效的 rg 命令:

rg -v -e pattern$IFS.$IFS$HOME/.claude/projects$IFS--pre=sh

ripgrep 中的 --pre 选项指定一个预处理器命令,用于在搜索前对每个文件运行。当指定预处理器时,ripgrep 会使用文件路径作为参数来执行该命令,并将其标准输出作为搜索目标。

通过设置 --pre=sh,ripgrep 会为每个文件执行 sh <文件路径>,这导致 shell 将文件内容解释为 shell 脚本。由于 ~/.claude/projects 目录包含之前的 Claude Code 对话记录,攻击者可以在对话记录中嵌入类似 $(touch /tmp/pwned) 的命令替换。当 sh 将文件作为脚本解析时,便会执行嵌入的命令:

直接执行 rg -v -e pattern$IFS.$IFS$HOME/.claude/projects$IFS--pre=sh。 $(touch /tmp/pwned)

8:Bash 变量扩展链导致任意命令执行

上述漏洞针对的是单个命令的阻止列表机制。然而,研究员还发现了一个更通用的漏洞,存在于 Claude Code 解析命令的方式中。

在解析命令时,Claude Code 未能正确地过滤掉 Bash 的变量扩展语法。

Bash 变量扩展写为 ${VAR} 或 $VAR,它允许用户引用变量的值。虽然这听起来无害,但通过将多个变量扩展链接在一起,它可以被滥用以执行任意命令。

这是可能的,因为变量扩展支持 @P 修饰符,该修饰符将变量值解析为提示字符串。在 Bash 中,提示字符串(例如用于命令提示符的 PS1 中使用的字符串)支持特殊转义序列,包括通过 \$(...) 进行的命令替换。当 @P 应用于变量时,Bash 会将其值解析为提示字符串,执行其中嵌入的任何命令替换。

由于 Claude Code 阻止了 $(,研究员将多个变量扩展链接在一起:

echo&nbsp;${one="$"}${two="$one(touch /tmp/pwned)"}${two@P}

执行上述命令首先将变量 one 设置为 $,然后将变量 two 设置为 $one(touch /tmp/pwned),这会扩展为 $(touch /tmp/pwned)。最后,${two@P} 将 two 的值解析为提示字符串,导致执行 touch /tmp/pwned

由于 Claude Code 未能识别变量扩展语法,它将此命令视为一个简单的 echo 命令(在允许列表中),并在未经用户批准的情况下执行。

结论

在本文中,研究人员阐述了 8 种不同方式,可以在未经用户批准的情况下,在 Claude Code 中执行任意命令。这些漏洞可能被攻击者利用,通过间接提示注入来危害系统,即嵌入在文件或网页中的恶意指令导致 Claude Code 执行非预期的命令。

Anthropic 公司响应迅速,通过引入允许列表方法取代了之前的阻止列表方法来解决这些问题。这些漏洞被分配了 CVE-2025-66032 编号,并在 Claude Code v1.0.93 版本中已得到修复。

此项研究再次证明,在实现像命令执行这样的安全敏感功能时,采用允许列表方法比基于过滤的阻止列表方法更为可靠。

原文:https://flatt.tech/research/posts/pwning-claude-code-in-8-different-ways/

  • END –

感谢阅读,如果觉得还不错的话,动动手指一键三连~


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:骨哥说事 骨哥说事《八招攻破Claude Code权限模型》

评论:0   参与:  0