Vulnerability: An out of bounds write is possible via a specially crafted packet in certain configurations of Proxygen due to improper handling of Base64 when parsing malformed binary content in Structured HTTP Headers. This issue affects versions of proxygen prior to v2019.07.22.00.
Vulnerability ID: CVE-2019-11921
CWE: CWE-787
Severity score: 6.4
Project: https://github.com/facebook/proxygen
Published: 2019-07-25
File: proxygen/lib/http/structuredheaders/StructuredHeadersUtilities.cpp
Commit message: Fix SEGV in StructuredHeaders::decodeBase64

Summary:
The existing code can potentially cause a SEGV due to an out of bounds write.

This fixes CVE-2019-11921.

Reviewed By: knekritz

Differential Revision: D12983120

fbshipit-source-id: 1d48063595c8d518fd8afcbc941de260af7e37fd
Commit author: Aman Sharma

Vulnerable version (before fix):
  */  #include "StructuredHeadersUtilities.h" #include <boost/archive/iterators/binary_from_base64.hpp> #include <boost/archive/iterators/base64_from_binary.hpp> #include <boost/archive/iterators/transform_width.hpp> #include "StructuredHeadersConstants.h"  namespace proxygen { namespace StructuredHeaders {  std::string decodeBase64(    if (encoded.size() == 0) {     // special case, to prevent an integer overflow down below.     return "";   }    using namespace boost::archive::iterators;   using b64it =     transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;    std::string decoded = std::string(b64it(std::begin(encoded)),                                     b64it(std::end(encoded)));    uint32_t numPadding = std::count(encoded.begin(), encoded.end(), '=');   decoded.erase(decoded.end() - numPadding, decoded.end());    return decoded; }  std::string encodeBase64(const std::string& input) {   using namespace boost::archive::iterators;   using b64it = base64_from_binary<transform_width<const char*, 6, 8>>;    auto data = input.data();   std::string encoded(b64it(data), b64it(data + (input.length())));   encoded.append((3 - (input.length() % 3)) % 3, '=');    return encoded; }  }

Fixed version (after fix):
  */  #include "StructuredHeadersUtilities.h" #include "StructuredHeadersConstants.h"  #include "proxygen/lib/utils/Base64.h"  namespace proxygen { namespace StructuredHeaders {  std::string decodeBase64(    if (encoded.size() == 0) {     // special case, to prevent an integer overflow down below.     return std::string();   }    int padding = 0;   for (auto it = encoded.rbegin();        padding < 2 && it != encoded.rend() && *it == '=';        ++it) {     ++padding;   }    return Base64::decode(encoded, padding); }  std::string encodeBase64(const std::string& input) {   return Base64::encode(folly::ByteRange(                             reinterpret_cast<const uint8_t*>(input.c_str()),                             input.length())); }  }

Patch:
patch: @@ -9,11 +9,10 @@
  */
 
 #include "StructuredHeadersUtilities.h"
-#include <boost/archive/iterators/binary_from_base64.hpp>
-#include <boost/archive/iterators/base64_from_binary.hpp>
-#include <boost/archive/iterators/transform_width.hpp>
 #include "StructuredHeadersConstants.h"
 
+#include "proxygen/lib/utils/Base64.h"
+
 namespace proxygen {
 namespace StructuredHeaders {
 
@@ -107,31 +106,23 @@ std::string decodeBase64(
 
   if (encoded.size() == 0) {
     // special case, to prevent an integer overflow down below.
-    return "";
+    return std::string();
   }
 
-  using namespace boost::archive::iterators;
-  using b64it =
-    transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
-
-  std::string decoded = std::string(b64it(std::begin(encoded)),
-                                    b64it(std::end(encoded)));
-
-  uint32_t numPadding = std::count(encoded.begin(), encoded.end(), '=');
-  decoded.erase(decoded.end() - numPadding, decoded.end());
+  int padding = 0;
+  for (auto it = encoded.rbegin();
+       padding < 2 && it != encoded.rend() && *it == '=';
+       ++it) {
+    ++padding;
+  }
 
-  return decoded;
+  return Base64::decode(encoded, padding);
 }
 
 std::string encodeBase64(const std::string& input) {
-  using namespace boost::archive::iterators;
-  using b64it = base64_from_binary<transform_width<const char*, 6, 8>>;
-
-  auto data = input.data();
-  std::string encoded(b64it(data), b64it(data + (input.length())));
-  encoded.append((3 - (input.length() % 3)) % 3, '=');
-
-  return encoded;
+  return Base64::encode(folly::ByteRange(
+                            reinterpret_cast<const uint8_t*>(input.c_str()),
+                            input.length()));
 }
 
 }
path: proxygen/lib/http/structuredheaders/StructuredHeadersUtilities.cpp