CVE-2023-42799: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow') (CWE-120)
value: Moonlight-common-c contains the core GameStream client code shared between Moonlight clients. Moonlight-common-c is vulnerable to buffer overflow starting in commit 50c0a51b10ecc5b3415ea78c21d96d679e2288f9 due to unmitigated usage of unsafe C functions and improper bounds checking. A malicious game streaming server could exploit a buffer overflow vulnerability to crash a moonlight client, or achieve remote code execution (RCE) on the client (with insufficient exploit mitigations or if mitigations can be bypassed). The bug was addressed in commit 02b7742f4d19631024bd766bd2bb76715780004e.
CWE description: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
CVSS v3 base score: 8.8
Published: 2023-12-14T17:15Z
Repository: https://github.com/moonlight-stream/moonlight-common-c
Language: C
Files changed: src/RtspConnection.c
Commit date: 2023-10-06 21:14:30 -0500
Commit message: Fix buffer overflow in parseUrlAddrFromRtspUrlString (CVE-2023-42799)

Diff: diff --git a/src/RtspConnection.c b/src/RtspConnection.c
index d1fc3c7..18e15dd 100644
--- a/src/RtspConnection.c
+++ b/src/RtspConnection.c
@@ -655,11 +655,11 @@ static int parseOpusConfigurations(PRTSP_MESSAGE response) {
     }
 
     return 0;
 }
 
-static bool parseUrlAddrFromRtspUrlString(const char* rtspUrlString, char* destination) {
+static bool parseUrlAddrFromRtspUrlString(const char* rtspUrlString, char* destination, size_t destinationLength) {
     char* rtspUrlScratchBuffer;
     char* portSeparator;
     char* v6EscapeEndChar;
     char* urlPathSeparator;
     int prefixLen;
@@ -699,11 +699,12 @@ static bool parseUrlAddrFromRtspUrlString(const char* rtspUrlString, char* desti
     else if (urlPathSeparator) {
         // Terminate the string prior to the path separator
         *urlPathSeparator = 0;
     }
 
-    strcpy(destination, rtspUrlScratchBuffer + prefixLen);
+    PltSafeStrcpy(destination, destinationLength, rtspUrlScratchBuffer + prefixLen);
+    destination[destinationLength - 1] = '\0';
 
     free(rtspUrlScratchBuffer);
     return true;
 }
 
@@ -772,26 +773,26 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
     LC_ASSERT(StreamConfig.streamingRemotely != STREAM_CFG_AUTO);
     if (OriginalVideoBitrate >= HIGH_AUDIO_BITRATE_THRESHOLD &&
             (AudioCallbacks.capabilities & CAPABILITY_SLOW_OPUS_DECODER) == 0 &&
             (StreamConfig.streamingRemotely != STREAM_CFG_REMOTE || CHANNEL_COUNT_FROM_AUDIO_CONFIGURATION(StreamConfig.audioConfiguration) <= 2)) {
         // If we have an RTSP URL string and it was successfully parsed, use that string
-        if (serverInfo->rtspSessionUrl != NULL && parseUrlAddrFromRtspUrlString(serverInfo->rtspSessionUrl, urlAddr)) {
+        if (serverInfo->rtspSessionUrl != NULL && parseUrlAddrFromRtspUrlString(serverInfo->rtspSessionUrl, urlAddr, sizeof(urlAddr))) {
             strcpy(rtspTargetUrl, serverInfo->rtspSessionUrl);
         }
         else {
             // If an RTSP URL string was not provided or failed to parse, we will construct one now as best we can.
             //
             // NB: If the remote address is not a LAN address, the host will likely not enable high quality
             // audio since it only does that for local streaming normally. We can avoid this limitation,
             // but only if the caller gave us the RTSP session URL that it received from the host during launch.
             addrToUrlSafeString(&RemoteAddr, urlAddr, sizeof(urlAddr));
-            sprintf(rtspTargetUrl, "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
+			snprintf(rtspTargetUrl, sizeof(rtspTargetUrl), "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
         }
     }
     else {
         PltSafeStrcpy(urlAddr, sizeof(urlAddr), "0.0.0.0");
-        sprintf(rtspTargetUrl, "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
+		snprintf(rtspTargetUrl, sizeof(rtspTargetUrl), "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
     }
 
     switch (AppVersionQuad[0]) {
         case 3:
             rtspClientVersion = 10;

Vulnerable code: static bool parseUrlAddrFromRtspUrlString(const char* rtspUrlString, char* destination) {
    strcpy(destination, rtspUrlScratchBuffer + prefixLen);
        if (serverInfo->rtspSessionUrl != NULL && parseUrlAddrFromRtspUrlString(serverInfo->rtspSessionUrl, urlAddr)) {
            sprintf(rtspTargetUrl, "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
        sprintf(rtspTargetUrl, "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
Fixed code: static bool parseUrlAddrFromRtspUrlString(const char* rtspUrlString, char* destination, size_t destinationLength) {
    PltSafeStrcpy(destination, destinationLength, rtspUrlScratchBuffer + prefixLen);
    destination[destinationLength - 1] = '\0';
        if (serverInfo->rtspSessionUrl != NULL && parseUrlAddrFromRtspUrlString(serverInfo->rtspSessionUrl, urlAddr, sizeof(urlAddr))) {
			snprintf(rtspTargetUrl, sizeof(rtspTargetUrl), "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
		snprintf(rtspTargetUrl, sizeof(rtspTargetUrl), "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);