Improve loading of validator tokens (RIPD-1687):

A deliberately malformed token can cause the server to crash during
startup. This is not remotely exploitable and would require someone
with access to the configuration file of the server to make changes
and then restart the server.

Acknowledgements:
Guido Vranken for responsibly disclosing this issue.

Bug Bounties and Responsible Disclosures:
We welcome reviews of the rippled code and urge researchers to
responsibly disclose any issues they may find.

Ripple is generously sponsoring a bug bounty program for the
rippled project. For more information please visit:

    https://ripple.com/bug-bounty
This commit is contained in:
Nik Bougalis
2020-04-25 00:27:23 -07:00
parent 2827de4d63
commit 6c72d5cf7e
4 changed files with 23 additions and 38 deletions

View File

@@ -171,18 +171,11 @@ struct ValidatorToken
{ {
std::string manifest; std::string manifest;
SecretKey validationSecret; SecretKey validationSecret;
private:
ValidatorToken(std::string const& m, SecretKey const& valSecret);
public:
ValidatorToken(ValidatorToken const&) = delete;
ValidatorToken(ValidatorToken&& other) = default;
static boost::optional<ValidatorToken>
make_ValidatorToken(std::vector<std::string> const& tokenBlob);
}; };
boost::optional<ValidatorToken>
loadValidatorToken(std::vector<std::string> const& blob);
enum class ManifestDisposition { enum class ManifestDisposition {
/// Manifest is valid /// Manifest is valid
accepted = 0, accepted = 0,

View File

@@ -240,52 +240,44 @@ Manifest::getMasterSignature() const
return st.getFieldVL(sfMasterSignature); return st.getFieldVL(sfMasterSignature);
} }
ValidatorToken::ValidatorToken(std::string const& m, SecretKey const& valSecret)
: manifest(m), validationSecret(valSecret)
{
}
boost::optional<ValidatorToken> boost::optional<ValidatorToken>
ValidatorToken::make_ValidatorToken(std::vector<std::string> const& tokenBlob) loadValidatorToken(std::vector<std::string> const& blob)
{ {
try try
{ {
std::string tokenStr; std::string tokenStr;
tokenStr.reserve(std::accumulate( tokenStr.reserve(std::accumulate(
tokenBlob.cbegin(), blob.cbegin(),
tokenBlob.cend(), blob.cend(),
std::size_t(0), std::size_t(0),
[](std::size_t init, std::string const& s) { [](std::size_t init, std::string const& s) {
return init + s.size(); return init + s.size();
})); }));
for (auto const& line : tokenBlob) for (auto const& line : blob)
tokenStr += beast::rfc2616::trim(line); tokenStr += beast::rfc2616::trim(line);
tokenStr = base64_decode(tokenStr); tokenStr = base64_decode(tokenStr);
Json::Reader r; Json::Reader r;
Json::Value token; Json::Value token;
if (!r.parse(tokenStr, token))
return boost::none;
if (token.isMember("manifest") && token["manifest"].isString() && if (r.parse(tokenStr, token))
token.isMember("validation_secret_key") &&
token["validation_secret_key"].isString())
{ {
auto const ret = auto const m = token.get("manifest", Json::Value{});
strUnHex(token["validation_secret_key"].asString()); auto const k = token.get("validation_secret_key", Json::Value{});
if (!ret || ret->empty())
return boost::none;
return ValidatorToken( if (m.isString() && k.isString())
token["manifest"].asString(), {
SecretKey(Slice{ret->data(), ret->size()})); auto const key = strUnHex(k.asString());
}
else if (key && key->size() == 32)
{ return ValidatorToken{m.asString(), makeSlice(*key)};
return boost::none; }
} }
return boost::none;
} }
catch (std::exception const&) catch (std::exception const&)
{ {

View File

@@ -39,7 +39,7 @@ ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j)
if (config.exists(SECTION_VALIDATOR_TOKEN)) if (config.exists(SECTION_VALIDATOR_TOKEN))
{ {
if (auto const token = ValidatorToken::make_ValidatorToken( if (auto const token = loadValidatorToken(
config.section(SECTION_VALIDATOR_TOKEN).lines())) config.section(SECTION_VALIDATOR_TOKEN).lines()))
{ {
auto const pk = auto const pk =

View File

@@ -534,14 +534,14 @@ public:
"r7C0kw" "r7C0kw"
"2AiTzSCjIzditQ8="; "2AiTzSCjIzditQ8=";
auto const token = ValidatorToken::make_ValidatorToken(tokenBlob); auto const token = loadValidatorToken(tokenBlob);
BEAST_EXPECT(token); BEAST_EXPECT(token);
BEAST_EXPECT(token->validationSecret == *valSecret); BEAST_EXPECT(token->validationSecret == *valSecret);
BEAST_EXPECT(token->manifest == manifest); BEAST_EXPECT(token->manifest == manifest);
} }
{ {
std::vector<std::string> const badToken = {"bad token"}; std::vector<std::string> const badToken = {"bad token"};
BEAST_EXPECT(!ValidatorToken::make_ValidatorToken(badToken)); BEAST_EXPECT(!loadValidatorToken(badToken));
} }
} }