iOS APP安全杂谈之三【转】

0x00 序

听说最近IOS让人操碎了心,不到三个月的时间里先后发生太极越狱被曝存在后门,22万iCloud账号及机密信息被多款内置后门越狱插件窃取并泄露,XcodeGhost事件多个知名企业中枪,吓的大家纷纷改了密码。当然像XcodeGhost这样的事情用户是没办法避免的,其编译的APP毕竟是在AppStore上上架的,但是还有一大部分的安全问题其实是完全可以避免的,那就是用户不要越狱,而厂商的APP在运行前检查运行的设备是否安全。现在也有很多APP在使用前都会检查设备是否越狱,如果越狱了则强制退出或者只是提醒一下。那么这次我们就来聊聊越狱之后的那点事。PS:由于本人能力有限,文章难免会有些错误,还望小伙伴们见谅。

0x01 越狱之后更美好?

越狱之后可以干什么,可以免费的玩各种付费的游戏,可以自动的抢红包,可以在接听电话时自动录音,是的,想想还有点小激动。

但事实是越狱之后的风险是用户和厂商都不想看到的,攻击者可以在越狱环境下改变你的APP执行逻辑(例如通过更改函数的返回值来绕过手势密码验证)、可以通过复制应用数据克隆用户或者登陆任意用户、可以通过打补丁的方式绕过目标APP的一些限制等,当然以上的这些危害都是需要一个前提条件:目标APP在越狱环境下能正常使用。

0x02 工具准备

越狱的iPhone或iPad(我这里演示的是IOS 8.4,低版本或高版本的可能会有些不同); IDA或者Hopper Disassembler; Xcode(主要使用其附带的LLDB),当然对于低版本的IOS可以使用gdb,LLDB具体如何调试可以参考这里; OpenSSH,itools等其他工具。

检测越狱的几种方法(可参考《Hacking and Securing ios Application》):

(1)沙盒的完整性校验一些越狱工具会移除沙盒的限制,使程序可以不受限制的运行,这里要说的是关于fork函数的限制。fork函数可以允许你的程序生成一个新的进程,如果沙盒被破坏或者程序在沙盒外运行,那么fork函数就会成功执行,如果沙盒没有被篡改则fork函数执行失败。这里我们通过fork()的返回值判断子进程是否成功,程序代码如下:

#!c 
#include <stdio.h> 
#include <stdlib.h> 
static inline int sandbox_integrity_compromised(void) __attribute__((always_inline)); 
int sandbox_integrity_compromised(void){ 
    int result = fork(); 
    if (!result) 
        exit(0); 
    if (result >= 0) 
        return 1; 
    return 0; 
} 
int main(int argc,char *argv[]){ 
    if(sandbox_integrity_compromised()) { 
        printf("Device is JailBroken\n"); 
    }else{ 
        printf("Device is not JailBroken\n"); 
    } 
    return 0; 
} 

(2)文件系统检测

1)越狱文件是否存在越狱之后的小伙伴们一定对Cydia程序很熟悉吧,它是最受欢迎的第三方程序安装器,大部分的越狱工具都会自动给设备进行安装。所以我们可以检测第三方程序文件是否存在来判断设备是否越狱,例如检测Cydia程序是否存在。

  1. /etc/fstab文件大小
    fstab文件通常被越狱工具替换使root分区有读写的权限,但是APP不允许查看该文件的内容,所以我们使用stat函数获得此文件的大小,再根据文件的大小来判断是否越狱。我这里显示越狱后该文件的大小为67字节,有资料中说如果没有越狱该文件的大小应该为80字节。


    使用itools查看fstab文件大小
  1. 符号链接检测IOS的磁盘被划分为两个分区:容量较小的是系统分区,另一个是比较大的数据分区。IOS预装的APP会安装在系统分区下的/Applications文件夹下,但是系统分区在设备升级时会被覆盖且容量太小,所以一些越狱工具会重定向这个目录到一个大的用户分区。通常情况下/Applications文件夹会被符号链接到/var/stash文件目录下,APP可以通过lstat函数检测/Applications的属性,如若是目录则代表未越狱,如若是符号链接则代表是越狱设备。


    使用itools查看符号链接
使用命令行查看符号链接

</br>

(3)检测system( )函数的返回值等多种方法这个方法来源于一个网站,同时还介绍了其他几种检测越狱的方法。其中一个方法就是检测system( )函数的返回值,调用System( )函数,不需要任何参数。在越狱设备上会返回1,在非越狱设备上会返回0。

0x04 可以动手了

我们使用文件检测的第一种方法来动手测试一下我们的越狱设备。代码如下:
#!c
#include <sys/stat.h>
#include <stdio.h>

int isJailBroken();
 
int main(){ 
  if(isJailBroken()){ 
    printf("Device is JailBroken\n"); 
    }else{ 
    printf("Device is not JailBroken\n"); 
  } 
  return 0; 
}
 
int isJailBroken(){ 
    struct stat buf; 
    int exist = 0; 
    char * jbFiles[] = {"/usr/sbin/sshd","/bin/bash","/Applications/Cydia.app","/private/var/lib/apt","/Libra ry/MobileSubstrate/MobileSubstrate.dylib"}; 
    for(int i=0;i < sizeof(jbFiles)/sizeof(char*);i++){ 
      exist = stat((jbFiles[i]),&buf); 
      if(exist == 0){ 
          return 1; 
      } 
    } 
    return 0; 
}

以上代码主要检测了/usr/sbin/sshd、/bin/bash、/Applications/Cydia.app、/private/var/lib/apt、/Library/MobileSubstrate/MobileSubstrate.dylib这几个文件是否存在,如果存在则证明该设备已经越狱。

检测越狱的POC

在Mac下使用以下命令编译代码:

clang -framework Foundation -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk ~/Desktop/jailbreak.c -o ~/Desktop/jailbreak64 -miphoneos-version-min=5.0

编译之后将二进制文件拷贝到越狱设备当中并在命令行中运行它。下图可看到运行程序后显示设备已经越狱。


赋予文件权限并执行
0x05 道高一尺魔高一丈

一些APP正是使用了类似上面的方法来检测自己所在设备是否进行了越狱,为了保证自身的安全性如果设备越狱则自动退出。是的这种做法很明智,但是这也只是增加了安全测试的难度,我们既然知道了检测越狱的原理那么就可以见招拆招了。

(1) 配置好LLDB和debugserver,ssh连接到设备,在设备上使用命令行输入debugserver -x backboard *:1234 /jailbreak64附加到jailbreak64,并开启1234端口,等待LLDB的接入。


在iPad端使用debugserver附加到程序

(2) Mac端切换到~/Xcode.app/Contents/Developer/usr/bin/目录下,输入lldb,启动后输入process connect connect://iosip:1234

Mac端使用LLDB连接IOS设备

(3) 使用IDA分析程序逻辑结构,这里有一点需要注意:IDA分析的二进制文件必须与LLDB调试的二进制文件相同,这样偏移前基地址、ASLR偏移、偏移后基地址才能对应得上。这里由于我的iPad对应的ARM是ARM64,所以之前在Mac上我使用arm64编译的程序,将编译的程序扔到IDA中分析,这里我使用的是IDAPro6.6中的idaq64.exe进行分析的。(这里所说的ASLR偏移指的是偏移后模块基地址-偏移前模块基地址,偏移后模块基地址:二进制文件加载到内存模块在内存中的首地址;偏移前模块基地址:模块在文件中的首地址)


ARM64编译后的程序在IDA中的分析

根据上图我们可以看到地址0x100007D1C下方有两个分支,即左边的判断是设备已经越狱,而右边的判断是设备没有越狱。

之所以说这里需要注意是因为我们在使用LLDB调试程序下断点时需要偏移前基地址,而这个偏移前基地址不同场景下是不一样的。下图为ARMv7编译后使用IDA分析的结果,可以看到我们关注的地址变为了0xBE28。


ARMv7编译后的程序在IDA中的分析

(4) 使用LLDB查看ASLR偏移,此时需要知道几个指令:ni执行下一条指令但不进入函数体,si执行下一条指令会进入函数体,image list列举当前所以模块。


使用LLDB查看ASLR偏移

上图显示jailbreak64还未启动,现在调试还发生在dyld内部,接下来一直执行ni命令,直到输出结果出现卡顿(大约3秒左右,可以明显察觉到)。到这里不要再使用ni命令,dyld已经开始加载jailbreak64,我们使用image list -o -f查看jailbreak64的ASLR偏移。
多次执行ni命令后查看jailbreak64的偏移

根据上图显示jailbreak64的ASLR偏移为0x40000,所以可以确定断点位置下在‘0x40000+0x100007D1C’处。

(5) 根据以上信息我们就可以安心的下断点了,使用br s -a 地址用来下断点,使用指令p可以打印某处的值,使用指令register write给指定的寄存器赋值。


下断点

上图是在‘0x40000+0x100007D1C’处设置断点


查看x0的值并重新赋值来更改程序逻辑

我们使用p命令查看一下此时x0的值,发现x0为1,使用register write命令将该x0的值改为0,然后输入命令c继续,可以看到我们成功的绕过了越狱检测,程序运行结果为设备未越狱。

在Cydia上可以安装xCon软件,据说是目前为止最强大的越狱检测绕过工具,然而我安装在我的iPad上并不适用,应该暂不支持IOS8.4吧。xCon可以绕过四种越狱检测方法(根据特定的越狱文件及文件权限判断设备是否越狱;根据沙盒完整性判断设备是否越狱;根据文件系统的分区是否发生变化来检查设备是否越狱;根据是否安装ssh来判断设备是否越狱)。用最近比较流行的一句话就是“你有一百种方式来检测越狱”,而别人也有一百种方式来抵抗你的检测。

0x06 魔高一尺道高一丈

刚才我们演示的是LLDB调试来绕过越狱检测,那么如果我的程序阻止调试器附加怎么办?我们来看下面的测试代码。
#!c
#include <sys/stat.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <unistd.h>

inline int checkDebugger() __attribute__((always_inline)); 

int main(){ 
    if(checkDebugger()){ 
    printf("Debugger attached\n"); 
    return 0; 
    } 
    printf("Debug detection bypassed\n"); 
    return 0; 
} 

int checkDebugger() { 
    int name[4]; 
    struct kinfo_proc info; 
    size_t info_size = sizeof(info); 
    info.kp_proc.p_flag = 0; 
    name[0] = CTL_KERN; 
    name[1] = KERN_PROC; 
    name[2] = KERN_PROC_PID; 
    name[3] = getpid(); 
    if(sysctl(name,4,&info,&info_size,NULL,0) == -1){ 
       return 1; 
    } 
    return (info.kp_proc.p_flag & P_TRACED) ? 1 : 0; 
}

当一个应用被调试的时候,会给进程设置一个标识(P_TRACED),可以检测该进程是否有设置这个标识来检测进程是否正在被调试来保护应用。如果该程序发现自己被调试器附加了进程,那么将会输出Debugger attached,如果正常运行该程序则会输出Debug detection bypassed。实验结果如下:


正常运行的输出结果

使用LLDB调试器附加后的输出结果

在实际应用中可以在得知自己的程序被调试器附加后自动退出,当然这也是可以通过下断点的方式绕过,但是可以通过多处调用该方法来增加攻击的难度。同时上面的代码采用声明内联函数的方法,使编译器将函数功能插到每处代码被调用的地方,而不至于某个特定的功能函数被劫持。

当然除了上述的这种反调试的方法,还可以通过优化标记、去除符号等方法使反汇编复杂化。

0x07 冤冤相报何时了

0x06中所说的反调试还可以通过打补丁的方式绕过,这样就不需要每次都下断点绕过,也会有更多的时间用LLDB来调试其他核心业务。是不是感觉有些乱?因为的确是攻击和防御的方法都有很多,厂商采用多种防御手段来增加攻击成本,而攻击者为了利益或者抱有挑战心理的态度来见招拆招,真是冤冤相报啊。

参考文献:
[美]Jonathan Zdziarski《Hacking and Securing ios Application》 沙梓社,吴航《ios应用逆向工程》 http://blog.ioactive.com/2015/09/the-ios-get-out-of-jail-free-card.html

转自iOS APP安全杂谈之三

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容