ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • FRIDA Hooking (OWASP-UnCrackable1)
    Mobile/Frida 2019. 6. 24. 20:19

    Frida로 함수를 후킹해서 우리 입맛대로 바꿔볼 것이다.

    실습 App은 OWASP에서 실습용으로 만든 Crackme 시리즈로 진행할 것이다.

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


    총 3개로 Level1 / Level2 / Level3로 나뉜다.

    이번 글에서는 Level1을 Clear하는것을 목표로 진행해보겠다.


    [ UnCrackable1 ]

    설치는 루팅된 기기에 [adb install APK경로]로 설치해줘도 되고, Nox같은 에뮬레이터에 올려도 된다.

    나는 루팅된 기기에 APK를 설치하고 진행하였다.



    [ App 실행 ]

    우선 실행하면 위와 같이 루팅된 것을 탐지하고 [OK]를 누르면 App이 종료된다.

    코드를 보면서 어떤 로직으로 루팅을 탐지하고 App을 종료시키는지 확인하도록 하자


    [ MainActivity ]

    App이 실행되면 제일 먼저 실행되는 함수다.

    onCreate( )에서 c.a( ) / c.b( ) / c.c( )의 반환값으로 루팅을 탐지하고,

    분기문이 참이되면, this.a( )가 실행되면서 [OK]버튼을 클릭하는 순간 onClick( )가 실행되어

    System.exit(0)로 App을 종료시키는 로직이다.


    여기서 후킹 시나리오는 여러가지로 나올 수 있다.

    onCreate( )의 루팅 분기문 조건들을 다 False로 반환되게 해도 되고,

    루팅이 탐지 되더라도 System.exit(0) 코드가 동작하지 않게 하면 App은 종료되지 않을 것이다.


    우리의 목표는 우선 App이 종료되지 않게 하는 것이니 가장 직관적인 방법으로

    System.exit( )를 후킹하여 함수가 실행되더라도 App이 종료되지 않게 하였다.


                 [ Frida Hooking Script ]                     

    1
    2
    3
    4
    5
    6
    7
    8
    hook_code = """
    Java.perform(function()
    {
        console.log("[*] Join Hooking Script");
        var exitClass=Java.use("java.lang.System");
        exitClass.exit.implementation=function()
    {
            console.log("[*] Exit() Hook Complete");
        }
    });
    """
    cs

    후킹 스크립트는 Java.perform( )의 인자로 넘겨주면 되는데, function( ) 내부에 스크립트를 작성하면 된다.

    [4번줄]에서 Java.use( )로 exit메소드가 포함되어 있는 클래스를 불러온 뒤,

    [5번줄]exit.implemetation으로 정상적인 exit( )동작 대신 임의의 동작을 정의해줄 수 있다.

    위 코드에서는 아무런 동작없이 콘솔에 exit( )를 후킹했다는 로그메시지만 출력하도록 하였다.


    [ Frida Setting ]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #Frida Logging
    def on_message(message,data):
        print("{} -> {}".format(message,data))
     
    #Device Setting
    device = frida.get_usb_device(timeout=10)
     
    #Process Setting
    Hook_package = "owasp.mstg.uncrackable1"
    pid = device.spawn([Hook_package])
    process = device.attach(pid)
    device.resume(pid)
     
    #Script Setting
    script = process.create_script(hook_code)                                         
    script.on('message',on_message)
    script.load()
     
    #Input
    sys.stdin.read()
    cs

    Frida Setting 부분이다.

    우선 나는 usb로 기기와 연결되어 있기 때문에 get_usb_device( )를 사용해서 기기와 연결했다.

    위와 같이 기기연결하고 Script로드해주는 세팅 스크립트는 거의 고정적이라고 보면된다.


    [ UnCrackable1 Exit Hooking ]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import sys,frida
     
    Hook_package = "owasp.mstg.uncrackable1"
     
    def on_message(message,data):
        print("{} -> {}".format(message,data))
     
    jscode = """
        Java.perform(function()
        {
            console.log("[*] Hooking calls to System.exit");
            var exitClass=Java.use("java.lang.System");
            exitClass.exit.implementation=function()
            {
                console.log("[*] System.exit Called");
            }
        });
    """
     
    try:
        device = frida.get_usb_device(timeout=10)
        pid = device.spawn([Hook_package])
        print("App is starting.. pid:{}".format(pid))                                 
        process = device.attach(pid)
        device.resume(pid)
        script = process.create_script(jscode)
        script.on('message',on_message)
        print('[*] Running Frida')
        script.load()
        sys.stdin.read()
     
    except Exception as e:
        print(e)
    cs

    UnCrackable1 App의 Exit( )를 후킹하는 전체 스크립트다.

    해당 스크립트를 실행하면 App이 실행되고, 루팅이 탐지되어 이전처럼 경고창이 뜨지만

    [OK]버튼을 눌러도 App이 꺼지지 않게 된다.


    [ Secret Code 알아내기 ]

    UnCrackable1의 내부 동작은 단순하다.

    문자열을 입력받고 입력받은 문자열이 SecretCode와 같으면 [Success]창을 띄운다.


    [ Verify( ) ]

    a.a( )함수에 Input값을 인자로 넘기고

    반환값에 따라 [Success][Nope]으로 분기시키는 코드다.


    [ SecretCode Check ]

    SecretCode는 소스코드에 AES/ECB방식으로 암호화되어 있다.

    노란박스에서 암호화된 값을 복호화하여 [v0_2]변수에 저장하고,

    빨간박스에서 리턴값으로 equals(Input,v0_2)의 결과를 반환하는 코드다.


    후킹 포인트는 두 가지가 있다.

    하나는 equals( )의 결과값을 반환하는 a.a( ),

    다른 하나는 복호화된 값을 반환하는 sg.vantagepoint.a.a.a( )


    a.a( )를 무조건 True값이 반환되게 조작하면 어떤 값을 넣어도 [Success]가 출력될 것이고,

    sg.vantagepoint.a.a.a( )의 반환값을 Console로 출력시키면 SecretCode를 알아낼 수 있을 것이다.


    우리는 문제에서 VantagePoint라고 지정해준 함수 sg.vantagepoint.a.a.a( )를 후킹해서

    복호화된 SecretCode를 알아내보도록 하자


    sg.vantagepoint.a.a.a( ) Hooking ]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var Dec = Java.use("sg.vantagepoint.a.a");
            Dec.a.implementation = function(a,b)
            {
            console.log("[*] Hooking Call Decrpyt");
            var retVal = this.a(a,b);
            var SecretCode="";
            for(var i=0; i<retVal.length; i++)
            {
                SecretCode += String.fromCharCode(retVal[i]);
            }                                                                       
            console.log("[*] Passcode: "+SecretCode);
            return retVal;
            }
    cs

    Java.use( )로 후킹할 함수를 지정해주고, implementation으로 후킹동작을 정의해준다.

    [retVal]변수에 sg.vantagepoint.a.a.a( )반환값. 즉, 복호화된 문자열을 저장해준다.

    그리고 [SecretCode]변수에 저장하고 Console.log( )로 값을 출력하면 복호화된 값이 콘솔에 출력될 것이다.


    [ SecretCode leak ]

    SecretCode는 "I want to believe"이며 해당 값을 입력하고 검증해보면

    아래와 같이 [Success]가 출력되는 것을 확인할 수 있다.


    [ Success ]


    댓글

Designed by Tistory.