Disable SSL certificates validation check on the iOS

11/22/2015 0 Comments

iOS App 보안 테스트를 할 때, 주로 Server – Client 간의 커뮤니케이션이 어떻게 구성되어 있는지 확인하고 조작하는 등의 작업을 하게됩니다.
바이너리 형태로 커뮤니케이션하는  경우도 있지만, 많은 경우는 HTTP/HTTPS 로 서버와 커뮤니케이션합니다.
HTTP의 경우는 문제가 되지 않지만, HTTPS의 경우 인증서의 유효성을 체크한다면 프록시를 이용하여 통신 데이터를 조작하는 것이 쉽지 않습니다.
App의 바이너리를 조작을 하지 않고, 서버간의 트래픽을 모니터링할 수 있는 방법 중 하나는 런타임으로 SSL 인증서 유효성 체크 함수를 후킹하여 하는 방법이 있습니다. SSL 인증서 유효성 체크 함수를 후킹하여 인증서 유효성 검사를 우회할 수 있습니다.
이러한 방식의 인증서 유효성 검사 무력화는 이미 2012년 블랙햇에서 발표된 바 있습니다.  관심 있으신 분들 께서는 해당 슬라이드를 참고하세요.

발표자인 iSECPartners의 Justine Osborne 과 Alban Diquet 는  공격도구의 소스코드를 공개했습니다. Github에서 다운 받을 수 있습니다.

SSL 인증서 유효성 검사는 Secure Transport API 를 사용하여 이루어 지는데요.
Secure Transport API는 iOS 플랫폼에서 로우 레벨 TLS 를 관장하는 API로 해당 API의 조작을 통해 인증서 유효성 검사를 무력화시키면 iOS 플랫폼에서 사용할 수 있는 대부분의 네트워크 API에 영향을 미치게 됩니다.

Apple Developer Document 에 따르면 사용자 지정 인증서 유효성 검사를 하거나 혹은 비활성화 시키기 위해서는 Secure Transport API를 이용하여 다음과 같은 방법으로 구현할 수 있습니다.

1. 커넥션을 맺기 전, SSL context에 kSSLSessionoptionBreakOnServerAuth 옵션을 세팅 및 true 로 만들기 위해 SSLSetSessionOption()를 호출 합니다. 이 옵션을 true로 세팅하게 되면 애플리케이션이 자체 인증서 검증을 하도록 프레임워크에 내장된 인증서 유효성 검사를 사용하지 않게 됩니다.

2. SSLHandshake() 함수를 이용하여 handshake 를 실행합니다.

3. SSLHandshake() 가 errSSLServerAuthCompleted를 반환하는 경우,  연결에 대한 신뢰 객체를 얻기 위해 SSLCopyPeerTrust() 를 호출합니다. 신뢰 객체는 당신이 원하는 모든 사용자 지정 서버 신뢰 평가를 구현하기 위해 사용됩니다.

4. SSLHandshake() 함수를 이용하여 handshake를 계속하거나 혹은 연결을 종료합니다.

Secure Transport API 의 SSLHandshake , SSLSetSessionOption, SSLCreateContext, 3개의 메소드에 Hook을 걸어 조작함으로써, SSL 인증서 유효성 검사를 무력화 시킬 수 있습니다.



%ctor {
 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
    // Should we enable the hook ?
 if (shouldHookFromPreference(@"killSwitchSSLHandshake")) {
        NSLog(@"SSL Kill Switch - Hook Enabled.");
        MSHookFunction((void *) SSLHandshake,(void *)  replaced_SSLHandshake, (void **) &original_SSLHandshake);
        MSHookFunction((void *) SSLSetSessionOption,(void *)  replaced_SSLSetSessionOption, (void **) &original_SSLSetSessionOption);
        MSHookFunction((void *) SSLCreateContext,(void *)  replaced_SSLCreateContext, (void **) &original_SSLCreateContext);
    }
    else {
     NSLog(@"SSL Kill Switch - Hook Disabled.");
        }
 
    [pool drain];
}

모든 SSL context에 내장된 인증서 유효성 검사를 사용하지 않도록 SSLCreateContext() 을 수정합니다. SSLCreateContext() 는 새로운 SSL context를 만들 때 사용되는데, 이 부분을 수정하여 생성되는 모든 새로운 SSL Context에 기본적으로 kSSLSessionOptionBreakOnServerAuth 플래그가 설정되도록 합니다.

// Hook SSLCreateContext()
static SSLContextRef (*original_SSLCreateContext) (
   CFAllocatorRef alloc,
   SSLProtocolSide protocolSide,
   SSLConnectionType connectionType
);
 
static SSLContextRef replaced_SSLCreateContext (
   CFAllocatorRef alloc,
   SSLProtocolSide protocolSide,
   SSLConnectionType connectionType
) {
    SSLContextRef sslContext = original_SSLCreateContext(alloc, protocolSide, connectionType);
    
    // Immediately set the kSSLSessionOptionBreakOnServerAuth option in order to disable cert validation
    original_SSLSetSessionOption(sslContext, kSSLSessionOptionBreakOnServerAuth, true);
    return sslContext;
}

SSLSetSessionOption()를 수정하여 내장된 인증서 유효성 검사를 다시 활성화 할 수 있는 기능을 제거합니다.
SSLSetSessionOption() 는 SSL context에 주어진 특정 옵션의 값을 설정하도록 호출될 수 있습니다.
SSLCreateContext()를 수정하여 새로이 생성된 SSL Context에 설정한 kSSLSessionOptionBreakOnServerAuth 값이 다른 값으로 바뀌는 것을 막을 수 있습니다.

// Hook SSLSetSessionOption()
static OSStatus (*original_SSLSetSessionOption)(
    SSLContextRef context, 
    SSLSessionOption option, 
    Boolean value);
 
static OSStatus replaced_SSLSetSessionOption(
    SSLContextRef context, 
    SSLSessionOption option, 
    Boolean value) {
 
    // Remove the ability to modify the value of the kSSLSessionOptionBreakOnServerAuth option
    if (option == kSSLSessionOptionBreakOnServerAuth)
        return noErr;
    else
        return original_SSLSetSessionOption(context, option, value);
}

errSSLServerAuthCompleted 는 발신자의 인증서 cheking 및 pinning  하도록 하는 리턴값으로 모든 사용자 지정 인증서의 유효성 검사를 신뢰하도록 SSLHandshake()를 수정합니다.

// Hook SSLHandshake()
static OSStatus (*original_SSLHandshake)(
    SSLContextRef context
);
 
static OSStatus replaced_SSLHandshake(
    SSLContextRef context
) {
    OSStatus result = original_SSLHandshake(context);
 
    // Hijack the flow when breaking on server authentication
    if (result == errSSLServerAuthCompleted) {
        // Do not check the cert and call SSLHandshake() again
        return original_SSLHandshake(context);
    }
    else
        return result;
}

이러한 3가지의 함수를 수정하게되면, 사파리, 트위터, AppStore 및 모든 써드 파티 애플리케이션에서의 인증서 유효성 검사를 우회하여 패킷들을 분석할 수 있습니다.

If you need my help, tell me anytime. Facebook