외로운 Nova의 작업실

insecurebankv2 - 브로드캐스트 리시버 결함 본문

Mobile App Penetesting/Android App Vulnerability

insecurebankv2 - 브로드캐스트 리시버 결함

Nova_ 2023. 5. 6. 19:44

- 취약점 소개

브로드캐스트가 무분별하게 인텐트를 받아들이면 악의적인 애플리케이션에 의해서, 공격자에 의해서 임의대로 실행될 수 있으며 수행을 조작할 수 있습니다. 

 

- 취약점 진단 과정

먼저 매니페스트 파일로 리시버를 봐보겠습니다.

run app.package.manifest com.android.insecurebankv2
<receiver name="com.android.insecurebankv2.MyBroadCastReceiver"
              exported="true">
      <intent-filter>
        <action name="theBroadcast">
        </action>
      </intent-filter>
    </receiver>

이름과 exported가 true로 되어있다보니 외부 앱의 인텐트를 리시브할 수 있는 것으로 확인됩니다. 실제 리시버가 무엇을 하는지 소스코드를 봐보겠습니다.

package com.android.insecurebankv2;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.telephony.SmsManager;
import android.util.Base64;

/*
The class that handles the broadcast receiver functionality in the application.
When a change password is successful, a SMS is sent as a confirmation to the phone
number used by the user
@author Dinesh Shetty
*/
public class MyBroadCastReceiver extends BroadcastReceiver {
	String usernameBase64ByteString;
	public static final String MYPREFS = "mySharedPreferences";

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub

        String phn = intent.getStringExtra("phonenumber");
        String newpass = intent.getStringExtra("newpass");

		if (phn != null) {
			try {
                SharedPreferences settings = context.getSharedPreferences(MYPREFS, Context.MODE_WORLD_READABLE);
                final String username = settings.getString("EncryptedUsername", null);
                byte[] usernameBase64Byte = Base64.decode(username, Base64.DEFAULT);
                usernameBase64ByteString = new String(usernameBase64Byte, "UTF-8");
                final String password = settings.getString("superSecurePassword", null);
                CryptoClass crypt = new CryptoClass();
                String decryptedPassword = crypt.aesDeccryptedString(password);
                String textPhoneno = phn.toString();
                String textMessage = "Updated Password from: "+decryptedPassword+" to: "+newpass;
                SmsManager smsManager = SmsManager.getDefault();
                System.out.println("For the changepassword - phonenumber: "+textPhoneno+" password is: "+textMessage);
                smsManager.sendTextMessage(textPhoneno, null, textMessage, null, null);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
        else {
            System.out.println("Phone number is null");
        }
	}

}

아래는 Bytecode Viewr로 본 브로드캐스트 리시버입니다.

package com.android.insecurebankv2;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.telephony.SmsManager;
import android.util.Base64;
import java.io.PrintStream;

public class MyBroadCastReceiver extends BroadcastReceiver {
   public static final String MYPREFS = "mySharedPreferences";
   String usernameBase64ByteString;

   public void onReceive(Context var1, Intent var2) {
      String var3 = var2.getStringExtra("phonenumber");
      String var8 = var2.getStringExtra("newpass");
      if (var3 != null) {
         try {
            SharedPreferences var4 = var1.getSharedPreferences("mySharedPreferences", 1);
            byte[] var5 = Base64.decode(var4.getString("EncryptedUsername", (String)null), 0);
            String var7 = new String(var5, "UTF-8");
            this.usernameBase64ByteString = var7;
            var7 = var4.getString("superSecurePassword", (String)null);
            CryptoClass var12 = new CryptoClass();
            String var13 = var12.aesDeccryptedString(var7);
            var7 = var3.toString();
            StringBuilder var10 = new StringBuilder();
            var13 = var10.append("Updated Password from: ").append(var13).append(" to: ").append(var8).toString();
            SmsManager var11 = SmsManager.getDefault();
            PrintStream var9 = System.out;
            StringBuilder var14 = new StringBuilder();
            var9.println(var14.append("For the changepassword - phonenumber: ").append(var7).append(" password is: ").append(var13).toString());
            var11.sendTextMessage(var7, (String)null, var13, (PendingIntent)null, (PendingIntent)null);
         } catch (Exception var6) {
            var6.printStackTrace();
         }
      } else {
         System.out.println("Phone number is null");
      }

   }
}

리시버는 전화번호와 새로운 패스워드를 받아서 해당 전화번호에 맞는 패스워드를 새로운 패스워드로 변경하는 역할을 합니다. 실제로 변경하지는 않습니다. 또한 프린트문으로 supersecurePassword를 평문으로 알 수 있습니다. 실제 브로드캐스트를 날려보겠습니다.

 

<ADB를 이용한 브로드캐스트>

ADB에서는 am명령어로 브로드캐스트를 생성할 수 있습니다. shell에 진입하며 아래 명령어를 쳐줍니다.

am broadcast -a theBroadcast -n com.android.insecurebankv2/.MyBroadCastReceiver

실행 결과는 위와 같지만 adb logcat 명령어로 로그를 보게되면 아래와 같습니다.

Phone number is null이라고 뜨는 걸 알 수 있습니다. 이제 파라미터 값을 줘보겠습니다. 이는 --es 옵션으로 줄 수 있습니다. 아래와 같이 명령어를 쳐줍니다.

adcast -n com.android.insecurebankv2/.MyBroadCastReceiver --es phonenumber 5555 --es newpass test

 

로그를 봐보겠습니다.

슈퍼시크릿 패스워드가 확인되고 새로운 패스워드로 변경된 것을 알 수 있습니다.(실제로 변경되지 않습니다.)

 

<드로저를 이용한 브로드캐스트>

먼저 취약점이 존재하는지 확인해줍니다.

run app.package.attacksurface com.android.insecurebankv2

브로드캐스트에 1개 있다고 합니다. 직접 확인해봅시다.

 run app.broadcast.info -a com.android.insecurebankv2

아무 권한없이 브로드캐스트를 받는 리시버가 존재함을 알 수 있습니다. 브로드캐스트를 만들어보겠습니다.

run app.broadcast.send --component com.android.insecurebankv2 com.android.insecurebankv2.MyBroadCastReceiver --extra string phonenumber 1111 --extra string newpass test

이제 로그를 봐보겠습니다.

패스워드가 변경되었음을 알 수 있습니다.

 

- 취약점 대응 방안

브로드캐스트 리시버의 오남용을 방지하기위해서는 두가지 방법이 있습니다. 한가지는 exported=true를 false로 변경하여 외부 애플리케이션에서 발생하는 인테트를 받지 않는 것입니다. 매니페스트파일을 아래처럼 고쳐줍니다.

<receiver name="com.android.insecurebankv2.MyBroadCastReceiver"
              exported="false">
      <intent-filter>
        <action name="theBroadcast">
        </action>
      </intent-filter>
    </receiver>

두번째 방법은 리시버가 권한을 가지고 브로드캐스를 하는지 확인하는 것입니다. 아래처럼 매니페스트 파일을 고쳐줍니다.

<receiver name="com.android.insecurebankv2.MyBroadCastReceiver"
              exported="true">
      <intent-filter>
        <action name="theBroadcast">
        </action>
        <receiver android:permission="com.android.insecurebankv2.MY_PERMISSION"/>
      </intent-filter>
    </receiver>

이렇게하면 리시버는 MY_PERMISSION 권한이 있는 브로드캐스트만 받게됩니다.

Comments