Güvenli olmayan makineden makineye iletişim kurulumu

OWASP kategorisi: MASVS-CODE: Kod Kalitesi

Genel Bakış

Kullanıcıların radyo frekansı (RF) iletişimi veya kablolu bağlantılar kullanarak veri aktarmasına ya da diğer cihazlarla etkileşimde bulunmasına olanak tanıyan işlevler uygulayan uygulamalarla sıkça karşılaşılır. Android'de bu amaçla kullanılan en yaygın teknolojiler klasik Bluetooth (Bluetooth BR/EDR), Bluetooth Low Energy (BLE), Wi-Fi P2P, NFC ve USB'dir.

Bu teknolojiler genellikle akıllı ev aksesuarları, sağlık izleme cihazları, toplu taşıma kioskları, ödeme terminalleri ve Android destekli diğer cihazlarla iletişim kurması beklenen uygulamalarda uygulanır.

Diğer tüm kanallarda olduğu gibi, makineden makineye iletişim de iki veya daha fazla cihaz arasında oluşturulan güven sınırını tehlikeye atmayı amaçlayan saldırılara karşı hassastır. Cihaz kimliğine bürünme gibi teknikler, kötü niyetli kullanıcılar tarafından iletişim kanalına karşı çok sayıda saldırı gerçekleştirmek için kullanılabilir.

Android, geliştiricilerin makineler arası iletişimi yapılandırması için özel API'ler sunar.

İletişim protokolleri uygulanırken yapılan hatalar, kullanıcı veya cihaz verilerinin yetkisiz üçüncü taraflara ifşa edilmesine yol açabileceğinden bu API'ler dikkatli bir şekilde kullanılmalıdır. En kötü senaryoda saldırganlar, bir veya daha fazla cihazı uzaktan ele geçirebilir ve böylece cihazdaki içeriklere tam erişim sağlayabilir.

Etki

Etki, uygulamada kullanılan cihazdan cihaza teknolojiye göre değişebilir.

Makineden makineye iletişim kanallarının yanlış kullanımı veya yapılandırılması, kullanıcı cihazının güvenilmeyen iletişim girişimlerine maruz kalmasına neden olabilir. Bu durum, cihazın ortadaki adam (MiTM), komut enjeksiyonu, hizmet reddi (DoS) veya kimliğe bürünme saldırıları gibi ek saldırılara karşı savunmasız kalmasına neden olabilir.

Risk: Kablosuz kanallar üzerinden hassas verilerin dinlenmesi

Makineden makineye iletişim mekanizmalarını uygularken hem kullanılan teknoloji hem de iletilmesi gereken veri türü dikkatle değerlendirilmelidir. Kablolu bağlantılar, ilgili cihazlar arasında fiziksel bir bağlantı gerektirdiğinden bu tür görevler için pratikte daha güvenli olsa da klasik Bluetooth, BLE, NFC ve Wi-Fi P2P gibi radyo frekanslarını kullanan iletişim protokolleri engellenebilir. Saldırgan, veri alışverişine dahil olan terminallerden veya erişim noktalarından birinin kimliğine bürünebilir ya da havadan iletişimi keserek hassas kullanıcı verilerine erişebilir. Ayrıca, cihaza yüklenen kötü amaçlı uygulamalar, iletişime özel çalışma zamanı izinleri verilirse sistem mesajı arabelleklerini okuyarak cihazlar arasında değiştirilen verileri alabilir.

Çözümler

Uygulama, kablosuz kanallar üzerinden hassas verilerin makineden makineye değişimi gerektiriyorsa şifreleme gibi uygulama katmanı güvenlik çözümleri uygulamanın kodunda uygulanmalıdır. Bu sayede saldırganların iletişim kanalını koklaması ve değiştirilen verileri düz metin olarak alması önlenir. Ek kaynaklar için Kriptografi belgelerine bakın.


Risk: Kablosuz kötü amaçlı veri ekleme

Kablosuz makineden makineye iletişim kanalları (klasik Bluetooth, BLE, NFC, Wi-Fi P2P) kötü amaçlı veriler kullanılarak kurcalanabilir. Yeterli beceriye sahip saldırganlar, kullanılan iletişim protokolünü belirleyebilir ve veri alışverişi akışına müdahale edebilir. Örneğin, uç noktalardan birinin kimliğine bürünerek özel olarak hazırlanmış yükler gönderebilir. Bu tür kötü amaçlı trafik, uygulamanın işlevselliğini düşürebilir ve en kötü durumda beklenmedik uygulama ve cihaz davranışlarına neden olabilir veya hizmet reddi (DoS), komut yerleştirme ya da cihaz ele geçirme gibi saldırılara yol açabilir.

Çözümler

Android, geliştiricilere klasik Bluetooth, BLE, NFC ve kablosuz ağ P2P gibi makineden makineye iletişimleri yönetmek için güçlü API'ler sunar. Bu önlemler, iki cihaz arasında değiştirilen verileri temizlemek için dikkatli bir şekilde uygulanmış veri doğrulama mantığıyla birleştirilmelidir.

Bu çözüm uygulama düzeyinde uygulanmalı ve verilerin beklenen uzunlukta ve biçimde olup olmadığını, ayrıca uygulama tarafından yorumlanabilecek geçerli bir yük içerip içermediğini doğrulayan kontrolleri içermelidir.

Aşağıdaki snippet'te örnek veri doğrulama mantığı gösterilmektedir. Bu, Bluetooth veri aktarımını uygulama konusunda Android geliştiricilerinin örneği üzerinden uygulanmıştır:

Kotlin

class MyThread(private val mmInStream: InputStream, private val handler: Handler) : Thread() {

    private val mmBuffer = ByteArray(1024)
      override fun run() {
        while (true) {
            try {
                val numBytes = mmInStream.read(mmBuffer)
                if (numBytes > 0) {
                    val data = mmBuffer.copyOf(numBytes)
                    if (isValidBinaryData(data)) {
                        val readMsg = handler.obtainMessage(
                            MessageConstants.MESSAGE_READ, numBytes, -1, data
                        )
                        readMsg.sendToTarget()
                    } else {
                        Log.w(TAG, "Invalid data received: $data")
                    }
                }
            } catch (e: IOException) {
                Log.d(TAG, "Input stream was disconnected", e)
                break
            }
        }
    }

    private fun isValidBinaryData(data: ByteArray): Boolean {
        if (// Implement data validation rules here) {
            return false
        } else {
            // Data is in the expected format
            return true
        }
    }
}

Java

public void run() {
            mmBuffer = new byte[1024];
            int numBytes; // bytes returned from read()
            // Keep listening to the InputStream until an exception occurs.
            while (true) {
                try {
                    // Read from the InputStream.
                    numBytes = mmInStream.read(mmBuffer);
                    if (numBytes > 0) {
                        // Handle raw data directly
                        byte[] data = Arrays.copyOf(mmBuffer, numBytes);
                        // Validate the data before sending it to the UI activity
                        if (isValidBinaryData(data)) {
                            // Data is valid, send it to the UI activity
                            Message readMsg = handler.obtainMessage(
                                    MessageConstants.MESSAGE_READ, numBytes, -1,
                                    data);
                            readMsg.sendToTarget();
                        } else {
                            // Data is invalid
                            Log.w(TAG, "Invalid data received: " + data);
                        }
                    }
                } catch (IOException e) {
                    Log.d(TAG, "Input stream was disconnected", e);
                    break;
                }
            }
        }

        private boolean isValidBinaryData(byte[] data) {
            if (// Implement data validation rules here) {
                return false;
            } else {
                // Data is in the expected format
                return true;
           }
    }

Risk: USB ile kötü amaçlı veri yerleştirme

İki cihaz arasındaki USB bağlantıları, iletişimi engellemek isteyen kötü niyetli kullanıcıların hedefi olabilir. Bu durumda, saldırganın herhangi bir mesajı dinleyebilmesi için terminalleri bağlayan kabloya erişmesi gerektiğinden, gerekli fiziksel bağlantı ek bir güvenlik katmanı oluşturur. Başka bir saldırı vektörü, cihaza kasıtlı veya kasıtsız olarak takılan güvenilmeyen USB cihazlarıdır.

Uygulama, belirli uygulama içi işlevleri tetiklemek için USB cihazlarını PID/VID kullanarak filtreliyorsa saldırganlar, meşru cihazın kimliğine bürünerek USB kanalı üzerinden gönderilen verileri kurcalayabilir. Bu tür saldırılar, kötü amaçlı kullanıcıların cihaza tuş vuruşları göndermesine veya uygulama etkinliklerini yürütmesine olanak tanıyabilir. Bu durum, en kötü ihtimalle uzaktan kod yürütmeye veya istenmeyen yazılımların indirilmesine yol açabilir.

Çözümler

Uygulama düzeyinde bir doğrulama mantığı uygulanmalıdır. Bu mantık, USB üzerinden gönderilen verileri filtreleyerek uzunluk, biçim ve içeriğin uygulama kullanım alanıyla eşleşip eşleşmediğini kontrol etmelidir. Örneğin, kalp atışı monitörü tuş vuruşu komutları gönderememelidir.

Ayrıca, mümkün olduğunda uygulamanın USB cihazından alabileceği USB paketlerinin sayısını kısıtlamaya dikkat edilmelidir. Bu, kötü amaçlı cihazların rubber ducky gibi saldırılar gerçekleştirmesini önler.

Bu doğrulama, arabellek içeriğini kontrol etmek için yeni bir iş parçacığı oluşturularak yapılabilir. Örneğin, bulkTransfer:

Kotlin

fun performBulkTransfer() {
    // Stores data received from a device to the host in a buffer
    val bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.size, 5000)

    if (bytesTransferred > 0) {
        if (//Checks against buffer content) {
            processValidData(buffer)
        } else {
            handleInvalidData()
        }
    } else {
        handleTransferError()
    }
}

Java

public void performBulkTransfer() {
        //Stores data received from a device to the host in a buffer
        int bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.length, 5000);
        if (bytesTransferred > 0) {
            if (//Checks against buffer content) {
                processValidData(buffer);
            } else {
                handleInvalidData();
            }
        } else {
            handleTransferError();
        }
    }

Belirli Riskler

Bu bölümde, standart dışı azaltma stratejileri gerektiren veya belirli bir SDK düzeyinde azaltılmış olan ve eksiksiz olması için buraya eklenen riskler yer almaktadır.

Risk: Bluetooth - yanlış bulunabilirlik süresi

Android geliştiricileri Bluetooth belgelerinde belirtildiği gibi, uygulamada Bluetooth arayüzü yapılandırılırken cihazın bulunabilirliğini etkinleştirmek için startActivityForResult(Intent, int) yönteminin kullanılması ve EXTRA_DISCOVERABLE_DURATION değerinin sıfır olarak ayarlanması, uygulama arka planda veya ön planda çalıştığı sürece cihazın bulunabilir olmasına neden olur. Klasik Bluetooth spesifikasyonunda ise bulunabilir cihazlar, diğer cihazların cihaz verilerini almasına veya cihaza bağlanmasına olanak tanıyan belirli bulma mesajlarını sürekli olarak yayınlar. Bu tür bir senaryoda, kötü amaçlı bir üçüncü taraf bu mesajları engelleyebilir ve Android destekli cihaza bağlanabilir. Bağlantı kurulduktan sonra saldırgan, veri hırsızlığı, hizmet reddi (DoS) veya komut ekleme gibi başka saldırılar gerçekleştirebilir.

Çözümler

EXTRA_DISCOVERABLE_DURATION hiçbir zaman sıfır olarak ayarlanmamalıdır. EXTRA_DISCOVERABLE_DURATION parametresi ayarlanmazsa Android, cihazları varsayılan olarak 2 dakika boyunca keşfedilebilir hale getirir. EXTRA_DISCOVERABLE_DURATION parametresi için ayarlanabilecek maksimum değer 2 saattir (7.200 saniye). Uygulama kullanım alanına göre, bulunabilir süre zamanını mümkün olan en kısa sürede tutmanız önerilir.


Risk: NFC - klonlanmış amaç filtreleri

Kötü amaçlı bir uygulama, belirli NFC etiketlerini veya NFC özellikli cihazları okumak için intent filtreleri kaydedebilir. Bu filtreler, meşru bir uygulama tarafından tanımlanan filtreleri kopyalayabilir. Bu sayede saldırgan, değiştirilen NFC verilerinin içeriğini okuyabilir. İki etkinliğin belirli bir NFC etiketi için aynı amaç filtrelerini belirttiği durumlarda Etkinlik Seçici'nin gösterileceği ve bu nedenle saldırının başarılı olması için kullanıcının yine de kötü amaçlı uygulamayı seçmesi gerekeceği unutulmamalıdır. Bununla birlikte, niyet filtrelerini gizleme ile birleştirdiğinizde bu senaryo hâlâ mümkündür. Bu saldırı yalnızca NFC üzerinden değiştirilen verilerin çok hassas olarak kabul edilebileceği durumlarda önemlidir.

Çözümler

Bir uygulamada NFC okuma özellikleri uygulanırken Android uygulama kayıtları (AAR'ler) ile birlikte amaç filtreleri kullanılabilir. AAR kaydını bir NDEF mesajına yerleştirmek, yalnızca meşru uygulamanın ve ilişkili NDEF işleme etkinliğinin başlatılacağına dair güçlü bir güvence verir. Bu sayede, NFC üzerinden değiştirilen son derece hassas etiket veya cihaz verilerinin istenmeyen uygulamalar ya da etkinlikler tarafından okunması önlenir.


Risk: NFC - NDEF mesajı doğrulamasının olmaması

Android destekli bir cihaz, NFC etiketinden veya NFC özellikli bir cihazdan veri aldığında sistem, NDEF mesajını işlemek üzere yapılandırılmış uygulamayı ya da belirli etkinliği otomatik olarak tetikler. Uygulamada uygulanan mantığa göre, etikette yer alan veya cihazdan alınan veriler, web sayfalarını açma gibi başka işlemlerin tetiklenmesi için diğer etkinliklere sunulabilir.

NDEF mesaj içeriği doğrulaması olmayan bir uygulama, saldırganların NFC özellikli cihazları veya NFC etiketlerini kullanarak uygulamaya kötü amaçlı yükler yerleştirmesine izin verebilir. Bu durum, kötü amaçlı dosya indirme, komut yerleştirme veya hizmet reddi (DoS) ile sonuçlanabilecek beklenmedik davranışlara neden olabilir.

Çözümler

Alınan NDEF mesajı başka bir uygulama bileşenine gönderilmeden önce, mesajdaki verilerin beklenen biçimde olduğu ve beklenen bilgileri içerdiği doğrulanmalıdır. Bu sayede, kötü amaçlı verilerin diğer uygulama bileşenlerine filtrelenmeden aktarılması önlenir. Böylece, beklenmedik davranış veya NFC verilerinin değiştirilmesiyle yapılan saldırı riski azaltılır.

Aşağıdaki snippet'te, bağımsız değişken olarak NDEF mesajı ve mesaj dizisindeki diziniyle bir yöntem olarak uygulanan örnek veri doğrulama mantığı gösterilmektedir. Bu, taranan bir NFC NDEF etiketinden veri almak için Android geliştiricilerinin örneği üzerinden uygulanmıştır:

Kotlin

//The method takes as input an element from the received NDEF messages array
fun isValidNDEFMessage(messages: Array<NdefMessage>, index: Int): Boolean {
    // Checks if the index is out of bounds
    if (index < 0 || index >= messages.size) {
        return false
    }
    val ndefMessage = messages[index]
    // Retrieves the record from the NDEF message
    for (record in ndefMessage.records) {
        // Checks if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
        if (record.tnf == NdefRecord.TNF_ABSOLUTE_URI && record.type.size == 1) {
            // Loads payload in a byte array
            val payload = record.payload

            // Declares the Magic Number that should be matched inside the payload
            val gifMagicNumber = byteArrayOf(0x47, 0x49, 0x46, 0x38, 0x39, 0x61) // GIF89a

            // Checks the Payload for the Magic Number
            for (i in gifMagicNumber.indices) {
                if (payload[i] != gifMagicNumber[i]) {
                    return false
                }
            }
            // Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
            if (payload.size == 13) {
                return true
            }
        }
    }
    return false
}

Java

//The method takes as input an element from the received NDEF messages array
    public boolean isValidNDEFMessage(NdefMessage[] messages, int index) {
        //Checks if the index is out of bounds
        if (index < 0 || index >= messages.length) {
            return false;
        }
        NdefMessage ndefMessage = messages[index];
        //Retrieve the record from the NDEF message
        for (NdefRecord record : ndefMessage.getRecords()) {
            //Check if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
            if ((record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) && (record.getType().length == 1)) {
                //Loads payload in a byte array
                byte[] payload = record.getPayload();
                //Declares the Magic Number that should be matched inside the payload
                byte[] gifMagicNumber = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61}; // GIF89a
                //Checks the Payload for the Magic Number
                for (int i = 0; i < gifMagicNumber.length; i++) {
                    if (payload[i] != gifMagicNumber[i]) {
                        return false;
                    }
                }
                //Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
                if (payload.length == 13) {
                    return true;
                }
            }
        }
        return false;
    }

Kaynaklar