Skip to content

Base58 decoding is done without checking that the input size is reasonable #17501

@practicalswift

Description

@practicalswift

Base58 decoding is currently done without checking that the input size is reasonable.

This can lead to excessive decoding run time if an attacker can control the base58 input being decoded.

DecodeBase58/DecodeBase58Check(…) run time sampled with varying input sizes:

  • 1 000 bytes: 1 ms
  • 10 000 bytes: 97 ms
  • 100 000 bytes: 8 865 ms (9 seconds)
  • 1 000 000 bytes: 857 440 ms (14 minutes)
  • 10 000 000 bytes: too long :)

DecodeBase58/DecodeBase58Check(…) is reachable via the RPC interface using the following code paths:

addmultisigaddress(JSONRPCRequest const&) → AddrToPubKey(CKeyStore*, std::string const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
createpsbt(JSONRPCRequest const&) → ConstructTransaction(UniValue const&, UniValue const&, UniValue const&, UniValue const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
createrawtransaction(JSONRPCRequest const&) → ConstructTransaction(UniValue const&, UniValue const&, UniValue const&, UniValue const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
createwallet(JSONRPCRequest const&) → CWallet::CreateWalletFromFile(interfaces::Chain&, WalletLocation const&, unsigned long) → CWallet::LoadWallet(bool&) → WalletBatch::LoadWallet(CWallet*) → ReadKeyValue(CWallet*, CDataStream&, CDataStream&, CWalletScanState&, std::string&, std::string&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
deriveaddresses(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeExtKey(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
deriveaddresses(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeExtPubKey(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
deriveaddresses(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
deriveaddresses(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
dumpprivkey(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
fundrawtransaction(JSONRPCRequest const&) → FundTransaction(CWallet*, CMutableTransaction&, long&, int&, UniValue) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
generatetoaddress(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
getaddressinfo(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
getdescriptorinfo(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeExtKey(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
getdescriptorinfo(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeExtPubKey(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
getdescriptorinfo(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
getdescriptorinfo(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
getreceivedbyaddress(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importaddress(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importmulti(JSONRPCRequest const&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importmulti(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeExtKey(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importmulti(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeExtPubKey(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importmulti(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importmulti(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importmulti(JSONRPCRequest const&) → ProcessImportLegacy(ImportData&, std::map<CKeyID, CPubKey, std::less<CKeyID>, std::allocator<std::pair<CKeyID const, CPubKey> > >&, std::map<CKeyID, CKey, std::less<CKeyID>, std::allocator<std::pair<CKeyID const, CKey> > >&, std::set<CScript, std::less<CScript>, std::allocator<CScript> >&, bool&, UniValue const&, std::vector<CKeyID>&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importmulti(JSONRPCRequest const&) → ProcessImportLegacy(ImportData&, std::map<CKeyID, CPubKey, std::less<CKeyID>, std::allocator<std::pair<CKeyID const, CPubKey> > >&, std::map<CKeyID, CKey, std::less<CKeyID>, std::allocator<std::pair<CKeyID const, CKey> > >&, std::set<CScript, std::less<CScript>, std::allocator<CScript> >&, bool&, UniValue const&, std::vector<CKeyID>&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importprivkey(JSONRPCRequest const&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
importwallet(JSONRPCRequest const&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
listreceivedbyaddress(JSONRPCRequest const&) → ListReceived(interfaces::Chain::Lock&, CWallet*, UniValue const&, bool) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
listreceivedbylabel(JSONRPCRequest const&) → ListReceived(interfaces::Chain::Lock&, CWallet*, UniValue const&, bool) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
listunspent(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
loadwallet(JSONRPCRequest const&) → LoadWallet(interfaces::Chain&, WalletLocation const&, std::string&, std::string&) → CWallet::CreateWalletFromFile(interfaces::Chain&, WalletLocation const&, unsigned long) → CWallet::LoadWallet(bool&) → WalletBatch::LoadWallet(CWallet*) → ReadKeyValue(CWallet*, CDataStream&, CDataStream&, CWalletScanState&, std::string&, std::string&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
scantxoutset(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeExtKey(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
scantxoutset(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeExtPubKey(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
scantxoutset(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → (anonymous namespace)::ParsePubkey(Span<char const> const&, bool, FlatSigningProvider&) → (anonymous namespace)::ParsePubkeyInner(Span<char const> const&, bool, FlatSigningProvider&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
scantxoutset(JSONRPCRequest const&) → Parse(std::string const&, FlatSigningProvider&, bool) → (anonymous namespace)::ParseScript(Span<char const>&, (anonymous namespace)::ParseScriptContext, FlatSigningProvider&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
sendmany(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
sendtoaddress(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
sethdseed(JSONRPCRequest const&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
setlabel(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
signmessage(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
signmessagewithprivkey(JSONRPCRequest const&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
signrawtransactionwithkey(JSONRPCRequest const&) → DecodeSecret(std::string const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
validateaddress(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
verifymessage(JSONRPCRequest const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
walletcreatefundedpsbt(JSONRPCRequest const&) → ConstructTransaction(UniValue const&, UniValue const&, UniValue const&, UniValue const&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
walletcreatefundedpsbt(JSONRPCRequest const&) → FundTransaction(CWallet*, CMutableTransaction&, long&, int&, UniValue) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)

Other code paths involving base58-decoding:

IsValidDestinationString(std::string const&, CChainParams const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
LoadWallet(interfaces::Chain&, std::string const&, std::string&, std::string&) → LoadWallet(interfaces::Chain&, WalletLocation const&, std::string&, std::string&) → CWallet::CreateWalletFromFile(interfaces::Chain&, WalletLocation const&, unsigned long) → CWallet::LoadWallet(bool&) → WalletBatch::LoadWallet(CWallet*) → ReadKeyValue(CWallet*, CDataStream&, CDataStream&, CWalletScanState&, std::string&, std::string&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
LoadWallets(interfaces::Chain&, std::vector<std::string, std::allocator<std::string > > const&) → CWallet::CreateWalletFromFile(interfaces::Chain&, WalletLocation const&, unsigned long) → CWallet::LoadWallet(bool&) → WalletBatch::LoadWallet(CWallet*) → ReadKeyValue(CWallet*, CDataStream&, CDataStream&, CWalletScanState&, std::string&, std::string&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)
WalletBatch::RecoverKeysOnlyFilter(void*, CDataStream, CDataStream) → ReadKeyValue(CWallet*, CDataStream&, CDataStream&, CWalletScanState&, std::string&, std::string&) → DecodeDestination(std::string const&) → (anonymous namespace)::DecodeDestination(std::string const&, CChainParams const&) → DecodeBase58Check(std::string const&, std::vector<unsigned char>&)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions