봔하는 수달

[안드로이드] Uncrakable1 본문

정보보안/모바일

[안드로이드] Uncrakable1

봔수달 2025. 7. 24. 23:27

안녕하세요, 이번 글에서는 OWASP MSTG에서 제공하는 안드로이드 보안 실습 앱 중 하나인 UnCrackable-Level1을 대상으로 Frida를 활용한 루팅 탐지 우회클라이언트 사이드 인증 우회를 수행한 과정을 공유합니다.

 

  • 앱 이름: UnCrackable-Level1
  • 패키지명: owasp.mstg.uncrackable1

UnCrackable-Level1은 Build.TAGS, su 바이너리 존재 여부, SystemProperties, Runtime.exec() 등을 통해 루팅 여부를 탐지함.

루팅 탐지

 

루팅 탐지 영역을 확인하기 위해 소스코드를 확인.

루팅 탐지는 class c에서 a(), b(), c() 메소드들의 반환값의 조건문을 통해 탐지가 진행됨.

 

루팅 탐지 구간

 

해당 메소드들을 확인하면 아래와 같은 로직이 존재.

루팅 탐지 구간 2

 

탐지 로직 탐지 방식
  • PATH 환경변수를 : 구분자로 나눠 탐색.
  • 각 경로에 su 바이너리 존재 여부 확인 (/system/bin/su, /system/xbin/su 등).
  • 존재하면 루팅 상태로 판단.


로직 우회 방법 File.exists() 메소드에서 su 포함된 값이 호출될 때 리턴값을 false 변조하거나 해당 c.a() 메소드 리턴값을 false로 변조하여 우회.
c.b()
  • Build.TAGS 값은 Android 빌드 서명 정보를 담음.
  • test-keys는 공개 키로 서명된 사용자 빌드 (즉, 루팅 가능성이 높음).
  • release-keys가 아닌 경우 보통 신뢰하지 않음.
Build.TAGS 전역 str값에서 test-keys를 확인하는 방법으로 전역에서 해당 문자열을 변조하거나 해당 메소드의 리턴값을 변조하여 우회 가능
c.c()
  • 루팅 앱 또는 su daemon의 흔적으로 알려진 파일 목록을 확인.
  • 각 경로가 존재하면 루팅된 기기로 판단.
File.exists()를 후킹해서 특정 루트 관련 파일에 대해서만 거짓(false) 를 반환하거나 해당 메소드 리턴값을 변조하여 우회 가능

 

전역적 우회 방법 지역적 우회 방법
Java.perform(() => {
    const File = Java.use("java.io.File");
    File.exists.implementation = function () {
        const absPath = this.getAbsolutePath();
        const shouldBypass = absPath.includes("su") || absPath.toLowerCase().includes("superuser");

        if (shouldBypass) {
            console.log(`[Bypass] File.exists("${absPath}") → false`);
            return false;
        }

        return this.exists();
    };

     // [선택] String.contains("test-keys") → false 변조해도됨
    const Build = Java.use("android.os.Build");
    console.log(Build.TAGS)
    Build.TAGS.value = "relase-keys"
    console.log(Build.TAGS)
});
Java.perform(() => {
    const RootCheckClass = Java.use("sg.vantagepoint.a.c");
       
        //1번 우회
        RootCheckClass.a.implementation = function () {
            console.log("a return check: ", this.a())
            return false;
        };

        //2번 우회 리빌드가 아니기에 우회 불필요
        RootCheckClass.b.implementation = function (){
            console.log("b return check: ",this.b())
            return false;
        }

        //3번 우회
        RootCheckClass.c.implementation = function (){
            console.log("c return check: ",this.c())
            return false;
        }

       
});

 

루팅 우회

 

인증 우회

소스코드가 양이 적어 정적으로 분석이 가능하지만 실제 취약점 분석을 수행하면 어택 벡터를 식별하는데 굉장한 리소스가 소모됨.

이러한 과정을 줄이기 위해 로드된 클래스 내 메소드 후킹을 통해 실제 사용되는 함수를 식별하는 과정이 필요함.

 

윈도우와 다르게 안드로이드가 이러한 기능이 가능한 이유는 java의 가장 큰 특징인 Write Once, Run Anywhere (WORA) 슬로건의 산물 JVM 때문.

 

아래는 특정 패키지 내 클래스 리스트를 출력해주고 해당 클래스를 hooAllMethods에 선언해주면 하위 메소드를 모니터링하는 코드이며 이를 통해 클래스들을 수행해보면서 인증 부분을 식별 가능.

perform(() => {
    const classes = Java.enumerateLoadedClassesSync();
    classes.forEach(className => {
        if (className.includes("sg.vantagepoint.a"))
            console.log(className);
    });
});


Java.perform(function () {
    console.log("=====================================")
    hookAllMethods("sg.vantagepoint.a.a");  
    function hookAllMethods(targetClass) {
      var total_hooked = 0;
      try {
        var clazz = Java.use(targetClass);
        var methods = clazz.class.getDeclaredMethods();
  
        methods.forEach((element) => {
          var method_name = element.getName();
          console.log("Hooking " + targetClass + "." + method_name);
  
          try {
            var overloads = clazz[element.getName()].overloads;
          //   console.log(
          //     "Method: " + method_name,
          //     "Overloads: " + overloads.length
          //   );
            total_hooked += overloads.length;
          //   console.log(total_hooked);
            overloads.forEach((overload) => {
              overload.implementation = function () {
                console.log("called " + targetClass + "." + method_name);
                console.log("Arguments: " + arguments);
                // console.log("Vibrate called with duration: " + args[0]);
                console.log("Argument count: " + arguments.length);
                for (let i = 0; i < arguments.length; i++) {
                    console.log("Arg[" + i + "]: Type=" + typeof arguments[i] + ", Value=" + arguments[i]);
                }
                console.log("\n");
                var result = this[element.getName()].apply(this, arguments);
                console.log("Result: " + result);
                return result;
              };
            });
          } catch (error) {
            console.log("Error: " + error);
          }
        });
  
        console.log("Total hooked: " + total_hooked);
      } catch (error) {
        console.log("Error: " + error);
      }
    }
  });

 

위 코드를 포함하여 verify를 수행하게되면 아래 결과 확인 가능

sg.vantagepoint.a.a.a

Result 결과는 HEX 코드로 text로 변환하면 인증 우회 가능

HEX to text

 

인증 우회

 

정적 분석 방법론으로 접근해보면 verify 함수를 식별하고 a.a(string)의 반환의 조건문으로 인증이 진행되는걸 확인 가능함

인증 부분

 

a.a(string str) 함수를 확인하면 반환값으로 str.eqauls로 클라이언트에서 인증을 처리하는걸 확인할 수 있음

* 인증은 모두 서버에서 이뤄져야함

인증 부분2

 

이러한 로직은 간단하게 해당 함수 리턴값을 변조하여 인증 조건문에 true값을 전달해도 쉽게 우회됨.

Java.perform(function () {
    const verify = Java.use("sg.vantagepoint.uncrackable1.a");
    verify.a.implementation = function (str) {
        console.log("args[0]: ",str)
        console.log("ret", this.a(str))
        console.log("ret mod")
        return true //인증 우회
    };
});

 

인증 우회

 

 

'정보보안 > 모바일' 카테고리의 다른 글

[안드로이드] Uncrackable2  (2) 2025.07.30
[안드로이드]InsecureBankv2 루팅 탐지 우회  (0) 2025.07.22
Comments