외로운 Nova의 작업실
insecurebankv2 - 브로드캐스트 리시버 결함 본문
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 권한이 있는 브로드캐스트만 받게됩니다.
'Mobile App Penetesting > Android App Vulnerability' 카테고리의 다른 글
insecurebankv2 - 로컬 암호화 이슈 취약점 (0) | 2023.05.08 |
---|---|
insecurebankv2 - 취약한 인증 메커니즘 취약점 (0) | 2023.05.07 |
insecurebankv2 - 취약점 진단 및 분석 도구 (1) | 2023.05.05 |
insecurebankv2 - 기본적인 명령어 정리 (0) | 2023.05.02 |
insecurebanckv2 - 사전 환경 구축 (0) | 2023.04.30 |