格式良好的printf通常有一个可以使用的格式:
$ var="Hello"
$ printf '%s\n' "$var"
Hello
Run Code Online (Sandbox Code Playgroud)
但是,不提供格式可能会产生什么安全隐患?
$ printf "$var"
Hello
Run Code Online (Sandbox Code Playgroud)
由于变量扩展被引用,应该不会有任何问题,不是吗?
Sté*_*las 31
在:
\nprintf "$var"\nRun Code Online (Sandbox Code Playgroud)\n有两个问题:
\n$var如果处于攻击者的控制之下可能会出现问题--),因此$var如果它以 开头,则可以将其视为选项-。情况会更糟:
\nprintf $var\nRun Code Online (Sandbox Code Playgroud)\n大多数类似 Bourne 的 shell 中的 split+glob 都是在$var扩展时执行的,这会导致安全隐患中提到的安全隐患:忘记在 bash/POSIX shell 中引用变量。
这里可以执行任意命令:
\n$ export var1=\'-va[1$(uname>&2)] x\' var2=\'%d a[1$(uname>&2)]\'\n$ bash -c \'printf $var1\'\nLinux\n$ ksh -c \'printf $var2\'\nLinux\n0\nRun Code Online (Sandbox Code Playgroud)\n任意uname命令(幸运的是这里无害)由printf.
为了
\nprintf "$var"\nRun Code Online (Sandbox Code Playgroud)\n就其本身而言,我能想到的问题就更少了。
\n最明显的一个是 DoS,var=%1000000000s它会在输出中发送大量空格字符,或者更糟糕的是%.1000000000f,还会占用大量内存和 CPU 时间:
$ var=%.1000000000f command time -f \'max mem: %MK, elapsed: %E\' bash -c \'printf "$var"\' | wc -c\nmax mem: 4885344K, elapsed: 0:12.33\n1000000002\nRun Code Online (Sandbox Code Playgroud)\n其他 DoS 可能是$var由于格式不正确或选项不正确而触发语法错误的值,导致printf失败以及在启用该errexit选项时调用的脚本。
printf "$var"with似乎对 、和var=\'-va[1$(uname>&2)]\'来说不是问题bash,这是我知道的唯一三个支持该选项的 shell,将其视为格式,而另外两个则视为语法错误(因为缺少格式)\xc2\ xb9.ksh93zsh-v varnamezsh
ksh93 和 bash 有一些小信息泄露,export var=\'%(%Z %z)T\\n\'揭示了脚本的时区。
$ bash -c \'printf "$var"\'\nBST +0100\nRun Code Online (Sandbox Code Playgroud)\n在 中yash,如果是一个具有多个元素的数组,则将使用多个参数printf "$var"进行调用,但's不会进行算术评估,并且无论如何,其算术评估不会受到影响 ksh 的同类命令注入漏洞的影响' s、bash 或 zshprintf$varyashprintf。
ksh93 是printf扩展最多的一个(所有日期格式、正则表达式格式转换、基于字形宽度的填充、URI/HTML 编码...),并且它仍然处于实验性阶段。该数据printf "$data"公开了数千行代码。如果其中存在任意命令执行的路径(可能通过某些算术表达式求值或通过在其自己的代码\xc2\xb2 中触发某些错误),我不会感到惊讶。当然,任何实施都可能发生这种情况。printf
C 函数中可变外部数据的问题printf()是当它们包含%最终取消引用堆栈上的随机内存区域的序列时。printf(var)当 var%12$s尝试打印存储在传递给 的第 12 个参数处的字节值时printf。由于printf没有传递任何其他参数,因此堆栈上恰好有其他东西,并且可能是指向保存敏感信息的内存某些区域的指针。使用%n,printf()最终会在那里写一些数字。
$ tcc -run -w -xc - $\'%6$s\\n\' <<<\'f(char*f){char*s="secret";printf(f);}main(int c,char**v){f(v[1]);}\'\nsecret\n$ tcc -run -w -xc - $\'%p%p%p%p%p\\n%s\\n\' <<<\'f(char*f){char*s="secret";printf(f);}main(int c,char**v){f(v[1]);}\'\n0x7fff1182db380x7fff1182db500x7900000x80x562b5ec0ba6a\nsecret\nRun Code Online (Sandbox Code Playgroud)\nprintf实用程序可能最终会调用printf()或可能自己实现所有这些(它们至少必须在某种程度上%b,printf()而对于数字格式,它们需要将参数转换为数字)。
如果他们确实调用printf(),他们将防止在没有足够参数来覆盖格式规范的情况下调用它。这是 POSIX 要求,例如printf "%s"不输出任何内容或输出 0,因此实现应将足够的空字符串或 0 个数字参数传递给.printf %dprintfprintf()
您可以想象编写得不好的printf实现无法正确执行此操作。我不知道有什么,但我awk过去见过他们自己的实现printf()受到影响(也通过OFMT或CONVFMT那里涉及printf()处理\xc2\xb3)。
\xc2\xb9print "$var"是通过该向量的任意命令注入漏洞zsh。在那里使用很重要print -- $var,甚至一般来说print -r -- "$var"都是你想要的。
\xc2\xb2 作为一个例子,我得到了一个var=\'%(%.999999999999s)T\'带有 Ubuntu 20.04 附带的 ksh93 的SEGV
\xc2\xb3 即使在今天,使用我当前版本的busybox、busybox awk -v OFMT=\'%#x %#x %#x %#x %g\' \'BEGIN {print 1.1}\'输出0x1 0x4 0x4 0x4624bb30 1.1和busybox awk -v OFMT=\'%n %g\' \'BEGIN {print 1.1}\'段错误。
由于您谈论的是 shellprintf命令而不是 Cprintf(3)函数,因此通过正确引用"$var". shell 命令不允许进行可以在 C 中完成的传统堆栈转储。但正如\nSt\xc3\xa9phane 的答案所示,即使如此,也存在一些危险,并且使用不带引号的扩展进行命令注入,如下例所示。
您如何使用输出也可能会对以后的处理产生影响。
\n创建一个愚蠢的例子:
\ntst()\n{\n while [ "$x" == "" ]\n do\n read x\n done\n \n printf $x\n}\nRun Code Online (Sandbox Code Playgroud)\n条件“$x 非空”将通过,但如果用户输入 ,则 printf 的输出将为空%s。因此,假设 的输出tst不为空的任何例程都可能会失败。这可能会导致采用意外的代码路径。
这可能会导致安全问题,具体取决于代码的其余部分。
\n请注意,这里有很多“如果”和“可以”的警告。这非常依赖于应用程序。这就是为什么我说不会立即产生影响。
\n因此,如果输入不可信,标准防御编码风格会建议指定格式字符串。如果输入是可信的,那么就没有必要。
\n从非安全角度来看,您通常希望输出与输入匹配;如果用户输入,hello%sthere您希望看到该内容,而不是hellothere.
| 归档时间: |
|
| 查看次数: |
2930 次 |
| 最近记录: |