기본 콘텐츠로 건너뛰기

Frida-tutorial_1

Frida-tutorial_1

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

OWASP Crackme Level 1

adb install sg.vantagepoint.uncrackable1.apk

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

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

    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 ...

CVE-2017-11352

CVE-2017-11352 ImageMagick 에서 발생했던 CVE-2017-9144 취약점의 미흡한 조치로 인하여 동일한 취약점이 다시 발생되었다. 재 발생된 취약점 CVE-2017-11352은 coders/rle.c 에서 RLE 이미지에 대한 부적절한 EOF 처리가 원인이었다. EOF 란? 파일의 끝(End of File, EOF)으로 데이터 소스로부터 더 이상 읽을 수 있는 데이터가 없음을 나타낸다. ImageMagick Github Page 에 들어가보면 해당 이슈를 상세히 확인할 수 있다. 부적절한 EOF 처리 원인은 소스 코드 수정 시 유사한 코드를 복사 붙여넣기 하는 과정에서 검증해야할 변수 명을 고치지 않고 그대로 적용해서 발생했다. operand=ReadBlobByte(image); if (opcode == EOF) ThrowRLEException(CorruptImageError, "UnexpectedEndOfFile" ); 이로 인해서 조치 완료된 줄 알았던 CVE-2017-9144 취약점은 CVE-2017-11352이라는 새로운 취약점 명으로 다시 조치 되었다. case SkipLinesOp: { operand=ReadBlobByte(image); - if (opcode == EOF) + if (operand == EOF) ThrowRLEException(CorruptImageError, "UnexpectedEndOfFile" ); if (opcode & 0x40 ) { operand=ReadBlobLSBSignedShort(image); - if (opcode == EOF) + if (operand == EO...