브로드캐스트 리시버 결함

Gwon SeolHyeong·2021년 1월 14일
1

이 글을 작성하기 전에 모의해킹 분야에 취업하기 위해서 웹 외에도 모바일 모의해킹에 대한 지식이 어느정도 필요하다는 것을 필자는 느꼈다.
모바일 관련 글 작성은 인시큐어뱅크 앱을 통해서 취약점을 진단하는 <안드로이드 모바일 앱 모의해킹> 책을 바탕으로 작성하는 내용이다.

취약점 소개

브로드캐스트 리시버는 안드로이드 시스템의 중요한 요소 중 하나로, 안드로이드 디바이스에서 이벤트가 발생하면 브로드캐스트 신호를 보내는데, 이 신호를 받아 처리하는 역할을 수행한다.
대상 애플리케이션에서 발생하는 브로드캐스트를 받기 위해서는 브로드캐스드 리시버가 설정되어 있어야 하며, 신호를 받는 경우 애플리케이션에 정의해 놓은 작업을 수행한다. 브로드캐스트 리시버는 AndroidManifest.xml의 < receiver >< /receiver > 항목에 선언된다.

브로드캐스트 리시버를 호출할 떄 발생하는 브로드캐스트가 정상이면 부팅 완료, 문자 메시지 송·수신, 배터리 상태 등을 나타내는 시스템 이벤트와 다른 애플리케이션에서 발생하는 경우가 있다. 이와 반대로 비정상이면 악의적인 애플리케이션에서 발생하거나, 공격자에 의해 임의대로 생성할 수 있다.

악의적인 목적을 갖고 수행하는 경우에는 사용자가 받는 알림(메시지, 전화 등)을 중간에서 가로채는 행위를 할 수 있으며, 특정한 상황에서만 발생하는 작업을 우회하여 수행하도록 조작할 수 있다.

이번 취약점에서는 브로드캐스트 리시버를 알아보고, 임의로 브로드캐스트를 발생시켜 해당 앱의 취약성 여부를 확인하고, 마지막에는 해당 취약점에 대응하는 방법을 설명한다.

취약점 진단 과정

진단에 앞서 AndroidManifest.xml에 선언된 리시버와 브로드캐스트 리시버를 상속 받은 메서드를 살펴보자. 먼저 리시버 선언 부분에 해당하는 xml코드는 다음과 같다.

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

이 코드에서 AndroidManifest.xml에 리시버가 선언되었다.
여기에 설정된 브로드캐스트의 이름은 theBroadcast이며, 브로드캐스트 신호를 받으면 MyBroadCastReceiver에 설정된 작업을 수행한다.
그리고 exported값이 true로 되어 있기 때문에 외부 애플리케이션으로부터 intent를 받을 수 있는 상태다.

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");
    }
}

}

MyBroadCast Receiver 메서드는 BroadcastReceiver를 사용하기 위해 Broadcast Receiver 객체를 상속 받도록 하고 브로드캐스트가 발생하는 경우에는 Intent-filter에 의해 걸러진 Intent들이 onReceive() 메서드로 들어와 입력된 전화번호에 문자 메시지를 전송하도록 정의되어 있다.

만약 전화번호가 입력되지 않았다면 "Phone number is null" 이 출력된다. 이 과정에서 애플리케이션이 사용자 정의 브로드캐스트 리시버를 사용하거나 Intent-filter에 특정 권한을 명시하지 않는다면 해당 애플리케이션은 디바이스의 다른 애플리케이션에 의해 브로드캐스트 리시버가 오·남용 될 수 있다.

해당 이슈를 알아보기 위해서 ADB와 드로저를 이용하여 진행한다.

(해당 책의 내용대로 환경 설정을 시도했지만.. 최신 업데이트 버전이 아니기 때문에 다른 환경 설정은 성공했지만 드로저는 성공하지 못 했기 때문에 ADB를 이용하는 방법만 작성했다.)

ADB를 이용한 브로드캐스트 생성

ADB에서 임의의 브로드캐스트를 생성하여 브로드캐스트 리시버를 속이기 위해서는 am 명령을 사용해야 한다. am은 액티비티 매니저로, 안드로이드 시스템에 포함된 다양한 액션을 명령으로 수행할 수 있다. 브로드캐스트를 생성하기 위해 먼저 대상 장치에 adb shell 명령으로 대상 장치의 프롬프트에 진입하고 다음 명령을 수행한다.

브로드캐스팅 된 것을 이제 adb logcat 명령어를 통해 확인한다.

메시지가 출력된 것을 확인할 수 있다. MyBroadCastReceiver 메서드의 소스 코드에서 확인했던 바와 같이 phonenumber 부분이 비어 있는 경우에 출력된다. 이번에는 소스 코드에 phonenumber와 newpass 값을 포함하여 브로드캐스트를 생성해 볼 차례이다.

am 명령과 --es 옵션을 사용하여 수행한다.

이 명령어를 통해서 phonenumber를 5554로, newpass를 1234로 임의로 변경하는 브로드캐스트를 전송시켰다. 마찬가지로 logcat을 통해서 확인해보면

이렇게 액션에 필요한 정보도 브로드캐스트를 생성하여 브로드캐스트 리시버가 실행되는 것을 볼 수 있다.
이 메시지에는 기존에 사용하던 비밀번호가 평문으로 포함된다.
계정의 비밀번호는 변경되지 않으며, 비밀번호만 노출된다.

취약점 대응 방안

브로드캐스트 리시버의 오·남용을 방지하기 위한 방법에는 두 가지가 있다.

첫 번째 방법은 AndroidManifest.xml의 리시버 항목에 위치하는 "android:exported=true" 항목을 "false"로 설정한다.

기본 세팅은 true로 설정되어 있는 상태이다. 이 값을 false로 변경하게 되면 발생하는 인텐트에 영향을 받지 않게 되므로 브로드캐스트 리시버도 임의의 브로드캐스트에 영향을 받지 않는다.

두 번째 방법은 각 리시버에 별도의 권한(permission)을 주는 것이다. (exported=false로 변경)
이렇게 적용시킨다면 권한이 Deny 되었다는 것을 로그캣을 통해 확인이 가능하다.

해당 기능은 안드로이드에서 중요한 기능을 하는 요소 중 하나로, 개발자의 입장에서는 이를 통해 다양한 기능 구현이 가능하다. 해당 취약점은 간단한 설정으로 쉽게 조치가 가능하기 때문에 약간의 주의만 있다면 공격 가능한 위험이 감소한다.

profile
정보보안 공부

0개의 댓글