“立即保存38%” C字符串中止程序

问题描述

TL; DR

C字符串"% n"'%''n'字符之间有空格(例如“立即保存38%”字符串中的字符)被视为{ {1}},自从最新的OS版本发布以来就被视为漏洞,并且导致程序中止。

(请参阅UPD 1和UPD 2)

背景

我正在调查我的应用程序中的登录问题。该应用程序从服务器接收一些字符串,我们使用类似于"%n"的功能对其进行登录。我们称该函数的格式为:

NSLog

在一种情况下,消息包含文本“立即保存38%”。此时,应用程序崩溃了。

问题

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

MyLog(@"%@",message);

在任何地方实例化课程,

#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”(在“%”和“ n”之间有空格)被威胁为“%n”,自从最新的OS版本发布以来,这被视为漏洞。

讨论

我仍在尝试证明自己的想法并找到解决问题的方法...

  1. 我相信,导致崩溃的代码是:https://opensource.apple.com/source/Libc/Libc-1244.30.3/stdio/FreeBSD/vfprintf.c我有风险吗?

  2. 为什么两个字符之间的空格对代码没有什么影响?

  3. 我怎么可能解决该问题,而又保持相同的方法呢?

...到目前为止,它看起来像个错误。

解决方案

根据社区给出的答案,现在我对这个问题的解决方法(请参见“ UPD”部分,我在其中添加了最初出现问题的实际代码):

%n used in a non-immutable format string

UPD 1

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

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

DebugOnlyLog(@"%@",message);

UPD 2

在社区做出回应后,很明显这里有两个问题:

  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,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. 以类似的方式使用私有API(print())而不是_os_log_internal(...)有问题(请参阅“ UPD 1”部分,我在其中提供了使用的代码)。

注意:我还发布了the same question to Apple Developer forum

解决方法

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

printf(foo);

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

printf("%s",foo);

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

空格无关紧要的原因是因为空格是格式字符串的一部分。 % n是一种格式说明符,其中包含空格填充(即使对于n类型而言,这没有意义,但仍然合法)。有关%指示符的完整说明,请参见printf man page。它们非常复杂且功能强大,这就是为什么它们如此危险并且您不能将不受控制的字符串交给它们的原因。

在Wikipedia的Uncontrolled format string中有关于安全性问题的简要说明。


在os_log包装器中,您似乎试图绕过现有的os_log定义。首先,您应该使用它而不是接触未公开的内部文件。问题(在编译时os_log可能会更清楚)是您无法在此字段中将非静态字符串传递给os_log。 os_log对所传递的字符串进行非常棘手且不明显的内插。这样做是出于性能和安全性的考虑。直接在os_log周围重写它,然后参阅WWDC video解释os_log。

,

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

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...