티스토리 뷰

보안/CTF

[Reversing] Leap - 2021 INCOGNITO CTF Writeup

돔돔이부하 2021. 8. 31. 01:13
728x90
반응형

하나밖에 없는 모바일 리버싱 관련 문제였습니다. CTF 참여 당시에는 분류가 Reversing 이라고 되어 있긴 했지만 저는 Reversing 보단 모바일 문제라고 생각되었습니다.

jadx 툴로 apk 를 먼저 분석해보았습니다. 들어가자마자 보이는 건 APK 서명이 없었습니다.

그래서 apktools 도구로 서명부터 해주었습니다.

그런 다음에 AndroidManifest.xml 파일을 확인해보았습니다. 다음과 같았습니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" android:compileSdkVersion="30" android:compileSdkVersionCodename="11" package="com.example.leap" platformBuildVersionCode="30" platformBuildVersionName="11">
    <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="30"/>
    <application android:theme="@style/AppTheme" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:debuggable="true" android:testOnly="true" android:allowBackup="true" android:supportsRtl="true" android:roundIcon="@mipmap/ic_launcher_round" android:appComponentFactory="androidx.core.app.CoreComponentFactory">
        <activity android:name="com.example.leap.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

중요한 건 MainActivity 가 하나 있고, 패키지명은 com.example.leap 이며 android:testOnly="true" 를 보면 test 용 앱임을 알 수 있었습니다.

test 용 앱을 설치하기 위해서는 adb install -t 옵션을 주면 설치가 가능하다고 합니다.

https://developer.android.com/studio/command-line/adb#-t-option

 

Android 디버그 브리지(adb)  |  Android 개발자  |  Android Developers

기기와 통신할 수 있는 다목적 명령줄 도구인 Android 디버그 브리지를 알아보세요.

developer.android.com

그리고 우선 소스코드를 분석해보았습니다. 액티비티가 하나밖에 없어서 MainActivity 만 분석해보았습니다.

package com.example.leap;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Random;

public class MainActivity extends AppCompatActivity {
    int cross = 1;
    int index = 0;
    Random random = new Random();

    public native String stringFromJNI();

    static {
        System.loadLibrary("native-lib");
    }

    /* access modifiers changed from: protected */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView((int) R.layout.activity_main);
        ((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                MainActivity.this.func();
                MainActivity.this.cross();
            }
        });
    }

    public void func() {
        ImageView stone2 = (ImageView) findViewById(R.id.stone2);
        ImageView stone3 = (ImageView) findViewById(R.id.stone3);
        ImageView stone4 = (ImageView) findViewById(R.id.stone4);
        ImageView stone5 = (ImageView) findViewById(R.id.stone5);
        int nextInt = this.random.nextInt(4);
        this.index = nextInt;
        if (nextInt == 0) {
            stone2.setVisibility(0);
            stone3.setVisibility(4);
            stone4.setVisibility(4);
            stone5.setVisibility(4);
            Log.d("tttt", String.valueOf(this.index));
        } else if (nextInt == 1) {
            stone2.setVisibility(4);
            stone3.setVisibility(0);
            stone4.setVisibility(4);
            stone5.setVisibility(4);
            Log.d("tttt", String.valueOf(this.index));
        } else if (nextInt == 2) {
            stone2.setVisibility(4);
            stone3.setVisibility(4);
            stone4.setVisibility(0);
            stone5.setVisibility(4);
            Log.d("tttt", String.valueOf(this.index));
        } else if (nextInt == 3) {
            stone2.setVisibility(4);
            stone3.setVisibility(4);
            stone4.setVisibility(4);
            stone5.setVisibility(0);
            Log.d("tttt", String.valueOf(this.index));
        }
    }

    public void cross() {
        ImageView stone2 = (ImageView) findViewById(R.id.stone2);
        ImageView stone3 = (ImageView) findViewById(R.id.stone3);
        ImageView stone4 = (ImageView) findViewById(R.id.stone4);
        ImageView stone5 = (ImageView) findViewById(R.id.stone5);
        ImageView people1 = (ImageView) findViewById(R.id.people1);
        ImageView people2 = (ImageView) findViewById(R.id.people2);
        ImageView people3 = (ImageView) findViewById(R.id.people3);
        ImageView people4 = (ImageView) findViewById(R.id.people4);
        ImageView people5 = (ImageView) findViewById(R.id.people5);
        ImageView help2 = (ImageView) findViewById(R.id.help2);
        ImageView help3 = (ImageView) findViewById(R.id.help3);
        ImageView help4 = (ImageView) findViewById(R.id.help4);
        ImageView help5 = (ImageView) findViewById(R.id.help5);
        ImageView imageView = (ImageView) findViewById(R.id.stone1);
        TextView textView = (TextView) findViewById(R.id.sample_text);
        int i = this.cross;
        ImageView people6 = (ImageView) findViewById(R.id.people6);
        if (i == 0) {
            people1.setVisibility(0);
            people2.setVisibility(4);
            people3.setVisibility(4);
            people4.setVisibility(4);
            people5.setVisibility(4);
            help2.setVisibility(4);
            help3.setVisibility(4);
            help4.setVisibility(4);
            help5.setVisibility(4);
            stone2.setVisibility(0);
            stone3.setVisibility(0);
            stone4.setVisibility(0);
            stone5.setVisibility(0);
            this.cross++;
        } else if (i == 1) {
            if (stone2.getVisibility() == 0) {
                people1.setVisibility(4);
                people2.setVisibility(0);
                people3.setVisibility(4);
                people4.setVisibility(4);
                people5.setVisibility(4);
                this.cross++;
            } else if (stone2.getVisibility() == 4) {
                people1.setVisibility(4);
                people2.setVisibility(4);
                people3.setVisibility(4);
                people4.setVisibility(4);
                people5.setVisibility(4);
                help2.setVisibility(0);
                this.cross = 0;
            } else {
                TextView textView2 = textView;
                ImageView imageView2 = people6;
            }
        } else if (i == 2) {
            if (stone3.getVisibility() == 0) {
                people1.setVisibility(4);
                people2.setVisibility(4);
                people3.setVisibility(0);
                people4.setVisibility(4);
                people5.setVisibility(4);
                this.cross++;
            } else if (stone3.getVisibility() == 4) {
                people1.setVisibility(4);
                people2.setVisibility(4);
                people3.setVisibility(4);
                people4.setVisibility(4);
                people5.setVisibility(4);
                help3.setVisibility(0);
                this.cross = 0;
            } else {
                TextView textView3 = textView;
                ImageView imageView3 = people6;
            }
        } else if (i == 3) {
            if (stone4.getVisibility() == 0) {
                people1.setVisibility(4);
                people2.setVisibility(4);
                people3.setVisibility(4);
                people4.setVisibility(0);
                people5.setVisibility(4);
                this.cross++;
            } else if (stone4.getVisibility() == 4) {
                people1.setVisibility(4);
                people2.setVisibility(4);
                people3.setVisibility(4);
                people4.setVisibility(4);
                people5.setVisibility(4);
                help4.setVisibility(0);
                this.cross = 0;
            } else {
                TextView textView4 = textView;
                ImageView imageView4 = people6;
            }
        } else if (i == 4) {
            if (stone5.getVisibility() == 0) {
                people1.setVisibility(4);
                people2.setVisibility(4);
                people3.setVisibility(4);
                people4.setVisibility(0);
                people5.setVisibility(4);
            } else if (stone5.getVisibility() == 4) {
                people1.setVisibility(4);
                people2.setVisibility(4);
                people3.setVisibility(4);
                people4.setVisibility(4);
                people5.setVisibility(4);
                help5.setVisibility(0);
                this.cross = 0;
            } else {
                TextView textView5 = textView;
                ImageView imageView5 = people6;
            }
        } else if (i == 5) {
            people1.setVisibility(4);
            people2.setVisibility(4);
            people3.setVisibility(4);
            people4.setVisibility(4);
            people6.setVisibility(0);
            textView.setText(stringFromJNI());
        } else {
            ImageView imageView6 = people6;
        }
    }
}

코드는 위 내용이 다였습니다. JNI 로 native-lib 모듈이 import 되어 있었습니다. 보니깐 cross 값이 초기엔 1로 설정되어 있고 5일 때 stringFromJNI 함수를 호출하게 되어 있었습니다.

 

별로 볼 것도 없이 그냥 stringFromJNI 함수가 호출되게 하면 되겠구나 싶었습니다. 지금 생각한 건 그냥 cross 값을 5로 바꾸었으면 훨씬 쉬웠을 것 같긴하지만 그 당시에는 그냥 stringFromJNI 함수를 Log.d 함수의 인자값으로 넣어서 logcat 으로부터 stringFromJNI 함수의 반환 값을 확인하고자 하였습니다.

 

그래서 아래는 MainActivity.smali 코드를 변경한 코드입니다.

iget v4, p0, Lcom/example/leap/MainActivity;->index:I
invoke-static {v4}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
move-result-object v4
invoke-virtual/range {p0 .. p0}, Lcom/example/leap/MainActivity;->stringFromJNI()Ljava/lang/String;
move-result-object v6
invoke-static {v6, v4}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

smali 코드 문법이나 문서를 따로 참고하지 않고 풀었어서 저게 정확히는 무슨 뜻인진 모르겠지만, 그냥 눈 대중으로 봤을 때 stringFromJNI 함수의 반환 값을 v6 에 넣고 Log.d 의 인자값으로 넘겨주는 코드인 것 같아서 위와 같이 짜집기 후 패키지를 리패키징해서 새로운 apk 파일로 만들었습니다.

 

그리고 nox 에서 실행 후 logcat을 확인해보았습니다.

08-30 04:57:59.826  3885  3885 D 6c30304b202062334630723320593055206c335e70: 3

확인해보니 hex 값이 나왔습니다. 무슨 값인지 확인을 위하여 ascii 값으로 우선 변환해보았습니다.

확인 해보니 Flag 값처럼 보여서 INCO{l00K b3F0r3 Y0U l3^p} 라고 입력해보았더니 문제가 풀렸습니다.

 

이번 문제는 요약하자면 apk 파일 분석 → smali 코드 분석 및 수정 → 앱 리패키징 → 모바일 서명키 생성 → adb install -t 과정으로 풀 수 있었던 것 같습니다.

728x90
반응형
댓글