导语:VerSprite发布了一个关于Dolphin浏览器漏洞的公告(CVE-2017-17551),影响Dolphin浏览器安卓版低于12.0.2所有版本。

VerSprite发布了一个关于Dolphin浏览器漏洞的公告(CVE-2017-17551),影响Dolphin浏览器安卓版低于12.0.2所有版本。

简介

12月11日,VerSprite发布了一个关于Dolphin浏览器漏洞的公告。

[VS-2017-001] Dolphin浏览器安卓备份和复原任意文件写漏洞

CVE ID:CVE-2017-17551

Vendor:Mobotap

影响Dolphin浏览器安卓版< 12.0.2版本

漏洞细节

Dolphin浏览器安卓12.0.2版本有一个备份和恢复的特征,当浏览器从恶意的备份文件恢复浏览器设定时,会出现一个任意文件写漏洞。攻击者可以利用任意文件写漏洞来用伪造的恶意可执行文件来覆写浏览器数据目录下的一个可执行文件。每次浏览器启动的时候,都会尝试从硬盘运行恶意的可执行文件——这样就可以执行攻击者的恶意代码了。

攻击面分析

安卓浏览器的一些可选的特征引入了额外的攻击面,这有时候会带来新的漏洞。而Dolphin浏览器安卓版有很多的特征,所以有很大的攻击面。漏洞是通过搜索ZipInputStream和ZipFile API时被发现的。com.dolphin.browser.util.g类含有一个与ZipInputStream类互相引用的方法。

try {
 
  v1_2 = new ZipInputStream(((InputStream)v3));
 
}
 
try {
 
  File v0_6 = new File(this.b.getApplicationInfo().dataDir);
 
  while(true) {
 
    ZipEntry v2_1 = v1_2.getNextEntry();
 
    if(v2_1 == null) {
 
      break;
 
    }
 
    BufferedOutputStream
 v2_2 = new BufferedOutputStream(new FileOutputStream(new File(v0_6 + 
File.separator + v2_1.getName())));
 
    IOUtilities.copy(((InputStream)v1_2), ((OutputStream)v2_2));
 
    v2_2.flush();
 
    v2_2.close();
 
    v1_2.closeEntry();
 
  }
 
}

对于正在处理的zip库,代码会轮训每个zip记录,并用应用的数据目录作为根目录把记录写入新的文件。如果我们可以控制库的内容,这就会导致任意文件写的原语。现在我们需要弄明白使用的功能。通过检查com.dolphin.util.g类的代码,我们发现更多有意思的代码。

try {
 
  label_129:
 
  Log.e("BackupHelper", ((Throwable)v0));
 
  throw new a(((Throwable)v0));
 
}
 
catch(Throwable v0_4) {
 
  goto label_135;
 
}

那么浏览器在备份什么呢?AndroidManifest.xml中的Activity是这样的:

<activity
 android:configChanges="keyboard|keyboardHidden|orientation|screenSize" 
android:name="mobi.mgeek.TunnyBrowser.BackupRestoreActivity" 
android:theme="@android:style/Theme.NoTitleBar"/>

通过分析浏览器的UI,我们找到了我们想要寻找的:

1513705684976434.png

那么根据备份和还原的功能,就可以抽取下面的信息。

可以保存和还原基本的浏览器设置;
备份文件的默认保存位置是/storage/emulated/0;
备份文件的文件扩展名为.dbk;
为备份文件设置密码不是必须的;
备份文件名格式为[ year:month:day:time ];
当尝试从备份文件恢复时,浏览器会读取最近生成的备份文件。

 可以从中提取的最重要的信息是备份文件实际上是有漏洞的代码正在处理的zip库,所以会导致一个文件写漏洞。

备份文件格式

加密

在逆向备份文件时,发现备份文件其实是加密了的。然而,在实践的时候还是有缺陷的。浏览器在加密备份文件时会处理两种可能的场景,那就是:设置密码和不设置密码。

如果为备份文件设置了密码,首先,密码是和静态key有联系的,其次,结果字符串是hash过的,最后,通过的哈希字符串还加入了0x10并被传递给SecretKeySpec()构造器。如果不设置密码,除了使用另一个静态key替换密码外,整个过程是一样的。

Header

备份文件含有header,header是恢复的过程中用来验证正在恢复的文件是不是浏览器创建的合法备份文件。

0D00010F 01000000 00010000 [sig]

0D00010F可以认为是备份文件的id。Header在00010000后含有一个签名。签名生成和备份加密是相同的机制。

最后,用header的内容生成CRC校验和,然后加到签名之后。

0D00010F 01000000 00010000 [sig] [checksum]

备份文件恢复后,从文件的头产生校验和,然后与header签名之后的CRC校验和进行比较。如果不匹配,那么放弃恢复过程。

现在我们就指导生成有效备份文件的过程了。

备份文件生成:

创建新的zip库;
把header 0D00010F 01000000 00010000 [sig]加入到zip库中;
加入header校验和;
加密zip库。

漏洞利用

文件写的目标

当需要把文件写入代码执行时需要寻找下面格式的文件:

DEX
JAR
ELF

幸运的是,浏览器中有一个写入文件的好目标

/data/data/mobi.mgeek.TunnyBrowser/files # ls -la
...
-rwxr-xr-x u0_a195  u0_a195      9496 2017-12-12 14:26 watch_server
 
$ file watch_server
 
watch_server:
 ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically 
linked, interpreter /system/bin/linker, stripped

watch_server

为了弄清楚watch_server的执行时间,搜素Runtime.getRuntime().exec()就可以了。我们发现,每次浏览器启动的 ,watch_server都会执行。

PoC

利用本漏洞的最大障碍是用户交互。受害者必须恢复恶意备份文件,但是没法通过IPC或其他方式触发恢复过程。唯一能控制的就是选择哪个备份文件。

备份文件的名字是[ year:month:day:time ];
当用户尝试恢复备份时,浏览器会读取最近一次生成的备份文件。
当恢复过程结束后,浏览器会快速重启并执行payload。
./build.sh
 
[+] Building ... [!]
[armeabi-v7a] Install        : payload => libs/armeabi-v7a/payload
[+] Creating tmp.zip and injecting payload [!]
 
[+] Launching backup file format generation [!]
 
[+] Generating cipher [!]
 
[+] key --> 95acde261f3e09d281498163958dd366
 
[+] Building backup  --> ./backup.dbk [!]
 
[+] Encrypting and saving backup [!]
 
[+] Cleaning up [!]
 
[+] Pushing backup.dbk to the device [!]
 
backup.dbk: 1 file pushed. 1.0 MB/s (13926 bytes in 0.014s)
 
[+] Waiting on restore process ... [!]
 
--------- beginning of system
 
--------- beginning of main
 
V/FlipperFlapper(14975):
 uid=10195(u0_a195) gid=10195(u0_a195) 
groups=1015(sdcard_rw),1028(sdcard_r),3003(inet),9997(everybody),50195(all_a195)
 context=u:r:untrusted_app:s0
源链接

Hacking more

...