////
Search
🚩

FlagReceiver

힌트

Intent Sniffing 문제
drozer.
flag값이 깨져보인다면, IDA 동적 디버깅 고려.

개념

Intent Sniffing 문제는 의사소통을 위해 메세지를 담아 전달되는 객체인 Intent의 내용을 탐지하는 문제이다.

분석 순서

1.
가장 먼저 APK 파일을 디컴파일한다.
2.
APK를 분석하여 확인이 필요한 곳(Break Point) 지점을 생각한다.
3.
분석에 필요한 so 파일을 추출한다.
4.
IDA 로 동적 디버깅을 한다.

1. APK 디컴파일

JEB 파일을 통해 APK를 업로드 하면 간편하게 디컴파일 된 자료를 확인할 수 있다.
가장 먼저 구조들 중, MainActivity 클래스를 확인한다.
이 그림과 같이 분석이 어렵게 되어 있을 것이다.
이때는 화면에서 우클릭 -> 디컴파일 을 사용하면 java 코드로 디컴파일을 해준다.

2. APK 파일 분석

디컴파일이 되었다면, 본격적으로 이 APK가 어떤 동작을 하고, 어디서 정보를 얻어야하는지 확인해야 한다.
우선 가장 먼저 실행되는 MainActivity 클래스를 확인해야 한다.
package com.flagstore.ctf.flagstore; import android.app.Activity; import android.content.IntentFilter; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity { @Override // android.app.Activity protected void onCreate(Bundle arg6) { // 1. 창을 만들고, 'To-do: UI pending' 라는 문자열을 출력 super.onCreate(arg6); TextView tv = new TextView(this.getApplicationContext()); tv.setText("To-do: UI pending"); this.setContentView(tv); // IntentFilter란? // 특정 인텐트를 받을지 말지를 정의하는 역할을 수행하며 // 이를 통해 컴포넌트의 특징이 정해진다. IntentFilter filter = new IntentFilter(); // IntentFilter의 action 함수로 많은 브로드캐스트 메세지들 중 // 어떤 것을 수신할지 등록하는 과정이다. filter.addAction("com.flagstore.ctf.INCOMING_INTENT"); // registerReceiver 메소드를 사용하여 리시버를 등록한다. // 리시버 클래스와 작성한 필터, 메세지를 함께 넘겨준다. this.registerReceiver(new Send_to_Activity(), filter, "ctf.permissions._MSG", null); } }
Java
복사
MainActivity 클래스에서 APK의 동작을 크게 확인하였다. 그 다음으로 리시버로 등록되는 Send_to_Activity 가 어떤 동작을 하는지 확인해야 한다.
package com.flagstore.ctf.flagstore; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; import android.widget.Toast; // BroadcastReceiver를 상속받는다. (기능 사용) public class Send_to_Activity extends BroadcastReceiver { @Override // android.content.BroadcastReceiver // 수신된 메세지가 들어오는 함수이다. public void onReceive(Context context, Intent intent) { // 만약 수신된 intent의 메세지 내용이 'OpenSesame'이라면 // Debug 레벨에 로그를 남기고, CTFReceiver 클래스를 실행한다. if(intent.getStringExtra("msg").equalsIgnoreCase("OpenSesame")) { Log.d("Here", "Intent"); context.startActivity(new Intent(context, CTFReceiver.class)); return; } // 만약 'OpenSesame'이 아닌 다른 내용이 전달되거나, 아무런 메세지가 없는 경우 // 실패 메세지를 보여준다. Toast.makeText(context, "Ah, ah, ah, you didn\'t say the magic word!", 1).show(); } }
Java
복사
CTFReceiver 클래스로 넘어가기 때문에, CTFReceiver 클래스도 분석을 해야한다.
package com.flagstore.ctf.flagstore; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View.OnClickListener; import android.view.View; import android.widget.Button; import android.widget.TextView; public class CTFReceiver extends AppCompatActivity { // Linux 기반 C에서 작성된 라이브러리를 참조한다. (우리가 분석해야 할 so 파일) static { System.loadLibrary("native-lib"); } // JNI 사용 public native String getFlag(String arg1, String arg2, String arg3) { } // JNI 사용 public native String getPhrase(String arg1, String arg2, String arg3) { } @Override // android.support.v7.app.AppCompatActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new TextView(this).setText("Clever Person!"); // 'Broadcast' 글자가 새겨진 버튼을 생성한다. Button button = new Button(this); button.setText("Broadcast"); this.setContentView(button); // 버튼 클릭 시 button.setOnClickListener(new View.OnClickListener() { @Override // android.view.View$OnClickListener public void onClick(View v) { // 새로운 Intent 객체를 생성하고 동작을 설정한다. Intent intent = new Intent(); intent.setAction("com.flagstore.ctf.OUTGOING_INTENT"); String a = CTFReceiver.this.getResources().getString(0x7F060026) + "fpcMpwfFurWGlWu`uDlUge"; // string:str3 "wgHoNi[nvVfptxF@hpsd9DhrM@sz]" String b = Utilities.doBoth(CTFReceiver.this.getResources().getString(0x7F060023)); // string:passphrase "LetMeIn" String name = this.getClass().getName().split("\\.")[4]; // getPhrase 함수에서 문자열 생성 후, msg에 값 전달 intent.putExtra("msg", CTFReceiver.this.getPhrase(a, b, Utilities.doBoth(name.substring(0, name.length() - 2)))); // 브로드캐스트로 전달한다. (Flag 전송) // 만약 com.flagstore.ctf.OUTGOING_INTENT을 수신하고 있다면 바로 확인가능하다. CTFReceiver.this.sendBroadcast(intent); } }); } }
Java
복사

3. 분석에 필요한 so 파일 추출

so 파일은 JEB에서 프로젝트 탐색기 ->Libraries -> x86에서 확인할 수 있다. (기기가 x64라면 x86_64)
우클릭 -> (으)로 추출 을 클릭하여 직접 파일로 추출이 가능하다.
이제 생성된 so 파일을 IDA 프로로 확인할 수 있다.
왼쪽에는 함수가 매우 많기 때문에, 특정 함수를 골라 확인해야 하는데, 이전에 분석하면서 문자열 처리는 getPhrase 함수가 진행하여 Intent의 msg 에 전달하였다.
그렇다면 모든 함수를 찾지 않고, getPhrase 함수만 확인하면 된다. 이름이 완전히 똑같진 않을 수 있지만, 대부분 비슷하다.
코드가 보인다면, F5 를 눌러 디컴파일을 진행한다. 우리는 어떤 문자열을 반환하는지만 알면 되기 때문에, 문자열을 생성하는 과정은 지금 보지 않아도 된다.
따라서 가장 마지막 출력 부분, return 부분만 확인하면 된다.
v12 라는 변수에 담아 출력을 하고, msg에 전송을 위한 값을 반환하는데도 사용된다.
우리는 동적 디버깅을 하면서 이 부분의 값 확인을 위해 F2 명령어로 printf ... 라인에 브레이크 포인트(BP)를 설정 해줘야 한다.
BP 설정이 완료되었다면 동적 디버깅 순서이다.

4. IDA와 가상 머신을 사용한 동적 디버깅

참고로, NOX에서는 Flag값이 모두 출력되지 않는 문제점이 발생하여 안드로이드 스튜디오의 가상머신을 사용하였다.
Version : 6.0
CPU : x86
기본 명령어를 사용하여 가상머신과 연결이 완료 되었다면, IDA에서 설정을 하여 동적 디버깅을 해야한다.
1.
가상머신에서 앱 실행
2.
IDA에서 Debugger 탭 -> select debbuger -> Remote Linux debugger -> OK
3.
이후, IDA에서 Debugger 탭을 다시 클릭하면 메뉴가 바뀌었을 것이다. 추가 설정을 해야한다.
4.
Debugger 탭 -> Process Options -> Hostname 창에 localhost 입력 (가상머신을 IDA와 같은 컴퓨터로 돌렸을 때), port 포워딩 한 포트 설정 -> OK
5.
Attach to Process 클릭 -> 실행한 앱 프로세스 선택 -> OK
ps | grep [앱 이름] 명령어로 PID 확인 가능
6.
경고문(so 파일이 같냐 라고 물어보는 영문 경고) -> Same 선택
7.
노란 동그라미일 경우 분석 중이므로 대기, 초록색 동그라미 일 경우 분석 완료, 분석 가능
이전에 break를 걸었으므로 우선 앱을 동작하여 메세지를 전송해야 한다. (개인적으로 브레이크 포인트가 Attach되면서 변경 되었다. 동작전에 확인 필수)
동작 전
동작 후
동작이 에러 없이 성공적으로 실행 되었다면, 이전에 앱에서 수신을 하고자하는 com.flagstore.ctf.INCOMING_INTENT 과 같은 메세지를 전송해야 한다.
쉘에 접속 후, 조건에 맞게 메세지를 전송한다.
adb shell am broadcast -a "com.flagstore.ctf.INCOMING_INTENT" --es msg "OpenSesame" -a <ACTION> --es <EXTRA_KEY> <EXTRA_STRING_VALUE>
Plain Text
복사
전송이 되었다면, 기존의 앱 화면이 다음과 같이 변경된다.
이후, BROADCAST 버튼을 누르면 메세지 출력관련 함수들이 실행되면서, 이전에 설정한 브레이크 포인트에 정지한다.
하지만, 다시 확인해보니 printf 부분 시작 직전이었기 때문에, 값 전달이 되지 않았다.
에셈블리어로 다시 확인해보았다.
esi 레지스터에 전달되기 전에 멈춘것으로 보인다. F8 로 한줄 씩 실행하여 esi 레지스터에 값이 들어가도록 만들어 준다.
esi 에 값을 넣는 라인이 실행되었다면, 레지스터를 확인하면 Flag를 확인할 수 있다.
레지스터의 정보는 Debbuger 탭 -> Debbuger Windows -> Locals 에서 확인 가능하다.

FLAG

esi 레지스터를 확인하면 Flag가 있는것을 확인할 수 있다.
4-6

참고 사이트

BroadcastReceiver 개념 : https://brunch.co.kr/@mystoryg/48