CVE-2019-11921: Out-of-bounds Write (CWE-787)
value: 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.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.8
Published: 2019-07-25T21:15Z
Repository: https://github.com/facebook/proxygen
Language: C++
Files changed: proxygen/lib/http/structuredheaders/StructuredHeadersUtilities.cpp
Commit date: 2019-07-19 16:45:50 -0700
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

Diff: diff --git a/proxygen/lib/http/structuredheaders/StructuredHeadersUtilities.cpp b/proxygen/lib/http/structuredheaders/StructuredHeadersUtilities.cpp
index 9464f3b7c..80b591e5f 100644
--- a/proxygen/lib/http/structuredheaders/StructuredHeadersUtilities.cpp
+++ b/proxygen/lib/http/structuredheaders/StructuredHeadersUtilities.cpp
@@ -7,15 +7,14 @@
  *  of patent rights can be found in the PATENTS file in the same directory.
  *
  */
 
 #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 {
 
 bool isLcAlpha(char c) {
   return c >= 0x61 && c <= 0x7A;
@@ -105,34 +104,26 @@ bool itemTypeMatchesContent(
 std::string decodeBase64(
     const std::string& encoded) {
 
   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()));
 }
 
 }
 }

Vulnerable code: #include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
    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;
  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 code: #include "proxygen/lib/utils/Base64.h"

    return std::string();
  int padding = 0;
  for (auto it = encoded.rbegin();
       padding < 2 && it != encoded.rend() && *it == '=';
       ++it) {
    ++padding;
  }
  return Base64::decode(encoded, padding);
  return Base64::encode(folly::ByteRange(
                            reinterpret_cast<const uint8_t*>(input.c_str()),
                            input.length()));