
1. SSL PINNING
SSL Pinning(SSL 고정)은 클라이언트가 서버의 특정 SSL/TLS 인증서를 미리 저장(고정)하여 네트워크 통신 시 사전에 지정된 인증서와 일치하는 경우에만 통신(연결)을 허용하는 보안 기술입니다.
SSL PINNING은 다음과 같은 방식으로 동작하게 됩니다.
-
(1) 애플리케이션 내부에 서버의 SSL 인증서(Certificate) 혹은 공개키를 저장
-
(2) 클라이언트(앱)는 서버와 SSL/TLS 핸드쉐이크 진행
-
(3) 클라이언트에 저장된 인증서
[Pinned Certificate]와 서버의 인증서를 비교 -
(4) 일치하는 경우에만 통신을 허용하여 중간자 공격(MITM)을 예방
SSL PINNING은 인증서 고정(CERTIFICATE PINNING) 방식과 공개 키 고정(PUBLIC KEY PINNING) 방식으로 나눌 수 있는데요. 인증서 고정 방식은 서버의 특정 인증서(leaf/중간/루트)를 앱에 저장하여 비교하는 방식(Root CA, Allow CA)으로 TLS 기본 검증(만료/서명/호스트명)을 수행한 뒤, 서버가 제시한 인증서 체인(leaf/중간/루트)이 앱에 미리 고정해 둔 인증서(또는 그 식별값)와 일치하는 경우에만 통신을 허용하는 방식입니다. 공개 키 고정(PUBLIC KEY PINNING) 방식은 인증서 체인(leaf/중간/루트) 중에서 내가 선택한 “어느 인증서의 공개키(SPKI)“를 뽑아서 해시한 값을 앱에 박아두고, 핸드셰이크 때 서버가 준 체인에서 같은 방식으로 계산한 값과 일치하면 통과시키는 방식입니다.
소스코드를 살펴보면 isCheckRootCA, isCheckAllowCA 함수에 의해 앱이 서버와 통신할 때 사전에 정의된 인증서(RootCA 혹은 AllowCA)를 사용하고 있는지를 확인하고 있습니다.
이를 우회하기 위해서는 간단하게 해당 함수들의 리턴 값을 변조하거나, 함수 내부 로직을 분석하여 인증서(Certificate) 검증 로직을 우회하면 됩니다. 인증서 검증 로직 우회 시에는 인증서 체이닝 리스트를 빈 값으로 설정하거나, 인증서 유효성을 검증하는 함수를 후킹하여 “Null” 값을 반환토록 하면 됩니다.

이번 실습에서는 간단히 함수의 리턴 값을 변조하는 방식으로 우회를 진행하였습니다.
Java.perform(function() {
var PinningDetector = Java.use('com.playground.anditer.PinningDetector');
PinningDetector.isCheckRootCA.implementation = function() {
return false;
}
PinningDetector.isCheckAllowCA.implementation = function() {
return false;
}
})