"Save 38% now" C 字符串中止程序



TL;DR

C字符串"% n"'%''n'字符之间有一个空格(如"立即保存38%"字符串(被视为"%n",自最新的操作系统发布以来,这被认为是一个漏洞,会导致程序中止。

(请参见UPD 1和UPD 2(

背景

我正在调查登录我的应用程序的问题。该应用程序从服务器接收一些字符串,我们使用类似于NSLog的功能记录这些字符串。我们称之为该函数的格式是:

MyLog(@"%@", message);

在一种情况下,消息包含文本";现在节省38%";。此时应用程序崩溃。

问题

在调查过程中,我能够准确指出问题所在。完整的代码如下。

#define FOO(FORMAT, ...) (
{
char *str;
str = [[NSString stringWithFormat:FORMAT, ##VA_ARGS] UTF8String];
str;
}
)
#import "MyClass.h"
@implementation MyClass
- (instancetype)init
{
self = [super init];
if (self) {
char *foo = FOO(@"%@", @"Save 38% now");
printf(foo);
}
return self;
}
@end

随时随地实例化类,

MyClass *my = [[MyClass alloc] init];

,应用程序将在printf(foo);上崩溃,并显示消息:

%n used in a non-immutable format string

基本上,字符串"%n〃;,具有介于"%"之间的间隔"以及";n〃;以"%"表示n〃;,自最新的操作系统发布以来,这被认为是一个漏洞。

讨论

我仍在努力证明我的想法,并找到解决问题的方法。。。

  1. 我相信,造成崩溃的代码是这样的:https://opensource.apple.com/source/Libc/Libc-1244.30.3/stdio/FreeBSD/vfprintf.c我疯了吗?

  2. 为什么两个字符之间的空格对代码没有任何区别?

  3. 我怎么可能绕过它,留下同样的方法?

。。。到目前为止,它看起来像一个bug。

解决方案

根据社区给出的答案,目前我对该问题的解决方法是这样的(另请参阅"UPD"部分,我在其中添加了问题最初发生的实际代码(:

message = [plainText stringByReplacingOccurrencesOfString:@"%"
withString:@"%%" 
options:NSRegularExpressionSearch 
range:NSMakeRange(0, message.length)];
DebugOnlyLog(@"%@", message);

更新1

这是我用来记录一些的代码

#import <os/log.h>
extern struct os_log_s _os_log_default;
extern __attribute__((weak)) void _os_log_internal(void *dso, os_log_t log, os_log_type_t type, const char *message, ...);
#define DebugOnlyLog(FORMAT, ...) 
void(*ptr_os_log_internal)(void *, __strong os_log_t, os_log_type_t type, const char *, ...) = _os_log_internal;
if (ptr_os_log_internal != NULL) {
_os_log_internal(&__dso_handle, OS_OBJECT_GLOBAL_OBJECT(os_log_t, _os_log_default), 0x00, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
}

更新2

经过社区的回应,很明显这里有两个问题:

  1. 将格式字符串传递到print()函数的方法存在问题
  2. 以类似的方式使用私有API(_os_log_internal(...)(而不是os_log存在问题(参见"UPD1"部分,其中我提供了所使用的代码(

注意:我也在苹果开发者论坛上发布了同样的问题。

这完全是错误的,系统会告诉你这一点,以及确切的原因:

printf(foo);

这是一个典型的安全问题。正确的代码是:

printf("%s", foo);

foo不是静态字符串,将foo传递给执行%-替换的函数是一个安全错误。

空格之所以无关紧要,是因为空格是格式字符串的一部分。% n是一个包含空格填充的格式说明符(尽管这对n类型没有意义,但它仍然是合法的(。有关%说明符的完整描述,请参阅printf手册页。它们非常复杂和强大,这正是它们如此危险的原因,你不能把不受控制的绳子交给它们。

维基百科的Uncontrolled格式字符串中有一个关于安全问题的快速描述。


在os_log包装器中,您似乎试图绕过现有的os_log定义。首先,您应该使用它,而不是触及未记录的内部。问题(os_log可能在编译时会更清楚(是,您不能在该字段中将非静态字符串传递给os_log。os_log对它传递的字符串进行非常棘手且不明显的插值。它这样做既是出于性能原因,也是出于安全原因。直接围绕os_log重写此内容,并查看WWDC视频中解释os_log的内容。

您需要使用puts而不是printf,因为printf要求第一个参数为字符串格式。puts只是将纯字符串放入输出中。

最新更新