Limit STVar recursion during deserialization (RIPD-1603):

Constructing deeply nested objects could allow an attacker to
cause a server to overflow its available stack.

We now enforce a 10-deep nesting limit, and signal an error
if we encounter objects that are nested deeper.

Acknowledgements:
Ripple thanks Guido Vranken for responsibly disclosing this
issues.

Bug Bounties and Responsible Disclosures:
We welcome reviews of the rippled codebase and urge reviewers
to responsibly disclose any issues that they may find. For
more on Ripple's Bug Bounty program, please visit
https://ripple.com/bug-bounty
This commit is contained in:
Howard Hinnant
2018-03-03 09:02:22 -05:00
committed by Nikolaos D. Bougalis
parent 9af994ceb4
commit 881cd4cfad
8 changed files with 1323 additions and 109 deletions

View File

@@ -721,92 +721,102 @@ static boost::optional <STObject> parseObject (
return boost::none;
}
STObject data (inName);
for (auto const& fieldName : json.getMemberNames ())
try
{
Json::Value const& value = json [fieldName];
auto const& field = SField::getField (fieldName);
STObject data (inName);
if (field == sfInvalid)
for (auto const& fieldName : json.getMemberNames ())
{
error = unknown_field (json_name, fieldName);
Json::Value const& value = json [fieldName];
auto const& field = SField::getField (fieldName);
if (field == sfInvalid)
{
error = unknown_field (json_name, fieldName);
return boost::none;
}
switch (field.fieldType)
{
// Object-style containers (which recurse).
case STI_OBJECT:
case STI_TRANSACTION:
case STI_LEDGERENTRY:
case STI_VALIDATION:
if (! value.isObject ())
{
error = not_an_object (json_name, fieldName);
return boost::none;
}
try
{
auto ret = parseObject (json_name + "." + fieldName,
value, field, depth + 1, error);
if (! ret)
return boost::none;
data.emplace_back (std::move (*ret));
}
catch (std::exception const&)
{
error = invalid_data (json_name, fieldName);
return boost::none;
}
break;
// Array-style containers (which recurse).
case STI_ARRAY:
try
{
auto array = parseArray (json_name + "." + fieldName,
value, field, depth + 1, error);
if (array == boost::none)
return boost::none;
data.emplace_back (std::move (*array));
}
catch (std::exception const&)
{
error = invalid_data (json_name, fieldName);
return boost::none;
}
break;
// Everything else (types that don't recurse).
default:
{
auto leaf =
parseLeaf (json_name, fieldName, &inName, value, error);
if (!leaf)
return boost::none;
data.emplace_back (std::move (*leaf));
}
break;
}
}
// Some inner object types have templates. Attempt to apply that.
if (data.setTypeFromSField (inName) == STObject::typeSetFail)
{
error = template_mismatch (inName);
return boost::none;
}
switch (field.fieldType)
{
return std::move (data);
// Object-style containers (which recurse).
case STI_OBJECT:
case STI_TRANSACTION:
case STI_LEDGERENTRY:
case STI_VALIDATION:
if (! value.isObjectOrNull ())
{
error = not_an_object (json_name, fieldName);
return boost::none;
}
try
{
auto ret = parseObject (json_name + "." + fieldName,
value, field, depth + 1, error);
if (! ret)
return boost::none;
data.emplace_back (std::move (*ret));
}
catch (std::exception const&)
{
error = invalid_data (json_name, fieldName);
return boost::none;
}
break;
// Array-style containers (which recurse).
case STI_ARRAY:
try
{
auto array = parseArray (json_name + "." + fieldName,
value, field, depth + 1, error);
if (array == boost::none)
return boost::none;
data.emplace_back (std::move (*array));
}
catch (std::exception const&)
{
error = invalid_data (json_name, fieldName);
return boost::none;
}
break;
// Everything else (types that don't recurse).
default:
{
auto leaf =
parseLeaf (json_name, fieldName, &inName, value, error);
if (!leaf)
return boost::none;
data.emplace_back (std::move (*leaf));
}
break;
}
}
// Some inner object types have templates. Attempt to apply that.
if (data.setTypeFromSField (inName) == STObject::typeSetFail)
catch (std::exception const&)
{
error = template_mismatch (inName);
error = invalid_data (json_name);
return boost::none;
}
return std::move (data);
}
static boost::optional <detail::STVar> parseArray (