목차

  • 시나리오
  • 정보 수집
  • 코드 분석
  • frida 풀이

 

[MISC] Mobile Pentest 시나리오
Mobile Pentest
시나리오: COVID 치료제 정보에 접근할 수 있는 암호 값이 내장된 애플리케이션을 받았다. 
하지만 바이러스가 애플리케이션까지 감염되어 암호 값을 확인할 수 없게 되었다. 암호 값을 확인하자!

Scenario: An application including password values to access information for COVID treantment was received.
However, the password values cannot be verified due to the virus that has infected the application.
Let's find the password values!

[그림 1] mobilepentest.apk

시나리오와 함께 문제 파일이 주어지는데 받아서 녹스(Nox)로 실행하면 [그림 1]과 같이 mobilepentest.apk 파일이 설치된다.

 

시나리오 내용에서 먼저 얻을 수 있는 정보로는 암호 값이 내장된 앱이며 해당 암호 값을 확인해야 한다는 것이다.

그러면 직접 apk 실행을 통해 추가적인 정보를 수집하자

 

 

정보 수집

[그림 2] 실행 화면

앱을 실행하면 "COVID 치료제 정보 확인"이라는 버튼 하나가 있는 것을 볼 수 있다.

 

 

[그림 3] medicine?

버튼을 누르면 메시지 박스창이 나오는데 제목은 코로나 치료제를 알고 있는지와 해당 치료제는 어디 있는지 묻는 내용이 포함되어 있다.

 

 

[그림 4] 체크 버튼 후

CHECK 버튼을 누르면 "check ok :)"라는 Toast.makeText 메시지가 출력된다.

 

"COVID 치료제 정보 확인" 버튼을 눌렀을 때 다른 반응이 나오게 하거나 앱 자체 내에 flag 값이 숨어 있을 것으로 보인다. 이유는 시나리오에서 언급한 것과 앱 실행 화면에서는 다른 단서를 찾을 수 없기 때문에 apk 파일을 직접 뜯어야 한다.

 

코드 분석

 

jadx-gui 도구를 이용하여 apk 내부 소스코드를 확인할 수 있다. 또는 웹에서 제공하는 디컴파일을 이용하는 방법도 있다.

 

package com.example.mobilepentest;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.InputStream;

// 1번 영역====================================================
public class MainActivity extends AppCompatActivity {
    static int input1 = 2;
    static int input2 = 2;
    static int input3 = 2;
    static int input4 = 2;
    String cryptotext = "";
    String flag = "where is medicine?";
    String flag0 = "";
    String flag1 = "";
    String flag2 = "";
    String flag3 = "";
    String flag4 = "";
    String flag5 = "";
    String title1 = "Do you know COVID medicine?";
    
// 1번 영역====================================================

// 2번 영역====================================================

    public void medicine() {
        try {
            InputStream openRawResource = getResources().openRawResource(R.raw.flag);
            byte[] bArr = new byte[openRawResource.available()];
            openRawResource.read(bArr);
            this.flag0 = new String(bArr);
        } catch (Exception unused) {
            this.flag = "fail";
        }
        this.title1 = "COVID medicine data";
        String str = this.flag0;
        this.cryptotext = str;
        this.flag1 = str.substring(15, 16);
        this.flag1 += this.cryptotext.substring(14, 15);
        this.flag1 += this.cryptotext.substring(23, 24);
        this.flag1 += this.cryptotext.substring(69, 70);
        if (input1 == 2) {
            this.flag2 = this.cryptotext.substring(38, 39);
            this.flag2 += this.cryptotext.substring(71, 72);
            this.flag2 += this.cryptotext.substring(27, 28);
            this.flag2 += this.cryptotext.substring(34, 35);
            this.flag2 += this.cryptotext.substring(72, 73);
            this.flag2 += this.cryptotext.substring(30, 31);
            this.flag2 += this.cryptotext.substring(52, 53);
        } else {
            this.flag2 = this.cryptotext.substring(5, 6);
            this.flag2 += this.cryptotext.substring(0, 1);
            this.flag2 += this.cryptotext.substring(10, 11);
            this.flag2 += this.cryptotext.substring(4, 5);
            this.flag2 += this.cryptotext.substring(52, 53);
        }
        if (input2 == 0) {
            this.flag3 = this.cryptotext.substring(75, 76);
            this.flag3 += this.cryptotext.substring(39, 40);
            this.flag3 += this.cryptotext.substring(29, 30);
            this.flag3 += this.cryptotext.substring(52, 53);
        } else {
            this.flag3 = this.cryptotext.substring(5, 6);
            this.flag3 += this.cryptotext.substring(0, 1);
            this.flag3 += this.cryptotext.substring(10, 11);
            this.flag3 += this.cryptotext.substring(4, 5);
            this.flag3 += this.cryptotext.substring(52, 53);
        }
        if (input3 == 2) {
            this.flag4 = this.cryptotext.substring(31, 32);
            this.flag4 += this.cryptotext.substring(43, 44);
            this.flag4 += this.cryptotext.substring(72, 73);
            this.flag4 += this.cryptotext.substring(29, 30);
            this.flag4 += this.cryptotext.substring(75, 76);
            this.flag4 += this.cryptotext.substring(52, 53);
        } else {
            this.flag4 = this.cryptotext.substring(5, 6);
            this.flag4 += this.cryptotext.substring(0, 1);
            this.flag4 += this.cryptotext.substring(10, 11);
            this.flag4 += this.cryptotext.substring(4, 5);
            this.flag4 += this.cryptotext.substring(52, 53);
        }
        if (input4 == 0) {
            this.flag5 = this.cryptotext.substring(32, 33);
            this.flag5 += this.cryptotext.substring(71, 72);
            this.flag5 += this.cryptotext.substring(76, 77);
            this.flag5 += this.cryptotext.substring(46, 47);
            this.flag5 += this.cryptotext.substring(70, 71);
        } else {
            this.flag5 = this.cryptotext.substring(5, 6);
            this.flag5 += this.cryptotext.substring(0, 1);
            this.flag5 += this.cryptotext.substring(10, 11);
            this.flag5 += this.cryptotext.substring(4, 5);
            this.flag5 += this.cryptotext.substring(70, 71);
        }
        this.flag = this.flag1 + this.flag2 + this.flag3 + this.flag4 + this.flag5;
    }
    
// 2번 영역====================================================

// 3번 영역====================================================

    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView((int) R.layout.activity_main);
        ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle(MainActivity.this.title1);
                builder.setMessage(MainActivity.this.flag);
                builder.setPositiveButton("check", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialogInterface, int i) {
                        Toast.makeText(MainActivity.this, "check ok :)", 0).show();
                    }
                });
                builder.show();
            }
        });
    }
}

// 3번 영역====================================================

이해를 돕기 위해 코드에 "1번, 2번, 3번 영역" 주석으로 코드 범위를 지정하였다.

 

 

[1번 영역]

// 1번 영역====================================================
public class MainActivity extends AppCompatActivity {
    static int input1 = 2;
    static int input2 = 2;
    static int input3 = 2;
    static int input4 = 2;
    String cryptotext = "";
    String flag = "where is medicine?";
    String flag0 = "";
    String flag1 = "";
    String flag2 = "";
    String flag3 = "";
    String flag4 = "";
    String flag5 = "";
    String title1 = "Do you know COVID medicine?";
    
// 1번 영역====================================================

전역 변수로 input1,2,3,4 와 cryptotext 그리고 우리가 찾으려는 flag로 보이는 flag 변수와 아까 앱에서 실행해서 확인한 문자열을 담고 있는 title1 변수가 있다.

 

 

[2번 영역]

// 2번 영역====================================================

    public void medicine() {
        try {
            InputStream openRawResource = getResources().openRawResource(R.raw.flag);
            byte[] bArr = new byte[openRawResource.available()];
            openRawResource.read(bArr);
            this.flag0 = new String(bArr);
        } catch (Exception unused) {
            this.flag = "fail";
        }
        this.title1 = "COVID medicine data";
        String str = this.flag0;
        this.cryptotext = str;
        this.flag1 = str.substring(15, 16);
        this.flag1 += this.cryptotext.substring(14, 15);
        this.flag1 += this.cryptotext.substring(23, 24);
        this.flag1 += this.cryptotext.substring(69, 70);
        if (input1 == 2) {
            this.flag2 = this.cryptotext.substring(38, 39);
            this.flag2 += this.cryptotext.substring(71, 72);
            this.flag2 += this.cryptotext.substring(27, 28);
            this.flag2 += this.cryptotext.substring(34, 35);
            this.flag2 += this.cryptotext.substring(72, 73);
            this.flag2 += this.cryptotext.substring(30, 31);
            this.flag2 += this.cryptotext.substring(52, 53);
        } else {
            this.flag2 = this.cryptotext.substring(5, 6);
            this.flag2 += this.cryptotext.substring(0, 1);
            this.flag2 += this.cryptotext.substring(10, 11);
            this.flag2 += this.cryptotext.substring(4, 5);
            this.flag2 += this.cryptotext.substring(52, 53);
        }
        if (input2 == 0) {
            this.flag3 = this.cryptotext.substring(75, 76);
            this.flag3 += this.cryptotext.substring(39, 40);
            this.flag3 += this.cryptotext.substring(29, 30);
            this.flag3 += this.cryptotext.substring(52, 53);
        } else {
            this.flag3 = this.cryptotext.substring(5, 6);
            this.flag3 += this.cryptotext.substring(0, 1);
            this.flag3 += this.cryptotext.substring(10, 11);
            this.flag3 += this.cryptotext.substring(4, 5);
            this.flag3 += this.cryptotext.substring(52, 53);
        }
        if (input3 == 2) {
            this.flag4 = this.cryptotext.substring(31, 32);
            this.flag4 += this.cryptotext.substring(43, 44);
            this.flag4 += this.cryptotext.substring(72, 73);
            this.flag4 += this.cryptotext.substring(29, 30);
            this.flag4 += this.cryptotext.substring(75, 76);
            this.flag4 += this.cryptotext.substring(52, 53);
        } else {
            this.flag4 = this.cryptotext.substring(5, 6);
            this.flag4 += this.cryptotext.substring(0, 1);
            this.flag4 += this.cryptotext.substring(10, 11);
            this.flag4 += this.cryptotext.substring(4, 5);
            this.flag4 += this.cryptotext.substring(52, 53);
        }
        if (input4 == 0) {
            this.flag5 = this.cryptotext.substring(32, 33);
            this.flag5 += this.cryptotext.substring(71, 72);
            this.flag5 += this.cryptotext.substring(76, 77);
            this.flag5 += this.cryptotext.substring(46, 47);
            this.flag5 += this.cryptotext.substring(70, 71);
        } else {
            this.flag5 = this.cryptotext.substring(5, 6);
            this.flag5 += this.cryptotext.substring(0, 1);
            this.flag5 += this.cryptotext.substring(10, 11);
            this.flag5 += this.cryptotext.substring(4, 5);
            this.flag5 += this.cryptotext.substring(70, 71);
        }
        this.flag = this.flag1 + this.flag2 + this.flag3 + this.flag4 + this.flag5;
    }
    
// 2번 영역====================================================

medicine이라는 메서드가 정의되어 있는데 내용을 요약하자면 raw에 존재하는 flag를 가져와서 flag0 변수에 담은 뒤 다시 str 변수를 거쳐서 cryptotext 변수에 대입된다.

이후 flag 변수와 cryptotext 변수가 서로 치환 조건을 거쳐서 각 flag 변수에 들어가게 되고 최종적으로 flag는 flag1,2,3,4,5 변수 값을 합친 값이 된다.

 

여기서 중요한 점은 input1,2,3,4 변수에 각 비교하는 값이 맞는 경우와 틀린 경우 치환하는 값이 다르다는 것이다.

 

 

[3번 영역]

// 3번 영역====================================================

    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView((int) R.layout.activity_main);
        ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle(MainActivity.this.title1);
                builder.setMessage(MainActivity.this.flag);
                builder.setPositiveButton("check", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialogInterface, int i) {
                        Toast.makeText(MainActivity.this, "check ok :)", 0).show();
                    }
                });
                builder.show();
            }
        });
    }
}

// 3번 영역====================================================

onCreate 메서드, 앱이 실행될 때 먼저 실행되는 부분이다. Android에서 main 영역으로 볼 수 있다.

내용을 보면 버튼을 눌렀을 때 Toast.makeText로 "check ok :)"가 출력 되도록 작성되어 있다.

해당 문자열은 [그림 4]에서 본 check ok :) 문자열인 것을 확인할 수 있다.

 

여기까지 봤을 때 풀이 방법은 3가지 정도로 볼 수 있다.

1. frida 풀이

2. flag 요청 값을 확인 후 치환 코드 적용

3. flag 요청 값을 확인 후 수동으로 확인

 

우선 문제 제작 의도는 frida를 사용할 줄 아는지 확인하기 위해서다. 그래서 해당 풀이에서는 1번 방법으로 진행한다.

2번과 3번 방법이 거의 비슷하기 때문에 간단하게 2, 3번에 대해 요약해서 설명한다.

 

flag 요청 값을 확인한다는 것이 무슨 말일까? flag는 [2번 영역] 코드 초반에 R.raw.flag로 flag를 불러오고 있다.

앱을 개발 해본 경험이 있으면 2, 3번 방법으로도 풀이가 가능하다. raw라는 폴더에 flag 관련 파일이 포함되어 있다는 것이므로 디컴파일에서 리소스를 거쳐서 raw 폴더를 찾아 flag 관련 파일에 무슨 내용인지 확인하면 된다.

그 내용을 불러와서 치환하고 있기 때문에 안에 내용이 무엇인지 알게 된다면 직접 치환 코드에 적용하거나 수동으로 확인하는 것도 가능하다.

 

그럼 어떻게 frida 풀이라는 것을 확인할 수 있을까?

시나리오 내용을 다시 보자. COVID 치료제 정보에 접근할 수 있는 암호 값이 내장된 애플리케이션이 감염되어 암호 값을 확인할 수 없다고 했다. [3번 영역] 코드를 보면 medicine 메서드를 따로 요청하여 이용하는 것을 볼 수 없다. 보통 메서드나 함수를 정의하면 해당 메서드나 함수를 사용한다는 부분이 메인에 포함되어야 한다. 즉, medicine 메서드는 정의만 되어 있을 뿐 사용되지 않는다.

 

버튼을 클릭할 때 where is medicine이라는 문자열이 계속 출력되었는데 [1번 영역]코드에서 flag(전역 변수)로 지정을 했기 때문이다. medicine 메서드를 실행하면 해당 flag 변수 값이 변동되어 출력된다.

 

그러므로 frida를 통해 medicine 메서드가 실행될 수 있도록 스크립트 코드를 작성하여 로드하면 된다.

[2번 영역] 코드를 보면 input1,2,3,4 변수에 각 비교하는 값이 맞는 경우로 맞춰야 하기 때문에 해당 변수 값 중 틀리게 설정되어 있는 값을 변경해야 한다.

 

전역 변수에서 input1,2,3,4 전부 2를 대입하고 있다.

비교하고 있는 값을 보면 input1 == 2 // input2 == 0 // input3 == 2 // input4 == 0 이다.

그러면 input2와 input4는 0을 대입해줘야 한다.

 

 

 

frida 풀이

frida 세팅을 하고 난 후 아래와 같이 로드할 JavaScript 코드를 작성한다.

 

//exploit.js
setImmediate(function(){
	Java.perform(function(){
		var input = Java.use("com.example.mobilepentest.MainActivity");
		input.input2.value = 0;
		input.input4.value = 0;
		Java.choose("com.example.mobilepentest.MainActivity", {
			onMatch : function(mobilepentest){
				mobilepentest.medicine();
			},
			onComplete : function(){
				console.log("\nflag find it!");
			}
		})
	})
})

코드 내용은 위에 코드 분석에서 언급한 내용들로 작성했다.

 

 

[그림 5] frida exploit.js load

frida를 이용하여 작성한 스크립트를 로드하고 조금 기다리면 flag find it! 문자열이 출력된다. 그리고 "COVID 치료제 정보 확인" 버튼을 클릭하면 [그림 5]와 같이 Do you know COVID medicine? 제목이 medicine 메서드 실행으로 인하여 COVID medicine data로 변경되었고, where is medicine? 문자열 또한 flag 값으로 변경된 상태로 출력된다.

'CTF' 카테고리의 다른 글

POX 2020 CTF - VaccineWeb Write Up  (0) 2020.12.14
POX 2020 CTF - Kiban64  (0) 2020.11.25
Power of XX 2020 Write up  (0) 2020.11.21
DownUnderCTF - Is this pwn or web? write-up  (0) 2020.09.28

+ Recent posts