%n格式说明符程序在不同的编译器上给出不同的输出.为什么?

Des*_*tor 18 c c++ printf

%n这个问题上,我正在阅读C中的格式说明符.但是当我在不同的C++编译器上尝试以下程序时,它给了我不同的输出.

为什么?是什么原因?是否存在未定义或实现定义的行为?

#include<stdio.h>

int main()
{
  int c = -1;
  printf("geeks for %ngeeks ", &c);
  printf("%d", c);
  getchar();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

代码块13.12 :(正确输出)

geeks for geeks 10
Run Code Online (Sandbox Code Playgroud)

Borland/CodeGear/Embarcadero C++ :(正确输出)

geeks for geeks 10
Run Code Online (Sandbox Code Playgroud)

Orwell Dev C++:

geeks -1
Run Code Online (Sandbox Code Playgroud)

Microsoft Visual Studio 2010:

Debug assertion failed ("'n' format specifier disabled",0) 
Run Code Online (Sandbox Code Playgroud)

Sha*_*our 10

正如问题所述Code Blocks,确实是正确的,但Orwell Dev C++不正确.另一方面,Visual Studio不符合要求.

cppreferences printf的C文档说:

返回此函数调用到目前为止写入的字符数.

我没有在草案标准中看到任何使这个可选的内容,C++指的是C标准printf.MSDN记录了这一点并说:

由于%n格式本质上是不安全的,因此默认情况下禁用它.如果在格式字符串中遇到%n,则调用无效参数处理程序,如参数验证中所述.要启用%n支持,请参阅_set_printf_count_output.

为什么%n格式本身就不安全?

我假设他们认为它不安全,因为安全问题,如格式字符串漏洞文档中概述的一个可能的方法来利用它.它取决于由用户输入控制的格式字符串.本文给出了以下示例:

char user_input[100];
scanf("%s", user_input); 
printf(user_input); 
Run Code Online (Sandbox Code Playgroud)

退休忍者链接到Bugtraq帖子,该帖子演示了这样一个错误的真实例子proftpd 1.2.0pre6:

  • ftp到主机
  • 登录(匿名或否)

(这应该是一行,没有空格)

ftp> ls aaaXXXX%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u u %u%u%u%u%u%u%u%u%u%u%u%u%u%653300u%n

(用连续的ascii值为0xdc,0x4f,0x07,0x08的字符替换X)

通过这种方式可以轻松完成许多其他恶意事件.由于proftpd会将用户输入数据传递给snprintf,因此参数攻击很容易.

Visual Studios方法的问题在于它破坏了可移植性.其他方法包括使用gcc使用的Wformat-security等标志,结合使用-WError可能会使其成为错误,但您可以选择此作为构建过程的一部分.

  • @meet MS认为它本身就是不安全的.这并不意味着它本身就是不安全的,只是至少有一个重要的供应商这么认为. (7认同)
  • @Keith Thompson默认情况下MS应该被认为是禁止"%n"的领导者,但允许某个编译器切换?或者这是[拥抱,延伸,熄灭](http://en.wikipedia.org/wiki/Embrace,_extend_and_extinguish)?Hmmmm (4认同)
  • @chux:这也意味着默认情况下Microsoft的实现不符合(这不是新闻). (2认同)