导语:iOS系统两周前发现的印度语漏洞加速了iOS的发布进程,苹果很快发布了iOS 11.2.6,这个漏洞同样存在于苹果的其他操作系统上,包括macOS 10.13.4、watchOS 4.3和tvOS 11.3。

u=2087296409,1630789425&fm=11&gp=0.jpg

前言

iOS系统两周前发现的印度语漏洞加速了iOS的发布进程,苹果很快于上周发布了iOS 11.2.6,而这已经是对iOS 11操作系统的第11次正式更新了,这也是让业界吐槽iOS 11是史上问题最多和更新最频繁的系统的原因。然而事实上,这个漏洞同样存在于苹果的其他操作系统上,包括macOS 10.13.4、watchOS 4.3和tvOS 11.3。

据报道,如果在iPhone、iPad上发送一个Telugu(泰鲁固语,在印度东部的德拉维拉使用)符号,则会造成信息APP(iMessage、WhatsApp、Twitter等)闪退和崩溃,用户必须重新打开甚至是重启设备。

上周苹果推出的iOS 11.2.6版本系统,已经解决了这一问题,提醒苹果用户赶紧更新。在发布iOS 11.2.6版本更新的同时,苹果还为macOS 10.13.3, watchOS 4.2.3以及tvOS 11.2.6发布了补充更新。

iOS正式版用户可以直接通过OTA进行升级,另外除了通过OTA升级之外,也可以下载相应的固件通过iTunes升级,具体方法是下载固件后在桌面端打开最新版iTunes,同时按下“Shift+更新”选取将要刷入的固件即可。

可以升级的设备包括iPhone X、iPhone8、iPhone8 Plus、iPhone7、iPhone7 Plus、iPhone5s、iPhone SE、iPhone6/Plus、iPhone6s/Plus;9.7英寸iPad、iPad Air、iPad Air 2、iPad Pro(9.7/12.9一二代、10.5英寸)、iPad mini 2、iPad mini 3、iPad mini 4,iPod方面仅有iPod Touch 6一款设备。

本次苹果主要修复的问题便是特殊字符串的漏洞,另外还有某些第三方应用无法连接到外部附件的问题。

对崩溃过程的技术还原

在对崩溃过程进行测试时,我是把这些泰鲁固语复制到Spotlight中进行测试的,所以我的浏览器不会因这些特殊字符串的运行而崩溃,Spotlight是一款非常轻量级的远程监控工具。

我在测试时用的泰鲁固语的字符串序列是U+0C1C U+0C4D U+0C1E U+200C U+0C3E,它是一串泰鲁固语字符序列,这个序列的顺序是辅音 ja (జ),virama ( ్ ),辅音nya (ఞ),ZWNJ(zero-width non-joiner)和元音aa( ా)。许多婆罗米系文本都有类似符号,一般称为virama.泰米尔文几乎都用可以看到的puḷḷi来识别一个不带元音的辅音。

当看到这个字符串序列时,我首先认为<ja,virama,nya>序列是造成系统崩溃的原因。因为该序列在许多印度文的脚本中代表一个特殊的字符,通常该字符会被认为是一个梵文字母——ज्ञ。

还有第二种可能,在测试中我发现,只要元音不是 ై (ai),这个漏洞似乎就会发生在任何一对带有元音的泰鲁固语的辅音中。

另外还有第三种可能,特殊字符ZWNJ(zero-width non-joiner)是个不可见的特殊字符,在数据库中往往会被保存为乱码,所以通常情况下要被过滤掉。而iOS 11.2.5中ZWNJ没有被删掉,我认为问题也可能出在这里。<辅音,virama,辅音,元音>是任何印度语中都相当常见的序列;但是在元音之前的ZWNJ对大多数脚本来说并不是非常有用(除了孟加拉语和奥里亚语)。

不过在测试中,我看到了孟加拉语中有一个类似的字符串序列也崩溃了。这个序列是U+09B8 U+09CD U+09B0 U+200C U+09C1,它的字符串序列是这样的:辅音“so”(স),virama(্),辅音“ro”(র),ZWNJ和元音 u (  ু)。

在深入分析这个字符串序列为何会发生崩溃之前,我们先来了解一下带有印度语的脚本是如何运行的?

印度语脚本群和辅音群

印度语脚本是abugidas(一种字母文字系统);这意味着脚本字母是辅音,你可以用它来改变元音。默认情况下,辅音有一个基础元音。例如,क是“kuh”(kə,通常写为“ka”),但我可以改变它的元音使 के(“ok”中的“ka”)成为कका(“kaa”类似于“car “)。

通常情况下,默认元音是ə的发音,但在孟加拉语中它更像是发o的音。

由于默认元音的存在,你需要一种组合辅音的方式。例如,如果你想写“ski”这个词,你不能把它写成स+की(sa + ki =“saki”),你必须把它写成字符串。但转变成字符串后,它的元音自动消失了,并附加了की形成一个辅音连缀群。

你也可以把它写成स्की, स被称为“virama”,它的意思是“删除这个元音”。如果没有简单的方法将这些字符连接成字符串时,就会出现明显的virama。例如ङ्ठ,就不是简单的ङ和ठ的连接。一些脚本也更喜欢明显的virama,例如马拉雅拉姆语中的“ski”就被写为“സ്കീ”,其中的小月牙形状就是明确的virama。

在unicode中,virama字符总是会形成辅音群。因此,स्की被写为<स,्,क,ी>或<sa,virama,ka,i>。如果这些字符支持集群,则它将显示为连字符,否则将使用显式virama。

对于梵文和孟加拉语,通常来说,在辅音群中,第一个辅音会被略微省去,第二个辅音则会保持完整。不过也有例外,有时辅音群会形成一个全新的字形(क+ष=क्ष),有时两个字形都将会改变(ड+ड=ड्ड,द+म=द्म,द+ब=द्ब)。最后一个应该是这样的:

1.png

孟加拉语环境导致的系统崩溃

有趣的是,与泰鲁固语的崩溃不同,孟加拉语的崩溃似乎只发生在第二个辅音——র (“ro”)。但是,除了当元音是 ো (o)或  ৌ (au)时,我可以选择第一个辅音或元来触发র (“ro”)。

现在,在一些印度语脚本中,র 是一个有趣的辅音,包括梵文。在梵文里,它看起来像र(“ra”)。但是,它在形成群时却会产生各种字符上的变化。如果你将它放在群中的另一个辅音之前,它会形成र्क(rka)一样的字符。在马拉地语中,该字符看起就像是र्क。作为后缀辅音,它可能会形成 क्र (kra)这样的字符。对于没有垂直笔划的字母来说,如ठ(tha),它就会变成ठ्र (thra)。

虽然大多数辅音在群内部仍会保留一些最初的形式,但 र 却是个例外。还有一件更特别的事情是,即使र 是群中第二个辅音时也会发生这种情况。正如我之前提到的,对于大多数辅音群来说,第二辅音完好无损。虽然也有例外,但它们通常是针对特定的群的,只有र 出现时群才会发生这种情况。

孟加拉语র 的情况也与र相似,因为第二个辅音在现有的辅音上增加了一些字符。例如,প + র (po + ro) 形成প্র (pro)。

但是,在孟加拉语中不但有র 还有辅音“jo”。 প + য (po + jo)形成প্য(pjo),য 转化为 “jophola”的波浪线。

经过测试后,我发现孟加拉语的崩溃也发生在য上。因此,孟加拉语的字符序列崩溃的一般情况是<辅音,virama, র OR য, ZWN,元音>,其中元音不是 ো or  ৌ。

加入辅音的后缀

现在我已经差不多找到字符串崩溃的原因了,至少对于孟加拉语来说,崩溃就发生在第二个辅音,且它通常与第一个辅音结合且没有修改其字符形式。

事实上,泰鲁固语也是如此。在泰鲁固语中的辅音群通常会通过保留原有的辅音来让崩溃发生,且会把第二个辅音字母串起来。

例如,原始的crashy字符串包含群జ+ఞ,看起来像జ్ఞ。可以看到第一个字母没有修改过,但第二个字母发生了变化。

由此,我可以猜测,这也将发生在梵文与र的结合中。U+0915 U+094D U+0930 U+200C U+093E就是一个这样的序列,即<क,  ्, र, zwnj,  ा> (< ka, virama, ra, zwnj, aa >)就是让程序崩溃 的字符串序列。

但这还不是崩溃发生的根本原因,例如,以上的字符串结合发生在孟加拉语中的“kro”+ zwnj +元音中,而在“kro”中(ক্র=ক+র= ko + ro),所产生的群涉及到前缀和后缀的转换。但是,这个崩溃不会发生在द्ब或ड्ड上。

更深层次的原因是,对于许多字体(可能是使用的字体),这些辅音在形成virama之前会形成“加入辅音的后缀(suffix joining consonants)”(这是我自定义的一个术语)。这就类似于pstf OpenType功能以及vatu

例如,序列virama +क 形成 ्क,即形成一个带有क占位符的virama。

但是,对于我来说,रvirama +र会形成  ्र,如下图所示。

2.png

事实上,其他辅音也是如此。对于我来说, ्र  ্র  ্য  ్ఞ  ్క 就是一个“加入辅音的后缀”:梵文virama-ra,孟加拉语virama-ro,孟加拉语virama-jo,泰鲁固语virama-nya,泰鲁固语virama-ka。

3.png

注意,所有泰鲁固语的辅音都是如此。

有趣的是,由于 र-virama-र使用了第一个र (र्र)的前缀加入形式,所以<र,virama,र,zwnj,vowel>不会发生崩溃。

坎那达语也有“加入辅音的后缀”,但由于某种原因,我无法用它触发崩溃,果鲁穆奇语的Ya也是如此。

ZWNJ

字符ZWNJ是个不可见的特殊字符,没有它就不会发生崩溃,但正如我在提到ZWNJ之前所讲的,元音对于大多数印度语脚本没有什么实际意义。在印度语脚本中,如果在virama之后使用ZWNJ,则可以使用ZWNJ明确突出virama,但这不是它在此处使用的方式。

在孟加拉语和奥里亚语中,ZWNJ可以用于在元音之前强制使用不同的元音形式(例如রুvsরু)。异常元音很有趣,它们基本上都是由两个字形组成的元音。Philippe Verdy表示:

为什么这个错误不会发生在某些元音中,这是因为这些元音是由两部分组成的,它们首先被分解为两个单独的字形,在字形缓冲区中重新排序,而其它元音则不需要这种先前的映射,并保持其初始字体,这意味着漏洞的发生与ZWNJ在字形缓冲区中查找元音的字形的方式有关,而不是在初始代码点缓冲区中,这说明字符的映射有一些不同步,更可能是未初始化的数据字段没有进行元音分解。

崩溃结果的总结

因此,最终造成这次字符串崩溃的全部原因是:在梵文,孟加拉语和泰鲁固语中的任何<consonant1,virama,consonant2,ZWNJ,vowel>序列都会导致崩溃。其中consonant2是加入(pstf/vatu)的后缀字母,即 र, র, য, ৰ和所有的泰鲁固语辅音,而consonant1不是一个类似于र/র (或如ৰ的变体)的reph-forming字母,而vowel则表示不是由两个字形组成的,它即不是  ై, ো也不是   ৌ。

这就又引出了一个问题:为什么该原因不适用于坎那达语?在高棉语中,一个类似于virama的的字符叫“coeng”

有没有在所有语言环境中都能发生崩溃的字符串?要发生崩溃,就要满足以下条件:

1.所有呈现的字符串都是有效的,最初的泰鲁固语是“knowledge”这个词的根源(因为这个原因,我把这个本次的漏洞称为“forbidden knowledge”);

2.在泰鲁固语和梵文中,在元音之前没有使用ZWNJ。ZWNJ本不应该在那里,人们不会在常用的语言中用到它;

3.在孟加拉语(也叫Oriya)中,在一些元音之前放置一个ZWNJ可以防止它们的字符变形;

4.在所有这些脚本中,在virama之后放置一个ZWNJ可以用来强制显示一个连字符的virama。这不是ZWNJ在这里使用的位置,但它暗示这可能是错误的。至少对于梵文来说,这样做也很少见;

5.Android在其键盘上为这些语言提供了明确的ZWNJ键,紧挨着空格键;

所以这些崩溃的字符串通常情况下都是无效的,但如果发生了,则是罕见情况。

@FakeUnicode举了“For/k”的字符输入错误的例子,“For/k”很容易被错误输入为“Foŕk”。上面的斜线并不是你要输入的内容,其实是生成的字符串乱码。

源链接

Hacking more

...