기본 콘텐츠로 건너뛰기

Frida-tutorial_1

Frida-tutorial_1

모바일 보안 검색을 하던 중 Frida를 알게 되었다. 간단히 살펴보려고 했으나, Frida의 강력함을 느끼고 시간을 두고 깊이 있게 다뤄봐야겠다는 생각이 들었다. 실제 프로젝트 진행에 앞서 크랙미나 CTF 문제들을 통해서 활용법을 알아보자.

OWASP Crackme Level 1

adb install sg.vantagepoint.uncrackable1.apk

어플리케이션 설치 후 실행해보면 루팅 디바이스 탐지로 인해 곧바로 종료되는 것을 확인할 수 있다.

디컴파일러를 이용하여 MainActivity의 onCreate함수를 확인해보자.

    protected void onCreate(Bundle bundle) {
        if (sg.vantagepoint.a.c.a() || sg.vantagepoint.a.c.b() || sg.vantagepoint.a.c.c()) {
            this.a("Root detected!");
        }
        if (sg.vantagepoint.a.b.a((Context)this.getApplicationContext())) {
            this.a("App is debuggable!");
        }
        super.onCreate(bundle);
        this.setContentView(2130903040);
    }

if (sg.vantagepoint.a.c.a() || sg.vantagepoint.a.c.b() || sg.vantagepoint.a.c.c()) 조건문을 이용하여 루팅 디바이스 탐지를, if (sg.vantagepoint.a.b.a((Context)this.getApplicationContext())) 조건문을 통해서 디버킹 탐지를 수행하고 있는 것을 확인할 수 있다.

이제 조건이 참인 경우 이동하는 a함수를 살펴보자.

    private void a(String string) {
        AlertDialog alertDialog = new AlertDialog.Builder((Context)this).create();
        alertDialog.setTitle((CharSequence)string);
        alertDialog.setMessage((CharSequence)"This in unacceptable. The app is now going to exit.");
        alertDialog.setButton(-3, (CharSequence)"OK", (DialogInterface.OnClickListener)new b(this));
        alertDialog.setCancelable(false);
        alertDialog.show();
    }

탐지구문을 출력한 다이얼로그를 사용자에게 표시한 후 “OK” 버튼 선택 시 b 클래스에 있는

    public void onClick(DialogInterface dialogInterface, int n) {
        System.exit(0);
    }

코드를 수행하여 어플리케이션이 종료된다. 먼저 어플리케이션 종료를 막아보자.
Frida를 이용해서 onClick 함수를 덮어쓰는 스크립트를 작성한다.

  1 setImmediate(function() {
  2   console.log("[*] Starting script");
  3   Java.perform(function() {
  4     bClass = Java.use("sg.vantagepoint.uncrackable1.b");
  5     bClass.onClick.implementation = function(v) {
  6       console.log("[*] onClick called");
  7     }
  8     console.log("[*] onClick handler modified");
  9   })
 10 })

setImmediate()
바로 다음에 실행 - 현재 이벤트 루프 주기 끝에 코드를 실행한다. 현재 이벤트 루프의 모든 I/O 작업 후 다음 이벤트 루프에 스케줄링 된 모든 타이머 이전에 실행된다.

Java.perform()
자바를 다루기 위한 Frida 함수

OnClickListener 인터페이스를 상속받아 구현한 onClick 메서드를 Frida가 삽입한 함수로 대체하였으므로 System.exit(0); 구문은 호출되지 않는다.

frida -U -l uncrackable1.js sg.vantagepoint.uncrackable1

어플리케이션을 구동해보면 루팅 탐지 다이얼로그 창의 “OK” 버튼을 선택하여도 종료되지 않는 것을 확인할 수 있다.
이제 다음 단계로 넘어가자.

 public void verify(View view) {
        String string = ((EditText)this.findViewById(2131230720)).getText().toString();
        AlertDialog alertDialog = new AlertDialog.Builder((Context)this).create();
        if (a.a((String)string)) {
            alertDialog.setTitle((CharSequence)"Success!");
            alertDialog.setMessage((CharSequence)"This is the correct secret.");
        } else {
            alertDialog.setTitle((CharSequence)"Nope...");
            alertDialog.setMessage((CharSequence)"That's not it. Try again.");
        }
        alertDialog.setButton(-3, (CharSequence)"OK", (DialogInterface.OnClickListener)new c(this));
        alertDialog.show();
    }

a.a((String)string)와 일치하는 문자열을 삽입하여야한다는 것을 verify 함수에서 확인할 수 있다.

public static boolean a(String string) {
        byte[] arrby = Base64.decode((String)"5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=", (int)0);
        byte[] arrby2 = new byte[]{};
        try {
            byte[] arrby3;
            arrby2 = arrby3 = sg.vantagepoint.a.a.a((byte[])a.b((String)"8d127684cbc37c17616d806cf50473cc"), (byte[])arrby);
        }
        catch (Exception var3_4) {
            Log.d((String)"CodeCheck", (String)("AES error:" + var3_4.getMessage()));
        }
        if (string.equals(new String(arrby2))) {
            return true;
        }
        return false;
    }

arrby2를 만들기 위해 호출하는 sg.vantagepoint.a.a.a를 살펴보자.

    public static byte[] a(byte[] arrby, byte[] arrby2) {
        SecretKeySpec secretKeySpec = new SecretKeySpec(arrby, "AES/ECB/PKCS7Padding");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(2, secretKeySpec);
        return cipher.doFinal(arrby2);
    }

AES 암호화를 수행하는 함수라는 것을 알 수 있다. 이 함수도 위에서 진행한 것 처럼 덮어써서 문자열 검증 로직을 우회하여 보자. 암호화를 진행하기 전 전달 받은 인자 값을 콘솔에 출력하는 방법으로 원본 문자열을 확인한 후, 해당 문자열을 입력하는 것으로 진행한다.

  1 setImmediate(function() {
  2   console.log("[*] Starting script");
  3   Java.perform(function() {
  4     bClass = Java.use("sg.vantagepoint.uncrackable1.b");
  5     bClass.onClick.implementation = function(v) {
  6       console.log("[*] onClick called");
  7     }
  8     console.log("[*] onClick handler modified");
  9 
 10     aaClass = Java.use("sg.vantagepoint.a.a");
 11     aaClass.a.implementation = function(arg1, arg2) {
 12       retval = this.a(arg1, arg2);
 13       password = ''
 14       for(i=0; i<retval.length; i++) {
 15         password += String.fromCharCode(retval[i]);
 16       }
 17       console.log("[*] Decrypted: " + password);
 18       return retval;
 19     }
 20     console.log("[*] sg.vantagepoint.a.a.a modified");
 21   })
 22 });
frida -U -l uncrackable1.js sg.vantagepoint.uncrackable1

어플리케이션을 실행한 후 임의의 문자열을 입력하여 Verify 버튼을 선택하면 콘솔 창에 암호화하기 전 원본 문자열이 출력되는 것을 확인할 수 있다. 해당 문자열을 입력 창에 입력하면 성공적으로 “Success” 다이얼로그를 확인할 수 있다.

이 블로그의 인기 게시물

데일 카네기 인간관계론 정리

Remove-Server-Header

응답 메시지 내 서버 버전 정보 제거 1. Apache 1) 조치 방법 “/etc/htpd/conf/httpd.conf” 파일 안에서 1. ServerTokens OS → ServerTokens Prod 2. ServerSignature On → ServerSignature Off 로 변경한 후 아파치를 재시작하면 헤더 값의 아파치 버전 정보 및 OS 정보를 제거할 수 있다. 2) 참고 URL http://zetawiki.com/wiki/CentOS_ 아파치_보안권장설정_ServerTokens_Prod,_ServerSignature_Off 2. IIS 1) 조치 방법 IIS 6.0 urlscan_setup 실행. 설치. \windows\system32\inetsrv\urlscan\urlscan.ini 파일을 열어 다음 수정(RemoveServerHeader=0 을 RemoveServerHeader=1 로 변경) 서비스에서 IIS Admin Service 재시작. IIS 7.0 IIS 관리자를 열고 관리하려는 수준으로 이동합니다. 기능 보기에서 HTTP 응답 헤더를 두 번 클릭합니다. HTTP 응답 헤더 페이지에서 제거할 헤더를 선택합니다. 작업 창에서 제거를 클릭하고 예를 클릭합니다. 2) 참고 URL IIS 6.0 : http://gonnie.tistory.com/entry/iis6- 응답헤더-감추기 IIS 7.0 : https://technet.microsoft.com/ko-kr/library/cc733102(v=ws.10).aspx 3. jetty 1) 조치 방법 “jetty.xml” 파일에서 jetty.send.server.version=false 설정 2) 참고 URL http://attenuated-perspicacity.blogspot.kr/2009/09/jetty-61x-hardening.html 4. Nginx ...

Insecure-programming-stack5

Insecure-programming-stack5 Insecure Programming by example Stack 5 이번 문제에서는 조건절 안에 “you win!” 문구 대신 "you loose!"가 있는 것을 볼 수 있다. # include <stdio.h> int main ( ) { int cookie ; char buf [ 80 ] ; printf ( "buf: %08x cookie: %08xn" , & buf , & cookie ) ; gets ( buf ) ; if ( cookie == 0x000d0a00 ) printf ( "you loose!\n" ) ; } 따라서 이번 문제를 해결하기 위해서는 임의로 작성한 별도의 코드를 프로그램에서 호출하도록 하여 “you win!” 구문 출력을 이끌어 내야한다. "you win!"을 출력하고 종료하는 간단한 어셈블리어 코드를 작성한다. section .text global _start _start: jmp short ender starter: xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx mov al, 4 mov dbl, 91 pop ecx mov bdl, 14 int 0x80 xor eax, eax inc eaxal dec bl int 0x80 ender: call starter db 'you win!\n' 작성이 완료되었다면 nasm 을 이용하여 오브젝트 파일을 생성한 후 ld 명령어로 실행 파일을 만들어 제대로 동작하는 지 확인해보하고 objdump 를 이용하여 코드 덤프를 만들자. # nasm -f elf -o stack54_sh_code....