CVE-2023-42801 (C, published 2023-12-14T17:15Z)
Description: 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 f57bd745b4cbed577ea654fad4701bea4d38b44c. A malicious game streaming server could exploit a buffer overflow vulnerability to crash a moonlight client. Achieving RCE is possible but unlikely, due to stack canaries in use by modern compiler toolchains. The published binaries for official clients Qt, Android, iOS/tvOS, and Embedded are built with stack canaries, but some unofficial clients may not use stack canaries. This vulnerability takes place after the pairing process, so it requires the client to be tricked into pairing to a malicious host. It is not possible to perform using a man-in-the-middle due to public key pinning that takes place during the pairing process. The bug was addressed in commit b2497a3918a6d79808d9fd0c04734786e70d5954.
Severity: nan (CVSS v3 base score: 7.6)
Weakness: CWE-120 \u2014 Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
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.
Repository: https://github.com/moonlight-stream/moonlight-common-c
Files changed:
src/Connection.c
src/ControlStream.c
src/InputStream.c
src/Limelight-internal.h
src/Limelight.h
src/Misc.c
src/RtspConnection.c
src/SdpGenerator.c
src/VideoDepacketizer.c
src/VideoStream.c
Commit message: API update to provide enough information to support GFE 3.0.7


Vulnerable code:
int ServerMajorVersion;
int LiStartConnection(const char* host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks,
    PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks,
    void* renderContext, int drFlags, int _serverMajorVersion) {
    ServerMajorVersion = _serverMajorVersion;
    RemoteAddrString = strdup(host);
    err = resolveHostName(host);
    if (ServerMajorVersion == 3) {
    else if (ServerMajorVersion == 4) {
    else if (ServerMajorVersion == 5) {
    LC_ASSERT(ServerMajorVersion >= 5);
    LC_ASSERT(ServerMajorVersion < 5);
    if (ServerMajorVersion >= 5) {
    if (ServerMajorVersion >= 5) {
    if (ServerMajorVersion >= 5) {
    LC_ASSERT(ServerMajorVersion >= 5);
    if (ServerMajorVersion >= 5) {
    if (ServerMajorVersion >= 7) {
        if (ServerMajorVersion < 5) {
            if (ServerMajorVersion >= 7 && encryptedSize >= 16 + sizeof(currentAesIv)) {
    if (ServerMajorVersion < 5) {
    if (ServerMajorVersion >= 5) {
    if (ServerMajorVersion >= 5) {
    if (ServerMajorVersion == 3) {
        if (ServerMajorVersion >= 5) {
    if (ServerMajorVersion >= 5) {
extern int ServerMajorVersion;
// _serverMajorVersion is the major version number of the 'appversion' tag in the /serverinfo request
//
int LiStartConnection(const char* host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks,
    PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, void* renderContext, int drFlags, int _serverMajorVersion);
}
    if (ServerMajorVersion >= 5) {
        if (ServerMajorVersion >= 6) {
    switch (ServerMajorVersion) {
    if (ServerMajorVersion >= 5) {
                         ServerMajorVersion >= 5 ? "streamid=audio/0/0" : "streamid=audio",
                         ServerMajorVersion >= 5 ? "streamid=video/0/0" : "streamid=video",
    if (ServerMajorVersion >= 5) {
    if (ServerMajorVersion >= 5) {
    if (ServerMajorVersion >= 5) {
    if (ServerMajorVersion == 3) {
    else if (ServerMajorVersion == 4) {
    if (ServerMajorVersion >= 4) {
        ServerMajorVersion < 4 ? 47996 : 47998);
    if (firstPacket && ServerMajorVersion >= 5) {
    if (ServerMajorVersion == 3) {
    if (ServerMajorVersion == 3) {

Fixed code:
int AppVersionQuad[4];
int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks,
    PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, void* renderContext, int drFlags) {
    RemoteAddrString = strdup(serverInfo->address);
    
    // Extract the appversion from the supplied string
    if (extractVersionQuadFromString(serverInfo->serverInfoAppVersion,
                                     AppVersionQuad) < 0) {
        Limelog("Invalid appversion string: %s\n", serverInfo->serverInfoAppVersion);
        return -1;
    }
    err = resolveHostName(serverInfo->address);
    if (AppVersionQuad[0] == 3) {
    else if (AppVersionQuad[0] == 4) {
    else if (AppVersionQuad[0] == 5) {
    LC_ASSERT(AppVersionQuad[0] >= 5);
    LC_ASSERT(AppVersionQuad[0] < 5);
    if (AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] >= 5) {
    LC_ASSERT(AppVersionQuad[0] >= 5);
    if (AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] >= 7) {
        if (AppVersionQuad[0] < 5) {
            if (AppVersionQuad[0] >= 7 && encryptedSize >= 16 + sizeof(currentAesIv)) {
    if (AppVersionQuad[0] < 5) {
    if (AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] == 3) {
        if (AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] >= 5) {
extern int AppVersionQuad[4];
int extractVersionQuadFromString(const char* string, int* quad);

typedef struct _SERVER_INFORMATION {
    // Server host name or IP address in text form
    const char* address;
    
    // Text inside 'appversion' tag in /serverinfo
    const char* serverInfoAppVersion;
    
    // Text inside 'GfeVersion' tag in /serverinfo (if present)
    const char* serverInfoGfeVersion;
} SERVER_INFORMATION, *PSERVER_INFORMATION;

// Use this function to zero the server information when allocated on the stack or heap
void LiInitializeServerInformation(PSERVER_INFORMATION serverInfo);

int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks,
    PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, void* renderContext, int drFlags);
int extractVersionQuadFromString(const char* string, int* quad) {
    char versionString[128];
    char* nextDot;
    char* nextNumber;
    int i;
    
    strcpy(versionString, string);
    nextNumber = versionString;
    
    for (i = 0; i < 4; i++) {
        if (i == 3) {
            nextDot = strchr(nextNumber, '\0');
        }
        else {
            nextDot = strchr(nextNumber, '.');
        }
        if (nextDot == NULL) {
            return -1;
        }
        
        // Cut the string off at the next dot
        *nextDot = '\0';
        
        quad[i] = atoi(nextNumber);
        
        // Move on to the next segment
        nextNumber = nextDot + 1;
    }
    
    return 0;
}

}

void LiInitializeServerInformation(PSERVER_INFORMATION serverInfo) {
    memset(serverInfo, 0, sizeof(*serverInfo));
}
    if (AppVersionQuad[0] >= 5) {
        if (AppVersionQuad[0] >= 6) {
    switch (AppVersionQuad[0]) {
    if (AppVersionQuad[0] >= 5) {
                         AppVersionQuad[0] >= 5 ? "streamid=audio/0/0" : "streamid=audio",
                         AppVersionQuad[0] >= 5 ? "streamid=video/0/0" : "streamid=video",
    if (AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] == 3) {
    else if (AppVersionQuad[0] == 4) {
    if (AppVersionQuad[0] >= 4) {
        AppVersionQuad[0] < 4 ? 47996 : 47998);
    if (firstPacket && AppVersionQuad[0] >= 5) {
    if (AppVersionQuad[0] == 3) {
    if (AppVersionQuad[0] == 3) {