CVE-2019-15691 (published 2019-12-26T15:15Z)

TigerVNC version prior to 1.10.1 is vulnerable to stack use-after-return, which occurs due to incorrect usage of stack memory in ZRLEDecoder. If decoding routine would throw an exception, ZRLEDecoder may try to access stack variable, which has been already freed during the process of stack unwinding. Exploitation of this vulnerability could potentially result into remote code execution. This attack appear to be exploitable via network connectivity.

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

Weakness information:
cwe_id: CWE-672
cwe_name: Operation on a Resource after Expiration or Release
description: The product uses, accesses, or otherwise operates on a resource after that resource has been expired, released, or revoked.
extended_description: 
            
         
         

Fix information:
repo_url: https://github.com/CendioOssman/tigervnc
commit_details: repo_url: https://github.com/CendioOssman/tigervnc
author: Pierre Ossman
author_date: 2019-09-10 11:05:48+02:00
msg: Make ZlibInStream more robust against failures

Move the checks around to avoid missing cases where we might access
memory that is no longer valid. Also avoid touching the underlying
stream implicitly (e.g. via the destructor) as it might also no
longer be valid.

A malicious server could theoretically use this for remote code
execution in the client.

Issue found by Pavel Cheremushkin from Kaspersky Lab
repository: repo_url: https://github.com/CendioOssman/tigervnc
repo_name: CendioOssman/tigervnc
repo_language: C++
owner: CendioOssman
file_changes: filename: ZlibInStream.cxx
change_type: ModificationType.MODIFY
diff: @@ -52,16 +52,16 @@ int ZlibInStream::pos()
   return offset + ptr - start;
 }
 
-void ZlibInStream::removeUnderlying()
+void ZlibInStream::flushUnderlying()
 {
   ptr = end = start;
-  if (!underlying) return;
 
   while (bytesIn > 0) {
     decompress(true);
     end = start; // throw away any data
   }
-  underlying = 0;
+
+  setUnderlying(NULL, 0);
 }
 
 void ZlibInStream::reset()
@@ -90,7 +90,7 @@ void ZlibInStream::init()
 void ZlibInStream::deinit()
 {
   assert(zs != NULL);
-  removeUnderlying();
+  setUnderlying(NULL, 0);
   inflateEnd(zs);
   delete zs;
   zs = NULL;
@@ -100,8 +100,6 @@ int ZlibInStream::overrun(int itemSize, int nItems, bool wait)
 {
   if (itemSize > bufSize)
     throw Exception("ZlibInStream overrun: max itemSize exceeded");
-  if (!underlying)
-    throw Exception("ZlibInStream overrun: no underlying stream");
 
   if (end - ptr != 0)
     memmove(start, ptr, end - ptr);
@@ -127,6 +125,9 @@ int ZlibInStream::overrun(int itemSize, int nItems, bool wait)
 
 bool ZlibInStream::decompress(bool wait)
 {
+  if (!underlying)
+    throw Exception("ZlibInStream overrun: no underlying stream");
+
   zs->next_out = (U8*)end;
   zs->avail_out = start + bufSize - end;
 

programming_language: C++
filename: ZlibInStream.h
change_type: ModificationType.MODIFY
diff: @@ -38,7 +38,7 @@ namespace rdr {
     virtual ~ZlibInStream();
 
     void setUnderlying(InStream* is, int bytesIn);
-    void removeUnderlying();
+    void flushUnderlying();
     int pos();
     void reset();
 

programming_language: C++
filename: CMsgReader.cxx
change_type: ModificationType.MODIFY
diff: @@ -242,7 +242,8 @@ void CMsgReader::readExtendedClipboard(rdr::S32 len)
       num++;
     }
 
-    zis.removeUnderlying();
+    zis.flushUnderlying();
+    zis.setUnderlying(NULL, 0);
 
     handler->handleClipboardProvide(flags, lengths, buffers);
 

programming_language: C++
filename: SMsgReader.cxx
change_type: ModificationType.MODIFY
diff: @@ -293,7 +293,8 @@ void SMsgReader::readExtendedClipboard(rdr::S32 len)
       num++;
     }
 
-    zis.removeUnderlying();
+    zis.flushUnderlying();
+    zis.setUnderlying(NULL, 0);
 
     handler->handleClipboardProvide(flags, lengths, buffers);
 

programming_language: C++
filename: TightDecoder.cxx
change_type: ModificationType.MODIFY
diff: @@ -341,7 +341,8 @@ void TightDecoder::decodeRect(const Rect& r, const void* buffer,
 
     zis[streamId].readBytes(netbuf, dataSize);
 
-    zis[streamId].removeUnderlying();
+    zis[streamId].flushUnderlying();
+    zis[streamId].setUnderlying(NULL, 0);
     delete ms;
 
     bufptr = netbuf;

programming_language: C++
filename: zrleDecode.h
change_type: ModificationType.MODIFY
diff: @@ -174,7 +174,8 @@ void ZRLE_DECODE (const Rect& r, rdr::InStream* is,
     }
   }
 
-  zis->removeUnderlying();
+  zis->flushUnderlying();
+  zis->setUnderlying(NULL, 0);
 }
 
 #undef ZRLE_DECODE

programming_language: C++