본문 바로가기

Technical Docs/Android

[분석] UnCrackable-Level1.apk - 1 (Rooted)

UnCrackable-Level1.apk - 2 (Secret String)

 

OWASP에는 모바일 보안 테스트 할 수 있는 앱을 제공함

분석을 하면서 참고해야하는 부분을 풀이할 예정

 

진단 환경

  • 단말 : Nexus6P - 7.1.1
    • jadx-gui : Dex to Java decompiler
    • APK Easy Tool : Decompile, Compile, Signing 등 쉽게해주는 툴
  • APP : UnCrackable-Level1.apk

https://github.com/OWASP/owasp-mstg/tree/master/Crackmes/Android

 

GitHub - OWASP/owasp-mstg: The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security testing an

The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes the technical processes for verifying the controls listed in...

github.com

 

1. Frida

2. 코드 패치(나중에)

3. gdb(나중에)

 

분석 1 - 앱 구동 확인

 

루팅 탐지

앱 실행하면 루팅 탐지를 하며, 앱이 종료됨

 

정적분석 진행

1. "AndroidManifest.xml" 파일에서 앱 구동시 시작되는 Activity를 확인

첫 실행되는 activity 확인

 

2.  "onCreate"에서 루팅 및 디버거 탐지를 하고 있음

***************************************** 안드로이드 생명주기 경로 추가 *****************************************

onCreate 확인

 

2-1 루팅탐지 : sg.vantagepoint.a.c 클래스에 있는 각 메소드는 boolean 타입이며, 리턴값이 True 일 경우 루팅 판단

3개 메소드를 이용해 루팅 탐지

c.a()

 -  시스템 PATH에 "su" 파일이 존재하는지 확인

 

c.b()

 - Build.TAGS가 Null이 아니면서 "test-keys"가 있는지 확인

    : "test-keys"를 확인하는 이유

      →  커널이 컴파일/빌드될 때 서명을 "test-keys"로 하고 있음

      →  커널이 컴파일/빌드됐다 == 변조된 OS일 가능성이 있다

 

c.c()

 - str 변수에 있는 파일이 있는지 확인

 

2-2 디버거탐지 : sg.vantagepoint.a.b 클래스에서 디버거 판단

c.b()

 - "context.getApplicationContext().getApplicationInfo().flags" 리턴되는 값이 0이 아닐경우 디버거 사용

 


 

풀이 1 - 1 FRIDA

공통점은 "String"을 이용해 루팅 탐지 하고 있음

소스코드를 봤을 때 아래와 같이 시도할 수 있다고 생각함

 

1. String 함수를 후킹할 것인가

  - 장점 : 패턴을 만들 수 있음

  - 단점 : 루팅 탐지 하지 않는 곳에서도 후킹을 계속 함(모든 문자열 후킹)

 

2. 리턴값을 변경할 것인가

  - 장점 : 직관적

  - 단점 : 소스코드 길어짐

 

3. 루팅탐지에 사용된 import 함수를 후킹할 것인가

  - 장점 : 자신만의 루팅탐지 함수를 만들 수 있음 

  - 단점 : ...

 

코드 예상 순서

1. 루팅 탐지에 사용된 문자열 정리
2. 루팅 탐지에 사용된 모듈 후킹 (Bulid.TAGS, java.io.File)
3. 루팅 탐지에 사용된 문자열이 있는지 확인
4. 사용된 문자열이 있을 경우 리턴 값 변경

1. 루팅 탐지에 사용된 문자열 정리

  sg.vantagepoint.a.c 클래스에서 루팅탐지에 사용된 문자열 정리

var checkRoot() = [
    //sg.vantagepoint.a.c.a()
    "su",

    //sg.vantagepoint.a.c.b()
    "test-keys",

    //sg.vantagepoint.a.c.c()
    "/system/app/Superuser.apk",
    "/system/xbin/daemonsu",
    "/system/etc/init.d/99SuperSUDaemon",
    "/system/bin/.ext/.su",
    "/system/etc/.has_su_daemon",
    "/system/etc/.installed_su_daemon",
    "/dev/com.koushikdutta.superuser.daemon/"
]

 

2. 호출되는 문자열 후킹

테스트 단말기는 OS 빌드를 하지 않아 Bulid.TAGS 테스트를 제외하며, java.io.File만 후킹할 예정
아래코드는 "exists()" 함수를 후킹해 파일명 출력하는 코드임

  var File_exists = Java.use('java.io.File');
  File_exists.exists.implementation = function () {
    var File_Name = File_exists.getName.call(this);
    console.log(File_Name)
    return this.exists.call(this);
  }

 

"exists()" 함수에 사용된 파일명 모두 출력함

frida -l test.js -U --no-pause -f owasp.mstg.uncrackable1​

 

3. 루팅 탐지에 사용된 문자열이 있는지 확인

출력된 파일명이 루팅 탐지에 사용된 문자열이 있는지 "indexOf" 함수로 확인

  var File_exists = Java.use('java.io.File');
  File_exists.exists.implementation = function () {
    var File_Name = File_exists.getName.call(this);
    if (checkRoot.indexOf(File_Name) > -1) {
      console.log(File_Name)
    }
    return this.exists.call(this);
  }

 

 모든 파일명이 출력되는게 아니라 "su" 파일명만 출력함

frida -l test.js -U --no-pause -f owasp.mstg.uncrackable1​

 

4. 사용된 문자열이 있을 경우 리턴 값 변경

exists는 파일이 있을 경우 "true"를 반환

  var File_exists = Java.use('java.io.File');
  File_exists.exists.implementation = function () {
    var File_Name = File_exists.getName.call(this);
    if (checkRoot.indexOf(File_Name) > -1) {
      return false;
    }
    return this.exists.call(this);
  }

 

루팅탐지 문자열이 있을 경우 리턴 값을 "false"로 변경

frida -l test.js -U --no-pause -f owasp.mstg.uncrackable1​

루팅 탐지 우회 끝

최종 소스

/*
frida -l test.js -U --no-pause -f owasp.mstg.uncrackable1
*/
Java.perform(function () {
  var checkRoot = [
    //sg.vantagepoint.a.c()
    "su",

    //sg.vantagepoint.a.b()
    "test-keys",

    //sg.vantagepoint.a.c()
    "/system/app/Superuser.apk",
    "/system/xbin/daemonsu",
    "/system/etc/init.d/99SuperSUDaemon",
    "/system/bin/.ext/.su",
    "/system/etc/.has_su_daemon",
    "/system/etc/.installed_su_daemon",
    "/dev/com.koushikdutta.superuser.daemon/"
  ]

  var File_exists = Java.use('java.io.File');
  File_exists.exists.implementation = function () {
    var File_Name = File_exists.getName.call(this);
    if (checkRoot.indexOf(File_Name) > -1) {
      console.log(File_Name)
      return false;
    }
    return this.exists.call(this);
  }
});

 

풀이 1 - 2 코드패치

풀이 1 - 3 디버거(gdb)