CVE-2020-26164 (published 2020-10-07T19:15Z)

In kdeconnect-kde (aka KDE Connect) before 20.08.2, an attacker on the local network could send crafted packets that trigger use of large amounts of CPU, memory, or network connection slots, aka a Denial of Service attack.

Severity: MEDIUM
CVSS v2 base score: 4.9 (AV:L/AC:L/Au:N/C:N/I:N/A:C)
CVSS v3 base score: 5.5, MEDIUM (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-400
cwe_name: Uncontrolled Resource Consumption
description: The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
extended_description: 
            Limited resources include memory, file system storage, database connection pool entries, and CPU. If an attacker can trigger the allocation of these limited resources, but the number or size of the resources is not controlled, then the attacker could cause a denial of service that consumes all available resources. This would prevent valid users from accessing the product, and it could potentially have an impact on the surrounding environment. For example, a memory exhaustion attack against an application could slow down the application as well as its host operating system.
            There are at least three distinct scenarios which can commonly lead to resource exhaustion:
               
                  Lack of throttling for the number of allocated resources
                  Losing all references to a resource before reaching the shutdown stage
                  Not closing/returning a resource after processing
               
            Resource exhaustion problems are often result due to an incorrect implementation of the following situations:
               
                  Error conditions and other exceptional circumstances.
                  Confusion over which part of the program is responsible for releasing the resource.
               
         
         

Fix information:
repo_url: https://github.com/KDE/kdeconnect-kde
commit_details: repo_url: https://github.com/KDE/kdeconnect-kde
author: Aleix Pol
author_date: 2020-09-16 02:27:13+02:00
msg: Don't brute-force reading the socket

The package will arrive eventually, and dataReceived will be emitted.
Otherwise we just end up calling dataReceived to no end.

Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
repository: repo_url: https://github.com/KDE/kdeconnect-kde
repo_name: KDE/kdeconnect-kde
description: Multi-platform app that allows your devices to communicate
repo_language: C++
owner: KDE
file_changes: filename: socketlinereader.cpp
change_type: ModificationType.MODIFY
diff: @@ -24,14 +24,6 @@ void SocketLineReader::dataReceived()
         }
     }
 
-    //If we still have things to read from the socket, call dataReceived again
-    //We do this manually because we do not trust readyRead to be emitted again
-    //So we call this method again just in case.
-    if (m_socket->bytesAvailable() > 0) {
-        QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection);
-        return;
-    }
-
     //If we have any packets, tell it to the world.
     if (!m_packets.isEmpty()) {
         Q_EMIT readyRead();

programming_language: C++
filename: testsocketlinereader.cpp
change_type: ModificationType.MODIFY
diff: @@ -13,16 +13,19 @@
 #include <QProcess>
 #include <QEventLoop>
 #include <QTimer>
+#include <QSignalSpy>
 
 class TestSocketLineReader : public QObject
 {
     Q_OBJECT
 public Q_SLOTS:
-    void initTestCase();
+    void init();
+    void cleanup() { delete m_server; }
     void newPacket();
 
 private Q_SLOTS:
     void socketLineReader();
+    void badData();
 
 private:
     QTimer m_timer;
@@ -33,8 +36,9 @@ private:
     SocketLineReader* m_reader;
 };
 
-void TestSocketLineReader::initTestCase()
+void TestSocketLineReader::init()
 {
+    m_packets.clear();
     m_server = new Server(this);
 
     QVERIFY2(m_server->listen(QHostAddress::LocalHost, 8694), "Failed to create local tcp server");
@@ -85,6 +89,29 @@ void TestSocketLineReader::socketLineReader()
     }
 }
 
+void TestSocketLineReader::badData()
+{
+    const QList<QByteArray> dataToSend = { "data1\n", "data" }; //does not end in a \n
+    for (const QByteArray& line : qAsConst(dataToSend)) {
+        m_conn->write(line);
+    }
+    m_conn->flush();
+
+    QSignalSpy spy(m_server, &QTcpServer::newConnection);
+    QVERIFY(m_server->hasPendingConnections() || spy.wait(1000));
+    QSslSocket* sock = m_server->nextPendingConnection();
+
+    QVERIFY2(sock != nullptr, "Could not open a connection to the client");
+
+    m_reader = new SocketLineReader(sock, this);
+    connect(m_reader, &SocketLineReader::readyRead, this, &TestSocketLineReader::newPacket);
+    m_timer.start();
+    m_loop.exec();
+
+    QCOMPARE(m_packets.count(), 1);
+    QCOMPARE(m_packets[0], dataToSend[0]);
+}
+
 void TestSocketLineReader::newPacket()
 {
     int maxLoops = 5;

programming_language: C++
repo_url: https://github.com/KDE/kdeconnect-kde
commit_details: repo_url: https://github.com/KDE/kdeconnect-kde
author: Aleix Pol
author_date: 2020-09-16 02:44:38+02:00
msg: Do not let lanlink connections stay open for long without authenticating

If there's no information received, close the socket to try again.

Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
repository: repo_url: https://github.com/KDE/kdeconnect-kde
repo_name: KDE/kdeconnect-kde
description: Multi-platform app that allows your devices to communicate
repo_language: C++
owner: KDE
file_changes: filename: lanlinkprovider.cpp
change_type: ModificationType.MODIFY
diff: @@ -392,6 +392,16 @@ void LanLinkProvider::newConnection()
         connect(socket, &QIODevice::readyRead,
                 this, &LanLinkProvider::dataReceived);
 
+        QTimer* timer = new QTimer(socket);
+        timer->setSingleShot(true);
+        timer->setInterval(1000);
+        connect(socket, &QSslSocket::encrypted,
+                timer, &QObject::deleteLater);
+        connect(timer, &QTimer::timeout, socket, [socket] {
+            qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Host timed out without sending any identity." << socket->peerAddress();
+            socket->disconnectFromHost();
+        });
+        timer->start();
     }
 }
 

programming_language: C++
repo_url: https://github.com/KDE/kdeconnect-kde
commit_details: repo_url: https://github.com/KDE/kdeconnect-kde
author: Albert Vaca Cintora
author_date: 2020-09-24 17:13:34+02:00
msg: Limit number of connected sockets from unpaired devices

Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
repository: repo_url: https://github.com/KDE/kdeconnect-kde
repo_name: KDE/kdeconnect-kde
description: Multi-platform app that allows your devices to communicate
repo_language: C++
owner: KDE
file_changes: filename: lanlinkprovider.cpp
change_type: ModificationType.MODIFY
diff: @@ -33,6 +33,8 @@
 
 #define MIN_VERSION_WITH_SSL_SUPPORT 6
 
+static const int MAX_UNPAIRED_CONNECTIONS = 42;
+
 LanLinkProvider::LanLinkProvider(
         bool testMode,
         quint16 udpBroadcastPort,
@@ -580,6 +582,15 @@ void LanLinkProvider::addLink(const QString& deviceId, QSslSocket* socket, Netwo
         deviceLink->reset(socket, connectionOrigin);
     } else {
         deviceLink = new LanDeviceLink(deviceId, this, socket, connectionOrigin);
+        // Socket disconnection will now be handled by LanDeviceLink
+        disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
+        bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId);
+        if (!isDeviceTrusted && m_links.size() > MAX_UNPAIRED_CONNECTIONS) {
+            qCWarning(KDECONNECT_CORE) << "Too many unpaired devices to remember them all. Ignoring " << deviceId;
+            socket->disconnectFromHost();
+            socket->deleteLater();
+            return;
+        }
         connect(deviceLink, &QObject::destroyed, this, &LanLinkProvider::deviceLinkDestroyed);
         m_links[deviceId] = deviceLink;
         if (m_pairingHandlers.contains(deviceId)) {

programming_language: C++
repo_url: https://github.com/KDE/kdeconnect-kde
commit_details: repo_url: https://github.com/KDE/kdeconnect-kde
author: Albert Vaca Cintora
author_date: 2020-09-24 17:16:02+02:00
msg: Do not remember more than a few identity packets at a time

To prevent the kdeconnect process from using too much memory.

Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
repository: repo_url: https://github.com/KDE/kdeconnect-kde
repo_name: KDE/kdeconnect-kde
description: Multi-platform app that allows your devices to communicate
repo_language: C++
owner: KDE
file_changes: filename: lanlinkprovider.cpp
change_type: ModificationType.MODIFY
diff: @@ -34,6 +34,7 @@
 #define MIN_VERSION_WITH_SSL_SUPPORT 6
 
 static const int MAX_UNPAIRED_CONNECTIONS = 42;
+static const int MAX_REMEMBERED_IDENTITY_PACKETS = 42;
 
 LanLinkProvider::LanLinkProvider(
         bool testMode,
@@ -243,6 +244,12 @@ void LanLinkProvider::udpBroadcastReceived()
 
         //qCDebug(KDECONNECT_CORE) << "Received Udp identity packet from" << sender << " asking for a tcp connection on port " << tcpPort;
 
+        if (m_receivedIdentityPackets.size() > MAX_REMEMBERED_IDENTITY_PACKETS) {
+            qCWarning(KDECONNECT_CORE) << "Too many remembered identities, ignoring" << receivedPacket->get<QString>(QStringLiteral("deviceId")) << "received via UDP";
+            delete receivedPacket;
+            continue;
+        }
+
         QSslSocket* socket = new QSslSocket(this);
         socket->setProxy(QNetworkProxy::NoProxy);
         m_receivedIdentityPackets[socket].np = receivedPacket;
@@ -453,6 +460,12 @@ void LanLinkProvider::dataReceived()
         return;
     }
 
+    if (m_receivedIdentityPackets.size() > MAX_REMEMBERED_IDENTITY_PACKETS) {
+        qCWarning(KDECONNECT_CORE) << "Too many remembered identities, ignoring" << np->get<QString>(QStringLiteral("deviceId")) << "received via TCP";
+        delete np;
+        return;
+    }
+
     // Needed in "encrypted" if ssl is used, similar to "tcpSocketConnected"
     m_receivedIdentityPackets[socket].np = np;
 

programming_language: C++
repo_url: https://github.com/KDE/kdeconnect-kde
commit_details: repo_url: https://github.com/KDE/kdeconnect-kde
author: Aleix Pol
author_date: 2020-09-16 02:28:58+02:00
msg: Limit identity packets to 8KiB

Healthy identity packages shouldn't be that big and we don't want to
allow systems around us to send us ever humongous packages that will
just leave us without any memory.

Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
repository: repo_url: https://github.com/KDE/kdeconnect-kde
repo_name: KDE/kdeconnect-kde
description: Multi-platform app that allows your devices to communicate
repo_language: C++
owner: KDE
file_changes: filename: lanlinkprovider.cpp
change_type: ModificationType.MODIFY
diff: @@ -399,6 +399,14 @@ void LanLinkProvider::newConnection()
 void LanLinkProvider::dataReceived()
 {
     QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
+    //the size here is arbitrary and is now at 8192 bytes. It needs to be considerably long as it includes the capabilities but there needs to be a limit
+    //Tested between my systems and I get around 2000 per identity package.
+    if (socket->bytesAvailable() > 8192) {
+        qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Suspiciously long identity package received. Closing connection." << socket->peerAddress() << socket->bytesAvailable();
+        socket->disconnectFromHost();
+        return;
+    }
+
 #if QT_VERSION < QT_VERSION_CHECK(5,7,0)
     if (!socket->canReadLine())
         return;

programming_language: C++
repo_url: https://github.com/KDE/kdeconnect-kde
commit_details: repo_url: https://github.com/KDE/kdeconnect-kde
author: Albert Vaca Cintora
author_date: 2020-09-24 17:18:06+02:00
msg: Limit the ports we try to connect to to the port range of KDE Connect

So we can't trigger connections to other services.

Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
repository: repo_url: https://github.com/KDE/kdeconnect-kde
repo_name: KDE/kdeconnect-kde
description: Multi-platform app that allows your devices to communicate
repo_language: C++
owner: KDE
file_changes: filename: lanlinkprovider.cpp
change_type: ModificationType.MODIFY
diff: @@ -241,6 +241,11 @@ void LanLinkProvider::udpBroadcastReceived()
         }
 
         int tcpPort = receivedPacket->get<int>(QStringLiteral("tcpPort"));
+        if (tcpPort < MIN_TCP_PORT || tcpPort > MAX_TCP_PORT) {
+            qCDebug(KDECONNECT_CORE) << "TCP port outside of kdeconnect's range";
+            delete receivedPacket;
+            continue;
+        }
 
         //qCDebug(KDECONNECT_CORE) << "Received Udp identity packet from" << sender << " asking for a tcp connection on port " << tcpPort;
 

programming_language: C++