Santoku: Bypass SSL Pinning using Frida

2020. 2. 1. 17:162020/Android App Hacking

사용자가 앱을 설치하고 해당 앱을 통해서 인터넷에 접속한다.

앱이 인터넷 서버에 접속할 때 서버와 통신을 하는데 암호 키를 교환하고 암호화 방식을 결정하고 서버의 인증서를 확인한다. 

앱에서는 SSL Pinning이라고 하는 앱 내부에 신뢰할 수 있는 인증서들을 저장해놓고 (hard-code)접속하려는 서버의 인증서와 비교한다.

Frida를 통해 앱을 Hooking하여 앱이 인증서를 검증하는 과정에서 내가 가진 서버의 인증서를 유효한 인증서로 인식하도록 한다.

먼저 인증서가 필요하니 인증서를 다운 받도록 하자. Burp suite의 인증서를 사용할 것이다.

 

#1 Desktop에서 Burp Suite 실행

#2 인터넷에서 프록시 연결 설정을 진행한다.

#3 http://burp에 접속한 뒤 인증서를 다운받는다. (cert.der => cert.crt형변환 해주어야한다)

#4-1 다운 받은 인증서를 디바이스에 push 한다. 

#4-2 디바이스에서 http://burp 접속 후 인증서를 설치하고 해당 파일을 사용했다 (2번째 시도)

  Certificate problem.. 
  => go to http://burp 
  => download certificate 
  /mnt/sdcard/downloads/#ls => cacert.der 
  /mnt/sdcard/downloads/# mv cacert.der cacer.crt 
  >>> install it. 
  >>> Go to internet: proxy서버에서 https패킷 캡쳐 가능

#5 Hooking the app using Frida ! - 앱과 프록시 서버간의 통신을 시키기 위해서.

 

 현재 디바이스에와 데스크탑의 프록시 서버가 통신중이다. burp suite의 인증서를 디바이스에서 신뢰할 수 있는 인증서로 저장해놓았기 때문에 인터넷은 사용가능하다. (다른 포스트에서)

 하지만 앱에서는 SSL Pinning과정으로 인해 프록시 서버로 데이터를 전송하기 전에 프록시 서버를 신뢰하지 않아 데이터를 전송하지 않는다. 앱 코드내에 brup의 인증서가 없기 때문. 이를 우회하기 위한 자바스크립트 코드는 앱이 인증서를 인증하는 과정에 침투해서 조작하는 방식의 코드이다.

 

Hooking #1 Run Frida-Server

  $ nox_adb push Frida-server /data/local/tmp

  $ chmod 755 frida-Server

  $ ./frida-Server &

 

* Usage of Frida

 

 

Hooking #2 js code: github에서 Bypass SSL Pinning코드를 가져왔다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/* 
   Android SSL Re-pinning frida script v0.2 030417-pier
   
   UPDATE 20191605: Fixed undeclared var. Thanks to @oleavr and @ehsanpc9999 !
*/
setTimeout(function(){
    Java.perform(function (){
     console.log("");
     console.log("[.] Cert Pinning Bypass/Re-Pinning");
var CertificateFactory = Java.use("java.security.cert.CertificateFactory");
     var FileInputStream = Java.use("java.io.FileInputStream");
     var BufferedInputStream = Java.use("java.io.BufferedInputStream");
     var X509Certificate = Java.use("java.security.cert.X509Certificate");
     var KeyStore = Java.use("java.security.KeyStore");
     var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
     var SSLContext = Java.use("javax.net.ssl.SSLContext");
// Load CAs from an InputStream
     console.log("[+] Loading our CA...")
     var cf = CertificateFactory.getInstance("X.509");
     
     try {
      var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt");
     }
     catch(err) {
      console.log("[o] " + err);
     }
     
     var bufferedInputStream = BufferedInputStream.$new(fileInputStream);
    var ca = cf.generateCertificate(bufferedInputStream);
     bufferedInputStream.close();
     console.log("[o] Our CA Info: " + certInfo.getSubjectDN());
// Create a KeyStore containing our trusted CAs
     console.log("[+] Creating a KeyStore for our CA...");
     var keyStoreType = KeyStore.getDefaultType();
     var keyStore = KeyStore.getInstance(keyStoreType);
     keyStore.load(nullnull);
     keyStore.setCertificateEntry("ca", ca);
     
     // Create a TrustManager that trusts the CAs in our KeyStore
     console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");
     var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
     var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
     console.log("[+] Our TrustManager is ready...");
console.log("[+] Hijacking SSLContext methods now...")
 
     console.log("[-] Waiting for the app to invoke SSLContext.init()...")
 
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;""[Ljavax.net.ssl.TrustManager;""java.security.SecureRandom").implementation = function(a,b,c) {
      console.log("[o] App invoked javax.net.ssl.SSLContext.init...");
      SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;""[Ljavax.net.ssl.TrustManager;""java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);
      console.log("[+] SSLContext initialized with our custom TrustManager!");
     }
    });
},0);
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

Hooking #3 Run the Command

  Command: frida -U -f [AppPackage] -l [ScriptPath] --no-paus(중단없이)

  C:\Users\eunhwankim>frida -U -f com.facebook.lite -l C:\Users\eunhwankim\Downloads\fridascript.js --no-paus

 

Result

 인증서를 확인하는 알고리즘이 시작되기를 대기하고 있고 실행되면 해당 알고리즘이 조작되어 burp인증서가 앱에게 유효한 인증서로 인식된다.

앱에서 아예 로그인 자체가 안됨. 인증서를 검증하는 알고리즘이 시행되지 않는 것같음.

페이스 북 앱에서는 이 취약점에 대해 대응하고 있음을 알 수 있다.

 

 

 

 

* 참고1

  frida server file : Github에서 다운 후 디바이스에 저장한다.

  $ adb push [frida-server-file] /data/local/tmp

 

* 참고2

  burp suite certificate은 데스크 탑에서 다운받은 것과 모바일 상에서 다운 받았고 두 개다 실행해보았다.

  자바스크립트 코드에서 지정된 /data/local/tmp 폴더에 cert-der.crt로 저장해서 후킹을 진행하였다.

  

Desktop directory: frida, javascript code file 

Device (/data/local/tmp): fride-server. certificate

'2020 > Android App Hacking' 카테고리의 다른 글

Santoku: Exposed Content provider Vulnerability (using Drozer)  (0) 2020.02.02
Santoku: Drozer  (4) 2020.02.01
Smali Code  (0) 2020.02.01
Santoku: Reversing an Application  (0) 2020.01.31
Santoku: Static Analysis Log 확인  (0) 2020.01.31