프리다 활용 (2)

옥영진·2020년 9월 10일
0

Frida

목록 보기
9/11
post-custom-banner

사전 준비

https://github.com/as0ler/Android-Examples/blob/master/sieve.apk

위 링크를 통해 sieve.apk 파일을 다운로드 받아 녹스 앱 플레이어에 설치한다. 이 앱은 처음 실행하면 패스워드를 설정하고, 그 후에는 PIN 번호를 설정한다. 앱 프로세스가 완전히 종료 후 다시 실행되면 패스워드를 입력하고, 홈 화면이나 다른 화면으로 전환되었다가 다시 앱 화면으로 전환되었을 경우에는 PIN 번호를 입력하도록 되어 있다. 일단 앱을 실행해보자.

첫 실행 시 패스워드를 설정하는 화면이 나온다.

패스워드 설정 후에는 PIN 번호를 설정하는 화면이 나온다. 임의의 4자리로 설정하자. 설정하고 나면 처음에 설정했던 패스워드를 입력해야 진행할 수 있다. 패스워드 입력 후 우측 상단에 있는 add 버튼을 통해 데이터를 등록하자.

그리고 apk 파일을 jadx 를 통해 디컴파일을 진행하고, frida-ps -Ua 명령어를 통해 해당 앱의 패키지 이름을 확인한다. 확인하면 패키지 이름이 com.mwr.example.sieve 임을 알 수 있다.

패스워드 우회

처음 패스워드를 입력하는 화면은 앱 프로세스가 완전 종료된 후에 시작되어야 한다. 하지만 이 앱은 백그라운드에서 계속 실행 중이기 때문에 설정에서 강제 종료를 시키거나 frida-kill 명령어로 종료 시킨 후에 재실행한다.

재실행 후 잘못된 패스워드를 입력하면 상단에 잘못된 패스워드라는 메세지가 표시된다. 이 메세지를 가지고 어떤 메소드에서 패스워드 검사를 하고 있는지 확인해 보자.

이전 루팅 탐지 우회 때와는 달리 메세지 문자열을 그대로 사용하지 않고 있다. 물론 캡처화면에서는 incorrect 라는 문자열이 포함된 변수명을 사용하고 있는것으로 보이지만, 보통은 이렇게 메세지에 포함된 문자열을 검색했을 때 얻어걸리기는 어려운 일이다. 앱에서 사용하는 메세지 문자열들은 문자열 리소스 파일을 확인하면 된다. /res/values/String.xml 파일에서 확인할 수 있는데, jadx 로 디컴파일했을 때는 해당 파일을 확인할 수가 없다. apktool 이라는 프로그램으로 디컴파일하여 확인할 수 있다.

apktool로 디컴파일하는 방법

  1. 최신 버전의 자바를 설치한다.
  2. apktool 최신버전의 .jar 파일을 다운로드 받는다.
  3. .jar 파일이 있는 곳에서 cmd를 실행한다.
  4. java -jar apktool.jar d [디컴파일 할 apk 파일] 명령어를 실행한다.

디컴파일 후 메세지 내용을 검색해보면 아래와 같이 나온다.

해당 메세지를 사용하는 문자열 리소스 이름이 error_incorrect_password 라는 것을 알 수 있다.

그 후에 해당 변수에 저장된 16진수 값을 10진수로 변환하면 위에서 검색했을 때 보았던 error_incorrect_password 값인 2131034176 임을 알 수 있다. 이제 이 변수명이 어디서 사용되는지 찾아도 되지만, MainActivity 클래스부터 확인해보자.

MainLoginActivity 라는 클래스가 로그인할 때 사용하는 액티비티로 추측할 수 있다. 해당 클래스 내부에 있는 checkKeyResult() 메소드를 보니 인자 값으로 전달받은 status 값이 참이면 loginSuccessful() 메소드를 호출하며 로그인하는 것으로 보인다. 이 checkKeyResult() 메소드를 후킹해보도록 하자.

// sieve.js
setImmediate(function() {
  Java.perform(function() {
    var loginBypass = Java.use("com.mwr.example.sieve.MainLoginActivity");
    loginBypass.checkKeyResult.implementation = function(arg) {
      console.log("[*] Success Login Bypass");
      this.checkKeyResult(true);
    }
  })
})

잘못된 패스워드를 입력하더라도 로그인에 성공했다는 로그 출력과 함께 등록해두었던 데이터를 확인할 수 있다.

브루트포스 PIN

이번에는 PIN 번호를 브루트포스하여 올바른 PIN 번호 값을 획득해보도록 하자.

error_incorrect_password 변수를 사용하는 다른 클래스들 중에 ShortLoginActivity 클래스를 살펴보니 submit() 메소드에서 PIN 번호를 검사하는 것처럼 추측되는 로직이 보인다. 이 메소드에서 브루트포스할 만한 포인트가 있는지 확인해보자. workingPIN 이라는 변수에 입력한 PIN 번호 값이 저장되는 것으로 보이고, 이 값이 serviceConnection.checkPin() 메소드의 인자값으로 전달되는 것으로 보아 해당 메소드가 PIN 번호를 검사하는 것으로 보인다.
따라서, 해당 메소드를 0000 ~ 9999 까지의 범위로 브루트포스하여 정확한 PIN 번호를 알아내보자. submit() 메소드를 재작성하여 PIN 번호를 검사하는 메소드를 반복문을 통해 호출하도록 할 것이다.

// sieve.js
setImmediate(function() {
  Java.perform(function() {
    // 패스워드 우회 로직 생략
    
    function padToFour(number) { // 자릿수가 모자란 정수의 경우 앞에 0을 채워넣는 함수
      if(number<=9999) {
        number = ("000"+number).slice(-4);
      }
      return number;
    }
    
    var pinBypass = Java.use("com.mwr.example.sieve.ShortLoginActivity");
    pinBypass.submit.implementation = function(arg) {
      var service = this.serviceConnection.value; // serviceConnection 클래스 객체 값을 가져옴
      for(var i=0;i<9999;i++) {
        service.checkPin(padToFour(i));
        console.log(padToFour(i));
      }
    }
  })
})

padToFour 함수를 정의했는데, 이 함수는 PIN 번호에 입력되는 값이 네 자리여야 하는데 반복문을 통해 나오는 정수값은 1000 이하의 값은 세 자리 이므로, 앞에 0을 채워 문자열로 만들어 주는 함수이다. checkPin() 메소드를 호출하기 위해 해당 메소드가 정의된 클래스를 가져와서 사용해도 되지만, 위에 작성한 코드처럼 this.serviceConnection.value 를 호출하여 해당 클래스 객체를 가져와서 사용할 수도 있다.

스크립트 인젝션 후에 틀린 PIN 번호를 입력하면 콘솔에 0000 ~ 9999까지 출력되면서 올바른 PIN 번호가 입력되어 저장된 데이터를 확인할 수 있다.

지금까지 프리다로 프로세스를 후킹하여 패스워드를 알아보았는데, 사실 이러한 계정 정보들이 클라이언트(안드로이드)에 저장되어 있을 수도 있다.


/data/data/[앱 패키지 명] 경로 내에 있는 파일들을 살펴보니 저장해놓았던 패스워드가 평문으로 저장되어 있는 것을 알 수 있다. 이런식으로 클라이언트 쪽에 계정 정보가 평문으로 저장되어 있을 수도 있으므로 앱 데이터가 저장되는 디렉토리도 살펴보면 취약점을 발견할 수 있다.

profile
안녕하세요 함께 공부합시다
post-custom-banner

0개의 댓글