美文网首页
ios ZipperDown 漏洞

ios ZipperDown 漏洞

作者: 天下林子 | 来源:发表于2018-05-17 15:05 被阅读311次

看了网易的新闻,得知很多app有ZipperDown安全隐患,故了解了一下。仅供参考,望指正。

漏洞的攻击原理。

  • 漏洞与zip文件有关,ios没有提供官方的unzip API函数,基本上现有的ios App都是使用的SSZipArchive或ziparchive这两个第三方库来实现解压的功能。使用第三方zip库在解压zip文件过程中没有考虑文件名中带有”../../”这样的情况,从而产生了目录穿越漏洞。因此,如果一个iOS 应用下载了恶意的zip文件,并且使用ziparchive库解压,利用漏洞可以做到app container目录下的任意文件覆盖,如果覆盖了应用重要的文件会造成应用崩溃(DOS),如果覆盖了app的hotpatch文件则会造成代码执行。
  • 攻击条件:
    1.APP用了ZipArchive
    2.原APP下发的某个zip包传输过程没加密,zip包也没加密
    3.原APP使用了JSPatch或其他执行引擎,且本地脚本没有加密,只要把脚本放指定目录即可执行
    4.用户连上第三方wifi遭受攻击。

漏洞原理

ZipperDown漏洞并非iOS平台自身问题,而是与Zip文件解压有关。iOS平台没有提供官方的unzipAPI函数,而是引用了第三方库来实现解压功能,由于现有的iOS App基本上采用SSZipArchive或Ziparchive来实现解压,因此漏洞是来自使用第三方Zip库解压Zip文件的过程中没有对Zip内文件名做校验导致的。如果文件名中含有“../”则可以实现目录的上一级跳转,从而实现应用内任意目录的跳转,进一步可以实现文件覆盖,如果把App的hotpatch文件覆盖替换了,可以达到执行黑客指定指令,从而按照黑客的意图实现任意应用内攻击。

这个漏洞不禁让易盾联想到不久前Android平台上的unZip解压文件漏洞,和这个漏洞几乎是完全一样,只是平台和第三方解压库不同而已。Android平台上的被称为unZip解压文件漏洞,网易云易盾安全检测平台已经可以实现扫描检测。

  • ------- SSZipArchive ---------------

压缩文件是允许路径指向类似../A/../B这种格式的, UNIX下../代表这个文件夹的上一层。比如说/A/B/../C实际上指的是/A/C
有问题的解压库没有对这种../做过滤,也就是说可以往解压路径外的地方解压文件。这不是一个系统级的沙盒逃逸漏洞

很多App会把所谓的热更新补丁放在沙盒内的某个路径下,比如说我们叫Documents/A.js吧,如果热更新的传输过程有中间人攻击的问题或者app被通过某种方式打开恶意的压缩包, 攻击者就可以覆盖A.js的内容,这样下次app启动加载热更新补丁A.js时就会执行恶意代码。

SSZipArchive
我们在开发app的时候,有时会需要对文件进行压缩和解压的操作,比如百度网盘,这个时候我们就必须要用到一个第三方的开源库,SSZipArchive ,来对目标文件进行压缩和解压的操作。

// Unzip 解压  
      
/** 
 * @param          path    源文件 
 * @param   destination    目的文件 
 * @param      uniqueId    标记,用于区别多个解压操作 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示解压失败。 
 */  
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination  uniqueId:(NSString *)uniqueId;  
  
/** 
 * @param          path    源文件 
 * @param   destination    目的文件 
 * @param     overwrite    YES 会覆盖 destination 路径下的同名文件,NO 则不会。 
 * @param      password    需要输入密码的才能解压的压缩包 
 * @param         error    返回解压时遇到的错误信息 
 * @param      uniqueId    标记,用于区别多个解压操作 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示解压失败。 
 */  
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error  uniqueId:(NSString *)uniqueId;  
  
/** 
 * @param          path    源文件 
 * @param   destination    目的文件 
 * @param      delegate    设置代理 
 * @param      uniqueId    标记,用于区别多个解压操作 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示解压失败。 
 */  
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(id<SSZipArchiveDelegate>)delegate  uniqueId:(NSString *)uniqueId;  
  
/** 
 * @param          path    源文件 
 * @param   destination    目的文件 
 * @param     overwrite    YES 会覆盖 destination 路径下的同名文件,NO 则不会。 
 * @param      password    需要输入密码的才能解压的压缩包 
 * @param         error    返回解压时遇到的错误信息 
 * @param      delegate    设置代理 
 * @param      uniqueId    标记,用于区别多个解压操作 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示解压失败。 
 */  
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error delegate:(id<SSZipArchiveDelegate>)delegate uniqueId:(NSString *)uniqueId; 

/*
 *  解压
 */
+ (BOOL)unzipFileAtPath:(NSString *)path
          toDestination:(NSString *)destination
     preserveAttributes:(BOOL)preserveAttributes
              overwrite:(BOOL)overwrite
         nestedZipLevel:(NSInteger)nestedZipLevel
               password:(nullable NSString *)password
                  error:(NSError **)error
               delegate:(nullable id<SSZipArchiveDelegate>)delegate
        progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
      completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler;

// Zip 压缩  
/** 
 * @param       path    目的路径(格式:~/xxx.zip 结尾的路径) 
 * @param  filenames    要压缩的文件路径 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示压缩失败。 
 */  
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)filenames;  
  
/** 
 * @param       path    目的路径(格式:~/xxx.zip 结尾的路径) 
 * @param  filenames    要压缩的文件目录路径 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示压缩失败。 
 */  
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath;  
  
/** 
 * 初始化压缩对象 
 * 
 * @param  path    目的路径(格式:~/xxx.zip 结尾的路径) 
 * 
 * @return 初始化后的对像 
 */  
- (id)initWithPath:(NSString *)path;  
  
/** 
 *  打开压缩对象 
 * @return 返回 YES 表示成功,返回 NO 表示失败。 
 */  
- (BOOL)open;  
  
/** 
 * 添加要压缩的文件的路径 
 * 
 * @param  path    文件路径 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示失败。 
 */  
- (BOOL)writeFile:(NSString *)path;  
  
/** 
 * 向此路径的文件里写入数据 
 * 
 * @param      data    要写入的数据 
 * @param  filename    文件路径 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示失败。 
 */  
- (BOOL)writeData:(NSData *)data filename:(NSString *)filename;  
  
/** 
 *  关闭压缩对象 
 * @return 返回 YES 表示成功,返回 NO 表示失败。 
 */  
- (BOOL)close; 

@optional  
  
//将要解压  
- (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo;  
//解压完成  
- (void)zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPat uniqueId:(NSString *)uniqueId;  
//将要解压  
- (void)zipArchiveWillUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo;  
//解压完成  
- (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo;  
  
@end 

当对文件进行解压的时候,如果文件名包含了../,则可以实现目录上一级跳转,从而实现应用内任意目录的跳转,进一步可以实现文件覆盖。
例如: 有一个文件名为wzg/../wwww.zip,在对该文件下载之后进行解压,如果正常情况下,为下图的第一种情况,解压的test.txt文件会在C文件位置解压出来,如果出现不正确的路径,如下图的第二种情况,解压的test.txt文件会在C的上面一级文件夹路径A中解压出来。

image.png

而在三方SSZipArchive中,底层实现如下:

#pragma mark - ================解压===unzipFileAtPath=============================
+ (BOOL)unzipFileAtPath:(NSString *)path
          toDestination:(NSString *)destination
     preserveAttributes:(BOOL)preserveAttributes
              overwrite:(BOOL)overwrite
         nestedZipLevel:(NSInteger)nestedZipLevel
               password:(nullable NSString *)password
                  error:(NSError **)error
               delegate:(nullable id<SSZipArchiveDelegate>)delegate
        progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
      completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
{
//==============核心代码=================
  unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
            filename[fileInfo.size_filename] = '\0';
            
            BOOL fileIsSymbolicLink = _fileIsSymbolicLink(&fileInfo);
        #pragma mark ------------------核查文件的路径是否包含不规范字符---------------------------------
            NSString * strPath = [SSZipArchive _filenameStringWithCString:filename size:fileInfo.size_filename];
            if ([strPath hasPrefix:@"__MACOSX/"]) {
                // ignoring resource forks: https://superuser.com/questions/104500/what-is-macosx-folder
                unzCloseCurrentFile(zip);
                ret = unzGoToNextFile(zip);
                continue;
            }
            if (!strPath.length) {
                // if filename data is unsalvageable, we default to currentFileNumber
                strPath = @(currentFileNumber).stringValue;
            }

            // Check if it contains directory
            BOOL isDirectory = NO;
            if (filename[fileInfo.size_filename-1] == '/' || filename[fileInfo.size_filename-1] == '\\') {
                isDirectory = YES;
            }
            free(filename);
            
            // Contains a path
            if ([strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location != NSNotFound) {
                strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
            }
            
            NSString *fullPath = [destination stringByAppendingPathComponent:strPath];
            NSError *err = nil;
            NSDictionary *directoryAttr;
            if (preserveAttributes) {
                NSDate *modDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dos_date];
                directoryAttr = @{NSFileCreationDate: modDate, NSFileModificationDate: modDate};
                [directoriesModificationDates addObject: @{@"path": fullPath, @"modDate": modDate}];
            }
#pragma mark ----------------解压路径---------------------------------
            //如果是一个沙盒路径, 就创建这个路径,将解压的 东西放到这个路径下
            //如果不是则创建一个路径,
            /*
             stringByDeletingLastPathComponent一个新的字符串由来自接收者的组件删除最后一个路径,以及最终的路径分隔符。
             Receiver’s String Value      Resulting String
             “/tmp/scratch.tiff”           “/tmp”
             “/tmp/lock/”                   “/tmp”
             “/tmp/”                        “/”
             “/tmp”                         “/”
             “/”                            “/”
             “scratch.tiff”                 “” (an empty string)
             
             */
            NSLog(@"-------dir-------%@", fullPath);
            if (isDirectory) {
                [fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:directoryAttr error:&err];
            } else {
                [fileManager createDirectoryAtPath:fullPath.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:directoryAttr error:&err];
            }
            NSLog(@"-------dir--2222-----%@", fullPath.stringByDeletingLastPathComponent);
            if (nil != err) {
                if ([err.domain isEqualToString:NSCocoaErrorDomain] &&
                    err.code == 640) {
                    unzippingError = err;
                    unzCloseCurrentFile(zip);
                    success = NO;
                    break;
                }
                NSLog(@"[SSZipArchive] Error: %@", err.localizedDescription);
            }
            
            if ([fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite) {
                //FIXME: couldBe CRC Check?
                unzCloseCurrentFile(zip);
                ret = unzGoToNextFile(zip);
                continue;
            }
            
            if (!fileIsSymbolicLink) {
                // ensure we are not creating stale file entries
                //确保我们没有创建的文件条目
                int readBytes = unzReadCurrentFile(zip, buffer, 4096);
                if (readBytes >= 0) {
                    FILE *fp = fopen(fullPath.fileSystemRepresentation, "wb");
                    while (fp) {
                        if (readBytes > 0) {
                            if (0 == fwrite(buffer, readBytes, 1, fp)) {
                                if (ferror(fp)) {
                                    NSString *message = [NSString stringWithFormat:@"Failed to write file (check your free space)"];
                                    NSLog(@"[SSZipArchive] %@", message);
                                    success = NO;
                                    unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedToWriteFile userInfo:@{NSLocalizedDescriptionKey: message}];
                                    break;
                                }
                            }
                        } else {
                            break;
                        }
                        readBytes = unzReadCurrentFile(zip, buffer, 4096);
                        if (readBytes < 0) {
                            // Let's assume error Z_DATA_ERROR is caused by an invalid password
                            // Let's assume other errors are caused by Content Not Readable
                            success = NO;
                        }
                    }
                    
                    if (fp) {
                        //关闭文件
                        fclose(fp);
                        
                        if (nestedZipLevel
                            && [fullPath.pathExtension.lowercaseString isEqualToString:@"zip"]
                            && [self unzipFileAtPath:fullPath
                                       toDestination:fullPath.stringByDeletingLastPathComponent
                                  preserveAttributes:preserveAttributes
                                           overwrite:overwrite
                                      nestedZipLevel:nestedZipLevel - 1
                                            password:password
                                               error:nil
                                            delegate:nil
                                     progressHandler:nil
                                   completionHandler:nil]) {
                            [directoriesModificationDates removeLastObject];
                            [[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil];
                        } else if (preserveAttributes) {
                            
                            // Set the original datetime property
                            if (fileInfo.dos_date != 0) {
                                NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dos_date];
                                NSDictionary *attr = @{NSFileModificationDate: orgDate};
                                
                                if (attr) {
                                    if (![fileManager setAttributes:attr ofItemAtPath:fullPath error:nil]) {
                                        // Can't set attributes
                                        NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting modification date");
                                    }
                                }
                            }
                            
                            // Set the original permissions on the file (+read/write to solve #293)
                            uLong permissions = fileInfo.external_fa >> 16 | 0b110000000;
                            if (permissions != 0) {
                                // Store it into a NSNumber
                                NSNumber *permissionsValue = @(permissions);
                                
                                // Retrieve any existing attributes
                                NSMutableDictionary *attrs = [[NSMutableDictionary alloc] initWithDictionary:[fileManager attributesOfItemAtPath:fullPath error:nil]];
                                
                                // Set the value in the attributes dict
                                attrs[NSFilePosixPermissions] = permissionsValue;
                                
                                // Update attributes
                                if (![fileManager setAttributes:attrs ofItemAtPath:fullPath error:nil]) {
                                    // Unable to set the permissions attribute
                                    NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting permissions");
                                }
                            }
                        }
                    }
                    else
                    {
                        // if we couldn't open file descriptor we can validate global errno to see the reason
                        if (errno == ENOSPC) {
                            NSError *enospcError = [NSError errorWithDomain:NSPOSIXErrorDomain
                                                                       code:ENOSPC
                                                                   userInfo:nil];
                            unzippingError = enospcError;
                            unzCloseCurrentFile(zip);
                            success = NO;
                            break;
                        }
                    }
                } else {
                    // Let's assume error Z_DATA_ERROR is caused by an invalid password
                    // Let's assume other errors are caused by Content Not Readable
                    success = NO;
                    break;
                }
            }
            else
            {
                // Assemble the path for the symbolic link
                NSMutableString *destinationPath = [NSMutableString string];
                int bytesRead = 0;
                while ((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0)
                {
                    buffer[bytesRead] = 0;
                    [destinationPath appendString:@((const char *)buffer)];
                }
                if (bytesRead < 0) {
                    // Let's assume error Z_DATA_ERROR is caused by an invalid password
                    // Let's assume other errors are caused by Content Not Readable
                    success = NO;
                    break;
                }

分析解压代码,可以知道,在进行解压的时候,三方库是有对解压的文件夹名字进行做路径判断和处理,

  • 过滤了包含__MACOSX/,如果有路径有以__MACOSX/开头,则直接关闭当前文件路径,
  • 如果出现\则将\替换为/,
  • 如果路径不是一个正确的沙盒路径,则使用fullPath.stringByDeletingLastPathComponent进行处理,参考官方文档如下:
                fullPath                 处理后的fullPath 
             “/tmp/scratch.tiff”           “/tmp”
             “/tmp/lock/”                   “/tmp”
             “/tmp/”                        “/”
             “/tmp”                         “/”
             “/”                            “/”
             “scratch.tiff”                 “” (an empty string)

但是三方库中并没有在解压的方法中处理出现../这种情况,
最完整的解决方案是对SSZipArchive库进行修补,在解压函数:

  • (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination preserveAttributes:(BOOL)preserveAttributes overwrite:(BOOL)overwrite nestedZipLevel:(NSInteger)nestedZipLevel password:(nullable NSString *)password error:(NSError **)error delegate:(nullable id<SSZipArchiveDelegate>)delegate progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
    中对最终解压的strPath进行检测,如果出现可能造成目录穿越的”../”字符串时进行拦截。
  • -------- JSPath 热更新 ---------------
    JSPath 是一个ios动态更新框架,它可以让JS调用或者替换任意OC的方法,让ios App具备热更新的能力。因为OC是动态语言,所以OC上所有方法的调用或者类的生成都是通过OC的Runtime 在运行时进行的,JS传递字符串给OC,OC通过Runtime接口调用和替换OC方法。
    JSPath 方法调用是借助JavaScriptCore将类名字符串和方法名字符串传递给OC,由OC借助Runtime来反射出类和方法来调用,例如创建一个UIView实例
    js端 UIView.alloc() ---------> OC端 [UIView alloc]


    image.png

js的方法调用规则是必须对已经存在的对象调用已经存在的方法,构建对象,就是在调用方法前使用require函数为每一个类在js中构建同名全局对象,源码如下:

var _require = function(clsName) {
    if (!global[clsName]) {
      global[clsName] = {
        __clsName: clsName
      }
    } 
    return global[clsName]
  }

  global.require = function(clsNames) {
    var lastRequire
    clsNames.split(',').forEach(function(clsName) {
      lastRequire = _require(clsName.trim())
    })
    return lastRequire
  }

js构建函数,为了避免内存消耗,只定义了一个元函数,在元函数中将类名、方法名以及参数传递给OC,js脚本代码在交由JavaScriptCore执行前,是先经过转换的,所有的方法调用都被转换成了调用__c函数,js源码的这样转换是通过正则匹配替换的,核心代码如下:

NSString *formatedScript = [NSString stringWithFormat:@";(function(){try{\n%@\n}catch(e){_OC_catch(e.message, e.stack)}})();",[_regex stringByReplacingMatchesInString:script options:0 range:NSMakeRange(0, script.length) withTemplate:_replaceStr]];

替换后的JS代码 ps:所有的方法调用都被替换成了__c函数调用并将方法名作为参数传入:

;(function(){try{
require('UIColor,UIImage');
defineClass('CustomCell', {
    configWithModel: function(model) {
        self.__c("headView")().__c("layer")().__c("setCornerRadius")(5.0);
        self.__c("headView")().__c("layer")().__c("setBorderColor")(UIColor.__c("darkGrayColor")().__c("CGColor")());
        self.__c("headView")().__c("layer")().__c("setBorderWidth")(1.0);
        self.__c("headView")().__c("layer")().__c("setMasksToBounds")(YES);
        self.__c("headView")().__c("setImage")(UIImage.__c("imageNamed")(model.__c("imgPath")()));

        self.__c("contentLabel")().__c("setText")(model.__c("content")());
        self.__c("contentLabel")().__c("setNumberOfLines")(0);
    },
});
JS将消息传递给OC,内部实现是根据实例方法或者类方法调用了_OC_callI和_OC_callC中的其中一个,而这两个函数在初始化JPEnige的时候就已经注册到JS上下文了,这是JavaScriptCore的接口,在JS上下文中创建JS函数。当函数被调用,会将消息传递给OC端,同时将参数传递给OC,OC执行相应的block,最后将返回值回传JS,其实js传递消息给oc,是借助于JavaScriptCore。OC从JS端接收了消息,需要调用指定方法。JSPatch在处理的时候是通过NSInvocation来调用的,这是因为:JS传过来的参数类型需要转换成OC相应的类型,而NSInvocation很方便从方法签名中获取方法参数类型。同时,也能根据返回值类型取出返回值。

ps:iOS中可以直接调用 某个对象的消息 方式有2种,一种是performSelector:withObject:另一种是NSInvocation,NSInvocation也是一种消息调用的方法,并且它的参数没有限制,可以处理参数、返回值等相对复杂的操作。
JSPatch通过下发JS脚本文件对app进行修复或更新,JS脚本的权限是很大的,如果在下发传输过程中文件被第三方截获,可以修改了脚本内容,故在使用时需对脚本文件进行加密处理。对脚本文件加密主要有以下方案:
a、可以使用对称加密,服务器端和客户端保存一把相同的私钥,下发脚本文件前先对文件进行加密,客户端拿到脚本文件后用相同的私钥解密。这种方案弊端很明显,密钥保存在客户端,一旦客户端被破解,密钥就泄露了。
b、https传输。不过需要购买证书,部署服务器,这种方案也比较安全可靠。
c、RSA签名验证。通过RSA非对称加密,此时需要服务器端,对要下发的脚本文件计算MD5值,用服务器私钥对MD5只进行加密,将脚本文件和加密后的MD5值下发给客户端,而客户端,需要用服务器端公钥解密加密过的MD5值,对接受的脚本文件计算MD5值,将解密出来的MD5与新计算出来的MD5进行比对校验,如果校验通过,则表明脚本在传输过程中没有被篡改。

  • -------- 梳理整个漏洞攻击的流程如下 ---------------
    在启动app的时候,有时会执行js的脚本,即加载自己目录下的/Library/Caches/A/B/patch.js并执行,当我们在使用app时,可能会有通过http下载一个zip包到本地,并使用SSZipArchive库进行解压,如果下载的zip文件含有../则可以实现应用内目录上的跳转,如果将应用内其他文件替换掉,再次运行时,会造成程序奔溃,如果刚好把js文件替换掉,则再次运行app时,会执行替换的js文件,执行下载的js内容。
    大致流程如下


    image.png

如何来检测ZipperDown漏洞?
通过指纹匹配可以获取疑似受影响的应用列表。但该漏洞形态灵活、变种类型多样,指纹匹配的漏报率很高。所以我们建议通过人工分析的方式确认漏洞是否存在。

ZipperDown漏洞如何触发?
ZipperDown漏洞攻击场景与受影响应用业务场景相关。常见攻击场景包括:在不安全网络环境下使用受影响应用、在攻击者诱导下使用某些应用功能等。

对漏洞进行安全防范

针对 iOS 应用的 ZipperDown 漏洞,对IOS 应用可以进行一下的几点防守策略。

  • 数据库文件安全
    开发中会使用SQLite数据库来存储应用数据,而数据库本身一般存储在沙盒文件中,如果数据库里面存储的数据没有进行复杂的加密处理,会是应用程序有敏感信息泄漏的风险,同时也有助于攻击者进行逆向分析。
    安全实施方案: 使用较复杂的加密加盐算法对敏感数据加密后存储。

  • NSUserDefaults 安全
    保存在 NSUserDefaults 中的信息在应用关闭后再次打开依然存在。且保存到 NSUserDefautls 中的数据是没有加密的,可以很轻易地从沙盒中找到。NSUserDefautls 被存储在一个以应用 Bundle ID 为名称的 Plist 文件中。
    安全实施方案:重要的敏感数据存储在 Keychain 中。

  • Keychain 安全
    Keychain是一个安全的存储容器,它是一个SQlite数据库,位于 /private/var/Keychains/keychain-2.db,其保存的所有数据都是加密过的。Keychain 在沙盒之外 App 会将部分重要数据存放在 Keychain 中使用进行读取,但如果写入后未清除就卸载App,则可能会导致下次安全的时候直接从KeyChain中读取密码登录或手势密码无法解除等问题。
    安全实施方案:首次安装应用程序启动后,进行删除keychain数据操作。

  • HTTPS 安全
    在使用HTTPS时, 也有可能因为没有校验服务器证书的原因导致被劫持,如果交互请求数据处理不当,攻击者可以解密得到明文通信数据;甚至进一步伪造 App 的请求,这是极大的安全隐患。
    安全措施:
    a. App内对HTTPS 进行证书做校验
    b.避免使用有漏洞的三方库
    c. 重要的数据,单独加密

  • WebView安全
    WebView 跨平台、动态等特性被广泛使用,同时在ios终端上,WebView可以注册一些敏感信息数据,比如发短信,付款,定位信息等,也会有安全风险。
    安全实施方法
    a.对输入进行过滤和编码
    b.服务端对App发送的数据进行过滤和编码
    c. 尽量减少敏感接口的注册,尽量不要加载第三方内容,如果需要加载,则必须在WebView 的Delegate方法中,通过白名单机制实现对调用者的检验

  • 加密算法
    1.对称加密算法要使用AES,DES 或3DES,避免使用RC4 等目前可能被爆破的算法
    2.对于数据安全性要求较高的地方,使用非对称加密算法如RSA等
    3.对称加密算法的KEY和IV,应避免硬编码,如果加密数据仅是本地存储,可基于设备相关的信息来生成KEY 和 IV

参考1
参考2
参考3
参考4

相关文章

  • ios ZipperDown 漏洞

    看了网易的新闻,得知很多app有ZipperDown安全隐患,故了解了一下。仅供参考,望指正。 漏洞的攻击原理。 ...

  • iOS安全-ZipperDown漏洞

    1.使用最新的SSZipArchive库防止目录穿越不确定版本可以查看库源码是否存在以下代码,如果有则可以继续使用...

  • ZipperDown漏洞修复

    ZipperDown漏洞是由盘古实验室发现的,该漏洞是盘古团队针对不同客户的iOS应用安全审计的过程中发现的,大约...

  • ios逆向

    #iOS-Jailbearking-Study ##iOS越狱学习 iOS越狱就是发现iOS设备硬件或者软件的漏洞...

  • 01_iOS Jailbreak初识

    iOS越狱(iOS Jailbreak)概念 利用iOS系统的漏洞,获取iOS系统的最高权限(Root),解开之前...

  • 越狱环境搭建

    越狱简介 iOS越狱(iOS Jailbreak):利用iOS系统漏洞,获取iOS系统的最高(root)权限,解开...

  • 【警告】iOS 12.1漏洞会绕过锁定屏幕以访问联系人

    【警告】iOS 12.1漏洞会绕过锁定屏幕以访问联系人 网络资料整理 iOS 12.1漏洞会绕过锁定屏幕以访问联系...

  • 漏洞验证 CVE-2016-1287

    Cisco ASA / IOS IKE Fragmentation Vulnerability 漏洞描述 -由于在...

  • 看我如何发现雅虎邮箱APP的存储型XSS漏洞!凭借该漏洞$350

    今天我要分享的是参与雅虎(Yahoo!)漏洞众测项目发现的一个关于雅虎邮箱 iOS应用的漏洞,最终,凭借该漏洞,我...

  • iOS逆向开发之环境配置

    一:越狱环境的配置 1、关于iOS越狱(Jailbreak) iOS越狱是指开发者利用iOS系统的漏洞、获取到iO...

网友评论

      本文标题:ios ZipperDown 漏洞

      本文链接:https://www.haomeiwen.com/subject/acvbdftx.html