Android/Diva

Hardcoding Issues(Part 2) - Level 12

앱 정보

  • 하드 코딩된 정보를 찾아 입력하면 해결할 수 있는 문제

기본 화면은 다음 [그림 1]과 같이 vendor key를 입력할 수 있는 부분과 ACCESS 버튼으로 구성되어있다.

[그림 1] 앱 기본 구성

 

아무 값이나 입력한 후 ACCESS를 눌러보면 다음과 같은 메시지를 확인할 수 있다.

[그림 2] 에러 메시지 확인

 

앱 분석

Hardcode2Activity

private DivaJni djni;
public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView((int) R.layout.activity_hardcode2);
      this.djni = new DivaJni();
  }

  public void access(View view) {
      if (this.djni.access(((EditText) findViewById(R.id.hc2Key)).getText().toString())!= 0) {
          Toast.makeText(this, "Access granted! See you on the other side :)", 0).show();
      } else {
          Toast.makeText(this, "Access denied! See you in hell :D", 0).show();
      }
  }

access 함수를 분석하면 다음과 같은 정보를 알 수 있다.

  • onCreate()에서 생성한 DivaJni 클래스의 access() 함수에 인자로 사용자의 입력값을 넘겨준 후 반환 값에 따라 다음과 같은 동작을 한다.
    • 0이라면 "Access granted! See you on the other side :)" 문자열이 출력된다.
    • 0이 아니라면 "Access denied! See you in hell :D" 문자열이 출력된다.

 

DivaJni

public class DivaJni {
    private static final String soName = "divajni";

    public native int access(String str);

    public native int initiateLaunchSequence(String str);

    static {
        System.loadLibrary(soName);
    }
}

이전 Hardcode2Activity의 access() 함수에서 호출한 access() 함수가 속한 DivaJni클래스를 확인해보면 코드와 같이 "divajni"라는 라이브러리를 로드하는 것을 확인할 수 있는데 이를 통해 access() 함수는 네이티브 코드로 작성되었 다는 것을 알 수 있다.

 

Exploit

라이브러리의 하드코딩을 이용한 방법

DivaJni 클래스에서 가져오고 있는 라이브러리를 확인하기 위해 먼저 해당 패키지의 pid를 확인한다.

  • ps -A | grep "패키지 이름"

[그림 3] 패키지 이름 확인 및 프로세스 아이디 확인

 

pid를 확인하였다면 /proc/[pid]/maps를 사용하여 프로세스의 메모리를 확인한다. 이때 그냥 사용하면 출력되는 라이브러리나 다양한 정보가 많기 때문에 이전 코드에서 확인한 "divajni"를 사용하여 관련된 라이브러리만 출력하도록 한다.

  • cat /proc/[pid]/maps | grep divajni

[그림 4] 프로세스 메모리를 통해 라이브러리 확인

 

확인해보면 "/lib/arm64/libdivajni.so" 라이브러리를 사용하는 것을 알 수 있으므로 diva.apk를 압축 해제하여 해당 경로에 있는 라이브러리를 찾는다.

[그림 5] 라이브러리 파일 획득

 

이후 찾은 라이브러리를 ida와 같은 disassassembly를 사용하여 열어준다.

[그림 6] ida를 사용하여 라이브러리 파일 확인

 

함수 목록을 확인해보면 "DivaJni_access"를 확인할 수 있는데 해당 함수를 눌러보면 다음과 같이 strncmp() 함수를 통해 비교하는 문자열이 하드 코딩되어 있는 것을 확인할 수 있다.

[그림 7] 하드코딩 문자열 확인

 

이후 발견한 하드코딩 문자열을 입력 후 ACCESS 버튼을 눌러주면 "Access granted! See you on the other side :)"라는 문자열이 출력되는 것을 확인할 수 있다.

[그림 8] 인증 성공

 

후킹을 이용한 우회

Exploit Code

Interceptor.attach(
    Module.findExportByName("libdivajni.so", "strncmp"), {
        onEnter: function(args) {
            if(Memory.readUtf8String(args[1]) == "A"){
                console.log("arg0 : " + Memory.readUtf8String(args[0]));
                console.log("arg1 : " + Memory.readUtf8String(args[1]));
                console.log("arg2 : " + args[2]);
            }
        },
        onLeave: function(retl) {
            retl.replace(0);             
        }
    }
);
  • Module.findExportByName(moduleName|null, exportName) : moduleName에서 exportNamed의 절대 주소 값을 반환한다.
  • onEnter : 함수가 실행될 때 실행할 코드를 지정한다(인자는 해당 함수가 호출될 때 넘어오는 인자를 의미한다).
  • onLeave : 함수가 종료될 때 실행항 코드를 지정한다(인자는 return 값을 의미한다).

코드는 libdivajni.so 라이브러리에서 사용 중인 strncmp() 함수를 후킹 한 후 입력 값이 A라면 모든 인자를 출력한다. 그리고 함수가 종료될 때 0을 반환한다.

실제 스크립트를 실행해보면 다음과 같이 에러가 발생하는 것을 확인할 수 있는데 이때 실행된 애플리케이션에서 level 12로 이동 후 아무 데이터를 넣어주고 ACCESS를 눌러준다.

[그림 9] 스크립트 실행

 

이후 스크립트 코드에서 엔터 한 줄을 추가한 후 저장해주면 정상적으로 후킹이 되는 것을 확인할 수 있다. 마지막으로 A를 입력하고 ACCESS를 눌러주면 인자 값이 출력되고 0을 반환하여 "Access granted! See you on the other side :)"라는 문자열이 출력되는 것을 확인할 수 있다.

[그림 10] 후킹을 통해 암호문 확인

 

Reference

'Android > Diva' 카테고리의 다른 글

Access Control Issues(Part 3) - Level 11  (0) 2021.11.01
Insecure Logging - Level 1  (0) 2021.11.01