查看原文
其他

iOS Wi-Fi 格式串拒绝服务 0day 分析

0xcc 非尝咸鱼贩 2022-05-04

最近国外社交网络的一个用户 Carl Schou(@vm_call) 发现,把路由器的 SSID 改成 %p%s%s%s%s%n,用 iPhone 连接之后,整个 Wi-Fi 界面都不能用了。



有 C 语言编程基础的读者马上就能反应过来,很可能是出现了格式串(format string)漏洞。格式串是 printf 等函数中使用的一个模板字符串,除了规定输出字符串使用各种方式打印变量之外,还隐式地用来确定可变参数的个数。


例如如下函数:

int printf(const char *format, ...);int fprintf(FILE *stream, const char *format, ...);int dprintf(int fd, const char *format, ...);int sprintf(char *str, const char *format, ...);int snprintf(char *str, size_t size, const char *format, ...);


不同 libc 的格式串支持可能存在一定差异。由于常见的格式串可以用来打印栈内容,甚至任意读写内存,经过精心构造参数可以造成任意代码执行。


然而现在已经是 2021 年,先不说编译器会针对格式串误用生成警告,就连很多 CTF 也觉得乏味,不喜欢出这种题目了。iOS 会出现这种多写几个测试用例就能抓到的 bug 吗?


来快速分析一下。




首先搭建一个特殊名字的 Wi-Fi,然后用测试机连,果然挂了。



用 XCode 或者 idevicecrashreport 拿到崩溃日志,其中有一个 wifid 进程的日志:


Thread 2 name: Dispatch queue: com.apple.wifid.managerQueue
Thread 2 Crashed:
0 libsystem_platform.dylib 0x00000001ebcb9724 _platform_strlen + 4
1 CoreFoundation 0x00000001a381d84c __CFStringAppendFormatCore + 8812
2 CoreFoundation 0x00000001a381efa8 _CFStringCreateWithFormatAndArgumentsReturningMetadata + 160
3 WiFiPolicy 0x00000001d0895f8c -[WFLogger WFLog:message:] + 192
4 ??? 0x000000010692c00c 0 + 4405248012
5 wifid 0x0000000100f58a74 0x100e40000 + 1149556
6 wifid 0x0000000100f58c74 0x100e40000 + 1150068


注意这个调用栈:


  • _CFStringCreateWithFormatAndArgumentsReturningMetadata

  • -[WFLogger WFLog:message:]


CFStringCreateWithFormatAndArgumentsReturningMetadata 的调用上下文如下:


if ( self->_destination != 2 && (!self->_wflRunningOnWatchClassDevice || self->_wflEnableDualLoggingOnWatchClassDevice) ){ *(_QWORD *)&v16.tm_sec = 0LL; *(_QWORD *)&v16.tm_hour = &v16; *(_QWORD *)&v16.tm_mon = 0x2020000000LL; *(_QWORD *)&v16.tm_wday = 0LL; v10 = j__CFStringCreateWithFormatAndArguments_26(0LL, 0LL, v7, v21); // <-- here


这个方法调用过于频繁,lldb 调试会比较难受。用 frida 跟踪这个方法,稍微修改一下自动生成的 hook 脚本以打印调用堆栈和参数上下文:


frida-trace -U wifid -m '-[WFLogger WFLog:message:]'


onEnter(log, args, state) { const msg = '' + args[3].readUtf8String(); log(`-[WFLogger WFLog:${args[2]} message:${msg}]`); if (msg.indexOf('%p%s%s%s%s%n') > -1) { for (let i = 3; i < 10; i++) { log(args[i], JSON.stringify(Process.findRangeByAddress(args[i]))); }
log('called from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join('\n') + '\n'); } },


注意这是在一台越狱设备上做的,正常手机无法调试自带进程。


在 wifid 进程崩溃之前,最后的日志如下


17863 ms -[WFLogger WFLog:0x3 message:Dequeuing command type: “%@” pending commands: %ld]17863 ms -[WFLogger WFLog:0x3 message:{ASSOC+} Attempting Apple80211AssociateAsync to %p%s%s%s%s%n]


这个 Attempting Apple80211AssociateAsync to %p%s%s%s%s%n 就是把 SSID 直接拼进了格式串。


上层调用在一个没有符号的函数里:


v27 = sub_1000A25D4(v21);v28 = objc_msgSend( &OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("Attempting Apple80211AssociateAsync to %@"), v27);v29 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("{ %@+} %@"), CFSTR("ASSOC"), v28);v30 = objc_autoreleasePoolPush();v31 = (void *)qword_100251888;if ( qword_100251888 ){ v32 = objc_msgSend(v29, "UTF8String"); objc_msgSend(v31, "WFLog:message:", 3LL, v32);}objc_autoreleasePoolPop(v30);


这个 bug 原理并不复杂,多写几个测试用例也许就能发现。


原作者的盲打思路也很实用。见过微信里不少人在个人资料里嵌入 HTML,可以被动发现 XSS 漏洞。




这个漏洞能不能利用呢?


在这个场景里不能做远程回显,所以虽然 %p 能打印相当多栈上的数据,但是不接触手机读不到。而 Foundation 里的格式串和平时在 CTF 里见到的 libc 的格式串有细微的差别,平时的读写原语并不好使。


针对 Foundation 的格式串利用其实可以用平台独有的 %@。做过 iOS 开发的读者应该知道,这在平时打日志时很常用,可以输出一个 NSObject 的 description 信息。


在函数内部会将传入的参数当作一个 id 指针解引用。如果这个参数可控,就有造成类型混淆的机会,在没有 Pointer Authentication Code (PAC) 的设备上结合 ASLR leak 是有机会做任意代码执行的。


但是在这个函数里,之后的参数并不好控制。笔者认为这个点不可利用。况且,为了触发拒绝服务,受害者还是需要主动连上热点——这个热点的名字看着就很可疑啊。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存